1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 13:07:46 +00:00

Merge branch 'master' into implement-more

This commit is contained in:
Sylvestre Ledru 2021-05-28 19:49:48 +02:00 committed by GitHub
commit fe42808e9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
146 changed files with 6701 additions and 4057 deletions

View file

@ -0,0 +1,26 @@
[package]
name = "uu_factor_benches"
version = "0.0.0"
authors = ["nicoo <nicoo@debian.org>"]
license = "MIT"
description = "Benchmarks for the uu_factor integer factorization tool"
homepage = "https://github.com/uutils/coreutils"
edition = "2018"
[dependencies]
uu_factor = { path = "../../../src/uu/factor" }
[dev-dependencies]
array-init = "2.0.0"
criterion = "0.3"
rand = "0.7"
rand_chacha = "0.2.2"
[[bench]]
name = "gcd"
harness = false
[[bench]]
name = "table"
harness = false

View file

@ -0,0 +1,29 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use uu_factor::numeric;
fn gcd(c: &mut Criterion) {
let inputs = {
// Deterministic RNG; use an explicitely-named RNG to guarantee stability
use rand::{RngCore, SeedableRng};
use rand_chacha::ChaCha8Rng;
const SEED: u64 = 0xa_b4d_1dea_dead_cafe;
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
std::iter::repeat_with(move || (rng.next_u64(), rng.next_u64()))
};
let mut group = c.benchmark_group("gcd");
for (n, m) in inputs.take(10) {
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_{}", n, m)),
&(n, m),
|b, &(n, m)| {
b.iter(|| numeric::gcd(n, m));
},
);
}
group.finish()
}
criterion_group!(benches, gcd);
criterion_main!(benches);

View file

@ -0,0 +1,78 @@
use array_init::array_init;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::convert::TryInto;
use uu_factor::{table::*, Factors};
fn table(c: &mut Criterion) {
#[cfg(target_os = "linux")]
check_personality();
const INPUT_SIZE: usize = 128;
assert!(
INPUT_SIZE % CHUNK_SIZE == 0,
"INPUT_SIZE ({}) is not divisible by CHUNK_SIZE ({})",
INPUT_SIZE,
CHUNK_SIZE
);
let inputs = {
// Deterministic RNG; use an explicitely-named RNG to guarantee stability
use rand::{RngCore, SeedableRng};
use rand_chacha::ChaCha8Rng;
const SEED: u64 = 0xdead_bebe_ea75_cafe;
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
std::iter::repeat_with(move || array_init::<_, _, INPUT_SIZE>(|_| rng.next_u64()))
};
let mut group = c.benchmark_group("table");
group.throughput(Throughput::Elements(INPUT_SIZE as _));
for a in inputs.take(10) {
let a_str = format!("{:?}", a);
group.bench_with_input(BenchmarkId::new("factor_chunk", &a_str), &a, |b, &a| {
b.iter(|| {
let mut n_s = a.clone();
let mut f_s: [_; INPUT_SIZE] = array_init(|_| Factors::one());
for (n_s, f_s) in n_s.chunks_mut(CHUNK_SIZE).zip(f_s.chunks_mut(CHUNK_SIZE)) {
factor_chunk(n_s.try_into().unwrap(), f_s.try_into().unwrap())
}
})
});
group.bench_with_input(BenchmarkId::new("factor", &a_str), &a, |b, &a| {
b.iter(|| {
let mut n_s = a.clone();
let mut f_s: [_; INPUT_SIZE] = array_init(|_| Factors::one());
for (n, f) in n_s.iter_mut().zip(f_s.iter_mut()) {
factor(n, f)
}
})
});
}
group.finish()
}
#[cfg(target_os = "linux")]
fn check_personality() {
use std::fs;
const ADDR_NO_RANDOMIZE: u64 = 0x0040000;
const PERSONALITY_PATH: &'static str = "/proc/self/personality";
let p_string = fs::read_to_string(PERSONALITY_PATH)
.expect(&format!("Couldn't read '{}'", PERSONALITY_PATH))
.strip_suffix("\n")
.unwrap()
.to_owned();
let personality = u64::from_str_radix(&p_string, 16).expect(&format!(
"Expected a hex value for personality, got '{:?}'",
p_string
));
if personality & ADDR_NO_RANDOMIZE == 0 {
eprintln!(
"WARNING: Benchmarking with ASLR enabled (personality is {:x}), results might not be reproducible.",
personality
);
}
}
criterion_group!(benches, table);
criterion_main!(benches);

View file

@ -98,7 +98,7 @@ fn test_wrap_bad_arg() {
.arg(wrap_param)
.arg("b")
.fails()
.stderr_only("base32: error: Invalid wrap size: b: invalid digit found in string\n");
.stderr_only("base32: Invalid wrap size: b: invalid digit found in string\n");
}
}
@ -109,7 +109,7 @@ fn test_base32_extra_operand() {
.arg("a.txt")
.arg("a.txt")
.fails()
.stderr_only("base32: error: extra operand a.txt");
.stderr_only("base32: extra operand a.txt");
}
#[test]
@ -117,5 +117,5 @@ fn test_base32_file_not_found() {
new_ucmd!()
.arg("a.txt")
.fails()
.stderr_only("base32: error: a.txt: No such file or directory");
.stderr_only("base32: a.txt: No such file or directory");
}

View file

@ -88,7 +88,7 @@ fn test_wrap_bad_arg() {
.arg(wrap_param)
.arg("b")
.fails()
.stderr_only("base64: error: Invalid wrap size: b: invalid digit found in string\n");
.stderr_only("base64: Invalid wrap size: b: invalid digit found in string\n");
}
}
@ -99,7 +99,7 @@ fn test_base64_extra_operand() {
.arg("a.txt")
.arg("a.txt")
.fails()
.stderr_only("base64: error: extra operand a.txt");
.stderr_only("base64: extra operand a.txt");
}
#[test]
@ -107,5 +107,5 @@ fn test_base64_file_not_found() {
new_ucmd!()
.arg("a.txt")
.fails()
.stderr_only("base64: error: a.txt: No such file or directory");
.stderr_only("base64: a.txt: No such file or directory");
}

View file

@ -1,4 +1,5 @@
use crate::common::util::*;
#[cfg(any(unix, target_os = "redox"))]
use std::ffi::OsStr;
#[test]
@ -108,7 +109,7 @@ fn test_no_args() {
fn test_no_args_output() {
new_ucmd!()
.fails()
.stderr_is("basename: error: missing operand\nTry 'basename --help' for more information.");
.stderr_is("basename: missing operand\nTry 'basename --help' for more information.");
}
#[test]
@ -118,11 +119,13 @@ fn test_too_many_args() {
#[test]
fn test_too_many_args_output() {
new_ucmd!().args(&["a", "b", "c"]).fails().stderr_is(
"basename: error: extra operand 'c'\nTry 'basename --help' for more information.",
);
new_ucmd!()
.args(&["a", "b", "c"])
.fails()
.stderr_is("basename: extra operand 'c'\nTry 'basename --help' for more information.");
}
#[cfg(any(unix, target_os = "redox"))]
fn test_invalid_utf8_args(os_str: &OsStr) {
let test_vec = vec![os_str.to_os_string()];
new_ucmd!().args(&test_vec).succeeds().stdout_is("fo<EFBFBD>o\n");

View file

@ -347,7 +347,13 @@ fn test_squeeze_blank_before_numbering() {
#[cfg(unix)]
fn test_dev_random() {
let mut buf = [0; 2048];
let mut proc = new_ucmd!().args(&["/dev/random"]).run_no_wait();
#[cfg(target_os = "linux")]
const DEV_RANDOM: &str = "/dev/urandom";
#[cfg(not(target_os = "linux"))]
const DEV_RANDOM: &str = "/dev/random";
let mut proc = new_ucmd!().args(&[DEV_RANDOM]).run_no_wait();
let mut proc_stdout = proc.stdout.take().unwrap();
proc_stdout.read_exact(&mut buf).unwrap();
@ -395,14 +401,14 @@ fn test_dev_full_show_all() {
#[test]
#[cfg(unix)]
#[ignore]
fn test_domain_socket() {
use std::io::prelude::*;
use std::sync::{Arc, Barrier};
use std::thread;
use tempdir::TempDir;
use unix_socket::UnixListener;
let dir = TempDir::new("unix_socket").expect("failed to create dir");
let dir = tempfile::Builder::new().prefix("unix_socket").tempdir().expect("failed to create dir");
let socket_path = dir.path().join("sock");
let listener = UnixListener::bind(&socket_path).expect("failed to create socket");

View file

@ -282,6 +282,26 @@ fn test_chmod_reference_file() {
run_single_test(&tests[0], at, ucmd);
}
#[test]
fn test_permission_denied() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.mkdir("d/");
at.mkdir("d/no-x");
at.mkdir("d/no-x/y");
scene.ucmd().arg("u=rw").arg("d/no-x").succeeds();
scene
.ucmd()
.arg("-R")
.arg("o=r")
.arg("d")
.fails()
.stderr_is("chmod: 'd/no-x/y': Permission denied");
}
#[test]
fn test_chmod_recursive() {
let _guard = UMASK_MUTEX.lock();
@ -338,7 +358,7 @@ fn test_chmod_preserve_root() {
.arg("755")
.arg("/")
.fails()
.stderr_contains(&"chmod: error: it is dangerous to operate recursively on '/'");
.stderr_contains(&"chmod: it is dangerous to operate recursively on '/'");
}
#[test]

View file

@ -21,7 +21,7 @@ fn test_enter_chroot_fails() {
assert!(result
.stderr_str()
.starts_with("chroot: error: cannot chroot to jail: Operation not permitted (os error 1)"));
.starts_with("chroot: cannot chroot to jail: Operation not permitted (os error 1)"));
}
#[test]
@ -32,7 +32,7 @@ fn test_no_such_directory() {
ucmd.arg("a")
.fails()
.stderr_is("chroot: error: cannot change root directory to `a`: no such directory");
.stderr_is("chroot: cannot change root directory to `a`: no such directory");
}
#[test]
@ -43,9 +43,7 @@ fn test_invalid_user_spec() {
let result = ucmd.arg("a").arg("--userspec=ARABA:").fails();
assert!(result
.stderr_str()
.starts_with("chroot: error: invalid userspec"));
assert!(result.stderr_str().starts_with("chroot: invalid userspec"));
}
#[test]

View file

@ -66,7 +66,7 @@ fn test_invalid_file() {
.arg(folder_name)
.fails()
.no_stdout()
.stderr_contains("cksum: error: 'asdf' No such file or directory");
.stderr_contains("cksum: 'asdf' No such file or directory");
// Then check when the file is of an invalid type
at.mkdir(folder_name);
@ -74,7 +74,7 @@ fn test_invalid_file() {
.arg(folder_name)
.fails()
.no_stdout()
.stderr_contains("cksum: error: 'asdf' Is a directory");
.stderr_contains("cksum: 'asdf' Is a directory");
}
// Make sure crc is correct for files larger than 32 bytes

View file

@ -214,8 +214,8 @@ fn test_cp_arg_symlink() {
fn test_cp_arg_no_clobber() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--no-clobber")
.arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("--no-clobber")
.succeeds();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
@ -305,7 +305,39 @@ fn test_cp_arg_backup() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--backup")
.arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("-b")
.succeeds();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_arg_backup_with_other_args() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("-vbL")
.succeeds();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_arg_backup_arg_first() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds();
@ -321,6 +353,7 @@ fn test_cp_arg_suffix() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg("-b")
.arg("--suffix")
.arg(".bak")
.arg(TEST_HOW_ARE_YOU_SOURCE)
@ -333,6 +366,207 @@ fn test_cp_arg_suffix() {
);
}
#[test]
fn test_cp_custom_backup_suffix_via_env() {
let (at, mut ucmd) = at_and_ucmd!();
let suffix = "super-suffix-of-the-century";
ucmd.arg("-b")
.env("SIMPLE_BACKUP_SUFFIX", suffix)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}{}", TEST_HOW_ARE_YOU_SOURCE, suffix)),
"How are you?\n"
);
}
#[test]
fn test_cp_backup_numbered_with_t() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=t")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_backup_numbered() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=numbered")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_backup_existing() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=existing")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_backup_nil() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=nil")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_numbered_if_existing_backup_existing() {
let (at, mut ucmd) = at_and_ucmd!();
let existing_backup = &*format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE);
at.touch(existing_backup);
ucmd.arg("--backup=existing")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert!(at.file_exists(TEST_HOW_ARE_YOU_SOURCE));
assert!(at.file_exists(existing_backup));
assert_eq!(
at.read(&*format!("{}.~2~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_numbered_if_existing_backup_nil() {
let (at, mut ucmd) = at_and_ucmd!();
let existing_backup = &*format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE);
at.touch(existing_backup);
ucmd.arg("--backup=nil")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert!(at.file_exists(TEST_HOW_ARE_YOU_SOURCE));
assert!(at.file_exists(existing_backup));
assert_eq!(
at.read(&*format!("{}.~2~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_backup_simple() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=simple")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_backup_never() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=never")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!(
at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
"How are you?\n"
);
}
#[test]
fn test_cp_backup_none() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=none")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert!(!at.file_exists(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)));
}
#[test]
fn test_cp_backup_off() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup=off")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.succeeds()
.no_stderr();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert!(!at.file_exists(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)));
}
#[test]
fn test_cp_backup_no_clobber_conflicting_options() {
let (_, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup")
.arg("--no-clobber")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.fails()
.stderr_is("cp: options --backup and --no-clobber are mutually exclusive\nTry 'cp --help' for more information.");
}
#[test]
fn test_cp_deref_conflicting_options() {
new_ucmd!()

View file

@ -208,7 +208,7 @@ fn test_up_to_match_repeat_over() {
ucmd.args(&["numbers50.txt", "/9$/", "{50}"])
.fails()
.stdout_is("16\n29\n30\n30\n30\n6\n")
.stderr_is("csplit: error: '/9$/': match not found on repetition 5");
.stderr_is("csplit: '/9$/': match not found on repetition 5");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -219,7 +219,7 @@ fn test_up_to_match_repeat_over() {
ucmd.args(&["numbers50.txt", "/9$/", "{50}", "-k"])
.fails()
.stdout_is("16\n29\n30\n30\n30\n6\n")
.stderr_is("csplit: error: '/9$/': match not found on repetition 5");
.stderr_is("csplit: '/9$/': match not found on repetition 5");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -365,7 +365,7 @@ fn test_option_keep() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["-k", "numbers50.txt", "/20/", "/nope/"])
.fails()
.stderr_is("csplit: error: '/nope/': match not found")
.stderr_is("csplit: '/nope/': match not found")
.stdout_is("48\n93\n");
let count = glob(&at.plus_as_string("xx*"))
@ -541,7 +541,7 @@ fn test_up_to_match_context_overflow() {
ucmd.args(&["numbers50.txt", "/45/+10"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/45/+10': line number out of range");
.stderr_is("csplit: '/45/+10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -552,7 +552,7 @@ fn test_up_to_match_context_overflow() {
ucmd.args(&["numbers50.txt", "/45/+10", "-k"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/45/+10': line number out of range");
.stderr_is("csplit: '/45/+10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -567,7 +567,7 @@ fn test_skip_to_match_context_underflow() {
ucmd.args(&["numbers50.txt", "%5%-10"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '%5%-10': line number out of range");
.stderr_is("csplit: '%5%-10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -578,7 +578,7 @@ fn test_skip_to_match_context_underflow() {
ucmd.args(&["numbers50.txt", "%5%-10", "-k"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '%5%-10': line number out of range");
.stderr_is("csplit: '%5%-10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -592,7 +592,7 @@ fn test_skip_to_match_context_overflow() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%45%+10"])
.fails()
.stderr_only("csplit: error: '%45%+10': line number out of range");
.stderr_only("csplit: '%45%+10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -602,7 +602,7 @@ fn test_skip_to_match_context_overflow() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%45%+10", "-k"])
.fails()
.stderr_only("csplit: error: '%45%+10': line number out of range");
.stderr_only("csplit: '%45%+10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -616,7 +616,7 @@ fn test_up_to_no_match1() {
ucmd.args(&["numbers50.txt", "/4/", "/nope/"])
.fails()
.stdout_is("6\n135\n")
.stderr_is("csplit: error: '/nope/': match not found");
.stderr_is("csplit: '/nope/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -627,7 +627,7 @@ fn test_up_to_no_match1() {
ucmd.args(&["numbers50.txt", "/4/", "/nope/", "-k"])
.fails()
.stdout_is("6\n135\n")
.stderr_is("csplit: error: '/nope/': match not found");
.stderr_is("csplit: '/nope/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -643,7 +643,7 @@ fn test_up_to_no_match2() {
ucmd.args(&["numbers50.txt", "/4/", "/nope/", "{50}"])
.fails()
.stdout_is("6\n135\n")
.stderr_is("csplit: error: '/nope/': match not found");
.stderr_is("csplit: '/nope/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -654,7 +654,7 @@ fn test_up_to_no_match2() {
ucmd.args(&["numbers50.txt", "/4/", "/nope/", "{50}", "-k"])
.fails()
.stdout_is("6\n135\n")
.stderr_is("csplit: error: '/nope/': match not found");
.stderr_is("csplit: '/nope/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -670,7 +670,7 @@ fn test_up_to_no_match3() {
ucmd.args(&["numbers50.txt", "/0$/", "{50}"])
.fails()
.stdout_is("18\n30\n30\n30\n30\n3\n")
.stderr_is("csplit: error: '/0$/': match not found on repetition 5");
.stderr_is("csplit: '/0$/': match not found on repetition 5");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -681,7 +681,7 @@ fn test_up_to_no_match3() {
ucmd.args(&["numbers50.txt", "/0$/", "{50}", "-k"])
.fails()
.stdout_is("18\n30\n30\n30\n30\n3\n")
.stderr_is("csplit: error: '/0$/': match not found on repetition 5");
.stderr_is("csplit: '/0$/': match not found on repetition 5");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -701,7 +701,7 @@ fn test_up_to_no_match4() {
ucmd.args(&["numbers50.txt", "/nope/", "/4/"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/nope/': match not found");
.stderr_is("csplit: '/nope/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -712,7 +712,7 @@ fn test_up_to_no_match4() {
ucmd.args(&["numbers50.txt", "/nope/", "/4/", "-k"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/nope/': match not found");
.stderr_is("csplit: '/nope/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -741,7 +741,7 @@ fn test_up_to_no_match6() {
ucmd.args(&["numbers50.txt", "/nope/-5"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/nope/-5': match not found");
.stderr_is("csplit: '/nope/-5': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -752,7 +752,7 @@ fn test_up_to_no_match6() {
ucmd.args(&["numbers50.txt", "/nope/-5", "-k"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/nope/-5': match not found");
.stderr_is("csplit: '/nope/-5': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -767,7 +767,7 @@ fn test_up_to_no_match7() {
ucmd.args(&["numbers50.txt", "/nope/+5"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/nope/+5': match not found");
.stderr_is("csplit: '/nope/+5': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -778,7 +778,7 @@ fn test_up_to_no_match7() {
ucmd.args(&["numbers50.txt", "/nope/+5", "-k"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/nope/+5': match not found");
.stderr_is("csplit: '/nope/+5': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -792,7 +792,7 @@ fn test_skip_to_no_match1() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%nope%"])
.fails()
.stderr_only("csplit: error: '%nope%': match not found");
.stderr_only("csplit: '%nope%': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -805,7 +805,7 @@ fn test_skip_to_no_match2() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%nope%", "{50}"])
.fails()
.stderr_only("csplit: error: '%nope%': match not found");
.stderr_only("csplit: '%nope%': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -818,7 +818,7 @@ fn test_skip_to_no_match3() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%0$%", "{50}"])
.fails()
.stderr_only("csplit: error: '%0$%': match not found on repetition 5");
.stderr_only("csplit: '%0$%': match not found on repetition 5");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -831,7 +831,7 @@ fn test_skip_to_no_match4() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%nope%", "/4/"])
.fails()
.stderr_only("csplit: error: '%nope%': match not found");
.stderr_only("csplit: '%nope%': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -858,7 +858,7 @@ fn test_skip_to_no_match6() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%nope%-5"])
.fails()
.stderr_only("csplit: error: '%nope%-5': match not found");
.stderr_only("csplit: '%nope%-5': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -871,7 +871,7 @@ fn test_skip_to_no_match7() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%nope%+5"])
.fails()
.stderr_only("csplit: error: '%nope%+5': match not found");
.stderr_only("csplit: '%nope%+5': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -884,7 +884,7 @@ fn test_no_match() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "%nope%"])
.fails()
.stderr_only("csplit: error: '%nope%': match not found");
.stderr_only("csplit: '%nope%': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -895,7 +895,7 @@ fn test_no_match() {
ucmd.args(&["numbers50.txt", "/nope/"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '/nope/': match not found");
.stderr_is("csplit: '/nope/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -992,7 +992,7 @@ fn test_too_small_linenum_repeat() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/20/", "10", "{*}"])
.fails()
.stderr_is("csplit: error: '10': line number out of range on repetition 5")
.stderr_is("csplit: '10': line number out of range on repetition 5")
.stdout_is("48\n0\n0\n30\n30\n30\n3\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1003,7 +1003,7 @@ fn test_too_small_linenum_repeat() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/20/", "10", "{*}", "-k"])
.fails()
.stderr_is("csplit: error: '10': line number out of range on repetition 5")
.stderr_is("csplit: '10': line number out of range on repetition 5")
.stdout_is("48\n0\n0\n30\n30\n30\n3\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1025,7 +1025,7 @@ fn test_linenum_out_of_range1() {
ucmd.args(&["numbers50.txt", "100"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '100': line number out of range");
.stderr_is("csplit: '100': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1036,7 +1036,7 @@ fn test_linenum_out_of_range1() {
ucmd.args(&["numbers50.txt", "100", "-k"])
.fails()
.stdout_is("141\n")
.stderr_is("csplit: error: '100': line number out of range");
.stderr_is("csplit: '100': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1051,7 +1051,7 @@ fn test_linenum_out_of_range2() {
ucmd.args(&["numbers50.txt", "10", "100"])
.fails()
.stdout_is("18\n123\n")
.stderr_is("csplit: error: '100': line number out of range");
.stderr_is("csplit: '100': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1062,7 +1062,7 @@ fn test_linenum_out_of_range2() {
ucmd.args(&["numbers50.txt", "10", "100", "-k"])
.fails()
.stdout_is("18\n123\n")
.stderr_is("csplit: error: '100': line number out of range");
.stderr_is("csplit: '100': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1078,7 +1078,7 @@ fn test_linenum_out_of_range3() {
ucmd.args(&["numbers50.txt", "40", "{2}"])
.fails()
.stdout_is("108\n33\n")
.stderr_is("csplit: error: '40': line number out of range on repetition 1");
.stderr_is("csplit: '40': line number out of range on repetition 1");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1089,7 +1089,7 @@ fn test_linenum_out_of_range3() {
ucmd.args(&["numbers50.txt", "40", "{2}", "-k"])
.fails()
.stdout_is("108\n33\n")
.stderr_is("csplit: error: '40': line number out of range on repetition 1");
.stderr_is("csplit: '40': line number out of range on repetition 1");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1105,7 +1105,7 @@ fn test_linenum_out_of_range4() {
ucmd.args(&["numbers50.txt", "40", "{*}"])
.fails()
.stdout_is("108\n33\n")
.stderr_is("csplit: error: '40': line number out of range on repetition 1");
.stderr_is("csplit: '40': line number out of range on repetition 1");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1116,7 +1116,7 @@ fn test_linenum_out_of_range4() {
ucmd.args(&["numbers50.txt", "40", "{*}", "-k"])
.fails()
.stdout_is("108\n33\n")
.stderr_is("csplit: error: '40': line number out of range on repetition 1");
.stderr_is("csplit: '40': line number out of range on repetition 1");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1132,7 +1132,7 @@ fn test_skip_to_match_negative_offset_before_a_match() {
ucmd.args(&["numbers50.txt", "/20/-10", "/15/"])
.fails()
.stdout_is("18\n123\n")
.stderr_is("csplit: error: '/15/': match not found");
.stderr_is("csplit: '/15/': match not found");
let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
@ -1177,7 +1177,7 @@ fn test_corner_case2() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/10/-5", "/10/"])
.fails()
.stderr_is("csplit: error: '/10/': match not found")
.stderr_is("csplit: '/10/': match not found")
.stdout_is("8\n133\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1191,7 +1191,7 @@ fn test_corner_case3() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/15/-3", "14", "/15/"])
.fails()
.stderr_is("csplit: error: '/15/': match not found")
.stderr_is("csplit: '/15/': match not found")
.stdout_is("24\n6\n111\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1223,7 +1223,7 @@ fn test_up_to_match_context_underflow() {
ucmd.args(&["numbers50.txt", "/5/-10"])
.fails()
.stdout_is("0\n141\n")
.stderr_is("csplit: error: '/5/-10': line number out of range");
.stderr_is("csplit: '/5/-10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -1234,7 +1234,7 @@ fn test_up_to_match_context_underflow() {
ucmd.args(&["numbers50.txt", "/5/-10", "-k"])
.fails()
.stdout_is("0\n141\n")
.stderr_is("csplit: error: '/5/-10': line number out of range");
.stderr_is("csplit: '/5/-10': line number out of range");
let count = glob(&at.plus_as_string("xx*"))
.expect("counting splits")
@ -1251,7 +1251,7 @@ fn test_linenum_range_with_up_to_match1() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "10", "/12/-5"])
.fails()
.stderr_is("csplit: error: '/12/-5': line number out of range")
.stderr_is("csplit: '/12/-5': line number out of range")
.stdout_is("18\n0\n123\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1262,7 +1262,7 @@ fn test_linenum_range_with_up_to_match1() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "10", "/12/-5", "-k"])
.fails()
.stderr_is("csplit: error: '/12/-5': line number out of range")
.stderr_is("csplit: '/12/-5': line number out of range")
.stdout_is("18\n0\n123\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1281,7 +1281,7 @@ fn test_linenum_range_with_up_to_match2() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "10", "/12/-15"])
.fails()
.stderr_is("csplit: error: '/12/-15': line number out of range")
.stderr_is("csplit: '/12/-15': line number out of range")
.stdout_is("18\n0\n123\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1292,7 +1292,7 @@ fn test_linenum_range_with_up_to_match2() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "10", "/12/-15", "-k"])
.fails()
.stderr_is("csplit: error: '/12/-15': line number out of range")
.stderr_is("csplit: '/12/-15': line number out of range")
.stdout_is("18\n0\n123\n");
let count = glob(&at.plus_as_string("xx*"))
@ -1310,7 +1310,7 @@ fn test_linenum_range_with_up_to_match3() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "10", "/10/", "-k"])
.fails()
.stderr_is("csplit: error: '/10/': match not found")
.stderr_is("csplit: '/10/': match not found")
.stdout_is("18\n123\n");
let count = glob(&at.plus_as_string("xx*"))

View file

@ -149,11 +149,11 @@ fn test_directory_and_no_such_file() {
ucmd.arg("-b1")
.arg("some")
.run()
.stderr_is("cut: error: some: Is a directory\n");
.stderr_is("cut: some: Is a directory\n");
new_ucmd!()
.arg("-b1")
.arg("some")
.run()
.stderr_is("cut: error: some: No such file or directory\n");
.stderr_is("cut: some: No such file or directory\n");
}

View file

@ -104,6 +104,29 @@ fn test_date_format_full_day() {
.stdout_matches(&re);
}
#[test]
fn test_date_nano_seconds() {
// %N nanoseconds (000000000..999999999)
let re = Regex::new(r"^\d{1,9}$").unwrap();
new_ucmd!().arg("+%N").succeeds().stdout_matches(&re);
}
#[test]
fn test_date_format_without_plus() {
// [+FORMAT]
new_ucmd!()
.arg("%s")
.fails()
.stderr_contains("date: invalid date %s")
.code_is(1);
}
#[test]
fn test_date_format_literal() {
new_ucmd!().arg("+%%s").succeeds().stdout_is("%s\n");
new_ucmd!().arg("+%%N").succeeds().stdout_is("%N\n");
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
fn test_date_set_valid() {

View file

@ -27,7 +27,7 @@ fn test_df_output() {
stdout_only("Filesystem Size Used Available Capacity Use% Mounted on \n");
} else {
new_ucmd!().arg("-H").arg("-total").succeeds().stdout_only(
"Filesystem Size Used Available Use% Mounted on \n"
"Filesystem Size Used Available Use% Mounted on \n",
);
}
}

View file

@ -53,7 +53,15 @@ fn _du_basics_subdir(s: &str) {
fn _du_basics_subdir(s: &str) {
assert_eq!(s, "0\tsubdir/deeper\n");
}
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
#[cfg(target_os = "freebsd")]
fn _du_basics_subdir(s: &str) {
assert_eq!(s, "8\tsubdir/deeper\n");
}
#[cfg(all(
not(target_vendor = "apple"),
not(target_os = "windows"),
not(target_os = "freebsd")
))]
fn _du_basics_subdir(s: &str) {
// MS-WSL linux has altered expected output
if !uucore::os::is_wsl_1() {
@ -68,7 +76,7 @@ fn test_du_basics_bad_name() {
new_ucmd!()
.arg("bad_name")
.succeeds() // TODO: replace with ".fails()" once `du` is fixed
.stderr_only("du: error: bad_name: No such file or directory\n");
.stderr_only("du: bad_name: No such file or directory\n");
}
#[test]
@ -100,7 +108,15 @@ fn _du_soft_link(s: &str) {
fn _du_soft_link(s: &str) {
assert_eq!(s, "8\tsubdir/links\n");
}
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
#[cfg(target_os = "freebsd")]
fn _du_soft_link(s: &str) {
assert_eq!(s, "16\tsubdir/links\n");
}
#[cfg(all(
not(target_vendor = "apple"),
not(target_os = "windows"),
not(target_os = "freebsd")
))]
fn _du_soft_link(s: &str) {
// MS-WSL linux has altered expected output
if !uucore::os::is_wsl_1() {
@ -113,11 +129,9 @@ fn _du_soft_link(s: &str) {
#[test]
fn test_du_hard_link() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result_ln = scene.cmd("ln").arg(SUB_FILE).arg(SUB_LINK).run();
if !result_ln.succeeded() {
scene.ccmd("ln").arg(SUB_FILE).arg(SUB_LINK).succeeds();
}
at.hard_link(SUB_FILE, SUB_LINK);
let result = scene.ucmd().arg(SUB_DIR_LINKS).succeeds();
@ -141,7 +155,15 @@ fn _du_hard_link(s: &str) {
fn _du_hard_link(s: &str) {
assert_eq!(s, "8\tsubdir/links\n")
}
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
#[cfg(target_os = "freebsd")]
fn _du_hard_link(s: &str) {
assert_eq!(s, "16\tsubdir/links\n")
}
#[cfg(all(
not(target_vendor = "apple"),
not(target_os = "windows"),
not(target_os = "freebsd")
))]
fn _du_hard_link(s: &str) {
// MS-WSL linux has altered expected output
if !uucore::os::is_wsl_1() {
@ -181,7 +203,15 @@ fn _du_d_flag(s: &str) {
fn _du_d_flag(s: &str) {
assert_eq!(s, "8\t./subdir\n8\t./\n");
}
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
#[cfg(target_os = "freebsd")]
fn _du_d_flag(s: &str) {
assert_eq!(s, "28\t./subdir\n36\t./\n");
}
#[cfg(all(
not(target_vendor = "apple"),
not(target_os = "windows"),
not(target_os = "freebsd")
))]
fn _du_d_flag(s: &str) {
// MS-WSL linux has altered expected output
if !uucore::os::is_wsl_1() {

View file

@ -17,11 +17,11 @@ fn test_complex_arithmetic() {
.args(&["9223372036854775807", "+", "9223372036854775807"])
.run();
run.stdout_is("");
run.stderr_is("expr: error: +: Numerical result out of range");
run.stderr_is("expr: +: Numerical result out of range");
let run = new_ucmd!().args(&["9", "/", "0"]).run();
run.stdout_is("");
run.stderr_is("expr: error: division by zero");
run.stderr_is("expr: division by zero");
}
#[test]
@ -54,3 +54,32 @@ fn test_and() {
new_ucmd!().args(&["", "&", "1"]).run().stdout_is("0\n");
}
#[test]
fn test_substr() {
new_ucmd!()
.args(&["substr", "abc", "1", "1"])
.succeeds()
.stdout_only("a\n");
}
#[test]
fn test_invalid_substr() {
new_ucmd!()
.args(&["substr", "abc", "0", "1"])
.fails()
.status_code(1)
.stdout_only("\n");
new_ucmd!()
.args(&["substr", "abc", &(std::usize::MAX.to_string() + "0"), "1"])
.fails()
.status_code(1)
.stdout_only("\n");
new_ucmd!()
.args(&["substr", "abc", "0", &(std::usize::MAX.to_string() + "0")])
.fails()
.status_code(1)
.stdout_only("\n");
}

View file

@ -30,21 +30,19 @@ fn test_fmt_w_too_big() {
//.stdout_is_fixture("call_graph.expected");
assert_eq!(
result.stderr_str().trim(),
"fmt: error: invalid width: '2501': Numerical result out of range"
"fmt: invalid width: '2501': Numerical result out of range"
);
}
/* #[test]
Fails for now, see https://github.com/uutils/coreutils/issues/1501
#[test]
fn test_fmt_w() {
let result = new_ucmd!()
.arg("-w")
.arg("10")
.arg("one-word-per-line.txt")
.run();
//.stdout_is_fixture("call_graph.expected");
assert_eq!(result.stdout_str().trim(), "this is a file with one word per line");
//.stdout_is_fixture("call_graph.expected");
assert_eq!(
result.stdout_str().trim(),
"this is\na file\nwith one\nword per\nline"
);
}
fmt is pretty broken in general, needs more works to have more tests
*/

View file

@ -129,6 +129,15 @@ fn test_zero_terminated_syntax_2() {
.stdout_is("x\0y");
}
#[test]
fn test_zero_terminated_negative_lines() {
new_ucmd!()
.args(&["-z", "-n", "-1"])
.pipe_in("x\0y\0z\0")
.run()
.stdout_is("x\0y\0");
}
#[test]
fn test_negative_byte_syntax() {
new_ucmd!()
@ -162,6 +171,18 @@ fn test_no_such_file_or_directory() {
.stderr_contains("cannot open 'no_such_file.toml' for reading: No such file or directory");
}
/// Test that each non-existent files gets its own error message printed.
#[test]
fn test_multiple_nonexistent_files() {
new_ucmd!()
.args(&["bogusfile1", "bogusfile2"])
.fails()
.stdout_does_not_contain("==> bogusfile1 <==")
.stderr_contains("cannot open 'bogusfile1' for reading: No such file or directory")
.stdout_does_not_contain("==> bogusfile2 <==")
.stderr_contains("cannot open 'bogusfile2' for reading: No such file or directory");
}
// there was a bug not caught by previous tests
// where for negative n > 3, the total amount of lines
// was correct, but it would eat from the second line
@ -196,3 +217,28 @@ fn test_obsolete_extras() {
.succeeds()
.stdout_is("==> standard input <==\n1\02\03\04\05\0");
}
#[test]
fn test_multiple_files() {
new_ucmd!()
.args(&["emptyfile.txt", "emptyfile.txt"])
.succeeds()
.stdout_is("==> emptyfile.txt <==\n\n==> emptyfile.txt <==\n");
}
#[test]
fn test_multiple_files_with_stdin() {
new_ucmd!()
.args(&["emptyfile.txt", "-", "emptyfile.txt"])
.pipe_in("hello\n")
.succeeds()
.stdout_is(
"==> emptyfile.txt <==
==> standard input <==
hello
==> emptyfile.txt <==
",
);
}

View file

@ -7,7 +7,7 @@ use crate::common::util::*;
// From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)"
// stderr: "whoami: cannot find name for user ID 1001"
// Maybe: "adduser --uid 1001 username" can put things right?
// stderr = id: error: Could not find uid 1001: No such id: 1001
// stderr = id: Could not find uid 1001: No such id: 1001
fn skipping_test_is_okay(result: &CmdResult, needle: &str) -> bool {
if !result.succeeded() {
println!("result.stdout = {}", result.stdout_str());

View file

@ -301,7 +301,7 @@ fn test_install_target_new_file_with_group() {
.arg(format!("{}/{}", dir, file))
.run();
if is_ci() && result.stderr_str().contains("error: no such group:") {
if is_ci() && result.stderr_str().contains("no such group:") {
// In the CI, some server are failing to return the group.
// As seems to be a configuration issue, ignoring it
return;
@ -328,7 +328,7 @@ fn test_install_target_new_file_with_owner() {
.arg(format!("{}/{}", dir, file))
.run();
if is_ci() && result.stderr_str().contains("error: no such user:") {
if is_ci() && result.stderr_str().contains("no such user:") {
// In the CI, some server are failing to return the user id.
// As seems to be a configuration issue, ignoring it
return;

View file

@ -148,7 +148,7 @@ fn multitab_character() {
.arg("-t")
.arg("э")
.fails()
.stderr_is("join: error: multi-character tab э");
.stderr_is("join: multi-character tab э");
}
#[test]
@ -211,7 +211,7 @@ fn empty_format() {
.arg("-o")
.arg("")
.fails()
.stderr_is("join: error: invalid file number in field spec: ''");
.stderr_is("join: invalid file number in field spec: ''");
}
#[test]

View file

@ -23,7 +23,7 @@ fn test_link_no_circular() {
ucmd.args(&[link, link])
.fails()
.stderr_is("link: error: No such file or directory (os error 2)\n");
.stderr_is("link: No such file or directory (os error 2)\n");
assert!(!at.file_exists(link));
}
@ -35,7 +35,7 @@ fn test_link_nonexistent_file() {
ucmd.args(&[file, link])
.fails()
.stderr_is("link: error: No such file or directory (os error 2)\n");
.stderr_is("link: No such file or directory (os error 2)\n");
assert!(!at.file_exists(file));
assert!(!at.file_exists(link));
}

View file

@ -409,7 +409,7 @@ fn test_symlink_missing_destination() {
at.touch(file);
ucmd.args(&["-s", "-T", file]).fails().stderr_is(format!(
"ln: error: missing destination file operand after '{}'",
"ln: missing destination file operand after '{}'",
file
));
}

View file

@ -9,7 +9,7 @@ fn test_normal() {
for (key, value) in env::vars() {
println!("{}: {}", key, value);
}
if (is_ci() || uucore::os::is_wsl_1()) && result.stderr_str().contains("error: no login name") {
if (is_ci() || uucore::os::is_wsl_1()) && result.stderr_str().contains("no login name") {
// ToDO: investigate WSL failure
// In the CI, some server are failing to return logname.
// As seems to be a configuration issue, ignoring it

View file

@ -5,6 +5,7 @@ use crate::common::util::*;
extern crate regex;
use self::regex::Regex;
use std::collections::HashMap;
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;
@ -18,9 +19,7 @@ use std::path::PathBuf;
#[cfg(not(windows))]
use std::sync::Mutex;
#[cfg(not(windows))]
extern crate tempdir;
#[cfg(not(windows))]
use self::tempdir::TempDir;
extern crate tempfile;
#[cfg(not(windows))]
lazy_static! {
@ -166,7 +165,7 @@ fn test_ls_width() {
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.fails()
.stderr_only("ls: error: invalid line width: 1a");
.stderr_only("ls: invalid line width: 1a");
}
}
@ -308,6 +307,50 @@ fn test_ls_long() {
}
}
#[test]
fn test_ls_long_total_size() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-long"));
at.append("test-long", "1");
at.touch(&at.plus_as_string("test-long2"));
at.append("test-long2", "2");
let expected_prints: HashMap<_, _> = if cfg!(unix) {
[
("long_vanilla", "total 8"),
("long_human_readable", "total 8.0K"),
("long_si", "total 8.2k"),
]
.iter()
.cloned()
.collect()
} else {
[
("long_vanilla", "total 2"),
("long_human_readable", "total 2"),
("long_si", "total 2"),
]
.iter()
.cloned()
.collect()
};
for arg in &["-l", "--long", "--format=long", "--format=verbose"] {
let result = scene.ucmd().arg(arg).succeeds();
result.stdout_contains(expected_prints["long_vanilla"]);
for arg2 in &["-h", "--human-readable", "--si"] {
let result = scene.ucmd().arg(arg).arg(arg2).succeeds();
result.stdout_contains(if *arg2 == "--si" {
expected_prints["long_si"]
} else {
expected_prints["long_human_readable"]
});
}
}
}
#[test]
fn test_ls_long_formats() {
let scene = TestScenario::new(util_name!());
@ -640,7 +683,7 @@ fn test_ls_styles() {
at.touch("test");
let re_full = Regex::new(
r"[a-z-]* \d* \w* \w* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* \+\d{4} test\n",
r"[a-z-]* \d* \w* \w* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* (\+|\-)\d{4} test\n",
)
.unwrap();
let re_long =
@ -830,7 +873,7 @@ fn test_ls_files_dirs() {
.ucmd()
.arg("doesntexist")
.fails()
.stderr_contains(&"error: 'doesntexist': No such file or directory");
.stderr_contains(&"'doesntexist': No such file or directory");
// One exists, the other doesn't
scene
@ -838,7 +881,7 @@ fn test_ls_files_dirs() {
.arg("a")
.arg("doesntexist")
.fails()
.stderr_contains(&"error: 'doesntexist': No such file or directory")
.stderr_contains(&"'doesntexist': No such file or directory")
.stdout_contains(&"a:");
}
@ -1042,7 +1085,7 @@ fn test_ls_indicator_style() {
{
use self::unix_socket::UnixListener;
let dir = TempDir::new("unix_socket").expect("failed to create dir");
let dir = tempfile::Builder::new().prefix("unix_socket").tempdir().expect("failed to create dir");
let socket_path = dir.path().join("sock");
let _listener = UnixListener::bind(&socket_path).expect("failed to create socket");
@ -1921,3 +1964,48 @@ fn test_ls_sort_extension() {
expected,
);
}
#[test]
fn test_ls_path() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file1 = "file1";
let file2 = "file2";
let dir = "dir";
let path = &format!("{}/{}", dir, file2);
at.mkdir(dir);
at.touch(file1);
at.touch(path);
let expected_stdout = &format!("{}\n", path);
scene.ucmd().arg(path).run().stdout_is(expected_stdout);
let expected_stdout = &format!("./{}\n", path);
scene
.ucmd()
.arg(format!("./{}", path))
.run()
.stdout_is(expected_stdout);
let abs_path = format!("{}/{}", at.as_string(), path);
let expected_stdout = if cfg!(windows) {
format!("\'{}\'\n", abs_path)
} else {
format!("{}\n", abs_path)
};
scene.ucmd().arg(&abs_path).run().stdout_is(expected_stdout);
let expected_stdout = if cfg!(windows) {
format!("{} {}\n", path, file1)
} else {
format!("{}\n{}\n", path, file1)
};
scene
.ucmd()
.arg(file1)
.arg(path)
.run()
.stdout_is(expected_stdout);
}

View file

@ -2,9 +2,7 @@ use crate::common::util::*;
#[test]
fn test_create_fifo_missing_operand() {
new_ucmd!()
.fails()
.stderr_is("mkfifo: error: missing operand");
new_ucmd!().fails().stderr_is("mkfifo: missing operand");
}
#[test]
@ -43,5 +41,5 @@ fn test_create_one_fifo_already_exists() {
.arg("abcdef")
.arg("abcdef")
.fails()
.stderr_is("mkfifo: error: cannot create fifo 'abcdef': File exists");
.stderr_is("mkfifo: cannot create fifo 'abcdef': File exists");
}

View file

@ -1 +1,124 @@
// ToDO: add tests
use crate::common::util::*;
#[cfg(not(windows))]
#[test]
fn test_mknod_help() {
new_ucmd!()
.arg("--help")
.succeeds()
.no_stderr()
.stdout_contains("USAGE:");
}
#[test]
#[cfg(not(windows))]
fn test_mknod_version() {
assert!(new_ucmd!()
.arg("--version")
.succeeds()
.no_stderr()
.stdout_str()
.starts_with("mknod"));
}
#[test]
#[cfg(not(windows))]
fn test_mknod_fifo_default_writable() {
let ts = TestScenario::new(util_name!());
ts.ucmd().arg("test_file").arg("p").succeeds();
assert!(ts.fixtures.is_fifo("test_file"));
assert!(!ts.fixtures.metadata("test_file").permissions().readonly());
}
#[test]
#[cfg(not(windows))]
fn test_mknod_fifo_mnemonic_usage() {
let ts = TestScenario::new(util_name!());
ts.ucmd().arg("test_file").arg("pipe").succeeds();
assert!(ts.fixtures.is_fifo("test_file"));
}
#[test]
#[cfg(not(windows))]
fn test_mknod_fifo_read_only() {
let ts = TestScenario::new(util_name!());
ts.ucmd()
.arg("-m")
.arg("a=r")
.arg("test_file")
.arg("p")
.succeeds();
assert!(ts.fixtures.is_fifo("test_file"));
assert!(ts.fixtures.metadata("test_file").permissions().readonly());
}
#[test]
#[cfg(not(windows))]
fn test_mknod_fifo_invalid_extra_operand() {
new_ucmd!()
.arg("test_file")
.arg("p")
.arg("1")
.arg("2")
.fails()
.stderr_contains(&"Fifos do not have major and minor device numbers");
}
#[test]
#[cfg(not(windows))]
fn test_mknod_character_device_requires_major_and_minor() {
new_ucmd!()
.arg("test_file")
.arg("c")
.fails()
.status_code(1)
.stderr_contains(&"Special files require major and minor device numbers.");
new_ucmd!()
.arg("test_file")
.arg("c")
.arg("1")
.fails()
.status_code(1)
.stderr_contains(&"Special files require major and minor device numbers.");
new_ucmd!()
.arg("test_file")
.arg("c")
.arg("1")
.arg("c")
.fails()
.status_code(1)
.stderr_contains(&"Invalid value for '<MINOR>'");
new_ucmd!()
.arg("test_file")
.arg("c")
.arg("c")
.arg("1")
.fails()
.status_code(1)
.stderr_contains(&"Invalid value for '<MAJOR>'");
}
#[test]
#[cfg(not(windows))]
fn test_mknod_invalid_arg() {
new_ucmd!()
.arg("--foo")
.fails()
.status_code(1)
.no_stdout()
.stderr_contains(&"Found argument '--foo' which wasn't expected");
}
#[test]
#[cfg(not(windows))]
fn test_mknod_invalid_mode() {
new_ucmd!()
.arg("--mode")
.arg("rw")
.arg("test_file")
.arg("p")
.fails()
.no_stdout()
.status_code(1)
.stderr_contains(&"invalid mode");
}

View file

@ -120,7 +120,7 @@ fn test_mktemp_mktemp_t() {
.arg(TEST_TEMPLATE8)
.fails()
.no_stdout()
.stderr_contains("error: suffix cannot contain any path separators");
.stderr_contains("suffix cannot contain any path separators");
}
#[test]

View file

@ -251,6 +251,40 @@ fn test_mv_simple_backup() {
assert!(at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_simple_backup_with_file_extension() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_simple_backup_file_a.txt";
let file_b = "test_mv_simple_backup_file_b.txt";
at.touch(file_a);
at.touch(file_b);
ucmd.arg("-b")
.arg(file_a)
.arg(file_b)
.succeeds()
.no_stderr();
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
assert!(at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_arg_backup_arg_first() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_simple_backup_file_a";
let file_b = "test_mv_simple_backup_file_b";
at.touch(file_a);
at.touch(file_b);
ucmd.arg("--backup").arg(file_a).arg(file_b).succeeds();
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
assert!(at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_custom_backup_suffix() {
let (at, mut ucmd) = at_and_ucmd!();
@ -293,7 +327,7 @@ fn test_mv_custom_backup_suffix_via_env() {
}
#[test]
fn test_mv_backup_numbering() {
fn test_mv_backup_numbered_with_t() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_backup_numbering_file_a";
let file_b = "test_mv_backup_numbering_file_b";
@ -311,6 +345,25 @@ fn test_mv_backup_numbering() {
assert!(at.file_exists(&format!("{}.~1~", file_b)));
}
#[test]
fn test_mv_backup_numbered() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_backup_numbering_file_a";
let file_b = "test_mv_backup_numbering_file_b";
at.touch(file_a);
at.touch(file_b);
ucmd.arg("--backup=numbered")
.arg(file_a)
.arg(file_b)
.succeeds()
.no_stderr();
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
assert!(at.file_exists(&format!("{}.~1~", file_b)));
}
#[test]
fn test_mv_backup_existing() {
let (at, mut ucmd) = at_and_ucmd!();
@ -330,6 +383,67 @@ fn test_mv_backup_existing() {
assert!(at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_backup_nil() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_backup_numbering_file_a";
let file_b = "test_mv_backup_numbering_file_b";
at.touch(file_a);
at.touch(file_b);
ucmd.arg("--backup=nil")
.arg(file_a)
.arg(file_b)
.succeeds()
.no_stderr();
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
assert!(at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_numbered_if_existing_backup_existing() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_backup_numbering_file_a";
let file_b = "test_mv_backup_numbering_file_b";
let file_b_backup = "test_mv_backup_numbering_file_b.~1~";
at.touch(file_a);
at.touch(file_b);
at.touch(file_b_backup);
ucmd.arg("--backup=existing")
.arg(file_a)
.arg(file_b)
.succeeds()
.no_stderr();
assert!(at.file_exists(file_b));
assert!(at.file_exists(file_b_backup));
assert!(at.file_exists(&*format!("{}.~2~", file_b)));
}
#[test]
fn test_mv_numbered_if_existing_backup_nil() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_backup_numbering_file_a";
let file_b = "test_mv_backup_numbering_file_b";
let file_b_backup = "test_mv_backup_numbering_file_b.~1~";
at.touch(file_a);
at.touch(file_b);
at.touch(file_b_backup);
ucmd.arg("--backup=nil")
.arg(file_a)
.arg(file_b)
.succeeds()
.no_stderr();
assert!(at.file_exists(file_b));
assert!(at.file_exists(file_b_backup));
assert!(at.file_exists(&*format!("{}.~2~", file_b)));
}
#[test]
fn test_mv_backup_simple() {
let (at, mut ucmd) = at_and_ucmd!();
@ -349,6 +463,25 @@ fn test_mv_backup_simple() {
assert!(at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_backup_never() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_backup_numbering_file_a";
let file_b = "test_mv_backup_numbering_file_b";
at.touch(file_a);
at.touch(file_b);
ucmd.arg("--backup=never")
.arg(file_a)
.arg(file_b)
.succeeds()
.no_stderr();
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
assert!(at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_backup_none() {
let (at, mut ucmd) = at_and_ucmd!();
@ -369,17 +502,14 @@ fn test_mv_backup_none() {
}
#[test]
fn test_mv_existing_backup() {
fn test_mv_backup_off() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_mv_existing_backup_file_a";
let file_b = "test_mv_existing_backup_file_b";
let file_b_backup = "test_mv_existing_backup_file_b.~1~";
let resulting_backup = "test_mv_existing_backup_file_b.~2~";
let file_a = "test_mv_backup_numbering_file_a";
let file_b = "test_mv_backup_numbering_file_b";
at.touch(file_a);
at.touch(file_b);
at.touch(file_b_backup);
ucmd.arg("--backup=nil")
ucmd.arg("--backup=off")
.arg(file_a)
.arg(file_b)
.succeeds()
@ -387,8 +517,19 @@ fn test_mv_existing_backup() {
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
assert!(at.file_exists(file_b_backup));
assert!(at.file_exists(resulting_backup));
assert!(!at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_mv_backup_no_clobber_conflicting_options() {
let (_, mut ucmd) = at_and_ucmd!();
ucmd.arg("--backup")
.arg("--no-clobber")
.arg("file1")
.arg("file2")
.fails()
.stderr_is("mv: options --backup and --no-clobber are mutually exclusive\nTry 'mv --help' for more information.");
}
#[test]
@ -472,7 +613,7 @@ fn test_mv_overwrite_nonempty_dir() {
at.touch(dummy);
// Not same error as GNU; the error message is a rust builtin
// TODO: test (and implement) correct error message (or at least decide whether to do so)
// Current: "mv: error: couldn't rename path (Directory not empty; from=a; to=b)"
// Current: "mv: couldn't rename path (Directory not empty; from=a; to=b)"
// GNU: "mv: cannot move a to b: Directory not empty"
// Verbose output for the move should not be shown on failure
@ -539,7 +680,7 @@ fn test_mv_errors() {
.arg(dir)
.fails()
.stderr_is(format!(
"mv: error: cannot overwrite directory {} with non-directory\n",
"mv: cannot overwrite directory {} with non-directory\n",
dir
));
@ -587,6 +728,24 @@ fn test_mv_verbose() {
));
}
#[test]
fn test_mv_permission_error() {
let scene = TestScenario::new("mkdir");
let folder1 = "bar";
let folder2 = "foo";
let folder_to_move = "bar/foo";
scene.ucmd().arg("-m444").arg(folder1).succeeds();
scene.ucmd().arg("-m777").arg(folder2).succeeds();
scene
.cmd_keepenv(util_name!())
.arg(folder2)
.arg(folder_to_move)
.run()
.stderr_str()
.ends_with("Permission denied");
}
// Todo:
// $ at.touch a b

View file

@ -25,7 +25,7 @@ fn test_adjustment_with_no_command_should_error() {
new_ucmd!()
.args(&["-n", "19"])
.run()
.stderr_is("nice: error: A command must be given with an adjustment.\nTry \"nice --help\" for more information.\n");
.stderr_is("nice: A command must be given with an adjustment.\nTry \"nice --help\" for more information.\n");
}
#[test]

View file

@ -281,6 +281,7 @@ fn test_leading_whitespace_in_free_argument_should_imply_padding() {
}
#[test]
#[ignore]
fn test_should_calculate_implicit_padding_per_free_argument() {
new_ucmd!()
.args(&["--from=auto", " 1Ki", " 2K"])

View file

@ -20,42 +20,37 @@ fn test_long_format() {
let ulogin = "root";
let pw: Passwd = Passwd::locate(ulogin).unwrap();
let real_name = pw.user_info().replace("&", &pw.name().capitalize());
new_ucmd!().arg("-l").arg(ulogin).run().stdout_is(format!(
"Login name: {:<28}In real life: {}\nDirectory: {:<29}Shell: {}\n\n",
ulogin,
real_name,
pw.user_dir(),
pw.user_shell()
));
new_ucmd!()
.arg("-l")
.arg(ulogin)
.succeeds()
.stdout_is(format!(
"Login name: {:<28}In real life: {}\nDirectory: {:<29}Shell: {}\n\n",
ulogin,
real_name,
pw.user_dir(),
pw.user_shell()
));
new_ucmd!().arg("-lb").arg(ulogin).run().stdout_is(format!(
"Login name: {:<28}In real life: {1}\n\n",
ulogin, real_name
));
new_ucmd!()
.arg("-lb")
.arg(ulogin)
.succeeds()
.stdout_is(format!(
"Login name: {:<28}In real life: {1}\n\n",
ulogin, real_name
));
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_long_format_multiple_users() {
let scene = TestScenario::new(util_name!());
let args = ["-l", "root", "root", "root"];
let expected = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.arg("-l")
.arg("root")
.arg("root")
.arg("root")
.succeeds();
scene
.ucmd()
.arg("-l")
.arg("root")
.arg("root")
.arg("root")
new_ucmd!()
.args(&args)
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
}
#[test]
@ -64,46 +59,53 @@ fn test_long_format_wo_user() {
new_ucmd!().arg("-l").fails().code_is(1);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_short_format_i() {
// allow whitespace variation
// * minor whitespace differences occur between platform built-in outputs; specifically, the number of trailing TABs may be variant
let args = ["-i"];
let actual = TestScenario::new(util_name!())
.ucmd()
.args(&args)
.succeeds()
.stdout_move_str();
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
let v_expect: Vec<&str> = expect.split_whitespace().collect();
assert_eq!(v_actual, v_expect);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_short_format_q() {
// allow whitespace variation
// * minor whitespace differences occur between platform built-in outputs; specifically, the number of trailing TABs may be variant
let args = ["-q"];
let actual = TestScenario::new(util_name!())
.ucmd()
.args(&args)
.succeeds()
.stdout_move_str();
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
let v_expect: Vec<&str> = expect.split_whitespace().collect();
assert_eq!(v_actual, v_expect);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_no_flag() {
let actual = new_ucmd!().succeeds().stdout_move_str();
let expect = expected_result(&[]);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
let v_expect: Vec<&str> = expect.split_whitespace().collect();
assert_eq!(v_actual, v_expect);
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn expected_result(args: &[&str]) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LANGUAGE", "C")
.args(args)
.run()
.succeeds()
.stdout_move_str()
}

View file

@ -155,7 +155,7 @@ fn test_relpath_no_from_with_d() {
at.mkdir_all(to);
// d is part of subpath -> expect relative path
let mut result_stdout = scene
let _result_stdout = scene
.ucmd()
.arg(to)
.arg(&format!("-d{}", pwd))
@ -163,10 +163,10 @@ fn test_relpath_no_from_with_d() {
.stdout_move_str();
// relax rules for windows test environment
#[cfg(not(windows))]
assert!(Path::new(&result_stdout).is_relative());
assert!(Path::new(&_result_stdout).is_relative());
// d is not part of subpath -> expect absolut path
result_stdout = scene
let result_stdout = scene
.ucmd()
.arg(to)
.arg("-dnon_existing")

View file

@ -258,7 +258,7 @@ fn test_rm_no_operand() {
let mut ucmd = new_ucmd!();
ucmd.fails()
.stderr_is("rm: error: missing an argument\nrm: error: for help, try 'rm --help'\n");
.stderr_is("rm: missing an argument\nrm: for help, try 'rm --help'\n");
}
#[test]

View file

@ -39,7 +39,7 @@ fn test_rmdir_nonempty_directory_no_parents() {
assert!(at.file_exists(file));
ucmd.arg(dir).fails().stderr_is(
"rmdir: error: failed to remove 'test_rmdir_nonempty_no_parents': Directory not \
"rmdir: failed to remove 'test_rmdir_nonempty_no_parents': Directory not \
empty\n",
);
@ -59,9 +59,9 @@ fn test_rmdir_nonempty_directory_with_parents() {
assert!(at.file_exists(file));
ucmd.arg("-p").arg(dir).fails().stderr_is(
"rmdir: error: failed to remove 'test_rmdir_nonempty/with/parents': Directory not \
empty\nrmdir: error: failed to remove 'test_rmdir_nonempty/with': Directory not \
empty\nrmdir: error: failed to remove 'test_rmdir_nonempty': Directory not \
"rmdir: failed to remove 'test_rmdir_nonempty/with/parents': Directory not \
empty\nrmdir: failed to remove 'test_rmdir_nonempty/with': Directory not \
empty\nrmdir: failed to remove 'test_rmdir_nonempty': Directory not \
empty\n",
);

View file

@ -1,48 +1,79 @@
use crate::common::util::*;
fn test_helper(file_name: &str, args: &str) {
new_ucmd!()
.arg(format!("{}.txt", file_name))
.args(&args.split(' ').collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected", file_name));
fn test_helper(file_name: &str, possible_args: &[&str]) {
for args in possible_args {
new_ucmd!()
.arg(format!("{}.txt", file_name))
.args(&args.split(' ').collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected", file_name));
new_ucmd!()
.arg(format!("{}.txt", file_name))
.arg("--debug")
.args(&args.split(' ').collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected.debug", file_name));
new_ucmd!()
.arg(format!("{}.txt", file_name))
.arg("--debug")
.args(&args.split(' ').collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected.debug", file_name));
}
}
// FYI, the initialization size of our Line struct is 96 bytes.
//
// At very small buffer sizes, with that overhead we are certainly going
// to overrun our buffer way, way, way too quickly because of these excess
// bytes for the struct.
//
// For instance, seq 0..20000 > ...text = 108894 bytes
// But overhead is 1920000 + 108894 = 2028894 bytes
//
// Or kjvbible-random.txt = 4332506 bytes, but minimum size of its
// 99817 lines in memory * 96 bytes = 9582432 bytes
//
// Here, we test 108894 bytes with a 50K buffer
//
#[test]
fn test_larger_than_specified_segment() {
fn test_buffer_sizes() {
let buffer_sizes = [
"0", "50K", "50k", "1M", "100M", "1000G", "10T", "500E", "1Y",
];
for buffer_size in &buffer_sizes {
new_ucmd!()
.arg("-n")
.arg("-S")
.arg(buffer_size)
.arg("ext_sort.txt")
.succeeds()
.stdout_is_fixture("ext_sort.expected");
}
}
#[test]
fn test_invalid_buffer_size() {
let buffer_sizes = ["asd", "100f"];
for invalid_buffer_size in &buffer_sizes {
new_ucmd!()
.arg("-S")
.arg(invalid_buffer_size)
.fails()
.stderr_only(format!(
"sort: failed to parse buffer size `{}`: invalid digit found in string",
invalid_buffer_size
));
}
}
#[test]
fn test_ext_sort_stable() {
new_ucmd!()
.arg("-n")
.arg("--stable")
.arg("-S")
.arg("50K")
.arg("ext_sort.txt")
.arg("0M")
.arg("ext_stable.txt")
.succeeds()
.stdout_is_fixture(format!("{}", "ext_sort.expected"));
.stdout_only_fixture("ext_stable.expected");
}
#[test]
fn test_extsort_zero_terminated() {
new_ucmd!()
.arg("-z")
.arg("-S")
.arg("10K")
.arg("zero-terminated.txt")
.succeeds()
.stdout_is_fixture("zero-terminated.expected");
}
#[test]
fn test_months_whitespace() {
test_helper("months-whitespace", "-M");
test_helper("months-whitespace", &["-M", "--month-sort", "--sort=month"]);
}
#[test]
@ -56,7 +87,10 @@ fn test_version_empty_lines() {
#[test]
fn test_human_numeric_whitespace() {
test_helper("human-numeric-whitespace", "-h");
test_helper(
"human-numeric-whitespace",
&["-h", "--human-numeric-sort", "--sort=human-numeric"],
);
}
// This tests where serde often fails when reading back JSON
@ -73,12 +107,18 @@ fn test_extsort_as64_bailout() {
#[test]
fn test_multiple_decimals_general() {
test_helper("multiple_decimals_general", "-g")
test_helper(
"multiple_decimals_general",
&["-g", "--general-numeric-sort", "--sort=general-numeric"],
)
}
#[test]
fn test_multiple_decimals_numeric() {
test_helper("multiple_decimals_numeric", "-n")
test_helper(
"multiple_decimals_numeric",
&["-n", "--numeric-sort", "--sort=numeric"],
)
}
#[test]
@ -88,7 +128,7 @@ fn test_check_zero_terminated_failure() {
.arg("-c")
.arg("zero-terminated.txt")
.fails()
.stdout_is("sort: disorder in line 0\n");
.stdout_is("sort: zero-terminated.txt:2: disorder: ../../fixtures/du\n");
}
#[test]
@ -157,72 +197,93 @@ fn test_random_shuffle_contains_two_runs_not_the_same() {
#[test]
fn test_numeric_floats_and_ints() {
test_helper("numeric_floats_and_ints", "-n");
test_helper(
"numeric_floats_and_ints",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_numeric_floats() {
test_helper("numeric_floats", "-n");
test_helper(
"numeric_floats",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_numeric_floats_with_nan() {
test_helper("numeric_floats_with_nan", "-n");
test_helper(
"numeric_floats_with_nan",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_numeric_unfixed_floats() {
test_helper("numeric_unfixed_floats", "-n");
test_helper(
"numeric_unfixed_floats",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_numeric_fixed_floats() {
test_helper("numeric_fixed_floats", "-n");
test_helper(
"numeric_fixed_floats",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_numeric_unsorted_ints() {
test_helper("numeric_unsorted_ints", "-n");
test_helper(
"numeric_unsorted_ints",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_human_block_sizes() {
test_helper("human_block_sizes", "-h");
test_helper(
"human_block_sizes",
&["-h", "--human-numeric-sort", "--sort=human-numeric"],
);
}
#[test]
fn test_month_default() {
test_helper("month_default", "-M");
test_helper("month_default", &["-M", "--month-sort", "--sort=month"]);
}
#[test]
fn test_month_stable() {
test_helper("month_stable", "-Ms");
test_helper("month_stable", &["-Ms"]);
}
#[test]
fn test_default_unsorted_ints() {
test_helper("default_unsorted_ints", "");
test_helper("default_unsorted_ints", &[""]);
}
#[test]
fn test_numeric_unique_ints() {
test_helper("numeric_unsorted_ints_unique", "-nu");
test_helper("numeric_unsorted_ints_unique", &["-nu"]);
}
#[test]
fn test_version() {
test_helper("version", "-V");
test_helper("version", &["-V"]);
}
#[test]
fn test_ignore_case() {
test_helper("ignore_case", "-f");
test_helper("ignore_case", &["-f"]);
}
#[test]
fn test_dictionary_order() {
test_helper("dictionary_order", "-d");
test_helper("dictionary_order", &["-d"]);
}
#[test]
@ -249,47 +310,53 @@ fn test_non_printing_chars() {
#[test]
fn test_exponents_positive_general_fixed() {
test_helper("exponents_general", "-g");
test_helper("exponents_general", &["-g"]);
}
#[test]
fn test_exponents_positive_numeric() {
test_helper("exponents-positive-numeric", "-n");
test_helper(
"exponents-positive-numeric",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_months_dedup() {
test_helper("months-dedup", "-Mu");
test_helper("months-dedup", &["-Mu"]);
}
#[test]
fn test_mixed_floats_ints_chars_numeric() {
test_helper("mixed_floats_ints_chars_numeric", "-n");
test_helper(
"mixed_floats_ints_chars_numeric",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_mixed_floats_ints_chars_numeric_unique() {
test_helper("mixed_floats_ints_chars_numeric_unique", "-nu");
test_helper("mixed_floats_ints_chars_numeric_unique", &["-nu"]);
}
#[test]
fn test_words_unique() {
test_helper("words_unique", "-u");
test_helper("words_unique", &["-u"]);
}
#[test]
fn test_numeric_unique() {
test_helper("numeric_unique", "-nu");
test_helper("numeric_unique", &["-nu"]);
}
#[test]
fn test_mixed_floats_ints_chars_numeric_reverse() {
test_helper("mixed_floats_ints_chars_numeric_unique_reverse", "-nur");
test_helper("mixed_floats_ints_chars_numeric_unique_reverse", &["-nur"]);
}
#[test]
fn test_mixed_floats_ints_chars_numeric_stable() {
test_helper("mixed_floats_ints_chars_numeric_stable", "-ns");
test_helper("mixed_floats_ints_chars_numeric_stable", &["-ns"]);
}
#[test]
@ -318,12 +385,15 @@ fn test_numeric_floats2() {
#[test]
fn test_numeric_floats_with_nan2() {
test_helper("numeric-floats-with-nan2", "-n");
test_helper(
"numeric-floats-with-nan2",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_human_block_sizes2() {
for human_numeric_sort_param in vec!["-h", "--human-numeric-sort"] {
for human_numeric_sort_param in &["-h", "--human-numeric-sort", "--sort=human-numeric"] {
let input = "8981K\n909991M\n-8T\n21G\n0.8M";
new_ucmd!()
.arg(human_numeric_sort_param)
@ -335,7 +405,7 @@ fn test_human_block_sizes2() {
#[test]
fn test_month_default2() {
for month_sort_param in vec!["-M", "--month-sort"] {
for month_sort_param in &["-M", "--month-sort", "--sort=month"] {
let input = "JAn\nMAY\n000may\nJun\nFeb";
new_ucmd!()
.arg(month_sort_param)
@ -368,32 +438,32 @@ fn test_numeric_unique_ints2() {
#[test]
fn test_keys_open_ended() {
test_helper("keys_open_ended", "-k 2.3");
test_helper("keys_open_ended", &["-k 2.3"]);
}
#[test]
fn test_keys_closed_range() {
test_helper("keys_closed_range", "-k 2.2,2.2");
test_helper("keys_closed_range", &["-k 2.2,2.2"]);
}
#[test]
fn test_keys_multiple_ranges() {
test_helper("keys_multiple_ranges", "-k 2,2 -k 3,3");
test_helper("keys_multiple_ranges", &["-k 2,2 -k 3,3"]);
}
#[test]
fn test_keys_no_field_match() {
test_helper("keys_no_field_match", "-k 4,4");
test_helper("keys_no_field_match", &["-k 4,4"]);
}
#[test]
fn test_keys_no_char_match() {
test_helper("keys_no_char_match", "-k 1.2");
test_helper("keys_no_char_match", &["-k 1.2"]);
}
#[test]
fn test_keys_custom_separator() {
test_helper("keys_custom_separator", "-k 2.2,2.2 -t x");
test_helper("keys_custom_separator", &["-k 2.2,2.2 -t x"]);
}
#[test]
@ -401,7 +471,7 @@ fn test_keys_invalid_field() {
new_ucmd!()
.args(&["-k", "1."])
.fails()
.stderr_only("sort: error: failed to parse character index for key `1.`: cannot parse integer from empty string");
.stderr_only("sort: failed to parse character index for key `1.`: cannot parse integer from empty string");
}
#[test]
@ -409,7 +479,7 @@ fn test_keys_invalid_field_option() {
new_ucmd!()
.args(&["-k", "1.1x"])
.fails()
.stderr_only("sort: error: invalid option for key: `x`");
.stderr_only("sort: invalid option for key: `x`");
}
#[test]
@ -417,14 +487,15 @@ fn test_keys_invalid_field_zero() {
new_ucmd!()
.args(&["-k", "0.1"])
.fails()
.stderr_only("sort: error: field index was 0");
.stderr_only("sort: field index was 0");
}
#[test]
fn test_keys_invalid_char_zero() {
new_ucmd!().args(&["-k", "1.0"]).fails().stderr_only(
"sort: error: invalid character index 0 in `1.0` for the start position of a field",
);
new_ucmd!()
.args(&["-k", "1.0"])
.fails()
.stderr_only("sort: invalid character index 0 in `1.0` for the start position of a field");
}
#[test]
@ -505,7 +576,7 @@ aaaa
#[test]
fn test_zero_terminated() {
test_helper("zero-terminated", "-z");
test_helper("zero-terminated", &["-z"]);
}
#[test]
@ -544,6 +615,18 @@ fn test_merge_unique() {
.stdout_only_fixture("merge_ints_interleaved.expected");
}
#[test]
fn test_merge_stable() {
new_ucmd!()
.arg("-m")
.arg("--stable")
.arg("-n")
.arg("merge_stable_1.txt")
.arg("merge_stable_2.txt")
.succeeds()
.stdout_only_fixture("merge_stable.expected");
}
#[test]
fn test_merge_reversed() {
new_ucmd!()
@ -575,7 +658,7 @@ fn test_check() {
.arg("-c")
.arg("check_fail.txt")
.fails()
.stdout_is("sort: disorder in line 4\n");
.stdout_is("sort: check_fail.txt:6: disorder: 5\n");
new_ucmd!()
.arg("-c")

View file

@ -4,11 +4,15 @@ extern crate regex;
use self::rand::{thread_rng, Rng};
use self::regex::Regex;
use crate::common::util::*;
use rand::SeedableRng;
#[cfg(not(windows))]
use std::env;
use std::fs::{read_dir, File};
use std::io::Write;
use std::path::Path;
use std::{
fs::{read_dir, File},
io::BufWriter,
};
fn random_chars(n: usize) -> String {
thread_rng()
@ -58,7 +62,7 @@ impl Glob {
files.sort();
let mut data: Vec<u8> = vec![];
for name in &files {
data.extend(self.directory.read(name).into_bytes());
data.extend(self.directory.read_bytes(name));
}
data
}
@ -81,20 +85,30 @@ impl RandomFile {
}
fn add_bytes(&mut self, bytes: usize) {
let chunk_size: usize = if bytes >= 1024 { 1024 } else { bytes };
let mut n = bytes;
while n > chunk_size {
let _ = write!(self.inner, "{}", random_chars(chunk_size));
n -= chunk_size;
// Note that just writing random characters isn't enough to cover all
// cases. We need truly random bytes.
let mut writer = BufWriter::new(&self.inner);
// Seed the rng so as to avoid spurious test failures.
let mut rng = rand::rngs::StdRng::seed_from_u64(123);
let mut buffer = [0; 1024];
let mut remaining_size = bytes;
while remaining_size > 0 {
let to_write = std::cmp::min(remaining_size, buffer.len());
let buf = &mut buffer[..to_write];
rng.fill(buf);
writer.write(buf).unwrap();
remaining_size -= to_write;
}
let _ = write!(self.inner, "{}", random_chars(n));
}
/// Add n lines each of size `RandomFile::LINESIZE`
fn add_lines(&mut self, lines: usize) {
let mut n = lines;
while n > 0 {
let _ = writeln!(self.inner, "{}", random_chars(RandomFile::LINESIZE));
writeln!(self.inner, "{}", random_chars(RandomFile::LINESIZE)).unwrap();
n -= 1;
}
}
@ -104,18 +118,18 @@ impl RandomFile {
fn test_split_default() {
let (at, mut ucmd) = at_and_ucmd!();
let name = "split_default";
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
RandomFile::new(&at, name).add_lines(2000);
ucmd.args(&[name]).succeeds();
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
assert_eq!(glob.count(), 2);
assert_eq!(glob.collate(), at.read(name).into_bytes());
assert_eq!(glob.collate(), at.read_bytes(name));
}
#[test]
fn test_split_numeric_prefixed_chunks_by_bytes() {
let (at, mut ucmd) = at_and_ucmd!();
let name = "split_num_prefixed_chunks_by_bytes";
let glob = Glob::new(&at, ".", r"a\d\d$");
RandomFile::new(&at, name).add_bytes(10000);
ucmd.args(&[
"-d", // --numeric-suffixes
@ -123,52 +137,89 @@ fn test_split_numeric_prefixed_chunks_by_bytes() {
"1000", name, "a",
])
.succeeds();
let glob = Glob::new(&at, ".", r"a\d\d$");
assert_eq!(glob.count(), 10);
assert_eq!(glob.collate(), at.read(name).into_bytes());
for filename in glob.collect() {
assert_eq!(glob.directory.metadata(&filename).len(), 1000);
}
assert_eq!(glob.collate(), at.read_bytes(name));
}
#[test]
fn test_split_str_prefixed_chunks_by_bytes() {
let (at, mut ucmd) = at_and_ucmd!();
let name = "split_str_prefixed_chunks_by_bytes";
let glob = Glob::new(&at, ".", r"b[[:alpha:]][[:alpha:]]$");
RandomFile::new(&at, name).add_bytes(10000);
// Important that this is less than 1024 since that's our internal buffer
// size. Good to test that we don't overshoot.
ucmd.args(&["-b", "1000", name, "b"]).succeeds();
let glob = Glob::new(&at, ".", r"b[[:alpha:]][[:alpha:]]$");
assert_eq!(glob.count(), 10);
assert_eq!(glob.collate(), at.read(name).into_bytes());
for filename in glob.collect() {
assert_eq!(glob.directory.metadata(&filename).len(), 1000);
}
assert_eq!(glob.collate(), at.read_bytes(name));
}
// This is designed to test what happens when the desired part size is not a
// multiple of the buffer size and we hopefully don't overshoot the desired part
// size.
#[test]
fn test_split_bytes_prime_part_size() {
let (at, mut ucmd) = at_and_ucmd!();
let name = "test_split_bytes_prime_part_size";
RandomFile::new(&at, name).add_bytes(10000);
// 1753 is prime and greater than the buffer size, 1024.
ucmd.args(&["-b", "1753", name, "b"]).succeeds();
let glob = Glob::new(&at, ".", r"b[[:alpha:]][[:alpha:]]$");
assert_eq!(glob.count(), 6);
let mut fns = glob.collect();
// glob.collect() is not guaranteed to return in sorted order, so we sort.
fns.sort();
for i in 0..5 {
assert_eq!(glob.directory.metadata(&fns[i]).len(), 1753);
}
assert_eq!(glob.directory.metadata(&fns[5]).len(), 1235);
assert_eq!(glob.collate(), at.read_bytes(name));
}
#[test]
fn test_split_num_prefixed_chunks_by_lines() {
let (at, mut ucmd) = at_and_ucmd!();
let name = "split_num_prefixed_chunks_by_lines";
let glob = Glob::new(&at, ".", r"c\d\d$");
RandomFile::new(&at, name).add_lines(10000);
ucmd.args(&["-d", "-l", "1000", name, "c"]).succeeds();
let glob = Glob::new(&at, ".", r"c\d\d$");
assert_eq!(glob.count(), 10);
assert_eq!(glob.collate(), at.read(name).into_bytes());
assert_eq!(glob.collate(), at.read_bytes(name));
}
#[test]
fn test_split_str_prefixed_chunks_by_lines() {
let (at, mut ucmd) = at_and_ucmd!();
let name = "split_str_prefixed_chunks_by_lines";
let glob = Glob::new(&at, ".", r"d[[:alpha:]][[:alpha:]]$");
RandomFile::new(&at, name).add_lines(10000);
ucmd.args(&["-l", "1000", name, "d"]).succeeds();
let glob = Glob::new(&at, ".", r"d[[:alpha:]][[:alpha:]]$");
assert_eq!(glob.count(), 10);
assert_eq!(glob.collate(), at.read(name).into_bytes());
assert_eq!(glob.collate(), at.read_bytes(name));
}
#[test]
fn test_split_additional_suffix() {
let (at, mut ucmd) = at_and_ucmd!();
let name = "split_additional_suffix";
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]].txt$");
RandomFile::new(&at, name).add_lines(2000);
ucmd.args(&["--additional-suffix", ".txt", name]).succeeds();
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]].txt$");
assert_eq!(glob.count(), 2);
assert_eq!(glob.collate(), at.read(name).into_bytes());
assert_eq!(glob.collate(), at.read_bytes(name));
}
// note: the test_filter* tests below are unix-only
@ -182,15 +233,16 @@ fn test_filter() {
// like `test_split_default()` but run a command before writing
let (at, mut ucmd) = at_and_ucmd!();
let name = "filtered";
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
let n_lines = 3;
RandomFile::new(&at, name).add_lines(n_lines);
// change all characters to 'i'
ucmd.args(&["--filter=sed s/./i/g > $FILE", name])
.succeeds();
// assert all characters are 'i' / no character is not 'i'
// (assert that command succeded)
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
assert!(
glob.collate().iter().find(|&&c| {
// is not i
@ -209,7 +261,6 @@ fn test_filter_with_env_var_set() {
// implemented like `test_split_default()` but run a command before writing
let (at, mut ucmd) = at_and_ucmd!();
let name = "filtered";
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
let n_lines = 3;
RandomFile::new(&at, name).add_lines(n_lines);
@ -217,7 +268,9 @@ fn test_filter_with_env_var_set() {
env::set_var("FILE", &env_var_value);
ucmd.args(&[format!("--filter={}", "cat > $FILE").as_str(), name])
.succeeds();
assert_eq!(glob.collate(), at.read(name).into_bytes());
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
assert_eq!(glob.collate(), at.read_bytes(name));
assert!(env::var("FILE").unwrap_or("var was unset".to_owned()) == env_var_value);
}

View file

@ -5,69 +5,6 @@ use crate::common::util::*;
extern crate stat;
pub use self::stat::*;
#[cfg(test)]
mod test_fsext {
use super::*;
#[test]
fn test_access() {
assert_eq!("drwxr-xr-x", pretty_access(S_IFDIR | 0o755));
assert_eq!("-rw-r--r--", pretty_access(S_IFREG | 0o644));
assert_eq!("srw-r-----", pretty_access(S_IFSOCK | 0o640));
assert_eq!("lrw-r-xr-x", pretty_access(S_IFLNK | 0o655));
assert_eq!("?rw-r-xr-x", pretty_access(0o655));
assert_eq!(
"brwSr-xr-x",
pretty_access(S_IFBLK | S_ISUID as mode_t | 0o655)
);
assert_eq!(
"brwsr-xr-x",
pretty_access(S_IFBLK | S_ISUID as mode_t | 0o755)
);
assert_eq!(
"prw---sr--",
pretty_access(S_IFIFO | S_ISGID as mode_t | 0o614)
);
assert_eq!(
"prw---Sr--",
pretty_access(S_IFIFO | S_ISGID as mode_t | 0o604)
);
assert_eq!(
"c---r-xr-t",
pretty_access(S_IFCHR | S_ISVTX as mode_t | 0o055)
);
assert_eq!(
"c---r-xr-T",
pretty_access(S_IFCHR | S_ISVTX as mode_t | 0o054)
);
}
#[test]
fn test_file_type() {
assert_eq!("block special file", pretty_filetype(S_IFBLK, 0));
assert_eq!("character special file", pretty_filetype(S_IFCHR, 0));
assert_eq!("regular file", pretty_filetype(S_IFREG, 1));
assert_eq!("regular empty file", pretty_filetype(S_IFREG, 0));
assert_eq!("weird file", pretty_filetype(0, 0));
}
#[test]
fn test_fs_type() {
assert_eq!("ext2/ext3", pretty_fstype(0xEF53));
assert_eq!("tmpfs", pretty_fstype(0x01021994));
assert_eq!("nfs", pretty_fstype(0x6969));
assert_eq!("btrfs", pretty_fstype(0x9123683e));
assert_eq!("xfs", pretty_fstype(0x58465342));
assert_eq!("zfs", pretty_fstype(0x2FC12FC1));
assert_eq!("ntfs", pretty_fstype(0x5346544e));
assert_eq!("fat", pretty_fstype(0x4006));
assert_eq!("UNKNOWN (0x1234)", pretty_fstype(0x1234));
}
}
#[test]
fn test_scanutil() {
assert_eq!(Some((-5, 2)), "-5zxc".scan_num::<i32>());
@ -159,10 +96,10 @@ fn test_invalid_option() {
new_ucmd!().arg("-w").arg("-q").arg("/").fails();
}
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
const NORMAL_FMTSTR: &'static str =
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s %u %U %x %X %y %Y %z %Z"; // avoid "%w %W" (birth/creation) due to `stat` limitations and linux kernel & rust version capability variations
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "linux"))]
const DEV_FMTSTR: &'static str =
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (%t/%T) %u %U %w %W %x %X %y %Y %z %Z";
#[cfg(target_os = "linux")]
@ -188,8 +125,8 @@ fn test_fs_format() {
.stdout_is(expected_result(&args));
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_terse_normal_format() {
// note: contains birth/creation date which increases test fragility
// * results may vary due to built-in `stat` limitations as well as linux kernel and rust version capability variations
@ -198,9 +135,16 @@ fn test_terse_normal_format() {
let expect = expected_result(&args);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
let v_actual: Vec<&str> = actual.split(' ').collect();
let v_expect: Vec<&str> = expect.split(' ').collect();
let v_actual: Vec<&str> = actual.trim().split(' ').collect();
let mut v_expect: Vec<&str> = expect.trim().split(' ').collect();
assert!(!v_expect.is_empty());
// uu_stat does not support selinux
if v_actual.len() == v_expect.len() - 1 && v_expect[v_expect.len() - 1].contains(":") {
// assume last element contains: `SELinux security context string`
v_expect.pop();
}
// * allow for inequality if `stat` (aka, expect) returns "0" (unknown value)
assert!(
expect == "0"
@ -212,10 +156,10 @@ fn test_terse_normal_format() {
);
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_format_created_time() {
let args = ["-c", "%w", "/boot"];
let args = ["-c", "%w", "/bin"];
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
@ -236,10 +180,10 @@ fn test_format_created_time() {
);
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_format_created_seconds() {
let args = ["-c", "%W", "/boot"];
let args = ["-c", "%W", "/bin"];
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
@ -260,65 +204,97 @@ fn test_format_created_seconds() {
);
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_normal_format() {
let args = ["-c", NORMAL_FMTSTR, "/boot"];
let args = ["-c", NORMAL_FMTSTR, "/bin"];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_follow_symlink() {
let args = ["-L", "-c", DEV_FMTSTR, "/dev/cdrom"];
new_ucmd!()
.args(&args)
.run()
.stdout_is(expected_result(&args));
fn test_symlinks() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let mut tested: bool = false;
// arbitrarily chosen symlinks with hope that the CI environment provides at least one of them
for file in vec![
"/bin/sh",
"/bin/sudoedit",
"/usr/bin/ex",
"/etc/localtime",
"/etc/aliases",
] {
if at.file_exists(file) && at.is_symlink(file) {
tested = true;
let args = ["-c", NORMAL_FMTSTR, file];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
// -L, --dereference follow links
let args = ["-L", "-c", NORMAL_FMTSTR, file];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
}
}
if !tested {
panic!("No symlink found to test in this environment");
}
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_symlink() {
let args = ["-c", DEV_FMTSTR, "/dev/cdrom"];
new_ucmd!()
.args(&args)
.run()
.stdout_is(expected_result(&args));
}
#[test]
#[cfg(target_os = "linux")]
fn test_char() {
let args = ["-c", DEV_FMTSTR, "/dev/pts/ptmx"];
// TODO: "(%t) (%x) (%w)" deviate from GNU stat for `character special file` on macOS
// Diff < left / right > :
// <"(f0000) (2021-05-20 23:08:03.442555000 +0200) (1970-01-01 01:00:00.000000000 +0100)\n"
// >"(f) (2021-05-20 23:08:03.455598000 +0200) (-)\n"
let args = [
"-c",
#[cfg(target_os = "linux")]
DEV_FMTSTR,
#[cfg(target_os = "linux")]
"/dev/pts/ptmx",
#[cfg(any(target_vendor = "apple"))]
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (/%T) %u %U %W %X %y %Y %z %Z",
#[cfg(any(target_vendor = "apple"))]
"/dev/ptmx",
];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_multi_files() {
let args = [
"-c",
NORMAL_FMTSTR,
"/dev",
"/usr/lib",
#[cfg(target_os = "linux")]
"/etc/fstab",
"/var",
];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
#[cfg(target_os = "linux")]
fn test_printf() {
let args = [
"--printf=123%-# 15q\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.23m\\x12\\167\\132\\112\\n",
@ -326,16 +302,21 @@ fn test_printf() {
];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn expected_result(args: &[&str]) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LANGUAGE", "C")
.args(args)
.run()
.succeeds()
.stdout_move_str()
}

View file

@ -27,12 +27,12 @@ fn test_stdbuf_line_buffered_stdout() {
fn test_stdbuf_no_buffer_option_fails() {
new_ucmd!().args(&["head"]).fails().stderr_is(
"error: The following required arguments were not provided:\n \
--error <MODE>\n \
--input <MODE>\n \
--output <MODE>\n\n\
USAGE:\n \
stdbuf OPTION... COMMAND\n\n\
For more information try --help",
--error <MODE>\n \
--input <MODE>\n \
--output <MODE>\n\n\
USAGE:\n \
stdbuf OPTION... COMMAND\n\n\
For more information try --help",
);
}
@ -49,10 +49,9 @@ fn test_stdbuf_trailing_var_arg() {
#[cfg(not(target_os = "windows"))]
#[test]
fn test_stdbuf_line_buffering_stdin_fails() {
new_ucmd!()
.args(&["-i", "L", "head"])
.fails()
.stderr_is("stdbuf: error: line buffering stdin is meaningless\nTry 'stdbuf --help' for more information.");
new_ucmd!().args(&["-i", "L", "head"]).fails().stderr_is(
"stdbuf: line buffering stdin is meaningless\nTry 'stdbuf --help' for more information.",
);
}
#[cfg(not(target_os = "windows"))]
@ -61,5 +60,5 @@ fn test_stdbuf_invalid_mode_fails() {
new_ucmd!()
.args(&["-i", "1024R", "head"])
.fails()
.stderr_is("stdbuf: error: invalid mode 1024R\nTry 'stdbuf --help' for more information.");
.stderr_is("stdbuf: invalid mode 1024R\nTry 'stdbuf --help' for more information.");
}

View file

@ -59,9 +59,7 @@ fn test_invalid_file() {
at.mkdir("a");
ucmd.arg("a")
.fails()
.stderr_is("sum: error: 'a' Is a directory");
ucmd.arg("a").fails().stderr_is("sum: 'a' Is a directory");
}
#[test]
@ -70,5 +68,5 @@ fn test_invalid_metadata() {
ucmd.arg("b")
.fails()
.stderr_is("sum: error: 'b' No such file or directory");
.stderr_is("sum: 'b' No such file or directory");
}

View file

@ -37,5 +37,5 @@ fn test_sync_no_existing_files() {
.arg("--data")
.arg("do-no-exist")
.fails()
.stderr_contains("error: cannot stat");
.stderr_contains("cannot stat");
}

View file

@ -348,3 +348,43 @@ fn test_negative_indexing() {
fn test_sleep_interval() {
new_ucmd!().arg("-s").arg("10").arg(FOOBAR_TXT).succeeds();
}
/// Test for reading all but the first NUM bytes: `tail -c +3`.
#[test]
fn test_positive_bytes() {
new_ucmd!()
.args(&["-c", "+3"])
.pipe_in("abcde")
.succeeds()
.stdout_is("cde");
}
/// Test for reading all bytes, specified by `tail -c +0`.
#[test]
fn test_positive_zero_bytes() {
new_ucmd!()
.args(&["-c", "+0"])
.pipe_in("abcde")
.succeeds()
.stdout_is("abcde");
}
/// Test for reading all but the first NUM lines: `tail -n +3`.
#[test]
fn test_positive_lines() {
new_ucmd!()
.args(&["-n", "+3"])
.pipe_in("a\nb\nc\nd\ne\n")
.succeeds()
.stdout_is("c\nd\ne\n");
}
/// Test for reading all lines, specified by `tail -n +0`.
#[test]
fn test_positive_zero_lines() {
new_ucmd!()
.args(&["-n", "+0"])
.pipe_in("a\nb\nc\nd\ne\n")
.succeeds()
.stdout_is("a\nb\nc\nd\ne\n");
}

View file

@ -122,6 +122,13 @@ fn test_zero_len_not_equals_zero_len_is_false() {
new_ucmd!().args(&["", "!=", ""]).run().status_code(1);
}
#[test]
fn test_double_equal_is_string_comparison_op() {
// undocumented but part of the GNU test suite
new_ucmd!().args(&["t", "==", "t"]).succeeds();
new_ucmd!().args(&["t", "==", "f"]).run().status_code(1);
}
#[test]
fn test_string_comparison() {
let scenario = TestScenario::new(util_name!());
@ -131,11 +138,22 @@ fn test_string_comparison() {
["(", "=", "("],
["(", "!=", ")"],
["!", "=", "!"],
["=", "=", "="],
];
for test in &tests {
scenario.ucmd().args(&test[..]).succeeds();
}
// run the inverse of all these tests
for test in &tests {
scenario
.ucmd()
.arg("!")
.args(&test[..])
.run()
.status_code(1);
}
}
#[test]
@ -419,10 +437,9 @@ fn test_not_is_not_empty() {
#[cfg(not(windows))]
fn test_symlink_is_symlink() {
let scenario = TestScenario::new(util_name!());
let mut ln = scenario.cmd("ln");
let at = &scenario.fixtures;
// creating symlinks requires admin on Windows
ln.args(&["-s", "regular_file", "symlink"]).succeeds();
at.symlink_file("regular_file", "symlink");
// FIXME: implement on Windows
scenario.ucmd().args(&["-h", "symlink"]).succeeds();
@ -485,6 +502,81 @@ fn test_op_prec_and_or_2_overridden_by_parentheses() {
.status_code(1);
}
#[test]
fn test_negated_boolean_precedence() {
let scenario = TestScenario::new(util_name!());
let tests = [
vec!["!", "(", "foo", ")", "-o", "bar"],
vec!["!", "", "-o", "", "-a", ""],
vec!["!", "(", "", "-a", "", ")", "-o", ""],
];
for test in &tests {
scenario.ucmd().args(&test[..]).succeeds();
}
let negative_tests = [
vec!["!", "-n", "", "-a", ""],
vec!["", "-a", "", "-o", ""],
vec!["!", "", "-a", "", "-o", ""],
vec!["!", "(", "", "-a", "", ")", "-a", ""],
];
for test in &negative_tests {
scenario.ucmd().args(&test[..]).run().status_code(1);
}
}
#[test]
fn test_bang_boolop_precedence() {
// For a Boolean combination of two literals, bang inverts the entire expression
new_ucmd!().args(&["!", "", "-a", ""]).succeeds();
new_ucmd!().args(&["!", "", "-o", ""]).succeeds();
new_ucmd!()
.args(&["!", "a value", "-o", "another value"])
.run()
.status_code(1);
// Introducing a UOP — even one that is equivalent to a bare string — causes
// bang to invert only the first term
new_ucmd!()
.args(&["!", "-n", "", "-a", ""])
.run()
.status_code(1);
new_ucmd!()
.args(&["!", "", "-a", "-n", ""])
.run()
.status_code(1);
// for compound Boolean expressions, bang inverts the _next_ expression
// only, not the entire compound expression
new_ucmd!()
.args(&["!", "", "-a", "", "-a", ""])
.run()
.status_code(1);
// parentheses can override this
new_ucmd!()
.args(&["!", "(", "", "-a", "", "-a", "", ")"])
.succeeds();
}
#[test]
fn test_inverted_parenthetical_boolop_precedence() {
// For a Boolean combination of two literals, bang inverts the entire expression
new_ucmd!()
.args(&["!", "a value", "-o", "another value"])
.run()
.status_code(1);
// only the parenthetical is inverted, not the entire expression
new_ucmd!()
.args(&["!", "(", "a value", ")", "-o", "another value"])
.succeeds();
}
#[test]
#[ignore = "fixme: error reporting"]
fn test_dangling_parenthesis() {

View file

@ -206,7 +206,7 @@ fn test_round_up() {
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(TFILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", "*4", TFILE2]).succeeds();
ucmd.args(&["--size", "%4", TFILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
@ -235,3 +235,30 @@ fn test_size_and_reference() {
actual
);
}
#[test]
fn test_invalid_numbers() {
// TODO For compatibility with GNU, `truncate -s 0X` should cause
// the same error as `truncate -s 0X file`, but currently it returns
// a different error.
new_ucmd!()
.args(&["-s", "0X", "file"])
.fails()
.stderr_contains("Invalid number: 0X");
new_ucmd!()
.args(&["-s", "0XB", "file"])
.fails()
.stderr_contains("Invalid number: 0XB");
new_ucmd!()
.args(&["-s", "0B", "file"])
.fails()
.stderr_contains("Invalid number: 0B");
}
#[test]
fn test_reference_file_not_found() {
new_ucmd!()
.args(&["-r", "a", "b"])
.fails()
.stderr_contains("cannot stat 'a': No such file or directory");
}

View file

@ -43,5 +43,5 @@ fn test_uname_kernel() {
}
#[cfg(not(target_os = "linux"))]
let result = ucmd.arg("-o").succeeds();
ucmd.arg("-o").succeeds();
}

View file

@ -145,7 +145,7 @@ fn test_invalid_utf8() {
.arg("not-utf8-sequence.txt")
.run()
.failure()
.stderr_only("uniq: error: invalid utf-8 sequence of 1 bytes from index 0");
.stderr_only("uniq: invalid utf-8 sequence of 1 bytes from index 0");
}
#[test]

View file

@ -22,7 +22,7 @@ fn test_unlink_multiple_files() {
at.touch(file_b);
ucmd.arg(file_a).arg(file_b).fails().stderr_is(
"unlink: error: extra operand: 'test_unlink_multiple_file_b'\nTry 'unlink --help' \
"unlink: extra operand: 'test_unlink_multiple_file_b'\nTry 'unlink --help' \
for more information.\n",
);
}
@ -35,7 +35,7 @@ fn test_unlink_directory() {
at.mkdir(dir);
ucmd.arg(dir).fails().stderr_is(
"unlink: error: cannot unlink 'test_unlink_empty_directory': Not a regular file \
"unlink: cannot unlink 'test_unlink_empty_directory': Not a regular file \
or symlink\n",
);
}
@ -45,7 +45,7 @@ fn test_unlink_nonexistent() {
let file = "test_unlink_nonexistent";
new_ucmd!().arg(file).fails().stderr_is(
"unlink: error: Cannot stat 'test_unlink_nonexistent': No such file or directory \
"unlink: Cannot stat 'test_unlink_nonexistent': No such file or directory \
(os error 2)\n",
);
}

View file

@ -33,7 +33,16 @@ fn test_stdin_default() {
new_ucmd!()
.pipe_in_fixture("lorem_ipsum.txt")
.run()
.stdout_is(" 13 109 772\n");
.stdout_is(" 13 109 772\n");
}
#[test]
fn test_stdin_explicit() {
new_ucmd!()
.pipe_in_fixture("lorem_ipsum.txt")
.arg("-")
.run()
.stdout_is(" 13 109 772 -\n");
}
#[test]
@ -42,9 +51,11 @@ fn test_utf8() {
.args(&["-lwmcL"])
.pipe_in_fixture("UTF_8_test.txt")
.run()
.stdout_is(" 300 4969 22781 22213 79\n");
// GNU returns " 300 2086 22219 22781 79"
// TODO: we should fix that to match GNU's behavior
.stdout_is(" 300 4969 22781 22213 79\n");
// GNU returns " 300 2086 22219 22781 79"
//
// TODO: we should fix the word, character, and byte count to
// match the behavior of GNU wc
}
#[test]
@ -71,7 +82,7 @@ fn test_stdin_all_counts() {
.args(&["-c", "-m", "-l", "-L", "-w"])
.pipe_in_fixture("alice_in_wonderland.txt")
.run()
.stdout_is(" 5 57 302 302 66\n");
.stdout_is(" 5 57 302 302 66\n");
}
#[test]
@ -79,7 +90,7 @@ fn test_single_default() {
new_ucmd!()
.arg("moby_dick.txt")
.run()
.stdout_is(" 18 204 1115 moby_dick.txt\n");
.stdout_is(" 18 204 1115 moby_dick.txt\n");
}
#[test]
@ -95,7 +106,7 @@ fn test_single_all_counts() {
new_ucmd!()
.args(&["-c", "-l", "-L", "-m", "-w", "alice_in_wonderland.txt"])
.run()
.stdout_is(" 5 57 302 302 66 alice_in_wonderland.txt\n");
.stdout_is(" 5 57 302 302 66 alice_in_wonderland.txt\n");
}
#[test]
@ -108,64 +119,101 @@ fn test_multiple_default() {
])
.run()
.stdout_is(
" 13 109 772 lorem_ipsum.txt\n 18 204 1115 moby_dick.txt\n 5 57 302 \
alice_in_wonderland.txt\n 36 370 2189 total\n",
" 13 109 772 lorem_ipsum.txt\n 18 204 1115 moby_dick.txt\n 5 57 302 \
alice_in_wonderland.txt\n 36 370 2189 total\n",
);
}
/// Test for an empty file.
#[test]
fn test_file_empty() {
// TODO There is a leading space in the output that should be
// removed; see issue #2173.
new_ucmd!()
.args(&["-clmwL", "emptyfile.txt"])
.run()
.stdout_is(" 0 0 0 0 0 emptyfile.txt\n");
.stdout_is("0 0 0 0 0 emptyfile.txt\n");
}
/// Test for an file containing a single non-whitespace character
/// *without* a trailing newline.
#[test]
fn test_file_single_line_no_trailing_newline() {
// TODO There is a leading space in the output that should be
// removed; see issue #2173.
new_ucmd!()
.args(&["-clmwL", "notrailingnewline.txt"])
.run()
.stdout_is(" 1 1 2 2 1 notrailingnewline.txt\n");
.stdout_is("1 1 2 2 1 notrailingnewline.txt\n");
}
/// Test for a file that has 100 empty lines (that is, the contents of
/// the file are the newline character repeated one hundred times).
#[test]
fn test_file_many_empty_lines() {
// TODO There is a leading space in the output that should be
// removed; see issue #2173.
new_ucmd!()
.args(&["-clmwL", "manyemptylines.txt"])
.run()
.stdout_is(" 100 0 100 100 0 manyemptylines.txt\n");
.stdout_is("100 0 100 100 0 manyemptylines.txt\n");
}
/// Test for a file that has one long line comprising only spaces.
#[test]
fn test_file_one_long_line_only_spaces() {
// TODO There is a leading space in the output that should be
// removed; see issue #2173.
new_ucmd!()
.args(&["-clmwL", "onelongemptyline.txt"])
.run()
.stdout_is(" 1 0 10001 10001 10000 onelongemptyline.txt\n");
.stdout_is(" 1 0 10001 10001 10000 onelongemptyline.txt\n");
}
/// Test for a file that has one long line comprising a single "word".
#[test]
fn test_file_one_long_word() {
// TODO There is a leading space in the output that should be
// removed; see issue #2173.
new_ucmd!()
.args(&["-clmwL", "onelongword.txt"])
.run()
.stdout_is(" 1 1 10001 10001 10000 onelongword.txt\n");
.stdout_is(" 1 1 10001 10001 10000 onelongword.txt\n");
}
/// Test that the number of bytes in the file dictate the display width.
///
/// The width in digits of any count is the width in digits of the
/// number of bytes in the file, regardless of whether the number of
/// bytes are displayed.
#[test]
fn test_file_bytes_dictate_width() {
// This file has 10,001 bytes. Five digits are required to
// represent that. Even though the number of lines is 1 and the
// number of words is 0, each of those counts is formatted with
// five characters, filled with whitespace.
new_ucmd!()
.args(&["-lw", "onelongemptyline.txt"])
.run()
.stdout_is(" 1 0 onelongemptyline.txt\n");
// This file has zero bytes. Only one digit is required to
// represent that.
new_ucmd!()
.args(&["-lw", "emptyfile.txt"])
.run()
.stdout_is("0 0 emptyfile.txt\n");
}
/// Test that getting counts from a directory is an error.
#[test]
fn test_read_from_directory_error() {
// TODO To match GNU `wc`, the `stdout` should be:
//
// " 0 0 0 .\n"
//
new_ucmd!()
.args(&["."])
.fails()
.stderr_contains(".: Is a directory\n")
.stdout_is("0 0 0 .\n");
}
/// Test that getting counts from nonexistent file is an error.
#[test]
fn test_read_from_nonexistent_file() {
new_ucmd!()
.args(&["bogusfile"])
.fails()
.stderr_contains("bogusfile: No such file or directory\n");
}

View file

@ -1,28 +1,28 @@
use crate::common::util::*;
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_count() {
for opt in vec!["-q", "--count"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_boot() {
for opt in vec!["-b", "--boot"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_heading() {
for opt in vec!["-H", "--heading"] {
@ -30,7 +30,7 @@ fn test_heading() {
// * minor whitespace differences occur between platform built-in outputs;
// specifically number of TABs between "TIME" and "COMMENT" may be variant
let actual = new_ucmd!().arg(opt).succeeds().stdout_move_str();
let expect = expected_result(opt);
let expect = expected_result(&[opt]);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
@ -39,205 +39,208 @@ fn test_heading() {
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_short() {
for opt in vec!["-s", "--short"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_login() {
for opt in vec!["-l", "--login"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_m() {
for opt in vec!["-m"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_process() {
for opt in vec!["-p", "--process"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[test]
fn test_runlevel() {
for opt in vec!["-r", "--runlevel"] {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
#[cfg(not(target_os = "linux"))]
new_ucmd!().arg(opt).succeeds().stdout_is("");
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_time() {
for opt in vec!["-t", "--time"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_mesg() {
for opt in vec!["-w", "-T", "--users", "--message", "--writable"] {
// -T, -w, --mesg
// add user's message status as +, - or ?
// --message
// same as -T
// --writable
// same as -T
for opt in vec!["-T", "-w", "--mesg", "--message", "--writable"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[test]
fn test_arg1_arg2() {
let scene = TestScenario::new(util_name!());
let args = ["am", "i"];
let expected = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.arg("am")
.arg("i")
.succeeds();
scene
.ucmd()
.arg("am")
.arg("i")
new_ucmd!()
.args(&args)
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
}
#[test]
fn test_too_many_args() {
let expected =
const EXPECTED: &str =
"error: The value 'u' was provided to '<FILE>...', but it wasn't expecting any more values";
new_ucmd!()
.arg("am")
.arg("i")
.arg("u")
.fails()
.stderr_contains(expected);
let args = ["am", "i", "u"];
new_ucmd!().args(&args).fails().stderr_contains(EXPECTED);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_users() {
for opt in vec!["-u", "--users"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
let actual = new_ucmd!().arg(opt).succeeds().stdout_move_str();
let expect = expected_result(&[opt]);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
let mut v_actual: Vec<&str> = actual.split_whitespace().collect();
let mut v_expect: Vec<&str> = expect.split_whitespace().collect();
// TODO: `--users` differs from GNU's output on macOS
// Diff < left / right > :
// <"runner console 2021-05-20 22:03 00:08 196\n"
// >"runner console 2021-05-20 22:03 old 196\n"
if cfg!(target_os = "macos") {
v_actual.remove(4);
v_expect.remove(4);
}
assert_eq!(v_actual, v_expect);
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_lookup() {
for opt in vec!["--lookup"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_dead() {
for opt in vec!["-d", "--dead"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_all_separately() {
if cfg!(target_os = "macos") {
// TODO: fix `-u`, see: test_users
return;
}
// -a, --all same as -b -d --login -p -r -t -T -u
let args = ["-b", "-d", "--login", "-p", "-r", "-t", "-T", "-u"];
let scene = TestScenario::new(util_name!());
let expected = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.arg("-b")
.arg("-d")
.arg("--login")
.arg("-p")
.arg("-r")
.arg("-t")
.arg("-T")
.arg("-u")
.succeeds();
scene
.ucmd()
.arg("-b")
.arg("-d")
.arg("--login")
.arg("-p")
.arg("-r")
.arg("-t")
.arg("-T")
.arg("-u")
.args(&args)
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
scene
.ucmd()
.arg("--all")
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_all() {
if cfg!(target_os = "macos") {
// TODO: fix `-u`, see: test_users
return;
}
for opt in vec!["-a", "--all"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
fn expected_result(arg: &str) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn expected_result(args: &[&str]) -> String {
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LANGUAGE", "C")
.args(&[arg])
.args(args)
.succeeds()
.stdout_move_str()
}

View file

@ -5,7 +5,7 @@ use crate::common::util::*;
// considered okay. If we are not inside the CI this calls assert!(result.success).
//
// From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)"
// stderr: "whoami: error: failed to get username"
// stderr: "whoami: failed to get username"
// Maybe: "adduser --uid 1001 username" can put things right?
fn skipping_test_is_okay(result: &CmdResult, needle: &str) -> bool {
if !result.succeeded() {

View file

@ -7,7 +7,7 @@ use std::env;
#[cfg(not(windows))]
use std::ffi::CString;
use std::ffi::OsStr;
use std::fs::{self, File, OpenOptions};
use std::fs::{self, hard_link, File, OpenOptions};
use std::io::{Read, Result, Write};
#[cfg(unix)]
use std::os::unix::fs::{symlink as symlink_dir, symlink as symlink_file};
@ -163,7 +163,7 @@ impl CmdResult {
/// asserts that the command's exit code is the same as the given one
pub fn status_code(&self, code: i32) -> &CmdResult {
assert!(self.code == Some(code));
assert_eq!(self.code, Some(code));
self
}
@ -295,17 +295,32 @@ impl CmdResult {
}
pub fn stdout_contains<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
assert!(self.stdout_str().contains(cmp.as_ref()));
assert!(
self.stdout_str().contains(cmp.as_ref()),
"'{}' does not contain '{}'",
self.stdout_str(),
cmp.as_ref()
);
self
}
pub fn stderr_contains<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
assert!(self.stderr_str().contains(cmp.as_ref()));
assert!(
self.stderr_str().contains(cmp.as_ref()),
"'{}' does not contain '{}'",
self.stderr_str(),
cmp.as_ref()
);
self
}
pub fn stdout_does_not_contain<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
assert!(!self.stdout_str().contains(cmp.as_ref()));
assert!(
!self.stdout_str().contains(cmp.as_ref()),
"'{}' contains '{}' but should not",
self.stdout_str(),
cmp.as_ref(),
);
self
}
@ -509,6 +524,14 @@ impl AtPath {
}
}
pub fn hard_link(&self, src: &str, dst: &str) {
log_info(
"hard_link",
&format!("{},{}", self.plus_as_string(src), self.plus_as_string(dst)),
);
hard_link(&self.plus(src), &self.plus(dst)).unwrap();
}
pub fn symlink_file(&self, src: &str, dst: &str) {
log_info(
"symlink",
@ -665,6 +688,10 @@ impl TestScenario {
cmd
}
/// Returns builder for invoking any system command. Paths given are treated
/// relative to the environment's unique temporary test directory.
/// Differs from the builder returned by `cmd` in that `cmd_keepenv` does not call
/// `Command::env_clear` (Clears the entire environment map for the child process.)
pub fn cmd_keepenv<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
UCommand::new_from_tmp(bin, self.tmpd.clone(), false)
}

0
tests/fixtures/head/emptyfile.txt vendored Normal file
View file

View file

@ -0,0 +1,4 @@
0a
0a
0b
0b

4
tests/fixtures/sort/ext_stable.txt vendored Normal file
View file

@ -0,0 +1,4 @@
0a
0a
0b
0b

View file

@ -0,0 +1,3 @@
0a
0c
0b

View file

@ -0,0 +1,2 @@
0a
0c

View file

@ -0,0 +1 @@
0b