1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-08-01 21:47:46 +00:00

Merge branch 'master' of https://github.com/uutils/coreutils into uutils-master

This commit is contained in:
Tyler 2021-07-01 14:33:30 -07:00
commit 92281585a7
754 changed files with 92913 additions and 17074 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 explicitly-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 explicitly-named RNG to guarantee stability
use rand::{RngCore, SeedableRng};
use rand_chacha::ChaCha8Rng;
const SEED: u64 = 0xdead_bebe_ea75_cafe; // spell-checker:disable-line
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

@ -2,17 +2,13 @@ use crate::common::util::*;
#[test]
fn test_arch() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(result.success);
new_ucmd!().succeeds();
}
#[test]
fn test_arch_help() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--help").run();
assert!(result.success);
assert!(result.stdout.contains("architecture name"));
new_ucmd!()
.arg("--help")
.succeeds()
.stdout_contains("architecture name");
}

View file

@ -14,13 +14,28 @@ fn test_encode() {
new_ucmd!()
.pipe_in(input)
.succeeds()
.stdout_only("JBSWY3DPFQQFO33SNRSCC===\n");
.stdout_only("JBSWY3DPFQQFO33SNRSCC===\n"); // spell-checker:disable-line
// Using '-' as our file
new_ucmd!()
.arg("-")
.pipe_in(input)
.succeeds()
.stdout_only("JBSWY3DPFQQFO33SNRSCC===\n"); // spell-checker:disable-line
}
#[test]
fn test_base32_encode_file() {
new_ucmd!()
.arg("input-simple.txt")
.succeeds()
.stdout_only("JBSWY3DPFQQFO33SNRSCCCQ=\n"); // spell-checker:disable-line
}
#[test]
fn test_decode() {
for decode_param in vec!["-d", "--decode"] {
let input = "JBSWY3DPFQQFO33SNRSCC===\n";
for decode_param in &["-d", "--decode"] {
let input = "JBSWY3DPFQQFO33SNRSCC===\n"; // spell-checker:disable-line
new_ucmd!()
.arg(decode_param)
.pipe_in(input)
@ -31,7 +46,7 @@ fn test_decode() {
#[test]
fn test_garbage() {
let input = "aGVsbG8sIHdvcmxkIQ==\0";
let input = "aGVsbG8sIHdvcmxkIQ==\0"; // spell-checker:disable-line
new_ucmd!()
.arg("-d")
.pipe_in(input)
@ -41,8 +56,8 @@ fn test_garbage() {
#[test]
fn test_ignore_garbage() {
for ignore_garbage_param in vec!["-i", "--ignore-garbage"] {
let input = "JBSWY\x013DPFQ\x02QFO33SNRSCC===\n";
for ignore_garbage_param in &["-i", "--ignore-garbage"] {
let input = "JBSWY\x013DPFQ\x02QFO33SNRSCC===\n"; // spell-checker:disable-line
new_ucmd!()
.arg("-d")
.arg(ignore_garbage_param)
@ -54,7 +69,7 @@ fn test_ignore_garbage() {
#[test]
fn test_wrap() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
let input = "The quick brown fox jumps over the lazy dog.";
new_ucmd!()
.arg(wrap_param)
@ -62,28 +77,50 @@ fn test_wrap() {
.pipe_in(input)
.succeeds()
.stdout_only(
"KRUGKIDROVUWG2ZAMJZG\n653OEBTG66BANJ2W24DT\nEBXXMZLSEB2GQZJANRQX\nU6JAMRXWOLQ=\n",
"KRUGKIDROVUWG2ZAMJZG\n653OEBTG66BANJ2W24DT\nEBXXMZLSEB2GQZJANRQX\nU6JAMRXWOLQ=\n", // spell-checker:disable-line
);
}
}
#[test]
fn test_wrap_no_arg() {
for wrap_param in vec!["-w", "--wrap"] {
new_ucmd!().arg(wrap_param).fails().stderr_only(format!(
"base32: error: Argument to option '{}' missing\n",
if wrap_param == "-w" { "w" } else { "wrap" }
));
for wrap_param in &["-w", "--wrap"] {
let expected_stderr = "error: The argument '--wrap <wrap>\' requires a value but none was \
supplied\n\nUSAGE:\n base32 [OPTION]... [FILE]\n\nFor more \
information try --help"
.to_string();
new_ucmd!()
.arg(wrap_param)
.fails()
.stderr_only(expected_stderr);
}
}
#[test]
fn test_wrap_bad_arg() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
new_ucmd!()
.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");
}
}
#[test]
fn test_base32_extra_operand() {
// Expect a failure when multiple files are specified.
new_ucmd!()
.arg("a.txt")
.arg("a.txt")
.fails()
.stderr_only("base32: extra operand 'a.txt'");
}
#[test]
fn test_base32_file_not_found() {
new_ucmd!()
.arg("a.txt")
.fails()
.stderr_only("base32: a.txt: No such file or directory");
}

View file

@ -6,13 +6,28 @@ fn test_encode() {
new_ucmd!()
.pipe_in(input)
.succeeds()
.stdout_only("aGVsbG8sIHdvcmxkIQ==\n");
.stdout_only("aGVsbG8sIHdvcmxkIQ==\n"); // spell-checker:disable-line
// Using '-' as our file
new_ucmd!()
.arg("-")
.pipe_in(input)
.succeeds()
.stdout_only("aGVsbG8sIHdvcmxkIQ==\n"); // spell-checker:disable-line
}
#[test]
fn test_base64_encode_file() {
new_ucmd!()
.arg("input-simple.txt")
.succeeds()
.stdout_only("SGVsbG8sIFdvcmxkIQo=\n"); // spell-checker:disable-line
}
#[test]
fn test_decode() {
for decode_param in vec!["-d", "--decode"] {
let input = "aGVsbG8sIHdvcmxkIQ==";
for decode_param in &["-d", "--decode"] {
let input = "aGVsbG8sIHdvcmxkIQ=="; // spell-checker:disable-line
new_ucmd!()
.arg(decode_param)
.pipe_in(input)
@ -23,7 +38,7 @@ fn test_decode() {
#[test]
fn test_garbage() {
let input = "aGVsbG8sIHdvcmxkIQ==\0";
let input = "aGVsbG8sIHdvcmxkIQ==\0"; // spell-checker:disable-line
new_ucmd!()
.arg("-d")
.pipe_in(input)
@ -33,8 +48,8 @@ fn test_garbage() {
#[test]
fn test_ignore_garbage() {
for ignore_garbage_param in vec!["-i", "--ignore-garbage"] {
let input = "aGVsbG8sIHdvcmxkIQ==\0";
for ignore_garbage_param in &["-i", "--ignore-garbage"] {
let input = "aGVsbG8sIHdvcmxkIQ==\0"; // spell-checker:disable-line
new_ucmd!()
.arg("-d")
.arg(ignore_garbage_param)
@ -46,34 +61,52 @@ fn test_ignore_garbage() {
#[test]
fn test_wrap() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
let input = "The quick brown fox jumps over the lazy dog.";
new_ucmd!()
.arg(wrap_param)
.arg("20")
.pipe_in(input)
.succeeds()
// spell-checker:disable-next-line
.stdout_only("VGhlIHF1aWNrIGJyb3du\nIGZveCBqdW1wcyBvdmVy\nIHRoZSBsYXp5IGRvZy4=\n");
}
}
#[test]
fn test_wrap_no_arg() {
for wrap_param in vec!["-w", "--wrap"] {
new_ucmd!().arg(wrap_param).fails().stderr_only(format!(
"base64: error: Argument to option '{}' missing\n",
if wrap_param == "-w" { "w" } else { "wrap" }
));
for wrap_param in &["-w", "--wrap"] {
new_ucmd!().arg(wrap_param).fails().stderr_contains(
&"The argument '--wrap <wrap>' requires a value but none was supplied",
);
}
}
#[test]
fn test_wrap_bad_arg() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
new_ucmd!()
.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");
}
}
#[test]
fn test_base64_extra_operand() {
// Expect a failure when multiple files are specified.
new_ucmd!()
.arg("a.txt")
.arg("a.txt")
.fails()
.stderr_only("base64: extra operand 'a.txt'");
}
#[test]
fn test_base64_file_not_found() {
new_ucmd!()
.arg("a.txt")
.fails()
.stderr_only("base64: a.txt: No such file or directory");
}

View file

@ -1,4 +1,31 @@
// spell-checker:ignore (words) reallylongexecutable
use crate::common::util::*;
#[cfg(any(unix, target_os = "redox"))]
use std::ffi::OsStr;
#[test]
fn test_help() {
for help_flg in &["-h", "--help"] {
new_ucmd!()
.arg(&help_flg)
.succeeds()
.no_stderr()
.stdout_contains("USAGE:");
}
}
#[test]
fn test_version() {
for version_flg in &["-V", "--version"] {
assert!(new_ucmd!()
.arg(&version_flg)
.succeeds()
.no_stderr()
.stdout_str()
.starts_with("basename"));
}
}
#[test]
fn test_directory() {
@ -25,7 +52,7 @@ fn test_remove_suffix() {
}
#[test]
fn test_dont_remove_suffix() {
fn test_do_not_remove_suffix() {
new_ucmd!()
.args(&["/foo/bar/baz", "baz"])
.succeeds()
@ -34,29 +61,29 @@ fn test_dont_remove_suffix() {
#[test]
fn test_multiple_param() {
for multiple_param in vec!["-a", "--multiple"] {
for &multiple_param in &["-a", "--multiple"] {
let path = "/foo/bar/baz";
new_ucmd!()
.args(&[multiple_param, path, path])
.succeeds()
.stdout_only("baz\nbaz\n");
.stdout_only("baz\nbaz\n"); // spell-checker:disable-line
}
}
#[test]
fn test_suffix_param() {
for suffix_param in vec!["-s", "--suffix"] {
for &suffix_param in &["-s", "--suffix"] {
let path = "/foo/bar/baz.exe";
new_ucmd!()
.args(&[suffix_param, ".exe", path, path])
.succeeds()
.stdout_only("baz\nbaz\n");
.stdout_only("baz\nbaz\n"); // spell-checker:disable-line
}
}
#[test]
fn test_zero_param() {
for zero_param in vec!["-z", "--zero"] {
for &zero_param in &["-z", "--zero"] {
let path = "/foo/bar/baz";
new_ucmd!()
.args(&[zero_param, "-a", path, path])
@ -66,7 +93,12 @@ fn test_zero_param() {
}
fn expect_error(input: Vec<&str>) {
assert!(new_ucmd!().args(&input).fails().no_stdout().stderr.len() > 0);
assert!(!new_ucmd!()
.args(&input)
.fails()
.no_stdout()
.stderr_str()
.is_empty());
}
#[test]
@ -80,7 +112,38 @@ fn test_no_args() {
expect_error(vec![]);
}
#[test]
fn test_no_args_output() {
new_ucmd!()
.fails()
.stderr_is("basename: missing operand\nTry 'basename --help' for more information.");
}
#[test]
fn test_too_many_args() {
expect_error(vec!["a", "b", "c"]);
}
#[test]
fn test_too_many_args_output() {
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");
}
#[cfg(any(unix, target_os = "redox"))]
#[test]
fn invalid_utf8_args_unix() {
use std::os::unix::ffi::OsStrExt;
let source = [0x66, 0x6f, 0x80, 0x6f];
let os_str = OsStr::from_bytes(&source[..]);
test_invalid_utf8_args(os_str);
}

View file

@ -1,42 +1,249 @@
#[cfg(unix)]
extern crate unix_socket;
use crate::common::util::*;
#[cfg(unix)]
use std::fs::OpenOptions;
#[cfg(unix)]
use std::io::Read;
#[test]
fn test_output_simple() {
new_ucmd!()
.args(&["alpha.txt"])
.succeeds()
.stdout_only("abcde\nfghij\nklmno\npqrst\nuvwxyz\n"); // spell-checker:disable-line
}
#[test]
fn test_no_options() {
// spell-checker:disable-next-line
for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
// Give fixture through command line file argument
new_ucmd!()
.args(&[fixture])
.succeeds()
.stdout_is_fixture(fixture);
// Give fixture through stdin
new_ucmd!()
.pipe_in_fixture(fixture)
.succeeds()
.stdout_is_fixture(fixture);
}
}
#[test]
#[cfg(any(target_vendor = "apple", target_os = "linux", target_os = "android"))]
fn test_no_options_big_input() {
for &n in &[
0,
1,
42,
16 * 1024 - 7,
16 * 1024 - 1,
16 * 1024,
16 * 1024 + 1,
16 * 1024 + 3,
32 * 1024,
64 * 1024,
80 * 1024,
96 * 1024,
112 * 1024,
128 * 1024,
] {
let data = vec_of_size(n);
let data2 = data.clone();
assert_eq!(data.len(), data2.len());
new_ucmd!().pipe_in(data).succeeds().stdout_is_bytes(&data2);
}
}
#[test]
#[cfg(unix)]
fn test_fifo_symlink() {
use std::io::Write;
use std::thread;
let s = TestScenario::new(util_name!());
s.fixtures.mkdir("dir");
s.fixtures.mkfifo("dir/pipe");
assert!(s.fixtures.is_fifo("dir/pipe"));
// Make cat read the pipe through a symlink
s.fixtures.symlink_file("dir/pipe", "sympipe"); // spell-checker:disable-line
let proc = s.ucmd().args(&["sympipe"]).run_no_wait(); // spell-checker:disable-line
let data = vec_of_size(128 * 1024);
let data2 = data.clone();
let pipe_path = s.fixtures.plus("dir/pipe");
let thread = thread::spawn(move || {
let mut pipe = OpenOptions::new()
.write(true)
.create(false)
.open(pipe_path)
.unwrap();
pipe.write_all(&data).unwrap();
});
let output = proc.wait_with_output().unwrap();
assert_eq!(&output.stdout, &data2);
thread.join().unwrap();
}
#[test]
#[cfg(unix)]
fn test_piped_to_regular_file() {
use std::fs::read_to_string;
for &append in &[true, false] {
let s = TestScenario::new(util_name!());
let file_path = s.fixtures.plus("file.txt");
{
let file = OpenOptions::new()
.create_new(true)
.write(true)
.append(append)
.open(&file_path)
.unwrap();
s.ucmd()
.set_stdout(file)
.pipe_in_fixture("alpha.txt")
.succeeds();
}
let contents = read_to_string(&file_path).unwrap();
assert_eq!(contents, "abcde\nfghij\nklmno\npqrst\nuvwxyz\n"); // spell-checker:disable-line
}
}
#[test]
#[cfg(unix)]
fn test_piped_to_dev_null() {
for &append in &[true, false] {
let s = TestScenario::new(util_name!());
{
let dev_null = OpenOptions::new()
.write(true)
.append(append)
.open("/dev/null")
.unwrap();
s.ucmd()
.set_stdout(dev_null)
.pipe_in_fixture("alpha.txt")
.succeeds();
}
}
}
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_piped_to_dev_full() {
for &append in &[true, false] {
let s = TestScenario::new(util_name!());
{
let dev_full = OpenOptions::new()
.write(true)
.append(append)
.open("/dev/full")
.unwrap();
s.ucmd()
.set_stdout(dev_full)
.pipe_in_fixture("alpha.txt")
.fails()
.stderr_contains(&"No space left on device".to_owned());
}
}
}
#[test]
fn test_directory() {
let s = TestScenario::new(util_name!());
s.fixtures.mkdir("test_directory");
s.ucmd()
.args(&["test_directory"])
.fails()
.stderr_is("cat: test_directory: Is a directory");
}
#[test]
fn test_directory_and_file() {
let s = TestScenario::new(util_name!());
s.fixtures.mkdir("test_directory2");
// spell-checker:disable-next-line
for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
s.ucmd()
.args(&["test_directory2", fixture])
.fails()
.stderr_is("cat: test_directory2: Is a directory")
.stdout_is_fixture(fixture);
}
}
#[test]
#[cfg(unix)]
fn test_three_directories_and_file_and_stdin() {
let s = TestScenario::new(util_name!());
s.fixtures.mkdir("test_directory3");
s.fixtures.mkdir("test_directory3/test_directory4");
s.fixtures.mkdir("test_directory3/test_directory5");
s.ucmd()
.args(&[
"test_directory3/test_directory4",
"alpha.txt",
"-",
"file_which_does_not_exist.txt",
"nonewline.txt", // spell-checker:disable-line
"test_directory3/test_directory5",
"test_directory3/../test_directory3/test_directory5",
"test_directory3",
])
.pipe_in("stdout bytes")
.fails()
.stderr_is_fixture("three_directories_and_file_and_stdin.stderr.expected")
.stdout_is(
"abcde\nfghij\nklmno\npqrst\nuvwxyz\nstdout bytestext without a trailing newline", // spell-checker:disable-line
);
}
#[test]
fn test_output_multi_files_print_all_chars() {
// spell-checker:disable
new_ucmd!()
.args(&["alpha.txt", "256.txt", "-A", "-n"])
.succeeds()
.stdout_only(
" 1\tabcde$\n 2\tfghij$\n 3\tklmno$\n 4\tpqrst$\n \
5\tuvwxyz$\n 6\t^@^A^B^C^D^E^F^G^H^I$\n \
7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
!\"#$%&\'()*+,-./0123456789:;\
<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
5\tuvwxyz$\n 6\t^@^A^B^C^D^E^F^G^H^I$\n \
7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
!\"#$%&\'()*+,-./0123456789:;\
<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
);
// spell-checker:enable
}
#[test]
fn test_numbered_lines_no_trailing_newline() {
// spell-checker:disable
new_ucmd!()
.args(&["nonewline.txt", "alpha.txt", "-n"])
.succeeds()
.stdout_only(
" 1\ttext without a trailing newlineabcde\n 2\tfghij\n \
3\tklmno\n 4\tpqrst\n 5\tuvwxyz\n",
3\tklmno\n 4\tpqrst\n 5\tuvwxyz\n",
);
// spell-checker:enable
}
#[test]
fn test_stdin_show_nonprinting() {
for same_param in vec!["-v", "--show-nonprinting"] {
for same_param in &["-v", "--show-nonprinting"] {
new_ucmd!()
.args(&[same_param])
.pipe_in("\t\0\n")
@ -47,7 +254,7 @@ fn test_stdin_show_nonprinting() {
#[test]
fn test_stdin_show_tabs() {
for same_param in vec!["-T", "--show-tabs"] {
for same_param in &["-T", "--show-tabs"] {
new_ucmd!()
.args(&[same_param])
.pipe_in("\t\0\n")
@ -58,7 +265,7 @@ fn test_stdin_show_tabs() {
#[test]
fn test_stdin_show_ends() {
for same_param in vec!["-E", "--show-ends"] {
for &same_param in &["-E", "--show-ends"] {
new_ucmd!()
.args(&[same_param, "-"])
.pipe_in("\t\0\n\t")
@ -69,7 +276,7 @@ fn test_stdin_show_ends() {
#[test]
fn test_stdin_show_all() {
for same_param in vec!["-A", "--show-all"] {
for same_param in &["-A", "--show-all"] {
new_ucmd!()
.args(&[same_param])
.pipe_in("\t\0\n")
@ -98,7 +305,7 @@ fn test_stdin_nonprinting_and_tabs() {
#[test]
fn test_stdin_squeeze_blank() {
for same_param in vec!["-s", "--squeeze-blank"] {
for same_param in &["-s", "--squeeze-blank"] {
new_ucmd!()
.arg(same_param)
.pipe_in("\n\na\n\n\n\n\nb\n\n\n")
@ -109,7 +316,8 @@ fn test_stdin_squeeze_blank() {
#[test]
fn test_stdin_number_non_blank() {
for same_param in vec!["-b", "--number-nonblank"] {
// spell-checker:disable-next-line
for same_param in &["-b", "--number-nonblank"] {
new_ucmd!()
.arg(same_param)
.arg("-")
@ -121,7 +329,8 @@ fn test_stdin_number_non_blank() {
#[test]
fn test_non_blank_overrides_number() {
for same_param in vec!["-b", "--number-nonblank"] {
// spell-checker:disable-next-line
for &same_param in &["-b", "--number-nonblank"] {
new_ucmd!()
.args(&[same_param, "-"])
.pipe_in("\na\nb\n\n\nc")
@ -132,7 +341,7 @@ fn test_non_blank_overrides_number() {
#[test]
fn test_squeeze_blank_before_numbering() {
for same_param in vec!["-s", "--squeeze-blank"] {
for &same_param in &["-s", "--squeeze-blank"] {
new_ucmd!()
.args(&[same_param, "-n", "-"])
.pipe_in("a\n\n\nb")
@ -141,29 +350,96 @@ fn test_squeeze_blank_before_numbering() {
}
}
/// This tests reading from Unix character devices
#[test]
#[cfg(foo)]
fn test_domain_socket() {
use self::tempdir::TempDir;
use self::unix_socket::UnixListener;
use std::io::prelude::*;
use std::thread;
#[cfg(unix)]
fn test_dev_random() {
let mut buf = [0; 2048];
#[cfg(target_os = "linux")]
const DEV_RANDOM: &str = "/dev/urandom";
let dir = TempDir::new("unix_socket").expect("failed to create dir");
#[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();
let num_zeroes = buf.iter().fold(0, |mut acc, &n| {
if n == 0 {
acc += 1;
}
acc
});
// The probability of more than 512 zero bytes is essentially zero if the
// output is truly random.
assert!(num_zeroes < 512);
proc.kill().unwrap();
}
/// Reading from /dev/full should return an infinite amount of zero bytes.
/// Wikipedia says there is support on Linux, FreeBSD, and NetBSD.
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_dev_full() {
let mut buf = [0; 2048];
let mut proc = new_ucmd!().args(&["/dev/full"]).run_no_wait();
let mut proc_stdout = proc.stdout.take().unwrap();
let expected = [0; 2048];
proc_stdout.read_exact(&mut buf).unwrap();
assert_eq!(&buf[..], &expected[..]);
proc.kill().unwrap();
}
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
fn test_dev_full_show_all() {
let mut buf = [0; 2048];
let mut proc = new_ucmd!().args(&["-A", "/dev/full"]).run_no_wait();
let mut proc_stdout = proc.stdout.take().unwrap();
proc_stdout.read_exact(&mut buf).unwrap();
let expected: Vec<u8> = (0..buf.len())
.map(|n| if n & 1 == 0 { b'^' } else { b'@' })
.collect();
assert_eq!(&buf[..], &expected[..]);
proc.kill().unwrap();
}
#[test]
#[cfg(unix)]
#[ignore]
fn test_domain_socket() {
use std::io::prelude::*;
use std::sync::{Arc, Barrier};
use std::thread;
use unix_socket::UnixListener;
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");
// use a barrier to ensure we don't run cat before the listener is setup
let barrier = Arc::new(Barrier::new(2));
let barrier2 = Arc::clone(&barrier);
let thread = thread::spawn(move || {
let mut stream = listener.accept().expect("failed to accept connection").0;
barrier2.wait();
stream
.write_all(b"a\tb")
.expect("failed to write test data");
});
new_ucmd!()
.args(&[socket_path])
.succeeds()
.stdout_only("a\tb");
let child = new_ucmd!().args(&[socket_path]).run_no_wait();
barrier.wait();
let stdout = &child.wait_with_output().unwrap().stdout;
let output = String::from_utf8_lossy(stdout);
assert_eq!("a\tb", output);
thread.join().unwrap();
}

View file

@ -1,3 +1,5 @@
// spell-checker:ignore (words) nosuchgroup groupname
use crate::common::util::*;
use rust_users::*;
@ -6,7 +8,34 @@ fn test_invalid_option() {
new_ucmd!().arg("-w").arg("/").fails();
}
static DIR: &'static str = "/tmp";
static DIR: &str = "/tmp";
// we should always get both arguments, regardless of whether --reference was used
#[test]
fn test_help() {
new_ucmd!()
.arg("--help")
.succeeds()
.stdout_contains("ARGS:\n <GROUP> \n <FILE>... ");
}
#[test]
fn test_help_ref() {
new_ucmd!()
.arg("--help")
.arg("--reference=ref_file")
.succeeds()
.stdout_contains("ARGS:\n <GROUP> \n <FILE>... ");
}
#[test]
fn test_ref_help() {
new_ucmd!()
.arg("--reference=ref_file")
.arg("--help")
.succeeds()
.stdout_contains("ARGS:\n <GROUP> \n <FILE>... ");
}
#[test]
fn test_invalid_group() {
@ -104,7 +133,7 @@ fn test_reference() {
// skip for root or MS-WSL
// * MS-WSL is bugged (as of 2019-12-25), allowing non-root accounts su-level privileges for `chgrp`
// * for MS-WSL, succeeds and stdout == 'group of /etc retained as root'
if !(get_effective_gid() == 0 || is_wsl()) {
if !(get_effective_gid() == 0 || uucore::os::is_wsl_1()) {
new_ucmd!()
.arg("-v")
.arg("--reference=/etc/passwd")
@ -115,13 +144,56 @@ fn test_reference() {
}
#[test]
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
fn test_reference() {
new_ucmd!()
.arg("-v")
.arg("--reference=/etc/passwd")
.arg("--reference=ref_file")
.arg("/etc")
.succeeds();
.fails()
// group name can differ, so just check the first part of the message
.stderr_contains("chgrp: changing group of '/etc': Operation not permitted (os error 1)\nfailed to change group of '/etc' from ");
}
#[test]
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
fn test_reference_multi_no_equal() {
new_ucmd!()
.arg("-v")
.arg("--reference")
.arg("ref_file")
.arg("file1")
.arg("file2")
.succeeds()
.stderr_contains("chgrp: group of 'file1' retained as ")
.stderr_contains("\nchgrp: group of 'file2' retained as ");
}
#[test]
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
fn test_reference_last() {
new_ucmd!()
.arg("-v")
.arg("file1")
.arg("file2")
.arg("file3")
.arg("--reference")
.arg("ref_file")
.succeeds()
.stderr_contains("chgrp: group of 'file1' retained as ")
.stderr_contains("\nchgrp: group of 'file2' retained as ")
.stderr_contains("\nchgrp: group of 'file3' retained as ");
}
#[test]
fn test_missing_files() {
new_ucmd!()
.arg("-v")
.arg("groupname")
.fails()
.stderr_contains(
"error: The following required arguments were not provided:\n <FILE>...\n",
);
}
#[test]
@ -133,7 +205,7 @@ fn test_big_p() {
.arg("bin")
.arg("/proc/self/cwd")
.fails()
.stderr_is(
.stderr_contains(
"chgrp: changing group of '/proc/self/cwd': Operation not permitted (os error 1)\n",
);
}
@ -149,7 +221,7 @@ fn test_big_h() {
.arg("bin")
.arg("/proc/self/fd")
.fails()
.stderr
.stderr_str()
.lines()
.fold(0, |acc, _| acc + 1)
> 1

View file

@ -4,10 +4,12 @@ use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
use std::sync::Mutex;
extern crate libc;
use self::chmod::strip_minus_from_mode;
extern crate chmod;
use self::libc::umask;
static TEST_FILE: &'static str = "file";
static REFERENCE_FILE: &'static str = "reference";
static TEST_FILE: &str = "file";
static REFERENCE_FILE: &str = "reference";
static REFERENCE_PERMS: u32 = 0o247;
lazy_static! {
static ref UMASK_MUTEX: Mutex<()> = Mutex::new(());
@ -19,7 +21,7 @@ struct TestCase {
after: u32,
}
fn mkfile(file: &str, mode: u32) {
fn make_file(file: &str, mode: u32) {
OpenOptions::new()
.mode(mode)
.create(true)
@ -32,30 +34,30 @@ fn mkfile(file: &str, mode: u32) {
}
fn run_single_test(test: &TestCase, at: AtPath, mut ucmd: UCommand) {
mkfile(&at.plus_as_string(TEST_FILE), test.before);
make_file(&at.plus_as_string(TEST_FILE), test.before);
let perms = at.metadata(TEST_FILE).permissions().mode();
if perms != test.before {
panic!(format!(
panic!(
"{}: expected: {:o} got: {:o}",
"setting permissions on test files before actual test run failed", test.after, perms
));
);
}
for arg in &test.args {
ucmd.arg(arg);
}
let r = ucmd.run();
if !r.success {
println!("{}", r.stderr);
panic!(format!("{:?}: failed", ucmd.raw));
if !r.succeeded() {
println!("{}", r.stderr_str());
panic!("{:?}: failed", ucmd.raw);
}
let perms = at.metadata(TEST_FILE).permissions().mode();
if perms != test.after {
panic!(format!(
panic!(
"{:?}: expected: {:o} got: {:o}",
ucmd.raw, test.after, perms
));
);
}
}
@ -67,6 +69,7 @@ fn run_tests(tests: Vec<TestCase>) {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_octal() {
let tests = vec![
TestCase {
@ -119,6 +122,8 @@ fn test_chmod_octal() {
}
#[test]
#[allow(clippy::unreadable_literal)]
// spell-checker:disable-next-line
fn test_chmod_ugoa() {
let _guard = UMASK_MUTEX.lock();
@ -214,6 +219,7 @@ fn test_chmod_ugoa() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_ugo_copy() {
let tests = vec![
TestCase {
@ -246,6 +252,7 @@ fn test_chmod_ugo_copy() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_many_options() {
let _guard = UMASK_MUTEX.lock();
@ -262,6 +269,7 @@ fn test_chmod_many_options() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_reference_file() {
let tests = vec![
TestCase {
@ -276,11 +284,32 @@ fn test_chmod_reference_file() {
},
];
let (at, ucmd) = at_and_ucmd!();
mkfile(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS);
make_file(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS);
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]
#[allow(clippy::unreadable_literal)]
fn test_chmod_recursive() {
let _guard = UMASK_MUTEX.lock();
@ -290,18 +319,19 @@ fn test_chmod_recursive() {
at.mkdir("a/b");
at.mkdir("a/b/c");
at.mkdir("z");
mkfile(&at.plus_as_string("a/a"), 0o100444);
mkfile(&at.plus_as_string("a/b/b"), 0o100444);
mkfile(&at.plus_as_string("a/b/c/c"), 0o100444);
mkfile(&at.plus_as_string("z/y"), 0o100444);
make_file(&at.plus_as_string("a/a"), 0o100444);
make_file(&at.plus_as_string("a/b/b"), 0o100444);
make_file(&at.plus_as_string("a/b/c/c"), 0o100444);
make_file(&at.plus_as_string("z/y"), 0o100444);
let result = ucmd
.arg("-R")
ucmd.arg("-R")
.arg("--verbose")
.arg("-r,a+w")
.arg("a")
.arg("z")
.succeeds();
.succeeds()
.stderr_contains(&"to 333 (-wx-wx-wx)")
.stderr_contains(&"to 222 (-w--w--w-)");
assert_eq!(at.metadata("z/y").permissions().mode(), 0o100222);
assert_eq!(at.metadata("a/a").permissions().mode(), 0o100222);
@ -310,8 +340,6 @@ fn test_chmod_recursive() {
println!("mode {:o}", at.metadata("a").permissions().mode());
assert_eq!(at.metadata("a").permissions().mode(), 0o40333);
assert_eq!(at.metadata("z").permissions().mode(), 0o40333);
assert!(result.stderr.contains("to 333 (-wx-wx-wx)"));
assert!(result.stderr.contains("to 222 (-w--w--w-)"));
unsafe {
umask(original_umask);
@ -320,57 +348,145 @@ fn test_chmod_recursive() {
#[test]
fn test_chmod_non_existing_file() {
let (_at, mut ucmd) = at_and_ucmd!();
let result = ucmd
new_ucmd!()
.arg("-R")
.arg("--verbose")
.arg("-r,a+w")
.arg("dont-exist")
.fails();
assert_eq!(
result.stderr,
"chmod: error: no such file or directory 'dont-exist'\n"
);
.arg("does-not-exist")
.fails()
.stderr_contains(&"cannot access 'does-not-exist': No such file or directory");
}
#[test]
fn test_chmod_preserve_root() {
let (_at, mut ucmd) = at_and_ucmd!();
let result = ucmd
new_ucmd!()
.arg("-R")
.arg("--preserve-root")
.arg("755")
.arg("/")
.fails();
assert!(result
.stderr
.contains("chmod: error: it is dangerous to operate recursively on '/'"));
.fails()
.stderr_contains(&"chmod: it is dangerous to operate recursively on '/'");
}
#[test]
fn test_chmod_symlink_non_existing_file() {
let (at, mut ucmd) = at_and_ucmd!();
at.symlink_file("/non-existing", "test-long.link");
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let _result = ucmd
.arg("-R")
let non_existing = "test_chmod_symlink_non_existing_file";
let test_symlink = "test_chmod_symlink_non_existing_file_symlink";
let expected_stdout = &format!(
"failed to change mode of '{}' from 0000 (---------) to 0000 (---------)",
test_symlink
);
let expected_stderr = &format!("cannot operate on dangling symlink '{}'", test_symlink);
at.symlink_file(non_existing, test_symlink);
// this cannot succeed since the symbolic link dangles
scene
.ucmd()
.arg("755")
.arg("-v")
.arg("test-long.link")
.fails();
.arg(test_symlink)
.fails()
.code_is(1)
.stdout_contains(expected_stdout)
.stderr_contains(expected_stderr);
// this should be the same than with just '-v' but without stderr
scene
.ucmd()
.arg("755")
.arg("-v")
.arg("-f")
.arg(test_symlink)
.run()
.code_is(1)
.no_stderr()
.stdout_contains(expected_stdout);
}
#[test]
fn test_chmod_symlink_non_existing_recursive() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("tmp");
at.symlink_file("/non-existing", "tmp/test-long.link");
fn test_chmod_symlink_non_existing_file_recursive() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = ucmd.arg("-R").arg("755").arg("-v").arg("tmp").succeeds();
// it should be a success
println!("stderr {}", result.stderr);
println!("stdout {}", result.stdout);
assert!(result
.stderr
.contains("neither symbolic link 'tmp/test-long.link' nor referent has been changed"));
let non_existing = "test_chmod_symlink_non_existing_file_recursive";
let test_symlink = "test_chmod_symlink_non_existing_file_recursive_symlink";
let test_directory = "test_chmod_symlink_non_existing_file_directory";
at.mkdir(test_directory);
at.symlink_file(
non_existing,
&format!("{}/{}", test_directory, test_symlink),
);
// this should succeed
scene
.ucmd()
.arg("-R")
.arg("755")
.arg(test_directory)
.succeeds()
.no_stderr()
.no_stdout();
let expected_stdout = &format!(
// spell-checker:disable-next-line
"mode of '{}' retained as 0755 (rwxr-xr-x)\nneither symbolic link '{}/{}' nor referent has been changed",
test_directory, test_directory, test_symlink
);
// '-v': this should succeed without stderr
scene
.ucmd()
.arg("-R")
.arg("-v")
.arg("755")
.arg(test_directory)
.succeeds()
.stdout_contains(expected_stdout)
.no_stderr();
// '-vf': this should be the same than with just '-v'
scene
.ucmd()
.arg("-R")
.arg("-v")
.arg("-f")
.arg("755")
.arg(test_directory)
.succeeds()
.stdout_contains(expected_stdout)
.no_stderr();
}
#[test]
fn test_chmod_strip_minus_from_mode() {
let tests = vec![
// ( before, after )
("chmod -v -xw -R FILE", "chmod -v xw -R FILE"),
("chmod g=rwx FILE -c", "chmod g=rwx FILE -c"),
(
"chmod -c -R -w,o+w FILE --preserve-root",
"chmod -c -R w,o+w FILE --preserve-root",
),
("chmod -c -R +w FILE ", "chmod -c -R +w FILE "),
("chmod a=r,=xX FILE", "chmod a=r,=xX FILE"),
(
"chmod -v --reference REF_FILE -R FILE",
"chmod -v --reference REF_FILE -R FILE",
),
("chmod -Rvc -w-x FILE", "chmod -Rvc w-x FILE"),
("chmod 755 -v FILE", "chmod 755 -v FILE"),
("chmod -v +0004 FILE -R", "chmod -v +0004 FILE -R"),
("chmod -v -0007 FILE -R", "chmod -v 0007 FILE -R"),
];
for test in tests {
let mut args: Vec<String> = test.0.split(' ').map(|v| v.to_string()).collect();
let _mode_had_minus_prefix = strip_minus_from_mode(&mut args);
assert_eq!(test.1, args.join(" "));
}
}

View file

@ -1,9 +1,39 @@
// spell-checker:ignore (words) agroupthatdoesntexist auserthatdoesntexist groupname notexisting passgrp
use crate::common::util::*;
#[cfg(target_os = "linux")]
use rust_users::get_effective_uid;
extern crate chown;
// Apparently some CI environments have configuration issues, e.g. with 'whoami' and 'id'.
// If we are running inside the CI and "needle" is in "stderr" skipping this test is
// 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: cannot find name for user ID 1001"
// TODO: Maybe `adduser --uid 1001 username` can put things right?
//
// stderr: "id: cannot find name for group ID 116"
// stderr: "thread 'main' panicked at 'called `Result::unwrap()` on an `Err`
// value: Custom { kind: NotFound, error: "No such id: 1001" }',
// /project/src/uucore/src/lib/features/perms.rs:176:44"
//
fn skipping_test_is_okay(result: &CmdResult, needle: &str) -> bool {
if !result.succeeded() {
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
if is_ci() && result.stderr_str().contains(needle) {
println!("test skipped:");
return true;
} else {
result.success();
}
}
false
}
#[cfg(test)]
mod test_passgrp {
use super::chown::entries::{gid2grp, grp2gid, uid2usr, usr2uid};
@ -11,7 +41,7 @@ mod test_passgrp {
#[test]
fn test_usr2uid() {
assert_eq!(0, usr2uid("root").unwrap());
assert!(usr2uid("88888888").is_err());
assert!(usr2uid("88_888_888").is_err());
assert!(usr2uid("auserthatdoesntexist").is_err());
}
@ -22,14 +52,14 @@ mod test_passgrp {
} else {
assert_eq!(0, grp2gid("wheel").unwrap());
}
assert!(grp2gid("88888888").is_err());
assert!(grp2gid("88_888_888").is_err());
assert!(grp2gid("agroupthatdoesntexist").is_err());
}
#[test]
fn test_uid2usr() {
assert_eq!("root", uid2usr(0).unwrap());
assert!(uid2usr(88888888).is_err());
assert!(uid2usr(88_888_888).is_err());
}
#[test]
@ -39,7 +69,7 @@ mod test_passgrp {
} else {
assert_eq!("wheel", gid2grp(0).unwrap());
}
assert!(gid2grp(88888888).is_err());
assert!(gid2grp(88_888_888).is_err());
}
}
@ -49,338 +79,427 @@ fn test_invalid_option() {
}
#[test]
fn test_chown_myself() {
fn test_chown_only_owner() {
// test chown username file.txt
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") {
return;
}
println!("results {}", result.stdout);
let username = result.stdout.trim_end();
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let file1 = "test_chown_file1";
at.touch(file1);
let result = ucmd.arg(username).arg(file1).run();
println!("results stdout {}", result.stdout);
println!("results stderr {}", result.stderr);
if is_ci() && result.stderr.contains("invalid user") {
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
return;
}
assert!(result.success);
}
#[test]
fn test_chown_myself_second() {
// test chown username: file.txt
let scene = TestScenario::new(util_name!());
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return;
}
println!("results {}", result.stdout);
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
at.touch(file1);
let result = ucmd
.arg(result.stdout.trim_end().to_owned() + ":")
// since only superuser can change owner, we have to change from ourself to ourself
let result = scene
.ucmd()
.arg(user_name)
.arg("--verbose")
.arg(file1)
.run();
result.stderr_contains(&"retained as");
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
// try to change to another existing user, e.g. 'root'
scene
.ucmd()
.arg("root")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
fn test_chown_myself_group() {
// test chown username:group file.txt
fn test_chown_only_owner_colon() {
// test chown username: file.txt
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") {
return;
}
println!("user name = {}", result.stdout);
let username = result.stdout.trim_end();
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let file1 = "test_chown_file1";
at.touch(file1);
scene
.ucmd()
.arg(format!("{}:", user_name))
.arg("--verbose")
.arg(file1)
.succeeds()
.stderr_contains(&"retained as");
scene
.ucmd()
.arg("root:")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
fn test_chown_only_colon() {
// test chown : file.txt
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file1 = "test_chown_file1";
at.touch(file1);
// expected:
// $ chown -v : file.txt 2>out_err ; echo $? ; cat out_err
// ownership of 'file.txt' retained
// 0
let result = scene.ucmd().arg(":").arg("--verbose").arg(file1).run();
if skipping_test_is_okay(&result, "No such id") {
return;
}
result.stderr_contains(&"retained as"); // TODO: verbose is not printed to stderr in GNU chown
// test chown : file.txt
// expected:
// $ chown -v :: file.txt 2>out_err ; echo $? ; cat out_err
// 1
// chown: invalid group: '::'
scene
.ucmd()
.arg("::")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"invalid group: '::'");
}
#[test]
fn test_chown_failed_stdout() {
// test chown root file.txt
// TODO: implement once output "failed to change" to stdout is fixed
// expected:
// $ chown -v root file.txt 2>out_err ; echo $? ; cat out_err
// failed to change ownership of 'file.txt' from jhs to root
// 1
// chown: changing ownership of 'file.txt': Operation not permitted
}
#[test]
fn test_chown_owner_group() {
// test chown username:group file.txt
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd("whoami").run();
if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") {
return;
}
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let file1 = "test_chown_file1";
at.touch(file1);
let result = scene.cmd("id").arg("-gn").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
if skipping_test_is_okay(&result, "id: cannot find name for group ID") {
return;
}
println!("group name = {}", result.stdout);
let group = result.stdout.trim_end();
let group_name = String::from(result.stdout_str().trim());
assert!(!group_name.is_empty());
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let perm = username.to_owned() + ":" + group;
at.touch(file1);
let result = ucmd.arg(perm).arg(file1).run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("chown: invalid group:") {
// With some Ubuntu into the CI, we can get this answer
let result = scene
.ucmd()
.arg(format!("{}:{}", user_name, group_name))
.arg("--verbose")
.arg(file1)
.run();
if skipping_test_is_okay(&result, "chown: invalid group:") {
return;
}
assert!(result.success);
result.stderr_contains(&"retained as");
// TODO: on macos group name is not recognized correctly: "chown: invalid group: 'root:root'
#[cfg(any(windows, all(unix, not(target_os = "macos"))))]
scene
.ucmd()
.arg("root:root")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
// TODO: on macos group name is not recognized correctly: "chown: invalid group: ':groupname'
#[cfg(any(windows, all(unix, not(target_os = "macos"))))]
fn test_chown_only_group() {
// test chown :group file.txt
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") {
return;
}
println!("results {}", result.stdout);
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let perm = ":".to_owned() + result.stdout.trim_end();
let file1 = "test_chown_file1";
at.touch(file1);
let result = ucmd.arg(perm).arg(file1).run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("Operation not permitted") {
let result = scene
.ucmd()
.arg(format!(":{}", user_name))
.arg("--verbose")
.arg(file1)
.run();
if is_ci() && result.stderr_str().contains("Operation not permitted") {
// With ubuntu with old Rust in the CI, we can get an error
return;
}
if is_ci() && result.stderr.contains("chown: invalid group:") {
if is_ci() && result.stderr_str().contains("chown: invalid group:") {
// With mac into the CI, we can get this answer
return;
}
assert!(result.success);
result.stderr_contains(&"retained as");
result.success();
scene
.ucmd()
.arg(":root")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
fn test_chown_only_id() {
fn test_chown_only_user_id() {
// test chown 1111 file.txt
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd_keepenv("id").arg("-u").run();
if skipping_test_is_okay(&result, "id: cannot find name for group ID") {
return;
}
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let id = String::from(result.stdout.trim());
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let user_id = String::from(result.stdout_str().trim());
assert!(!user_id.is_empty());
let file1 = "test_chown_file1";
at.touch(file1);
let result = ucmd.arg(id).arg(file1).run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("chown: invalid user:") {
// With some Ubuntu into the CI, we can get this answer
let result = scene.ucmd().arg(user_id).arg("--verbose").arg(file1).run();
if skipping_test_is_okay(&result, "invalid user") {
// From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)"
// stderr: "chown: invalid user: '1001'
return;
}
assert!(result.success);
result.stderr_contains(&"retained as");
scene
.ucmd()
.arg("0")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
fn test_chown_only_group_id() {
// test chown :1111 file.txt
let result = TestScenario::new("id").ucmd_keepenv().arg("-g").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd_keepenv("id").arg("-g").run();
if skipping_test_is_okay(&result, "id: cannot find name for group ID") {
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let id = String::from(result.stdout.trim());
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let group_id = String::from(result.stdout_str().trim());
assert!(!group_id.is_empty());
let file1 = "test_chown_file1";
at.touch(file1);
let perm = ":".to_owned() + &id;
let result = ucmd.arg(perm).arg(file1).run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("chown: invalid group:") {
let result = scene
.ucmd()
.arg(format!(":{}", group_id))
.arg("--verbose")
.arg(file1)
.run();
if skipping_test_is_okay(&result, "chown: invalid group:") {
// With mac into the CI, we can get this answer
return;
}
assert!(result.success);
result.stderr_contains(&"retained as");
// Apparently on CI "macos-latest, x86_64-apple-darwin, feat_os_macos"
// the process has the rights to change from runner:staff to runner:wheel
#[cfg(any(windows, all(unix, not(target_os = "macos"))))]
scene
.ucmd()
.arg(":0")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
fn test_chown_both_id() {
fn test_chown_owner_group_id() {
// test chown 1111:1111 file.txt
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd_keepenv("id").arg("-u").run();
if skipping_test_is_okay(&result, "id: cannot find name for group ID") {
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let id_user = String::from(result.stdout.trim());
let user_id = String::from(result.stdout_str().trim());
assert!(!user_id.is_empty());
let result = TestScenario::new("id").ucmd_keepenv().arg("-g").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
let result = scene.cmd_keepenv("id").arg("-g").run();
if skipping_test_is_okay(&result, "id: cannot find name for group ID") {
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let id_group = String::from(result.stdout.trim());
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let group_id = String::from(result.stdout_str().trim());
assert!(!group_id.is_empty());
let file1 = "test_chown_file1";
at.touch(file1);
let perm = id_user + &":".to_owned() + &id_group;
let result = ucmd.arg(perm).arg(file1).run();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("invalid user") {
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
let result = scene
.ucmd()
.arg(format!("{}:{}", user_id, group_id))
.arg("--verbose")
.arg(file1)
.run();
if skipping_test_is_okay(&result, "invalid user") {
// From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)"
// stderr: "chown: invalid user: '1001:116'
return;
}
result.stderr_contains(&"retained as");
assert!(result.success);
scene
.ucmd()
.arg("0:0")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
fn test_chown_both_mix() {
// test chown 1111:1111 file.txt
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
fn test_chown_owner_group_mix() {
// test chown 1111:group file.txt
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd_keepenv("id").arg("-u").run();
if skipping_test_is_okay(&result, "id: cannot find name for group ID") {
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let id_user = String::from(result.stdout.trim());
let user_id = String::from(result.stdout_str().trim());
assert!(!user_id.is_empty());
let result = TestScenario::new("id").ucmd_keepenv().arg("-gn").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
let result = scene.cmd_keepenv("id").arg("-gn").run();
if skipping_test_is_okay(&result, "id: cannot find name for group ID") {
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let group_name = String::from(result.stdout.trim());
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let group_name = String::from(result.stdout_str().trim());
assert!(!group_name.is_empty());
let file1 = "test_chown_file1";
at.touch(file1);
let perm = id_user + &":".to_owned() + &group_name;
let result = ucmd.arg(perm).arg(file1).run();
let result = scene
.ucmd()
.arg(format!("{}:{}", user_id, group_name))
.arg("--verbose")
.arg(file1)
.run();
result.stderr_contains(&"retained as");
if is_ci() && result.stderr.contains("invalid user") {
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
return;
}
assert!(result.success);
// TODO: on macos group name is not recognized correctly: "chown: invalid group: '0:root'
#[cfg(any(windows, all(unix, not(target_os = "macos"))))]
scene
.ucmd()
.arg("0:root")
.arg("--verbose")
.arg(file1)
.fails()
.stderr_contains(&"failed to change");
}
#[test]
fn test_chown_recursive() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") {
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let username = result.stdout.trim_end();
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
at.mkdir("a/b");
at.mkdir("a/b/c");
at.mkdir_all("a/b/c");
at.mkdir("z");
at.touch(&at.plus_as_string("a/a"));
at.touch(&at.plus_as_string("a/b/b"));
at.touch(&at.plus_as_string("a/b/c/c"));
at.touch(&at.plus_as_string("z/y"));
let result = ucmd
let result = scene
.ucmd()
.arg("-R")
.arg("--verbose")
.arg(username)
.arg(user_name)
.arg("a")
.arg("z")
.run();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("invalid user") {
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
return;
}
assert!(result.stderr.contains("ownership of 'a/a' retained as"));
assert!(result.stderr.contains("ownership of 'z/y' retained as"));
assert!(result.success);
result.stderr_contains(&"ownership of 'a/a' retained as");
result.stderr_contains(&"ownership of 'z/y' retained as");
}
#[test]
fn test_root_preserve() {
let scene = TestScenario::new(util_name!());
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") {
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let username = result.stdout.trim_end();
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let result = new_ucmd!()
let result = scene
.ucmd()
.arg("--preserve-root")
.arg("-R")
.arg(username)
.arg(user_name)
.arg("/")
.fails();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("invalid user") {
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
return;
}
assert!(result
.stderr
.contains("chown: it is dangerous to operate recursively"));
result.stderr_contains(&"chown: it is dangerous to operate recursively");
}
#[cfg(target_os = "linux")]
@ -392,8 +511,34 @@ fn test_big_p() {
.arg("bin")
.arg("/proc/self/cwd")
.fails()
.stderr_is(
"chown: changing ownership of '/proc/self/cwd': Operation not permitted (os error 1)\n",
.stderr_contains(
"chown: changing ownership of '/proc/self/cwd': Operation not permitted (os error 1)",
);
}
}
#[test]
fn test_chown_file_notexisting() {
// test chown username not_existing
let scene = TestScenario::new(util_name!());
let result = scene.cmd("whoami").run();
if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") {
return;
}
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let _result = scene
.ucmd()
.arg(user_name)
.arg("--verbose")
.arg("not_existing")
.fails();
// TODO: uncomment once "failed to change ownership of '{}' to {}" added to stdout
// result.stderr_contains(&"retained as");
// TODO: uncomment once message changed from "cannot dereference" to "cannot access"
// result.stderr_contains(&"cannot access 'not_existing': No such file or directory");
}

View file

@ -1 +1,91 @@
// ToDO: add tests
// spell-checker:ignore (words) araba newroot userspec
use crate::common::util::*;
#[test]
fn test_missing_operand() {
let result = new_ucmd!().run();
assert!(result
.stderr_str()
.starts_with("error: The following required arguments were not provided"));
assert!(result.stderr_str().contains("<newroot>"));
}
#[test]
fn test_enter_chroot_fails() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("jail");
let result = ucmd.arg("jail").fails();
assert!(result
.stderr_str()
.starts_with("chroot: cannot chroot to jail: Operation not permitted (os error 1)"));
}
#[test]
fn test_no_such_directory() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch(&at.plus_as_string("a"));
ucmd.arg("a")
.fails()
.stderr_is("chroot: cannot change root directory to `a`: no such directory");
}
#[test]
fn test_invalid_user_spec() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
let result = ucmd.arg("a").arg("--userspec=ARABA:").fails();
assert!(result.stderr_str().starts_with("chroot: invalid userspec"));
}
#[test]
fn test_preference_of_userspec() {
let scene = TestScenario::new(util_name!());
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr_str().contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return;
}
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
let username = result.stdout_str().trim_end();
let ts = TestScenario::new("id");
let result = ts.cmd("id").arg("-g").arg("-n").run();
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
if is_ci() && result.stderr_str().contains("cannot find name for user ID") {
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
return;
}
let group_name = result.stdout_str().trim_end();
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
let result = ucmd
.arg("a")
.arg("--user")
.arg("fake")
.arg("-G")
.arg("ABC,DEF")
.arg(format!("--userspec={}:{}", username, group_name))
.run();
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
}

View file

@ -1,3 +1,5 @@
// spell-checker:ignore (words) asdf
use crate::common::util::*;
#[test]
@ -24,3 +26,87 @@ fn test_stdin() {
.succeeds()
.stdout_is_fixture("stdin.expected");
}
#[test]
fn test_empty() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("a");
ucmd.arg("a")
.succeeds()
.no_stderr()
.normalized_newlines_stdout_is("4294967295 0 a\n");
}
#[test]
fn test_arg_overrides_stdin() {
let (at, mut ucmd) = at_and_ucmd!();
let input = "foobarfoobar"; // spell-checker:disable-line
at.touch("a");
ucmd.arg("a")
.pipe_in(input.as_bytes())
// the command might have exited before all bytes have been pipe in.
// in that case, we don't care about the error (broken pipe)
.ignore_stdin_write_error()
.succeeds()
.no_stderr()
.normalized_newlines_stdout_is("4294967295 0 a\n");
}
#[test]
fn test_invalid_file() {
let ts = TestScenario::new(util_name!());
let at = ts.fixtures.clone();
let folder_name = "asdf";
// First check when file doesn't exist
ts.ucmd()
.arg(folder_name)
.fails()
.no_stdout()
.stderr_contains("cksum: 'asdf' No such file or directory");
// Then check when the file is of an invalid type
at.mkdir(folder_name);
ts.ucmd()
.arg(folder_name)
.fails()
.no_stdout()
.stderr_contains("cksum: 'asdf' Is a directory");
}
// Make sure crc is correct for files larger than 32 bytes
// but <128 bytes (1 fold pclmul) // spell-checker:disable-line
#[test]
fn test_crc_for_bigger_than_32_bytes() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("chars.txt").succeeds();
let mut stdout_split = result.stdout_str().split(' ');
let cksum: i64 = stdout_split.next().unwrap().parse().unwrap();
let bytes_cnt: i64 = stdout_split.next().unwrap().parse().unwrap();
assert_eq!(cksum, 586_047_089);
assert_eq!(bytes_cnt, 16);
}
#[test]
fn test_stdin_larger_than_128_bytes() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("larger_than_2056_bytes.txt").succeeds();
let mut stdout_split = result.stdout_str().split(' ');
let cksum: i64 = stdout_split.next().unwrap().parse().unwrap();
let bytes_cnt: i64 = stdout_split.next().unwrap().parse().unwrap();
assert_eq!(cksum, 945_881_979);
assert_eq!(bytes_cnt, 2058);
}

View file

@ -1,3 +1,5 @@
// spell-checker:ignore (words) defaultcheck nocheck
use crate::common::util::*;
#[test]
@ -33,19 +35,19 @@ fn ab_dash_three() {
}
#[test]
fn aempty() {
fn a_empty() {
new_ucmd!()
.args(&["a", "empty"])
.succeeds()
.stdout_only_fixture("aempty.expected");
.stdout_only_fixture("aempty.expected"); // spell-checker:disable-line
}
#[test]
fn emptyempty() {
fn empty_empty() {
new_ucmd!()
.args(&["empty", "empty"])
.succeeds()
.stdout_only_fixture("emptyempty.expected");
.stdout_only_fixture("emptyempty.expected"); // spell-checker:disable-line
}
#[cfg_attr(not(feature = "test_unimplemented"), ignore)]
@ -68,13 +70,13 @@ fn output_delimiter_require_arg() {
// even though (info) documentation suggests this is an option
// in latest GNU Coreutils comm, it actually is not.
// this test is essentially an alarm in case someone well-intendingly
// implements it.
// this test is essentially an alarm in case some well-intending
// developer implements it.
//marked as unimplemented as error message not set yet.
#[cfg_attr(not(feature = "test_unimplemented"), ignore)]
#[test]
fn zero_terminated() {
for param in vec!["-z", "--zero-terminated"] {
for &param in &["-z", "--zero-terminated"] {
new_ucmd!()
.args(&[param, "a", "b"])
.fails()

File diff suppressed because it is too large Load diff

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")
@ -904,7 +904,7 @@ fn test_no_match() {
}
#[test]
fn test_too_small_linenum() {
fn test_too_small_line_num() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/20/", "10", "/40/"])
.succeeds()
@ -921,7 +921,7 @@ fn test_too_small_linenum() {
}
#[test]
fn test_too_small_linenum_equal() {
fn test_too_small_line_num_equal() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/20/", "20"])
.succeeds()
@ -937,7 +937,7 @@ fn test_too_small_linenum_equal() {
}
#[test]
fn test_too_small_linenum_elided() {
fn test_too_small_line_num_elided() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "-z", "/20/", "10", "/40/"])
.succeeds()
@ -953,7 +953,7 @@ fn test_too_small_linenum_elided() {
}
#[test]
fn test_too_small_linenum_negative_offset() {
fn test_too_small_line_num_negative_offset() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/20/-5", "10", "/40/"])
.succeeds()
@ -970,7 +970,7 @@ fn test_too_small_linenum_negative_offset() {
}
#[test]
fn test_too_small_linenum_twice() {
fn test_too_small_line_num_twice() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/20/", "10", "15", "/40/"])
.succeeds()
@ -988,11 +988,11 @@ fn test_too_small_linenum_twice() {
}
#[test]
fn test_too_small_linenum_repeat() {
fn test_too_small_line_num_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*"))
@ -1020,12 +1020,12 @@ fn test_too_small_linenum_repeat() {
}
#[test]
fn test_linenum_out_of_range1() {
fn test_line_num_out_of_range1() {
let (at, mut ucmd) = at_and_ucmd!();
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")
@ -1046,12 +1046,12 @@ fn test_linenum_out_of_range1() {
}
#[test]
fn test_linenum_out_of_range2() {
fn test_line_num_out_of_range2() {
let (at, mut ucmd) = at_and_ucmd!();
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")
@ -1073,12 +1073,12 @@ fn test_linenum_out_of_range2() {
}
#[test]
fn test_linenum_out_of_range3() {
fn test_line_num_out_of_range3() {
let (at, mut ucmd) = at_and_ucmd!();
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")
@ -1100,12 +1100,12 @@ fn test_linenum_out_of_range3() {
}
#[test]
fn test_linenum_out_of_range4() {
fn test_line_num_out_of_range4() {
let (at, mut ucmd) = at_and_ucmd!();
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")
@ -1141,7 +1141,7 @@ fn test_skip_to_match_negative_offset_before_a_match() {
}
#[test]
fn test_skip_to_match_negative_offset_before_a_linenum() {
fn test_skip_to_match_negative_offset_before_a_line_num() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["numbers50.txt", "/20/-10", "15"])
.succeeds()
@ -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")
@ -1247,11 +1247,11 @@ fn test_up_to_match_context_underflow() {
// the offset is out of range because of the first pattern
// NOTE: output different than gnu's: the empty split is written but the rest of the input file is not
#[test]
fn test_linenum_range_with_up_to_match1() {
fn test_line_num_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*"))
@ -1277,11 +1277,11 @@ fn test_linenum_range_with_up_to_match1() {
// the offset is out of range because more lines are needed than physically available
// NOTE: output different than gnu's: the empty split is not written but the rest of the input file is
#[test]
fn test_linenum_range_with_up_to_match2() {
fn test_line_num_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*"))
@ -1306,11 +1306,11 @@ fn test_linenum_range_with_up_to_match2() {
// NOTE: output different than gnu's: the pattern /10/ is matched but should not
#[test]
fn test_linenum_range_with_up_to_match3() {
fn test_line_num_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

@ -1,13 +1,13 @@
use crate::common::util::*;
static INPUT: &'static str = "lists.txt";
static INPUT: &str = "lists.txt";
struct TestedSequence<'b> {
name: &'b str,
sequence: &'b str,
}
static EXAMPLE_SEQUENCES: &'static [TestedSequence<'static>] = &[
static EXAMPLE_SEQUENCES: &[TestedSequence] = &[
TestedSequence {
name: "singular",
sequence: "2",
@ -34,14 +34,14 @@ static EXAMPLE_SEQUENCES: &'static [TestedSequence<'static>] = &[
},
];
static COMPLEX_SEQUENCE: &'static TestedSequence<'static> = &TestedSequence {
static COMPLEX_SEQUENCE: &TestedSequence = &TestedSequence {
name: "",
sequence: "9-,6-7,-2,4",
};
#[test]
fn test_byte_sequence() {
for param in vec!["-b", "--bytes"] {
for &param in &["-b", "--bytes"] {
for example_seq in EXAMPLE_SEQUENCES {
new_ucmd!()
.args(&[param, example_seq.sequence, INPUT])
@ -53,7 +53,7 @@ fn test_byte_sequence() {
#[test]
fn test_char_sequence() {
for param in vec!["-c", "--characters"] {
for &param in &["-c", "--characters"] {
for example_seq in EXAMPLE_SEQUENCES {
//as of coreutils 8.25 a char range is effectively the same as a byte range; there is no distinct treatment of utf8 chars.
new_ucmd!()
@ -66,7 +66,7 @@ fn test_char_sequence() {
#[test]
fn test_field_sequence() {
for param in vec!["-f", "--fields"] {
for &param in &["-f", "--fields"] {
for example_seq in EXAMPLE_SEQUENCES {
new_ucmd!()
.args(&[param, example_seq.sequence, INPUT])
@ -78,7 +78,7 @@ fn test_field_sequence() {
#[test]
fn test_specify_delimiter() {
for param in vec!["-d", "--delimiter"] {
for &param in &["-d", "--delimiter"] {
new_ucmd!()
.args(&[param, ":", "-f", COMPLEX_SEQUENCE.sequence, INPUT])
.succeeds()
@ -122,7 +122,7 @@ fn test_zero_terminated() {
#[test]
fn test_only_delimited() {
for param in vec!["-s", "--only-delimited"] {
for param in &["-s", "--only-delimited"] {
new_ucmd!()
.args(&["-d_", param, "-f", "1"])
.pipe_in("91\n82\n7_3")
@ -139,3 +139,30 @@ fn test_zero_terminated_only_delimited() {
.succeeds()
.stdout_only("82\n7\0");
}
#[test]
fn test_directory_and_no_such_file() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("some");
ucmd.arg("-b1")
.arg("some")
.run()
.stderr_is("cut: some: Is a directory\n");
new_ucmd!()
.arg("-b1")
.arg("some")
.run()
.stderr_is("cut: some: No such file or directory\n");
}
#[test]
fn test_equal_as_delimiter() {
new_ucmd!()
.args(&["-f", "2", "-d="])
.pipe_in("--dir=./out/lib")
.succeeds()
.stdout_only("./out/lib\n");
}

View file

@ -2,132 +2,216 @@ extern crate regex;
use self::regex::Regex;
use crate::common::util::*;
#[cfg(all(unix, not(target_os = "macos")))]
use rust_users::*;
#[test]
fn test_date_email() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--rfc-email").run();
assert!(result.success);
new_ucmd!().arg("--rfc-email").succeeds();
}
#[test]
fn test_date_email2() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-R").run();
assert!(result.success);
new_ucmd!().arg("-R").succeeds();
}
#[test]
fn test_date_rfc_3339() {
let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("--rfc-3339=ns").succeeds();
let rfc_regexp = concat!(
r#"(\d+)-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])\s([01]\d|2[0-3]):"#,
r#"([0-5]\d):([0-5]\d|60)(\.\d+)?(([Zz])|([\+|\-]([01]\d|2[0-3])))"#
);
let re = Regex::new(rfc_regexp).unwrap();
// Check that the output matches the regexp
let rfc_regexp = r"(\d+)-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])\s([01]\d|2[0-3]):([0-5]\d):([0-5]\d|60)(\.\d+)?(([Zz])|([\+|\-]([01]\d|2[0-3])))";
let re = Regex::new(rfc_regexp).unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene
.ucmd()
.arg("--rfc-3339=ns")
.succeeds()
.stdout_matches(&re);
result = scene.ucmd().arg("--rfc-3339=seconds").succeeds();
// Check that the output matches the regexp
let re = Regex::new(rfc_regexp).unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene
.ucmd()
.arg("--rfc-3339=seconds")
.succeeds()
.stdout_matches(&re);
}
#[test]
fn test_date_rfc_8601() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--iso-8601=ns").run();
assert!(result.success);
new_ucmd!().arg("--iso-8601=ns").succeeds();
}
#[test]
fn test_date_rfc_8601_second() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--iso-8601=second").run();
assert!(result.success);
new_ucmd!().arg("--iso-8601=second").succeeds();
}
#[test]
fn test_date_utc() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--utc").run();
assert!(result.success);
new_ucmd!().arg("--utc").succeeds();
}
#[test]
fn test_date_universal() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--universal").run();
assert!(result.success);
new_ucmd!().arg("--universal").succeeds();
}
#[test]
fn test_date_format_y() {
let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("+%Y").succeeds();
assert!(result.success);
let mut re = Regex::new(r"^\d{4}$").unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene.ucmd().arg("+%Y").succeeds().stdout_matches(&re);
result = scene.ucmd().arg("+%y").succeeds();
assert!(result.success);
re = Regex::new(r"^\d{2}$").unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene.ucmd().arg("+%y").succeeds().stdout_matches(&re);
}
#[test]
fn test_date_format_m() {
let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("+%b").succeeds();
assert!(result.success);
let mut re = Regex::new(r"\S+").unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene.ucmd().arg("+%b").succeeds().stdout_matches(&re);
result = scene.ucmd().arg("+%m").succeeds();
assert!(result.success);
re = Regex::new(r"^\d{2}$").unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene.ucmd().arg("+%m").succeeds().stdout_matches(&re);
}
#[test]
fn test_date_format_day() {
let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("+%a").succeeds();
assert!(result.success);
let mut re = Regex::new(r"\S+").unwrap();
assert!(re.is_match(&result.stdout.trim()));
result = scene.ucmd().arg("+%A").succeeds();
assert!(result.success);
scene.ucmd().arg("+%a").succeeds().stdout_matches(&re);
re = Regex::new(r"\S+").unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene.ucmd().arg("+%A").succeeds().stdout_matches(&re);
result = scene.ucmd().arg("+%u").succeeds();
assert!(result.success);
re = Regex::new(r"^\d{1}$").unwrap();
assert!(re.is_match(&result.stdout.trim()));
scene.ucmd().arg("+%u").succeeds().stdout_matches(&re);
}
#[test]
fn test_date_format_full_day() {
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("+'%a %Y-%m-%d'").succeeds();
assert!(result.success);
let re = Regex::new(r"\S+ \d{4}-\d{2}-\d{2}").unwrap();
assert!(re.is_match(&result.stdout.trim()));
new_ucmd!()
.arg("+'%a %Y-%m-%d'")
.succeeds()
.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() {
if get_effective_uid() == 0 {
new_ucmd!()
.arg("--set")
.arg("2020-03-12 13:30:00+08:00")
.succeeds()
.no_stdout()
.no_stderr();
}
}
#[test]
#[cfg(any(windows, all(unix, not(target_os = "macos"))))]
fn test_date_set_invalid() {
let result = new_ucmd!().arg("--set").arg("123abcd").fails();
result.no_stdout();
assert!(result.stderr_str().starts_with("date: invalid date "));
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
fn test_date_set_permissions_error() {
if !(get_effective_uid() == 0 || uucore::os::is_wsl_1()) {
let result = new_ucmd!()
.arg("--set")
.arg("2020-03-11 21:45:00+08:00")
.fails();
result.no_stdout();
assert!(result.stderr_str().starts_with("date: cannot set date: "));
}
}
#[test]
#[cfg(target_os = "macos")]
fn test_date_set_mac_unavailable() {
let result = new_ucmd!()
.arg("--set")
.arg("2020-03-11 21:45:00+08:00")
.fails();
result.no_stdout();
assert!(result
.stderr_str()
.starts_with("date: setting the date is not supported by macOS"));
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
/// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_2() {
if get_effective_uid() == 0 {
let result = new_ucmd!()
.arg("--set")
.arg("Sat 20 Mar 2021 14:53:01 AWST") // spell-checker:disable-line
.fails();
result.no_stdout();
assert!(result.stderr_str().starts_with("date: invalid date "));
}
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
/// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_3() {
if get_effective_uid() == 0 {
let result = new_ucmd!()
.arg("--set")
.arg("Sat 20 Mar 2021 14:53:01") // Local timezone
.fails();
result.no_stdout();
assert!(result.stderr_str().starts_with("date: invalid date "));
}
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
/// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_4() {
if get_effective_uid() == 0 {
let result = new_ucmd!()
.arg("--set")
.arg("2020-03-11 21:45:00") // Local timezone
.fails();
result.no_stdout();
assert!(result.stderr_str().starts_with("date: invalid date "));
}
}

View file

@ -1,9 +1,8 @@
use crate::common::util::*;
use std::io::{BufReader, Read, Write};
use std::path::{Path, PathBuf};
use std::fs::{self, OpenOptions, File};
use std::time::{Duration, SystemTime};
use std::path::{PathBuf};
use std::fs::{OpenOptions, File};
use tempfile::tempfile;
macro_rules! inf
@ -475,18 +474,18 @@ fn test_fullblock()
{
let tname = "fullblock-from-urand";
let tmp_fn = format!("TESTFILE-{}.tmp", &tname);
let stats = vec![
let exp_stats = vec![
"1+0 records in\n",
"1+0 records out\n",
"134217728 bytes (134 MB, 128 MiB) copied,",
];
let stats = stats.into_iter()
.fold(String::new(), | mut acc, s | {
acc.push_str(s);
acc
});
let exp_stats = exp_stats.into_iter()
.fold(Vec::new(), | mut acc, s | {
acc.extend(s.bytes());
acc
});
let res = new_ucmd!()
let ucmd = new_ucmd!()
.args(&[
"if=/dev/urandom",
of!(&tmp_fn),
@ -499,12 +498,11 @@ fn test_fullblock()
// a reasonable value for testing most systems.
"count=1",
"iflag=fullblock",
])
.run();
]).run();
ucmd.success();
res.success();
let res_stats = &res.stderr[..stats.len()];
assert_eq!(&stats, res_stats);
let run_stats = &ucmd.stderr()[..exp_stats.len()];
assert_eq!(exp_stats, run_stats);
}
// Fileio

View file

@ -2,30 +2,34 @@ use crate::common::util::*;
#[test]
fn test_df_compatible_no_size_arg() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-a").run();
assert!(result.success);
new_ucmd!().arg("-a").succeeds();
}
#[test]
fn test_df_compatible() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-ah").run();
assert!(result.success);
new_ucmd!().arg("-ah").succeeds();
}
#[test]
fn test_df_compatible_type() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-aT").run();
assert!(result.success);
new_ucmd!().arg("-aT").succeeds();
}
#[test]
fn test_df_compatible_si() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-aH").run();
assert!(result.success);
new_ucmd!().arg("-aH").succeeds();
}
#[test]
fn test_df_output() {
if cfg!(target_os = "macos") {
new_ucmd!().arg("-H").arg("-total").succeeds().
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",
);
}
}
// ToDO: more tests...

View file

@ -30,14 +30,14 @@ fn test_shell_syntax() {
}
#[test]
fn test_strutils() {
fn test_str_utils() {
let s = " asd#zcv #hk\t\n ";
assert_eq!("asd#zcv", s.purify());
let s = "con256asd";
assert!(s.fnmatch("*[2][3-6][5-9]?sd"));
assert!(s.fnmatch("*[2][3-6][5-9]?sd")); // spell-checker:disable-line
let s = "zxc \t\nqwe jlk hjl";
let s = "zxc \t\nqwe jlk hjl"; // spell-checker:disable-line
let (k, v) = s.split_two();
assert_eq!("zxc", k);
assert_eq!("qwe jlk hjl", v);

View file

@ -16,6 +16,21 @@ fn test_path_without_trailing_slashes() {
.stdout_is("/root/alpha/beta/gamma/delta/epsilon\n");
}
#[test]
fn test_path_without_trailing_slashes_and_zero() {
new_ucmd!()
.arg("-z")
.arg("/root/alpha/beta/gamma/delta/epsilon/omega")
.succeeds()
.stdout_is("/root/alpha/beta/gamma/delta/epsilon\u{0}");
new_ucmd!()
.arg("--zero")
.arg("/root/alpha/beta/gamma/delta/epsilon/omega")
.succeeds()
.stdout_is("/root/alpha/beta/gamma/delta/epsilon\u{0}");
}
#[test]
fn test_root() {
new_ucmd!().arg("/").run().stdout_is("/\n");

View file

@ -1,54 +1,79 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (paths) sublink subwords
use crate::common::util::*;
const SUB_DIR: &str = "subdir/deeper";
const SUB_DEEPER_DIR: &str = "subdir/deeper/deeper_dir";
const SUB_DIR_LINKS: &str = "subdir/links";
const SUB_DIR_LINKS_DEEPER_SYM_DIR: &str = "subdir/links/deeper_dir";
const SUB_FILE: &str = "subdir/links/subwords.txt";
const SUB_LINK: &str = "subdir/links/sublink.txt";
#[test]
fn test_du_basics() {
let (_at, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(result.success);
assert_eq!(result.stderr, "");
new_ucmd!().succeeds().no_stderr();
}
#[cfg(target_os = "macos")]
fn _du_basics(s: String) {
#[cfg(target_vendor = "apple")]
fn _du_basics(s: &str) {
let answer = "32\t./subdir
8\t./subdir/deeper
24\t./subdir/links
40\t./
40\t.
";
assert_eq!(s, answer);
}
#[cfg(not(target_os = "macos"))]
fn _du_basics(s: String) {
#[cfg(not(target_vendor = "apple"))]
fn _du_basics(s: &str) {
let answer = "28\t./subdir
8\t./subdir/deeper
16\t./subdir/links
36\t./
36\t.
";
assert_eq!(s, answer);
}
#[test]
fn test_du_basics_subdir() {
let (_at, mut ucmd) = at_and_ucmd!();
let scene = TestScenario::new(util_name!());
let result = ucmd.arg(SUB_DIR).run();
assert!(result.success);
assert_eq!(result.stderr, "");
_du_basics_subdir(result.stdout);
let result = scene.ucmd().arg(SUB_DIR).succeeds();
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg(SUB_DIR).run();
if result_reference.succeeded() {
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
_du_basics_subdir(result.stdout_str());
}
#[cfg(target_os = "macos")]
fn _du_basics_subdir(s: String) {
assert_eq!(s, "4\tsubdir/deeper\n");
#[cfg(target_vendor = "apple")]
fn _du_basics_subdir(s: &str) {
assert_eq!(s, "4\tsubdir/deeper/deeper_dir\n8\tsubdir/deeper\n");
}
#[cfg(not(target_os = "macos"))]
fn _du_basics_subdir(s: String) {
#[cfg(target_os = "windows")]
fn _du_basics_subdir(s: &str) {
assert_eq!(s, "0\tsubdir/deeper\\deeper_dir\n0\tsubdir/deeper\n");
}
#[cfg(target_os = "freebsd")]
fn _du_basics_subdir(s: &str) {
assert_eq!(s, "8\tsubdir/deeper/deeper_dir\n16\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 !is_wsl() {
if !uucore::os::is_wsl_1() {
assert_eq!(s, "8\tsubdir/deeper\n");
} else {
assert_eq!(s, "0\tsubdir/deeper\n");
@ -56,39 +81,74 @@ fn _du_basics_subdir(s: String) {
}
#[test]
fn test_du_basics_bad_name() {
let (_at, mut ucmd) = at_and_ucmd!();
fn test_du_invalid_size() {
let args = &["block-size", "threshold"];
for s in args {
new_ucmd!()
.arg(format!("--{}=1fb4t", s))
.arg("/tmp")
.fails()
.code_is(1)
.stderr_only(format!("du: invalid --{} argument '1fb4t'", s));
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.arg(format!("--{}=1Y", s))
.arg("/tmp")
.fails()
.code_is(1)
.stderr_only(format!("du: --{} argument '1Y' too large", s));
}
}
let result = ucmd.arg("bad_name").run();
assert_eq!(result.stdout, "");
assert_eq!(
result.stderr,
"du: error: bad_name: No such file or directory\n"
);
#[test]
fn test_du_basics_bad_name() {
new_ucmd!()
.arg("bad_name")
.succeeds() // TODO: replace with ".fails()" once `du` is fixed
.stderr_only("du: bad_name: No such file or directory\n");
}
#[test]
fn test_du_soft_link() {
let ts = TestScenario::new("du");
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let link = ts.cmd("ln").arg("-s").arg(SUB_FILE).arg(SUB_LINK).run();
assert!(link.success);
at.symlink_file(SUB_FILE, SUB_LINK);
let result = ts.ucmd().arg(SUB_DIR_LINKS).run();
assert!(result.success);
assert_eq!(result.stderr, "");
_du_soft_link(result.stdout);
let result = scene.ucmd().arg(SUB_DIR_LINKS).succeeds();
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg(SUB_DIR_LINKS).run();
if result_reference.succeeded() {
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
_du_soft_link(result.stdout_str());
}
#[cfg(target_os = "macos")]
fn _du_soft_link(s: String) {
#[cfg(target_vendor = "apple")]
fn _du_soft_link(s: &str) {
// 'macos' host variants may have `du` output variation for soft links
assert!((s == "12\tsubdir/links\n") || (s == "16\tsubdir/links\n"));
}
#[cfg(not(target_os = "macos"))]
fn _du_soft_link(s: String) {
#[cfg(target_os = "windows")]
fn _du_soft_link(s: &str) {
assert_eq!(s, "8\tsubdir/links\n");
}
#[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 !is_wsl() {
if !uucore::os::is_wsl_1() {
assert_eq!(s, "16\tsubdir/links\n");
} else {
assert_eq!(s, "8\tsubdir/links\n");
@ -97,26 +157,45 @@ fn _du_soft_link(s: String) {
#[test]
fn test_du_hard_link() {
let ts = TestScenario::new("du");
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let link = ts.cmd("ln").arg(SUB_FILE).arg(SUB_LINK).run();
assert!(link.success);
at.hard_link(SUB_FILE, SUB_LINK);
let result = ts.ucmd().arg(SUB_DIR_LINKS).run();
assert!(result.success);
assert_eq!(result.stderr, "");
let result = scene.ucmd().arg(SUB_DIR_LINKS).succeeds();
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg(SUB_DIR_LINKS).run();
if result_reference.succeeded() {
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
// We do not double count hard links as the inodes are identical
_du_hard_link(result.stdout);
_du_hard_link(result.stdout_str());
}
#[cfg(target_os = "macos")]
fn _du_hard_link(s: String) {
#[cfg(target_vendor = "apple")]
fn _du_hard_link(s: &str) {
assert_eq!(s, "12\tsubdir/links\n")
}
#[cfg(not(target_os = "macos"))]
fn _du_hard_link(s: String) {
#[cfg(target_os = "windows")]
fn _du_hard_link(s: &str) {
assert_eq!(s, "8\tsubdir/links\n")
}
#[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 !is_wsl() {
if !uucore::os::is_wsl_1() {
assert_eq!(s, "16\tsubdir/links\n");
} else {
assert_eq!(s, "8\tsubdir/links\n");
@ -125,24 +204,218 @@ fn _du_hard_link(s: String) {
#[test]
fn test_du_d_flag() {
let ts = TestScenario::new("du");
let scene = TestScenario::new(util_name!());
let result = ts.ucmd().arg("-d").arg("1").run();
assert!(result.success);
assert_eq!(result.stderr, "");
_du_d_flag(result.stdout);
let result = scene.ucmd().arg("-d1").succeeds();
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg("-d1").run();
if result_reference.succeeded() {
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
_du_d_flag(result.stdout_str());
}
#[cfg(target_os = "macos")]
fn _du_d_flag(s: String) {
assert_eq!(s, "16\t./subdir\n20\t./\n");
#[cfg(target_vendor = "apple")]
fn _du_d_flag(s: &str) {
assert_eq!(s, "20\t./subdir\n24\t.\n");
}
#[cfg(not(target_os = "macos"))]
fn _du_d_flag(s: String) {
#[cfg(target_os = "windows")]
fn _du_d_flag(s: &str) {
assert_eq!(s, "8\t.\\subdir\n8\t.\n");
}
#[cfg(target_os = "freebsd")]
fn _du_d_flag(s: &str) {
assert_eq!(s, "36\t./subdir\n44\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 !is_wsl() {
assert_eq!(s, "28\t./subdir\n36\t./\n");
if !uucore::os::is_wsl_1() {
assert_eq!(s, "28\t./subdir\n36\t.\n");
} else {
assert_eq!(s, "8\t./subdir\n8\t./\n");
assert_eq!(s, "8\t./subdir\n8\t.\n");
}
}
#[test]
fn test_du_dereference() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.symlink_dir(SUB_DEEPER_DIR, SUB_DIR_LINKS_DEEPER_SYM_DIR);
let result = scene.ucmd().arg("-L").arg(SUB_DIR_LINKS).succeeds();
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg("-L").arg(SUB_DIR_LINKS).run();
if result_reference.succeeded() {
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
_du_dereference(result.stdout_str());
}
#[cfg(target_vendor = "apple")]
fn _du_dereference(s: &str) {
assert_eq!(s, "4\tsubdir/links/deeper_dir\n16\tsubdir/links\n");
}
#[cfg(target_os = "windows")]
fn _du_dereference(s: &str) {
assert_eq!(s, "0\tsubdir/links\\deeper_dir\n8\tsubdir/links\n");
}
#[cfg(target_os = "freebsd")]
fn _du_dereference(s: &str) {
assert_eq!(s, "8\tsubdir/links/deeper_dir\n24\tsubdir/links\n");
}
#[cfg(all(
not(target_vendor = "apple"),
not(target_os = "windows"),
not(target_os = "freebsd")
))]
fn _du_dereference(s: &str) {
// MS-WSL linux has altered expected output
if !uucore::os::is_wsl_1() {
assert_eq!(s, "8\tsubdir/links/deeper_dir\n24\tsubdir/links\n");
} else {
assert_eq!(s, "0\tsubdir/links/deeper_dir\n8\tsubdir/links\n");
}
}
#[test]
fn test_du_h_flag_empty_file() {
new_ucmd!()
.arg("-h")
.arg("empty.txt")
.succeeds()
.stdout_only("0\tempty.txt\n");
}
#[cfg(feature = "touch")]
#[test]
fn test_du_time() {
let scene = TestScenario::new(util_name!());
scene
.ccmd("touch")
.arg("-a")
.arg("-t")
.arg("201505150000")
.arg("date_test")
.succeeds();
scene
.ccmd("touch")
.arg("-m")
.arg("-t")
.arg("201606160000")
.arg("date_test")
.succeeds();
let result = scene.ucmd().arg("--time").arg("date_test").succeeds();
result.stdout_only("0\t2016-06-16 00:00\tdate_test\n");
let result = scene.ucmd().arg("--time=atime").arg("date_test").succeeds();
result.stdout_only("0\t2015-05-15 00:00\tdate_test\n");
let result = scene.ucmd().arg("--time=ctime").arg("date_test").succeeds();
result.stdout_only("0\t2016-06-16 00:00\tdate_test\n");
#[cfg(not(target_env = "musl"))]
{
use regex::Regex;
let re_birth =
Regex::new(r"0\t[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}\tdate_test").unwrap();
let result = scene.ucmd().arg("--time=birth").arg("date_test").succeeds();
result.stdout_matches(&re_birth);
}
}
#[cfg(not(target_os = "windows"))]
#[cfg(feature = "chmod")]
#[test]
fn test_du_no_permission() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.mkdir_all(SUB_DIR_LINKS);
scene.ccmd("chmod").arg("-r").arg(SUB_DIR_LINKS).succeeds();
let result = scene.ucmd().arg(SUB_DIR_LINKS).run(); // TODO: replace with ".fails()" once `du` is fixed
result.stderr_contains(
"du: cannot read directory 'subdir/links': Permission denied (os error 13)",
);
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg(SUB_DIR_LINKS).fails();
if result_reference
.stderr_str()
.contains("du: cannot read directory 'subdir/links': Permission denied")
{
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
_du_no_permission(result.stdout_str());
}
#[cfg(target_vendor = "apple")]
fn _du_no_permission(s: &str) {
assert_eq!(s, "0\tsubdir/links\n");
}
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
fn _du_no_permission(s: &str) {
assert_eq!(s, "4\tsubdir/links\n");
}
#[test]
fn test_du_one_file_system() {
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("-x").arg(SUB_DIR).succeeds();
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg("-x").arg(SUB_DIR).run();
if result_reference.succeeded() {
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
_du_basics_subdir(result.stdout_str());
}
#[test]
fn test_du_threshold() {
let scene = TestScenario::new(util_name!());
let threshold = if cfg!(windows) { "7K" } else { "10K" };
scene
.ucmd()
.arg(format!("--threshold={}", threshold))
.succeeds()
.stdout_contains("links")
.stdout_does_not_contain("deeper_dir");
scene
.ucmd()
.arg(format!("--threshold=-{}", threshold))
.succeeds()
.stdout_does_not_contain("links")
.stdout_contains("deeper_dir");
}

View file

@ -1,23 +1,20 @@
// spell-checker:ignore (words) araba merci
use crate::common::util::*;
#[test]
fn test_default() {
//CmdResult.stdout_only(...) trims trailing newlines
assert_eq!("hi\n", new_ucmd!().arg("hi").succeeds().no_stderr().stdout);
new_ucmd!().arg("hi").succeeds().stdout_only("hi\n");
}
#[test]
fn test_no_trailing_newline() {
//CmdResult.stdout_only(...) trims trailing newlines
assert_eq!(
"hi",
new_ucmd!()
.arg("-n")
.arg("hi")
.succeeds()
.no_stderr()
.stdout
);
new_ucmd!()
.arg("-n")
.arg("hi")
.succeeds()
.no_stderr()
.stdout_only("hi");
}
#[test]
@ -173,3 +170,57 @@ fn test_disable_escapes() {
.succeeds()
.stdout_only(format!("{}\n", input_str));
}
#[test]
fn test_hyphen_value() {
new_ucmd!().arg("-abc").succeeds().stdout_is("-abc\n");
}
#[test]
fn test_multiple_hyphen_values() {
new_ucmd!()
.args(&["-abc", "-def", "-edf"])
.succeeds()
.stdout_is("-abc -def -edf\n");
}
#[test]
fn test_hyphen_values_inside_string() {
new_ucmd!()
.arg("'\"\n'CXXFLAGS=-g -O2'\n\"'") // spell-checker:disable-line
.succeeds()
.stdout_contains("CXXFLAGS"); // spell-checker:disable-line
}
#[test]
fn test_hyphen_values_at_start() {
new_ucmd!()
.arg("-E")
.arg("-test")
.arg("araba")
.arg("-merci")
.run()
.success()
.stdout_does_not_contain("-E")
.stdout_is("-test araba -merci\n");
}
#[test]
fn test_hyphen_values_between() {
new_ucmd!()
.arg("test")
.arg("-E")
.arg("araba")
.run()
.success()
.stdout_is("test -E araba\n");
new_ucmd!()
.arg("dumdum ")
.arg("dum dum dum")
.arg("-e")
.arg("dum")
.run()
.success()
.stdout_is("dumdum dum dum dum -e dum\n");
}

View file

@ -1,3 +1,5 @@
// spell-checker:ignore (words) bamf chdir
#[cfg(not(windows))]
use std::fs;
@ -8,45 +10,36 @@ use tempfile::tempdir;
#[test]
fn test_env_help() {
assert!(new_ucmd!()
new_ucmd!()
.arg("--help")
.succeeds()
.no_stderr()
.stdout
.contains("OPTIONS:"));
.stdout_contains("OPTIONS:");
}
#[test]
fn test_env_version() {
assert!(new_ucmd!()
new_ucmd!()
.arg("--version")
.succeeds()
.no_stderr()
.stdout
.contains(util_name!()));
.stdout_contains(util_name!());
}
#[test]
fn test_echo() {
// assert!(new_ucmd!().arg("printf").arg("FOO-bar").succeeds().no_stderr().stdout.contains("FOO-bar"));
let mut cmd = new_ucmd!();
cmd.arg("echo").arg("FOO-bar");
println!("cmd={:?}", cmd);
let result = new_ucmd!().arg("echo").arg("FOO-bar").succeeds();
let result = cmd.run();
println!("success={:?}", result.success);
println!("stdout={:?}", result.stdout);
println!("stderr={:?}", result.stderr);
assert!(result.success);
let out = result.stdout.trim_end();
assert_eq!(out, "FOO-bar");
assert_eq!(result.stdout_str().trim(), "FOO-bar");
}
#[test]
fn test_file_option() {
let out = new_ucmd!().arg("-f").arg("vars.conf.txt").run().stdout;
let out = new_ucmd!()
.arg("-f")
.arg("vars.conf.txt")
.run()
.stdout_move_str();
assert_eq!(
out.lines()
@ -63,7 +56,7 @@ fn test_combined_file_set() {
.arg("vars.conf.txt")
.arg("FOO=bar.alt")
.run()
.stdout;
.stdout_move_str();
assert_eq!(out.lines().filter(|&line| line == "FOO=bar.alt").count(), 1);
}
@ -76,8 +69,8 @@ fn test_combined_file_set_unset() {
.arg("-f")
.arg("vars.conf.txt")
.arg("FOO=bar.alt")
.run()
.stdout;
.succeeds()
.stdout_move_str();
assert_eq!(
out.lines()
@ -89,17 +82,18 @@ fn test_combined_file_set_unset() {
#[test]
fn test_single_name_value_pair() {
let out = new_ucmd!().arg("FOO=bar").run().stdout;
let out = new_ucmd!().arg("FOO=bar").run();
assert!(out.lines().any(|line| line == "FOO=bar"));
assert!(out.stdout_str().lines().any(|line| line == "FOO=bar"));
}
#[test]
fn test_multiple_name_value_pairs() {
let out = new_ucmd!().arg("FOO=bar").arg("ABC=xyz").run().stdout;
let out = new_ucmd!().arg("FOO=bar").arg("ABC=xyz").run();
assert_eq!(
out.lines()
out.stdout_str()
.lines()
.filter(|&line| line == "FOO=bar" || line == "ABC=xyz")
.count(),
2
@ -110,13 +104,8 @@ fn test_multiple_name_value_pairs() {
fn test_ignore_environment() {
let scene = TestScenario::new(util_name!());
let out = scene.ucmd().arg("-i").run().stdout;
assert_eq!(out, "");
let out = scene.ucmd().arg("-").run().stdout;
assert_eq!(out, "");
scene.ucmd().arg("-i").run().no_stdout();
scene.ucmd().arg("-").run().no_stdout();
}
#[test]
@ -126,12 +115,12 @@ fn test_null_delimiter() {
.arg("--null")
.arg("FOO=bar")
.arg("ABC=xyz")
.run()
.stdout;
.succeeds()
.stdout_move_str();
let mut vars: Vec<_> = out.split('\0').collect();
assert_eq!(vars.len(), 3);
vars.sort();
vars.sort_unstable();
assert_eq!(vars[0], "");
assert_eq!(vars[1], "ABC=xyz");
assert_eq!(vars[2], "FOO=bar");
@ -145,16 +134,19 @@ fn test_unset_variable() {
.ucmd_keepenv()
.arg("-u")
.arg("HOME")
.run()
.stdout;
.succeeds()
.stdout_move_str();
assert_eq!(out.lines().any(|line| line.starts_with("HOME=")), false);
assert!(!out.lines().any(|line| line.starts_with("HOME=")));
}
#[test]
fn test_fail_null_with_program() {
let out = new_ucmd!().arg("--null").arg("cd").fails().stderr;
assert!(out.contains("cannot specify --null (-0) with command"));
new_ucmd!()
.arg("--null")
.arg("cd")
.fails()
.stderr_contains("cannot specify --null (-0) with command");
}
#[cfg(not(windows))]
@ -173,8 +165,8 @@ fn test_change_directory() {
.arg("--chdir")
.arg(&temporary_path)
.arg(pwd)
.run()
.stdout;
.succeeds()
.stdout_move_str();
assert_eq!(out.trim(), temporary_path.as_os_str())
}
@ -193,20 +185,19 @@ fn test_change_directory() {
.ucmd()
.arg("--chdir")
.arg(&temporary_path)
.run()
.stdout;
assert_eq!(
out.lines()
.any(|line| line.ends_with(temporary_path.file_name().unwrap().to_str().unwrap())),
false
);
.succeeds()
.stdout_move_str();
assert!(!out
.lines()
.any(|line| line.ends_with(temporary_path.file_name().unwrap().to_str().unwrap())));
}
#[test]
fn test_fail_change_directory() {
let scene = TestScenario::new(util_name!());
let some_non_existing_path = "some_nonexistent_path";
assert_eq!(Path::new(some_non_existing_path).is_dir(), false);
assert!(!Path::new(some_non_existing_path).is_dir());
let out = scene
.ucmd()
@ -214,6 +205,6 @@ fn test_fail_change_directory() {
.arg(some_non_existing_path)
.arg("pwd")
.fails()
.stderr;
.stderr_move_str();
assert!(out.contains("env: cannot change directory to "));
}

View file

@ -2,47 +2,54 @@ use crate::common::util::*;
#[test]
fn test_with_tab() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("with-tab.txt").run();
assert!(result.success);
assert!(result.stdout.contains(" "));
assert!(!result.stdout.contains("\t"));
new_ucmd!()
.arg("with-tab.txt")
.succeeds()
.stdout_contains(" ")
.stdout_does_not_contain("\t");
}
#[test]
fn test_with_trailing_tab() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("with-trailing-tab.txt").run();
assert!(result.success);
assert!(result.stdout.contains("with tabs=> "));
assert!(!result.stdout.contains("\t"));
new_ucmd!()
.arg("with-trailing-tab.txt")
.succeeds()
.stdout_contains("with tabs=> ")
.stdout_does_not_contain("\t");
}
#[test]
fn test_with_trailing_tab_i() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("with-trailing-tab.txt").arg("-i").run();
assert!(result.success);
assert!(result.stdout.contains(" // with tabs=>\t"));
new_ucmd!()
.arg("with-trailing-tab.txt")
.arg("-i")
.succeeds()
.stdout_contains(" // with tabs=>\t");
}
#[test]
fn test_with_tab_size() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("with-tab.txt").arg("--tabs=10").run();
assert!(result.success);
assert!(result.stdout.contains(" "));
new_ucmd!()
.arg("with-tab.txt")
.arg("--tabs=10")
.succeeds()
.stdout_contains(" ");
}
#[test]
fn test_with_space() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("with-spaces.txt").run();
assert!(result.success);
assert!(result.stdout.contains(" return"));
new_ucmd!()
.arg("with-spaces.txt")
.succeeds()
.stdout_contains(" return");
}
#[test]
fn test_with_multiple_files() {
new_ucmd!()
.arg("with-spaces.txt")
.arg("with-tab.txt")
.succeeds()
.stdout_contains(" return")
.stdout_contains(" ");
}

View file

@ -2,55 +2,124 @@ use crate::common::util::*;
#[test]
fn test_simple_arithmetic() {
new_ucmd!().args(&["1", "+", "1"]).run().stdout_is("2\n");
new_ucmd!()
.args(&["1", "+", "1"])
.succeeds()
.stdout_only("2\n");
new_ucmd!().args(&["1", "-", "1"]).run().stdout_is("0\n");
new_ucmd!()
.args(&["1", "-", "1"])
.fails()
.status_code(1)
.stdout_only("0\n");
new_ucmd!().args(&["3", "*", "2"]).run().stdout_is("6\n");
new_ucmd!()
.args(&["3", "*", "2"])
.succeeds()
.stdout_only("6\n");
new_ucmd!().args(&["4", "/", "2"]).run().stdout_is("2\n");
new_ucmd!()
.args(&["4", "/", "2"])
.succeeds()
.stdout_only("2\n");
}
#[test]
fn test_complex_arithmetic() {
let run = new_ucmd!()
new_ucmd!()
.args(&["9223372036854775807", "+", "9223372036854775807"])
.run();
run.stdout_is("");
run.stderr_is("expr: error: +: Numerical result out of range");
.succeeds()
.stdout_only("18446744073709551614\n");
let run = new_ucmd!().args(&["9", "/", "0"]).run();
run.stdout_is("");
run.stderr_is("expr: error: division by zero");
new_ucmd!()
.args(&[
"92233720368547758076549841651981984981498415651",
"%",
"922337203685",
])
.succeeds()
.stdout_only("533691697086\n");
new_ucmd!()
.args(&[
"92233720368547758076549841651981984981498415651",
"*",
"922337203685",
])
.succeeds()
.stdout_only("85070591730190566808700855121818604965830915152801178873935\n");
new_ucmd!()
.args(&[
"92233720368547758076549841651981984981498415651",
"-",
"922337203685",
])
.succeeds()
.stdout_only("92233720368547758076549841651981984059161211966\n");
new_ucmd!()
.args(&["9", "/", "0"])
.fails()
.stderr_only("expr: division by zero\n");
}
#[test]
fn test_parenthesis() {
new_ucmd!()
.args(&["(", "1", "+", "1", ")", "*", "2"])
.run()
.stdout_is("4\n");
.succeeds()
.stdout_only("4\n");
}
#[test]
fn test_or() {
new_ucmd!()
.args(&["0", "|", "foo"])
.run()
.stdout_is("foo\n");
.succeeds()
.stdout_only("foo\n");
new_ucmd!()
.args(&["foo", "|", "bar"])
.run()
.stdout_is("foo\n");
.succeeds()
.stdout_only("foo\n");
}
#[test]
fn test_and() {
new_ucmd!()
.args(&["foo", "&", "1"])
.run()
.stdout_is("foo\n");
.succeeds()
.stdout_only("foo\n");
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

@ -4,6 +4,9 @@
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
#![allow(clippy::unreadable_literal)]
// spell-checker:ignore (methods) hexdigest
use crate::common::util::*;
use std::time::SystemTime;
@ -26,22 +29,31 @@ fn test_first_100000_integers() {
extern crate sha1;
let n_integers = 100_000;
let mut instring = String::new();
let mut input_string = String::new();
for i in 0..=n_integers {
instring.push_str(&(format!("{} ", i))[..]);
input_string.push_str(&(format!("{} ", i))[..]);
}
println!("STDIN='{}'", instring);
let result = new_ucmd!().pipe_in(instring.as_bytes()).run();
let stdout = result.stdout;
assert!(result.success);
println!("STDIN='{}'", input_string);
let result = new_ucmd!().pipe_in(input_string.as_bytes()).succeeds();
// `seq 0 100000 | factor | sha1sum` => "4ed2d8403934fa1c76fe4b84c5d4b8850299c359"
let hash_check = sha1::Sha1::from(stdout.as_bytes()).hexdigest();
let hash_check = sha1::Sha1::from(result.stdout()).hexdigest();
assert_eq!(hash_check, "4ed2d8403934fa1c76fe4b84c5d4b8850299c359");
}
#[test]
fn test_cli_args() {
// Make sure that factor works with CLI arguments as well.
new_ucmd!().args(&["3"]).succeeds().stdout_contains("3: 3");
new_ucmd!()
.args(&["3", "6"])
.succeeds()
.stdout_contains("3: 3")
.stdout_contains("6: 2 3");
}
#[test]
fn test_random() {
use conv::prelude::*;
@ -80,25 +92,25 @@ fn test_random() {
};
}
factors.sort();
factors.sort_unstable();
(product, factors)
};
// build an input and expected output string from factor
let mut instring = String::new();
let mut outstring = String::new();
let mut input_string = String::new();
let mut output_string = String::new();
for _ in 0..NUM_TESTS {
let (product, factors) = rand_gt(1 << 63);
instring.push_str(&(format!("{} ", product))[..]);
input_string.push_str(&(format!("{} ", product))[..]);
outstring.push_str(&(format!("{}:", product))[..]);
output_string.push_str(&(format!("{}:", product))[..]);
for factor in factors {
outstring.push_str(&(format!(" {}", factor))[..]);
output_string.push_str(&(format!(" {}", factor))[..]);
}
outstring.push_str("\n");
output_string.push('\n');
}
run(instring.as_bytes(), outstring.as_bytes());
run(input_string.as_bytes(), output_string.as_bytes());
}
#[test]
@ -110,31 +122,31 @@ fn test_random_big() {
println!("rng_seed={:?}", rng_seed);
let mut rng = SmallRng::seed_from_u64(rng_seed);
let bitrange_1 = Uniform::new(14_usize, 51);
let bit_range_1 = Uniform::new(14_usize, 51);
let mut rand_64 = move || {
// first, choose a random number of bits for the first factor
let f_bit_1 = bitrange_1.sample(&mut rng);
let f_bit_1 = bit_range_1.sample(&mut rng);
// how many more bits do we need?
let rem = 64 - f_bit_1;
// we will have a number of additional factors equal to nfacts + 1
// where nfacts is in [0, floor(rem/14) ) NOTE half-open interval
// we will have a number of additional factors equal to n_facts + 1
// where n_facts is in [0, floor(rem/14) ) NOTE half-open interval
// Each prime factor is at least 14 bits, hence floor(rem/14)
let nfacts = Uniform::new(0_usize, rem / 14).sample(&mut rng);
// we have to distribute extrabits among the (nfacts + 1) values
let extrabits = rem - (nfacts + 1) * 14;
let n_factors = Uniform::new(0_usize, rem / 14).sample(&mut rng);
// we have to distribute extra_bits among the (n_facts + 1) values
let extra_bits = rem - (n_factors + 1) * 14;
// (remember, a Range is a half-open interval)
let extrarange = Uniform::new(0_usize, extrabits + 1);
let extra_range = Uniform::new(0_usize, extra_bits + 1);
// to generate an even split of this range, generate n-1 random elements
// in the range, add the desired total value to the end, sort this list,
// and then compute the sequential differences.
let mut f_bits = Vec::new();
for _ in 0..nfacts {
f_bits.push(extrarange.sample(&mut rng));
for _ in 0..n_factors {
f_bits.push(extra_range.sample(&mut rng));
}
f_bits.push(extrabits);
f_bits.sort();
f_bits.push(extra_bits);
f_bits.sort_unstable();
// compute sequential differences here. We leave off the +14 bits
// so we can just index PRIMES_BY_BITS
@ -150,62 +162,65 @@ fn test_random_big() {
f_bits.push(f_bit_1 - 14); // index of f_bit_1 in PRIMES_BY_BITS
let f_bits = f_bits;
let mut nbits = 0;
let mut n_bits = 0;
let mut product = 1_u64;
let mut factors = Vec::new();
for bit in f_bits {
assert!(bit < 37);
nbits += 14 + bit;
n_bits += 14 + bit;
let elm = Uniform::new(0, PRIMES_BY_BITS[bit].len()).sample(&mut rng);
let factor = PRIMES_BY_BITS[bit][elm];
factors.push(factor);
product *= factor;
}
assert_eq!(nbits, 64);
assert_eq!(n_bits, 64);
factors.sort();
factors.sort_unstable();
(product, factors)
};
let mut instring = String::new();
let mut outstring = String::new();
let mut input_string = String::new();
let mut output_string = String::new();
for _ in 0..NUM_TESTS {
let (product, factors) = rand_64();
instring.push_str(&(format!("{} ", product))[..]);
input_string.push_str(&(format!("{} ", product))[..]);
outstring.push_str(&(format!("{}:", product))[..]);
output_string.push_str(&(format!("{}:", product))[..]);
for factor in factors {
outstring.push_str(&(format!(" {}", factor))[..]);
output_string.push_str(&(format!(" {}", factor))[..]);
}
outstring.push_str("\n");
output_string.push('\n');
}
run(instring.as_bytes(), outstring.as_bytes());
run(input_string.as_bytes(), output_string.as_bytes());
}
#[test]
fn test_big_primes() {
let mut instring = String::new();
let mut outstring = String::new();
let mut input_string = String::new();
let mut output_string = String::new();
for prime in PRIMES64 {
instring.push_str(&(format!("{} ", prime))[..]);
outstring.push_str(&(format!("{0}: {0}\n", prime))[..]);
input_string.push_str(&(format!("{} ", prime))[..]);
output_string.push_str(&(format!("{0}: {0}\n", prime))[..]);
}
run(instring.as_bytes(), outstring.as_bytes());
run(input_string.as_bytes(), output_string.as_bytes());
}
fn run(instring: &[u8], outstring: &[u8]) {
println!("STDIN='{}'", String::from_utf8_lossy(instring));
println!("STDOUT(expected)='{}'", String::from_utf8_lossy(outstring));
fn run(input_string: &[u8], output_string: &[u8]) {
println!("STDIN='{}'", String::from_utf8_lossy(input_string));
println!(
"STDOUT(expected)='{}'",
String::from_utf8_lossy(output_string)
);
// now run factor
new_ucmd!()
.pipe_in(instring)
.pipe_in(input_string)
.run()
.stdout_is(String::from_utf8(outstring.to_owned()).unwrap());
.stdout_is(String::from_utf8(output_string.to_owned()).unwrap());
}
const PRIMES_BY_BITS: &'static [&'static [u64]] = &[
const PRIMES_BY_BITS: &[&[u64]] = &[
PRIMES14, PRIMES15, PRIMES16, PRIMES17, PRIMES18, PRIMES19, PRIMES20, PRIMES21, PRIMES22,
PRIMES23, PRIMES24, PRIMES25, PRIMES26, PRIMES27, PRIMES28, PRIMES29, PRIMES30, PRIMES31,
PRIMES32, PRIMES33, PRIMES34, PRIMES35, PRIMES36, PRIMES37, PRIMES38, PRIMES39, PRIMES40,
@ -213,7 +228,7 @@ const PRIMES_BY_BITS: &'static [&'static [u64]] = &[
PRIMES50,
];
const PRIMES64: &'static [u64] = &[
const PRIMES64: &[u64] = &[
18446744073709551557,
18446744073709551533,
18446744073709551521,
@ -265,7 +280,7 @@ const PRIMES64: &'static [u64] = &[
18446744073709549571,
];
const PRIMES14: &'static [u64] = &[
const PRIMES14: &[u64] = &[
16381, 16369, 16363, 16361, 16349, 16339, 16333, 16319, 16301, 16273, 16267, 16253, 16249,
16231, 16229, 16223, 16217, 16193, 16189, 16187, 16183, 16141, 16139, 16127, 16111, 16103,
16097, 16091, 16087, 16073, 16069, 16067, 16063, 16061, 16057, 16033, 16007, 16001, 15991,
@ -277,7 +292,7 @@ const PRIMES14: &'static [u64] = &[
15373,
];
const PRIMES15: &'static [u64] = &[
const PRIMES15: &[u64] = &[
32749, 32719, 32717, 32713, 32707, 32693, 32687, 32653, 32647, 32633, 32621, 32611, 32609,
32603, 32587, 32579, 32573, 32569, 32563, 32561, 32537, 32533, 32531, 32507, 32503, 32497,
32491, 32479, 32467, 32443, 32441, 32429, 32423, 32413, 32411, 32401, 32381, 32377, 32371,
@ -288,7 +303,7 @@ const PRIMES15: &'static [u64] = &[
31847, 31817, 31799, 31793, 31771, 31769, 31751,
];
const PRIMES16: &'static [u64] = &[
const PRIMES16: &[u64] = &[
65521, 65519, 65497, 65479, 65449, 65447, 65437, 65423, 65419, 65413, 65407, 65393, 65381,
65371, 65357, 65353, 65327, 65323, 65309, 65293, 65287, 65269, 65267, 65257, 65239, 65213,
65203, 65183, 65179, 65173, 65171, 65167, 65147, 65141, 65129, 65123, 65119, 65111, 65101,
@ -298,7 +313,7 @@ const PRIMES16: &'static [u64] = &[
64627, 64621, 64613, 64609, 64601, 64591, 64579, 64577, 64567, 64553,
];
const PRIMES17: &'static [u64] = &[
const PRIMES17: &[u64] = &[
131071, 131063, 131059, 131041, 131023, 131011, 131009, 130987, 130981, 130973, 130969, 130957,
130927, 130873, 130859, 130843, 130841, 130829, 130817, 130811, 130807, 130787, 130783, 130769,
130729, 130699, 130693, 130687, 130681, 130657, 130651, 130649, 130643, 130639, 130633, 130631,
@ -309,7 +324,7 @@ const PRIMES17: &'static [u64] = &[
130073, 130069, 130057, 130051,
];
const PRIMES18: &'static [u64] = &[
const PRIMES18: &[u64] = &[
262139, 262133, 262127, 262121, 262111, 262109, 262103, 262079, 262069, 262051, 262049, 262027,
262007, 261983, 261977, 261973, 261971, 261959, 261917, 261887, 261881, 261847, 261823, 261799,
261791, 261787, 261773, 261761, 261757, 261739, 261721, 261713, 261707, 261697, 261673, 261643,
@ -319,7 +334,7 @@ const PRIMES18: &'static [u64] = &[
261169, 261167, 261127,
];
const PRIMES19: &'static [u64] = &[
const PRIMES19: &[u64] = &[
524287, 524269, 524261, 524257, 524243, 524231, 524221, 524219, 524203, 524201, 524197, 524189,
524171, 524149, 524123, 524119, 524113, 524099, 524087, 524081, 524071, 524063, 524057, 524053,
524047, 523997, 523987, 523969, 523949, 523937, 523927, 523907, 523903, 523877, 523867, 523847,
@ -329,7 +344,7 @@ const PRIMES19: &'static [u64] = &[
523403, 523387, 523357, 523351, 523349, 523333, 523307, 523297,
];
const PRIMES20: &'static [u64] = &[
const PRIMES20: &[u64] = &[
1048573, 1048571, 1048559, 1048549, 1048517, 1048507, 1048447, 1048433, 1048423, 1048391,
1048387, 1048367, 1048361, 1048357, 1048343, 1048309, 1048291, 1048273, 1048261, 1048219,
1048217, 1048213, 1048193, 1048189, 1048139, 1048129, 1048127, 1048123, 1048063, 1048051,
@ -339,7 +354,7 @@ const PRIMES20: &'static [u64] = &[
1047691, 1047689, 1047671, 1047667, 1047653, 1047649, 1047647, 1047589, 1047587, 1047559,
];
const PRIMES21: &'static [u64] = &[
const PRIMES21: &[u64] = &[
2097143, 2097133, 2097131, 2097097, 2097091, 2097083, 2097047, 2097041, 2097031, 2097023,
2097013, 2096993, 2096987, 2096971, 2096959, 2096957, 2096947, 2096923, 2096911, 2096909,
2096893, 2096881, 2096873, 2096867, 2096851, 2096837, 2096807, 2096791, 2096789, 2096777,
@ -349,7 +364,7 @@ const PRIMES21: &'static [u64] = &[
2096221, 2096209, 2096191, 2096183, 2096147,
];
const PRIMES22: &'static [u64] = &[
const PRIMES22: &[u64] = &[
4194301, 4194287, 4194277, 4194271, 4194247, 4194217, 4194199, 4194191, 4194187, 4194181,
4194173, 4194167, 4194143, 4194137, 4194131, 4194107, 4194103, 4194023, 4194011, 4194007,
4193977, 4193971, 4193963, 4193957, 4193939, 4193929, 4193909, 4193869, 4193807, 4193803,
@ -359,7 +374,7 @@ const PRIMES22: &'static [u64] = &[
4193297,
];
const PRIMES23: &'static [u64] = &[
const PRIMES23: &[u64] = &[
8388593, 8388587, 8388581, 8388571, 8388547, 8388539, 8388473, 8388461, 8388451, 8388449,
8388439, 8388427, 8388421, 8388409, 8388377, 8388371, 8388319, 8388301, 8388287, 8388283,
8388277, 8388239, 8388209, 8388187, 8388113, 8388109, 8388091, 8388071, 8388059, 8388019,
@ -368,7 +383,7 @@ const PRIMES23: &'static [u64] = &[
8387723, 8387707, 8387671, 8387611, 8387609, 8387591,
];
const PRIMES24: &'static [u64] = &[
const PRIMES24: &[u64] = &[
16777213, 16777199, 16777183, 16777153, 16777141, 16777139, 16777127, 16777121, 16777099,
16777049, 16777027, 16776989, 16776973, 16776971, 16776967, 16776961, 16776941, 16776937,
16776931, 16776919, 16776901, 16776899, 16776869, 16776857, 16776839, 16776833, 16776817,
@ -378,7 +393,7 @@ const PRIMES24: &'static [u64] = &[
16776317, 16776313, 16776289, 16776217, 16776211,
];
const PRIMES25: &'static [u64] = &[
const PRIMES25: &[u64] = &[
33554393, 33554383, 33554371, 33554347, 33554341, 33554317, 33554291, 33554273, 33554267,
33554249, 33554239, 33554221, 33554201, 33554167, 33554159, 33554137, 33554123, 33554093,
33554083, 33554077, 33554051, 33554021, 33554011, 33554009, 33553999, 33553991, 33553969,
@ -388,7 +403,7 @@ const PRIMES25: &'static [u64] = &[
33553519, 33553517, 33553511, 33553489, 33553463, 33553451, 33553417,
];
const PRIMES26: &'static [u64] = &[
const PRIMES26: &[u64] = &[
67108859, 67108837, 67108819, 67108777, 67108763, 67108757, 67108753, 67108747, 67108739,
67108729, 67108721, 67108709, 67108693, 67108669, 67108667, 67108661, 67108649, 67108633,
67108597, 67108579, 67108529, 67108511, 67108507, 67108493, 67108471, 67108463, 67108453,
@ -399,7 +414,7 @@ const PRIMES26: &'static [u64] = &[
67107863,
];
const PRIMES27: &'static [u64] = &[
const PRIMES27: &[u64] = &[
134217689, 134217649, 134217617, 134217613, 134217593, 134217541, 134217529, 134217509,
134217497, 134217493, 134217487, 134217467, 134217439, 134217437, 134217409, 134217403,
134217401, 134217367, 134217361, 134217353, 134217323, 134217301, 134217277, 134217257,
@ -410,7 +425,7 @@ const PRIMES27: &'static [u64] = &[
134216737, 134216729,
];
const PRIMES28: &'static [u64] = &[
const PRIMES28: &[u64] = &[
268435399, 268435367, 268435361, 268435337, 268435331, 268435313, 268435291, 268435273,
268435243, 268435183, 268435171, 268435157, 268435147, 268435133, 268435129, 268435121,
268435109, 268435091, 268435067, 268435043, 268435039, 268435033, 268435019, 268435009,
@ -421,7 +436,7 @@ const PRIMES28: &'static [u64] = &[
268434479, 268434461,
];
const PRIMES29: &'static [u64] = &[
const PRIMES29: &[u64] = &[
536870909, 536870879, 536870869, 536870849, 536870839, 536870837, 536870819, 536870813,
536870791, 536870779, 536870767, 536870743, 536870729, 536870723, 536870717, 536870701,
536870683, 536870657, 536870641, 536870627, 536870611, 536870603, 536870599, 536870573,
@ -431,7 +446,7 @@ const PRIMES29: &'static [u64] = &[
536870027, 536869999, 536869951, 536869943, 536869937, 536869919, 536869901, 536869891,
];
const PRIMES30: &'static [u64] = &[
const PRIMES30: &[u64] = &[
1073741789, 1073741783, 1073741741, 1073741723, 1073741719, 1073741717, 1073741689, 1073741671,
1073741663, 1073741651, 1073741621, 1073741567, 1073741561, 1073741527, 1073741503, 1073741477,
1073741467, 1073741441, 1073741419, 1073741399, 1073741387, 1073741381, 1073741371, 1073741329,
@ -440,7 +455,7 @@ const PRIMES30: &'static [u64] = &[
1073740853, 1073740847, 1073740819, 1073740807,
];
const PRIMES31: &'static [u64] = &[
const PRIMES31: &[u64] = &[
2147483647, 2147483629, 2147483587, 2147483579, 2147483563, 2147483549, 2147483543, 2147483497,
2147483489, 2147483477, 2147483423, 2147483399, 2147483353, 2147483323, 2147483269, 2147483249,
2147483237, 2147483179, 2147483171, 2147483137, 2147483123, 2147483077, 2147483069, 2147483059,
@ -449,7 +464,7 @@ const PRIMES31: &'static [u64] = &[
2147482763, 2147482739, 2147482697, 2147482693, 2147482681, 2147482663, 2147482661,
];
const PRIMES32: &'static [u64] = &[
const PRIMES32: &[u64] = &[
4294967291, 4294967279, 4294967231, 4294967197, 4294967189, 4294967161, 4294967143, 4294967111,
4294967087, 4294967029, 4294966997, 4294966981, 4294966943, 4294966927, 4294966909, 4294966877,
4294966829, 4294966813, 4294966769, 4294966667, 4294966661, 4294966657, 4294966651, 4294966639,
@ -457,7 +472,7 @@ const PRIMES32: &'static [u64] = &[
4294966373, 4294966367, 4294966337, 4294966297,
];
const PRIMES33: &'static [u64] = &[
const PRIMES33: &[u64] = &[
8589934583, 8589934567, 8589934543, 8589934513, 8589934487, 8589934307, 8589934291, 8589934289,
8589934271, 8589934237, 8589934211, 8589934207, 8589934201, 8589934187, 8589934151, 8589934141,
8589934139, 8589934117, 8589934103, 8589934099, 8589934091, 8589934069, 8589934049, 8589934027,
@ -466,7 +481,7 @@ const PRIMES33: &'static [u64] = &[
8589933647, 8589933641, 8589933637, 8589933631, 8589933629, 8589933619, 8589933601, 8589933581,
];
const PRIMES34: &'static [u64] = &[
const PRIMES34: &[u64] = &[
17179869143,
17179869107,
17179869071,
@ -517,7 +532,7 @@ const PRIMES34: &'static [u64] = &[
17179868183,
];
const PRIMES35: &'static [u64] = &[
const PRIMES35: &[u64] = &[
34359738337,
34359738319,
34359738307,
@ -550,7 +565,7 @@ const PRIMES35: &'static [u64] = &[
34359737371,
];
const PRIMES36: &'static [u64] = &[
const PRIMES36: &[u64] = &[
68719476731,
68719476719,
68719476713,
@ -602,7 +617,7 @@ const PRIMES36: &'static [u64] = &[
68719475729,
];
const PRIMES37: &'static [u64] = &[
const PRIMES37: &[u64] = &[
137438953447,
137438953441,
137438953427,
@ -628,7 +643,7 @@ const PRIMES37: &'static [u64] = &[
137438952491,
];
const PRIMES38: &'static [u64] = &[
const PRIMES38: &[u64] = &[
274877906899,
274877906857,
274877906837,
@ -668,7 +683,7 @@ const PRIMES38: &'static [u64] = &[
274877905931,
];
const PRIMES39: &'static [u64] = &[
const PRIMES39: &[u64] = &[
549755813881,
549755813869,
549755813821,
@ -714,7 +729,7 @@ const PRIMES39: &'static [u64] = &[
549755812867,
];
const PRIMES40: &'static [u64] = &[
const PRIMES40: &[u64] = &[
1099511627689,
1099511627609,
1099511627581,
@ -744,7 +759,7 @@ const PRIMES40: &'static [u64] = &[
1099511626771,
];
const PRIMES41: &'static [u64] = &[
const PRIMES41: &[u64] = &[
2199023255531,
2199023255521,
2199023255497,
@ -783,7 +798,7 @@ const PRIMES41: &'static [u64] = &[
2199023254567,
];
const PRIMES42: &'static [u64] = &[
const PRIMES42: &[u64] = &[
4398046511093,
4398046511087,
4398046511071,
@ -823,7 +838,7 @@ const PRIMES42: &'static [u64] = &[
4398046510093,
];
const PRIMES43: &'static [u64] = &[
const PRIMES43: &[u64] = &[
8796093022151,
8796093022141,
8796093022091,
@ -858,7 +873,7 @@ const PRIMES43: &'static [u64] = &[
8796093021269,
];
const PRIMES44: &'static [u64] = &[
const PRIMES44: &[u64] = &[
17592186044399,
17592186044299,
17592186044297,
@ -891,7 +906,7 @@ const PRIMES44: &'static [u64] = &[
17592186043409,
];
const PRIMES45: &'static [u64] = &[
const PRIMES45: &[u64] = &[
35184372088777,
35184372088763,
35184372088751,
@ -932,7 +947,7 @@ const PRIMES45: &'static [u64] = &[
35184372087869,
];
const PRIMES46: &'static [u64] = &[
const PRIMES46: &[u64] = &[
70368744177643,
70368744177607,
70368744177601,
@ -967,7 +982,7 @@ const PRIMES46: &'static [u64] = &[
70368744176711,
];
const PRIMES47: &'static [u64] = &[
const PRIMES47: &[u64] = &[
140737488355213,
140737488355201,
140737488355181,
@ -989,7 +1004,7 @@ const PRIMES47: &'static [u64] = &[
140737488354329,
];
const PRIMES48: &'static [u64] = &[
const PRIMES48: &[u64] = &[
281474976710597,
281474976710591,
281474976710567,
@ -1022,7 +1037,7 @@ const PRIMES48: &'static [u64] = &[
281474976709637,
];
const PRIMES49: &'static [u64] = &[
const PRIMES49: &[u64] = &[
562949953421231,
562949953421201,
562949953421189,
@ -1056,7 +1071,7 @@ const PRIMES49: &'static [u64] = &[
562949953420297,
];
const PRIMES50: &'static [u64] = &[
const PRIMES50: &[u64] = &[
1125899906842597,
1125899906842589,
1125899906842573,

View file

@ -5,7 +5,7 @@ fn test_fmt() {
let result = new_ucmd!().arg("one-word-per-line.txt").run();
//.stdout_is_fixture("call_graph.expected");
assert_eq!(
result.stdout.trim(),
result.stdout_str().trim(),
"this is a file with one word per line"
);
}
@ -15,7 +15,7 @@ fn test_fmt_q() {
let result = new_ucmd!().arg("-q").arg("one-word-per-line.txt").run();
//.stdout_is_fixture("call_graph.expected");
assert_eq!(
result.stdout.trim(),
result.stdout_str().trim(),
"this is a file with one word per line"
);
}
@ -29,22 +29,20 @@ fn test_fmt_w_too_big() {
.run();
//.stdout_is_fixture("call_graph.expected");
assert_eq!(
result.stderr.trim(),
"fmt: error: invalid width: '2501': Numerical result out of range"
result.stderr_str().trim(),
"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.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

@ -25,9 +25,521 @@ fn test_40_column_word_boundary() {
}
#[test]
fn test_default_warp_with_newlines() {
fn test_default_wrap_with_newlines() {
new_ucmd!()
.arg("lorem_ipsum_new_line.txt")
.run()
.stdout_is_fixture("lorem_ipsum_new_line_80_column.expected");
}
#[test]
fn test_should_preserve_empty_line_without_final_newline() {
new_ucmd!()
.arg("-w2")
.pipe_in("12\n\n34")
.succeeds()
.stdout_is("12\n\n34");
}
#[test]
fn test_should_preserve_empty_line_and_final_newline() {
new_ucmd!()
.arg("-w2")
.pipe_in("12\n\n34\n")
.succeeds()
.stdout_is("12\n\n34\n");
}
#[test]
fn test_should_preserve_empty_lines() {
new_ucmd!().pipe_in("\n").succeeds().stdout_is("\n");
new_ucmd!()
.arg("-w1")
.pipe_in("0\n1\n\n2\n\n\n")
.succeeds()
.stdout_is("0\n1\n\n2\n\n\n");
}
#[test]
fn test_word_boundary_split_should_preserve_empty_lines() {
new_ucmd!()
.arg("-s")
.pipe_in("\n")
.succeeds()
.stdout_is("\n");
new_ucmd!()
.args(&["-w1", "-s"])
.pipe_in("0\n1\n\n2\n\n\n")
.succeeds()
.stdout_is("0\n1\n\n2\n\n\n");
}
#[test]
fn test_should_not_add_newline_when_line_less_than_fold() {
new_ucmd!().pipe_in("1234").succeeds().stdout_is("1234");
}
#[test]
fn test_should_not_add_newline_when_line_longer_than_fold() {
new_ucmd!()
.arg("-w2")
.pipe_in("1234")
.succeeds()
.stdout_is("12\n34");
}
#[test]
fn test_should_not_add_newline_when_line_equal_to_fold() {
new_ucmd!()
.arg("-w1")
.pipe_in(" ")
.succeeds()
.stdout_is(" ");
}
#[test]
fn test_should_preserve_final_newline_when_line_less_than_fold() {
new_ucmd!().pipe_in("1234\n").succeeds().stdout_is("1234\n");
}
#[test]
fn test_should_preserve_final_newline_when_line_longer_than_fold() {
new_ucmd!()
.arg("-w2")
.pipe_in("1234\n")
.succeeds()
.stdout_is("12\n34\n");
}
#[test]
fn test_should_preserve_final_newline_when_line_equal_to_fold() {
new_ucmd!()
.arg("-w2")
.pipe_in("1\n")
.succeeds()
.stdout_is("1\n");
}
#[test]
fn test_single_tab_should_not_add_extra_newline() {
new_ucmd!()
.arg("-w1")
.pipe_in("\t")
.succeeds()
.stdout_is("\t");
}
#[test]
fn test_initial_tab_counts_as_8_columns() {
new_ucmd!()
.arg("-w8")
.pipe_in("\t1")
.succeeds()
.stdout_is("\t\n1");
}
#[test]
fn test_tab_should_advance_to_next_tab_stop() {
// tab advances the column count to the next tab stop, i.e. the width
// of the tab varies based on the leading text
new_ucmd!()
.args(&["-w8", "tab_stops.input"])
.succeeds()
.stdout_is_fixture("tab_stops_w8.expected");
}
#[test]
fn test_all_tabs_should_advance_to_next_tab_stops() {
new_ucmd!()
.args(&["-w16", "tab_stops.input"])
.succeeds()
.stdout_is_fixture("tab_stops_w16.expected");
}
#[test]
fn test_fold_before_tab_with_narrow_width() {
new_ucmd!()
.arg("-w7")
.pipe_in("a\t1")
.succeeds()
.stdout_is("a\n\t\n1");
}
#[test]
fn test_fold_at_word_boundary() {
new_ucmd!()
.args(&["-w4", "-s"])
.pipe_in("one two")
.succeeds()
.stdout_is("one \ntwo");
}
#[test]
fn test_fold_at_leading_word_boundary() {
new_ucmd!()
.args(&["-w3", "-s"])
.pipe_in(" aaa")
.succeeds()
.stdout_is(" \naaa");
}
#[test]
fn test_fold_at_word_boundary_preserve_final_newline() {
new_ucmd!()
.args(&["-w4", "-s"])
.pipe_in("one two\n")
.succeeds()
.stdout_is("one \ntwo\n");
}
#[test]
fn test_fold_at_tab() {
new_ucmd!()
.arg("-w8")
.pipe_in("a\tbbb\n")
.succeeds()
.stdout_is("a\t\nbbb\n");
}
#[test]
fn test_fold_after_tab() {
new_ucmd!()
.arg("-w10")
.pipe_in("a\tbbb\n")
.succeeds()
.stdout_is("a\tbb\nb\n");
}
#[test]
fn test_fold_at_tab_as_word_boundary() {
new_ucmd!()
.args(&["-w8", "-s"])
.pipe_in("a\tbbb\n")
.succeeds()
.stdout_is("a\t\nbbb\n");
}
#[test]
fn test_fold_after_tab_as_word_boundary() {
new_ucmd!()
.args(&["-w10", "-s"])
.pipe_in("a\tbbb\n")
.succeeds()
.stdout_is("a\t\nbbb\n");
}
#[test]
fn test_fold_at_word_boundary_only_whitespace() {
new_ucmd!()
.args(&["-w2", "-s"])
.pipe_in(" ")
.succeeds()
.stdout_is(" \n ");
}
#[test]
fn test_fold_at_word_boundary_only_whitespace_preserve_final_newline() {
new_ucmd!()
.args(&["-w2", "-s"])
.pipe_in(" \n")
.succeeds()
.stdout_is(" \n \n");
}
#[test]
fn test_backspace_should_be_preserved() {
new_ucmd!().pipe_in("\x08").succeeds().stdout_is("\x08");
}
#[test]
fn test_backspaced_char_should_be_preserved() {
new_ucmd!().pipe_in("x\x08").succeeds().stdout_is("x\x08");
}
#[test]
fn test_backspace_should_decrease_column_count() {
new_ucmd!()
.arg("-w2")
.pipe_in("1\x08345")
.succeeds()
.stdout_is("1\x0834\n5");
}
#[test]
fn test_backspace_should_not_decrease_column_count_past_zero() {
new_ucmd!()
.arg("-w2")
.pipe_in("1\x08\x083456")
.succeeds()
.stdout_is("1\x08\x0834\n56");
}
#[test]
fn test_backspace_is_not_word_boundary() {
new_ucmd!()
.args(&["-w10", "-s"])
.pipe_in("foobar\x086789abcdef")
.succeeds()
.stdout_is("foobar\x086789a\nbcdef"); // spell-checker:disable-line
}
#[test]
fn test_carriage_return_should_be_preserved() {
new_ucmd!().pipe_in("\r").succeeds().stdout_is("\r");
}
#[test]
fn test_carriage_return_overwritten_char_should_be_preserved() {
new_ucmd!().pipe_in("x\ry").succeeds().stdout_is("x\ry");
}
#[test]
fn test_carriage_return_should_reset_column_count() {
new_ucmd!()
.arg("-w6")
.pipe_in("12345\r123456789abcdef")
.succeeds()
.stdout_is("12345\r123456\n789abc\ndef");
}
#[test]
fn test_carriage_return_is_not_word_boundary() {
new_ucmd!()
.args(&["-w6", "-s"])
.pipe_in("fizz\rbuzz\rfizzbuzz") // spell-checker:disable-line
.succeeds()
.stdout_is("fizz\rbuzz\rfizzbu\nzz"); // spell-checker:disable-line
}
//
// bytewise tests
#[test]
fn test_bytewise_should_preserve_empty_line_without_final_newline() {
new_ucmd!()
.args(&["-w2", "-b"])
.pipe_in("123\n\n45")
.succeeds()
.stdout_is("12\n3\n\n45");
}
#[test]
fn test_bytewise_should_preserve_empty_line_and_final_newline() {
new_ucmd!()
.args(&["-w2", "-b"])
.pipe_in("12\n\n34\n")
.succeeds()
.stdout_is("12\n\n34\n");
}
#[test]
fn test_bytewise_should_preserve_empty_lines() {
new_ucmd!()
.arg("-b")
.pipe_in("\n")
.succeeds()
.stdout_is("\n");
new_ucmd!()
.args(&["-w1", "-b"])
.pipe_in("0\n1\n\n2\n\n\n")
.succeeds()
.stdout_is("0\n1\n\n2\n\n\n");
}
#[test]
fn test_bytewise_word_boundary_split_should_preserve_empty_lines() {
new_ucmd!()
.args(&["-s", "-b"])
.pipe_in("\n")
.succeeds()
.stdout_is("\n");
new_ucmd!()
.args(&["-w1", "-s", "-b"])
.pipe_in("0\n1\n\n2\n\n\n")
.succeeds()
.stdout_is("0\n1\n\n2\n\n\n");
}
#[test]
fn test_bytewise_should_not_add_newline_when_line_less_than_fold() {
new_ucmd!()
.arg("-b")
.pipe_in("1234")
.succeeds()
.stdout_is("1234");
}
#[test]
fn test_bytewise_should_not_add_newline_when_line_longer_than_fold() {
new_ucmd!()
.args(&["-w2", "-b"])
.pipe_in("1234")
.succeeds()
.stdout_is("12\n34");
}
#[test]
fn test_bytewise_should_not_add_newline_when_line_equal_to_fold() {
new_ucmd!()
.args(&["-w1", "-b"])
.pipe_in(" ")
.succeeds()
.stdout_is(" ");
}
#[test]
fn test_bytewise_should_preserve_final_newline_when_line_less_than_fold() {
new_ucmd!()
.arg("-b")
.pipe_in("1234\n")
.succeeds()
.stdout_is("1234\n");
}
#[test]
fn test_bytewise_should_preserve_final_newline_when_line_longer_than_fold() {
new_ucmd!()
.args(&["-w2", "-b"])
.pipe_in("1234\n")
.succeeds()
.stdout_is("12\n34\n");
}
#[test]
fn test_bytewise_should_preserve_final_newline_when_line_equal_to_fold() {
new_ucmd!()
.args(&["-w2", "-b"])
.pipe_in("1\n")
.succeeds()
.stdout_is("1\n");
}
#[test]
fn test_bytewise_single_tab_should_not_add_extra_newline() {
new_ucmd!()
.args(&["-w1", "-b"])
.pipe_in("\t")
.succeeds()
.stdout_is("\t");
}
#[test]
fn test_tab_counts_as_one_byte() {
new_ucmd!()
.args(&["-w2", "-b"])
.pipe_in("1\t2\n")
.succeeds()
.stdout_is("1\t\n2\n");
}
#[test]
fn test_bytewise_fold_before_tab_with_narrow_width() {
new_ucmd!()
.args(&["-w7", "-b"])
.pipe_in("a\t1")
.succeeds()
.stdout_is("a\t1");
}
#[test]
fn test_bytewise_fold_at_word_boundary_only_whitespace() {
new_ucmd!()
.args(&["-w2", "-s", "-b"])
.pipe_in(" ")
.succeeds()
.stdout_is(" \n ");
}
#[test]
fn test_bytewise_fold_at_word_boundary_only_whitespace_preserve_final_newline() {
new_ucmd!()
.args(&["-w2", "-s", "-b"])
.pipe_in(" \n")
.succeeds()
.stdout_is(" \n \n");
}
#[test]
fn test_bytewise_backspace_should_be_preserved() {
new_ucmd!()
.arg("-b")
.pipe_in("\x08")
.succeeds()
.stdout_is("\x08");
}
#[test]
fn test_bytewise_backspaced_char_should_be_preserved() {
new_ucmd!()
.arg("-b")
.pipe_in("x\x08")
.succeeds()
.stdout_is("x\x08");
}
#[test]
fn test_bytewise_backspace_should_not_decrease_column_count() {
new_ucmd!()
.args(&["-w2", "-b"])
.pipe_in("1\x08345")
.succeeds()
.stdout_is("1\x08\n34\n5");
}
#[test]
fn test_bytewise_backspace_is_not_word_boundary() {
new_ucmd!()
.args(&["-w10", "-s", "-b"])
.pipe_in("foobar\x0889abcdef")
.succeeds()
.stdout_is("foobar\x0889a\nbcdef"); // spell-checker:disable-line
}
#[test]
fn test_bytewise_carriage_return_should_be_preserved() {
new_ucmd!()
.arg("-b")
.pipe_in("\r")
.succeeds()
.stdout_is("\r");
}
#[test]
fn test_bytewise_carriage_return_overwritten_char_should_be_preserved() {
new_ucmd!()
.arg("-b")
.pipe_in("x\ry")
.succeeds()
.stdout_is("x\ry");
}
#[test]
fn test_bytewise_carriage_return_should_not_reset_column_count() {
new_ucmd!()
.args(&["-w6", "-b"])
.pipe_in("12345\r123456789abcdef")
.succeeds()
.stdout_is("12345\r\n123456\n789abc\ndef");
}
#[test]
fn test_bytewise_carriage_return_is_not_word_boundary() {
new_ucmd!()
.args(&["-w6", "-s", "-b"])
.pipe_in("fizz\rbuzz\rfizzbuzz") // spell-checker:disable-line
.succeeds()
.stdout_is("fizz\rb\nuzz\rfi\nzzbuzz"); // spell-checker:disable-line
}
#[test]
fn test_obsolete_syntax() {
new_ucmd!()
.arg("-5")
.arg("-s")
.arg("space_separated_words.txt")
.succeeds()
.stdout_is("test1\n \ntest2\n \ntest3\n \ntest4\n \ntest5\n \ntest6\n ");
}

View file

@ -1,46 +1,176 @@
use crate::common::util::*;
// spell-checker:ignore (ToDO) coreutil
// These tests run the GNU coreutils `(g)groups` binary in `$PATH` in order to gather reference values.
// If the `(g)groups` in `$PATH` doesn't include a coreutils version string,
// or the version is too low, the test is skipped.
// The reference version is 8.32. Here 8.30 was chosen because right now there's no
// ubuntu image for github action available with a higher version than 8.30.
const VERSION_MIN: &str = "8.30"; // minimum Version for the reference `groups` in $PATH
const VERSION_MIN_MULTIPLE_USERS: &str = "8.31"; // this feature was introduced in GNU's coreutils 8.31
const UUTILS_WARNING: &str = "uutils-tests-warning";
const UUTILS_INFO: &str = "uutils-tests-info";
macro_rules! unwrap_or_return {
( $e:expr ) => {
match $e {
Ok(x) => x,
Err(e) => {
println!("{}: test skipped: {}", UUTILS_INFO, e);
return;
}
}
};
}
fn whoami() -> String {
// Apparently some CI environments have configuration issues, e.g. with 'whoami' and 'id'.
//
// From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)"
// whoami: cannot find name for user ID 1001
// id --name: cannot find name for user ID 1001
// id --name: cannot find name for group ID 116
//
// However, when running "id" from within "/bin/bash" it looks fine:
// id: "uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),101(systemd-journal)"
// whoami: "runner"
// Use environment variable to get current user instead of
// invoking `whoami` and fall back to user "nobody" on error.
std::env::var("USER").unwrap_or_else(|e| {
println!("{}: {}, using \"nobody\" instead", UUTILS_WARNING, e);
"nobody".to_string()
})
}
#[test]
#[cfg(unix)]
fn test_groups() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stdout.trim().is_empty() {
// In the CI, some server are failing to return the group.
// As seems to be a configuration issue, ignoring it
return;
}
assert!(result.success);
assert!(!result.stdout.trim().is_empty());
let result = new_ucmd!().run();
let exp_result = unwrap_or_return!(expected_result(&[]));
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
#[test]
fn test_groups_arg() {
// get the username with the "id -un" command
let result = TestScenario::new("id").ucmd_keepenv().arg("-un").run();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let s1 = String::from(result.stdout.trim());
if is_ci() && s1.parse::<f64>().is_ok() {
// In the CI, some server are failing to return id -un.
// So, if we are getting a uid, just skip this test
// As seems to be a configuration issue, ignoring it
#[cfg(unix)]
fn test_groups_username() {
let test_users = [&whoami()[..]];
let result = new_ucmd!().args(&test_users).run();
let exp_result = unwrap_or_return!(expected_result(&test_users));
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
#[test]
#[cfg(unix)]
fn test_groups_username_multiple() {
// TODO: [2021-06; jhscheer] refactor this as `let util_name = host_name_for(util_name!())` when that function is added to 'tests/common'
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(all(unix, not(target_os = "linux")))]
let util_name = &format!("g{}", util_name!());
let version_check_string = check_coreutil_version(util_name, VERSION_MIN_MULTIPLE_USERS);
if version_check_string.starts_with(UUTILS_WARNING) {
println!("{}\ntest skipped", version_check_string);
return;
}
let test_users = ["root", "man", "postfix", "sshd", &whoami()];
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
assert!(!result.stdout.is_empty());
let username = result.stdout.trim();
let result = new_ucmd!().args(&test_users).run();
let exp_result = unwrap_or_return!(expected_result(&test_users));
// call groups with the user name to check that we
// are getting something
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg(username).run();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
assert!(!result.stdout.is_empty());
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
fn check_coreutil_version(util_name: &str, version_expected: &str) -> String {
// example:
// $ id --version | head -n 1
// id (GNU coreutils) 8.32.162-4eda
let scene = TestScenario::new(util_name);
let version_check = scene
.cmd_keepenv(&util_name)
.env("LC_ALL", "C")
.arg("--version")
.run();
version_check
.stdout_str()
.split('\n')
.collect::<Vec<_>>()
.get(0)
.map_or_else(
|| format!("{}: unexpected output format for reference coreutil: '{} --version'", UUTILS_WARNING, util_name),
|s| {
if s.contains(&format!("(GNU coreutils) {}", version_expected)) {
s.to_string()
} else if s.contains("(GNU coreutils)") {
let version_found = s.split_whitespace().last().unwrap()[..4].parse::<f32>().unwrap_or_default();
let version_expected = version_expected.parse::<f32>().unwrap_or_default();
if version_found > version_expected {
format!("{}: version for the reference coreutil '{}' is higher than expected; expected: {}, found: {}", UUTILS_INFO, util_name, version_expected, version_found)
} else {
format!("{}: version for the reference coreutil '{}' does not match; expected: {}, found: {}", UUTILS_WARNING, util_name, version_expected, version_found) }
} else {
format!("{}: no coreutils version string found for reference coreutils '{} --version'", UUTILS_WARNING, util_name)
}
},
)
}
#[allow(clippy::needless_borrow)]
#[cfg(unix)]
fn expected_result(args: &[&str]) -> Result<CmdResult, String> {
// TODO: [2021-06; jhscheer] refactor this as `let util_name = host_name_for(util_name!())` when that function is added to 'tests/common'
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(all(unix, not(target_os = "linux")))]
let util_name = &format!("g{}", util_name!());
let version_check_string = check_coreutil_version(util_name, VERSION_MIN);
if version_check_string.starts_with(UUTILS_WARNING) {
return Err(version_check_string);
}
println!("{}", version_check_string);
let scene = TestScenario::new(util_name);
let result = scene
.cmd_keepenv(util_name)
.env("LC_ALL", "C")
.args(args)
.run();
let (stdout, stderr): (String, String) = if cfg!(target_os = "linux") {
(
result.stdout_str().to_string(),
result.stderr_str().to_string(),
)
} else {
// strip 'g' prefix from results:
let from = util_name.to_string() + ":";
let to = &from[1..];
(
result.stdout_str().replace(&from, to),
result.stderr_str().replace(&from, to),
)
};
Ok(CmdResult::new(
Some(result.tmpd()),
Some(result.code()),
result.succeeded(),
stdout.as_bytes(),
stderr.as_bytes(),
))
}

View file

@ -17,14 +17,14 @@ macro_rules! test_digest {
fn test_single_file() {
let ts = TestScenario::new("hashsum");
assert_eq!(ts.fixtures.read(EXPECTED_FILE),
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("input.txt").succeeds().no_stderr().stdout));
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("input.txt").succeeds().no_stderr().stdout_str()));
}
#[test]
fn test_stdin() {
let ts = TestScenario::new("hashsum");
assert_eq!(ts.fixtures.read(EXPECTED_FILE),
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture("input.txt").succeeds().no_stderr().stdout));
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture("input.txt").succeeds().no_stderr().stdout_str()));
}
}
)*)

216
tests/by-util/test_head.rs Normal file → Executable file
View file

@ -1,6 +1,13 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (words) bogusfile emptyfile abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstu
use crate::common::util::*;
static INPUT: &'static str = "lorem_ipsum.txt";
static INPUT: &str = "lorem_ipsum.txt";
#[test]
fn test_stdin_default() {
@ -89,73 +96,214 @@ fn test_verbose() {
#[test]
#[ignore]
fn test_spams_newline() {
//this test is does not mirror what GNU does
new_ucmd!().pipe_in("a").succeeds().stdout_is("a\n");
}
#[test]
#[ignore]
fn test_unsupported_byte_syntax() {
fn test_byte_syntax() {
new_ucmd!()
.args(&["-1c"])
.pipe_in("abc")
.fails()
//GNU head returns "a"
.stdout_is("")
.stderr_is("head: error: Unrecognized option: \'1\'");
.run()
.stdout_is("a");
}
#[test]
#[ignore]
fn test_unsupported_line_syntax() {
fn test_line_syntax() {
new_ucmd!()
.args(&["-n", "2048m"])
.pipe_in("a\n")
.fails()
//.stdout_is("a\n"); What GNU head returns.
.stdout_is("")
.stderr_is("head: error: invalid line count \'2048m\': invalid digit found in string");
.run()
.stdout_is("a\n");
}
#[test]
#[ignore]
fn test_unsupported_zero_terminated_syntax() {
fn test_zero_terminated_syntax() {
new_ucmd!()
.args(&["-z -n 1"])
.args(&["-z", "-n", "1"])
.pipe_in("x\0y")
.fails()
//GNU Head returns "x\0"
.stderr_is("head: error: Unrecognized option: \'z\'");
.run()
.stdout_is("x\0");
}
#[test]
#[ignore]
fn test_unsupported_zero_terminated_syntax_2() {
fn test_zero_terminated_syntax_2() {
new_ucmd!()
.args(&["-z -n 2"])
.args(&["-z", "-n", "2"])
.pipe_in("x\0y")
.fails()
//GNU Head returns "x\0y"
.stderr_is("head: error: Unrecognized option: \'z\'");
.run()
.stdout_is("x\0y");
}
#[test]
#[ignore]
fn test_unsupported_negative_byte_syntax() {
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!()
.args(&["--bytes=-2"])
.pipe_in("a\n")
.fails()
//GNU Head returns ""
.stderr_is("head: error: invalid byte count \'-2\': invalid digit found in string");
.run()
.stdout_is("");
}
#[test]
#[ignore]
fn test_bug_in_negative_zero_lines() {
fn test_negative_zero_lines() {
new_ucmd!()
.args(&["--lines=-0"])
.pipe_in("a\nb\n")
.succeeds()
//GNU Head returns "a\nb\n"
.stdout_is("");
.stdout_is("a\nb\n");
}
#[test]
fn test_negative_zero_bytes() {
new_ucmd!()
.args(&["--bytes=-0"])
.pipe_in("qwerty")
.succeeds()
.stdout_is("qwerty");
}
#[test]
fn test_no_such_file_or_directory() {
new_ucmd!()
.arg("no_such_file.toml")
.fails()
.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
#[test]
fn test_sequence_fixture() {
new_ucmd!()
.args(&["-n", "-10", "sequence"])
.run()
.stdout_is_fixture("sequence.expected");
}
#[test]
fn test_file_backwards() {
new_ucmd!()
.args(&["-c", "-10", "lorem_ipsum.txt"])
.run()
.stdout_is_fixture("lorem_ipsum_backwards_file.expected");
}
#[test]
fn test_zero_terminated() {
new_ucmd!()
.args(&["-z", "zero_terminated.txt"])
.run()
.stdout_is_fixture("zero_terminated.expected");
}
#[test]
fn test_obsolete_extras() {
new_ucmd!()
.args(&["-5zv"])
.pipe_in("1\02\03\04\05\06")
.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 <==
",
);
}
#[test]
fn test_head_invalid_num() {
new_ucmd!()
.args(&["-c", "1024R", "emptyfile.txt"])
.fails()
.stderr_is("head: invalid number of bytes: '1024R'");
new_ucmd!()
.args(&["-n", "1024R", "emptyfile.txt"])
.fails()
.stderr_is("head: invalid number of lines: '1024R'");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["-c", "1Y", "emptyfile.txt"])
.fails()
.stderr_is("head: invalid number of bytes: '1Y': Value too large for defined data type");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["-n", "1Y", "emptyfile.txt"])
.fails()
.stderr_is("head: invalid number of lines: '1Y': Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["-c", size])
.fails()
.code_is(1)
.stderr_only(format!(
"head: invalid number of bytes: '{}': Value too large for defined data type",
size
));
}
}
}
#[test]
fn test_head_num_with_undocumented_sign_bytes() {
// tail: '-' is not documented (8.32 man pages)
// head: '+' is not documented (8.32 man pages)
const ALPHABET: &str = "abcdefghijklmnopqrstuvwxyz";
new_ucmd!()
.args(&["-c", "5"])
.pipe_in(ALPHABET)
.succeeds()
.stdout_is("abcde");
new_ucmd!()
.args(&["-c", "-5"])
.pipe_in(ALPHABET)
.succeeds()
.stdout_is("abcdefghijklmnopqrstu");
new_ucmd!()
.args(&["-c", "+5"])
.pipe_in(ALPHABET)
.succeeds()
.stdout_is("abcde");
}

View file

@ -4,10 +4,6 @@ use self::regex::Regex;
#[test]
fn test_normal() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(result.success);
let re = Regex::new(r"^[0-9a-f]{8}").unwrap();
assert!(re.is_match(&result.stdout.trim()));
new_ucmd!().succeeds().stdout_matches(&re);
}

View file

@ -6,25 +6,25 @@ fn test_hostname() {
let ls_short_res = new_ucmd!().arg("-s").succeeds();
let ls_domain_res = new_ucmd!().arg("-d").succeeds();
assert!(ls_default_res.stdout.len() >= ls_short_res.stdout.len());
assert!(ls_default_res.stdout.len() >= ls_domain_res.stdout.len());
assert!(ls_default_res.stdout().len() >= ls_short_res.stdout().len());
assert!(ls_default_res.stdout().len() >= ls_domain_res.stdout().len());
}
// FixME: fails for "MacOS"
#[cfg(not(target_os = "macos"))]
#[cfg(not(target_vendor = "apple"))]
#[test]
fn test_hostname_ip() {
let result = new_ucmd!().arg("-i").run();
println!("{:#?}", result);
assert!(result.success);
assert!(!result.stdout.trim().is_empty());
let result = new_ucmd!().arg("-i").succeeds();
assert!(!result.stdout_str().trim().is_empty());
}
#[test]
fn test_hostname_full() {
let result = new_ucmd!().arg("-f").succeeds();
assert!(!result.stdout.trim().is_empty());
let ls_short_res = new_ucmd!().arg("-s").succeeds();
assert!(result.stdout.trim().contains(ls_short_res.stdout.trim()));
assert!(!ls_short_res.stdout_str().trim().is_empty());
new_ucmd!()
.arg("-f")
.succeeds()
.stdout_contains(ls_short_res.stdout_str().trim());
}

View file

@ -1,187 +1,505 @@
use crate::common::util::*;
fn return_whoami_username() -> String {
let scene = TestScenario::new("whoami");
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("cannot find name for user ID") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return String::from("");
}
// spell-checker:ignore (ToDO) coreutil
result.stdout.trim().to_string()
// These tests run the GNU coreutils `(g)id` binary in `$PATH` in order to gather reference values.
// If the `(g)id` in `$PATH` doesn't include a coreutils version string,
// or the version is too low, the test is skipped.
// The reference version is 8.32. Here 8.30 was chosen because right now there's no
// ubuntu image for github action available with a higher version than 8.30.
const VERSION_MIN: &str = "8.30"; // minimum Version for the reference `id` in $PATH
const VERSION_MIN_MULTIPLE_USERS: &str = "8.31"; // this feature was introduced in GNU's coreutils 8.31
const UUTILS_WARNING: &str = "uutils-tests-warning";
const UUTILS_INFO: &str = "uutils-tests-info";
macro_rules! unwrap_or_return {
( $e:expr ) => {
match $e {
Ok(x) => x,
Err(e) => {
println!("{}: test skipped: {}", UUTILS_INFO, e);
return;
}
}
};
}
fn whoami() -> String {
// Apparently some CI environments have configuration issues, e.g. with 'whoami' and 'id'.
//
// From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)"
// whoami: cannot find name for user ID 1001
// id --name: cannot find name for user ID 1001
// id --name: cannot find name for group ID 116
//
// However, when running "id" from within "/bin/bash" it looks fine:
// id: "uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),101(systemd-journal)"
// whoami: "runner"
// Use environment variable to get current user instead of
// invoking `whoami` and fall back to user "nobody" on error.
std::env::var("USER").unwrap_or_else(|e| {
println!("{}: {}, using \"nobody\" instead", UUTILS_WARNING, e);
"nobody".to_string()
})
}
#[test]
fn test_id() {
let scene = TestScenario::new(util_name!());
#[cfg(unix)]
fn test_id_no_specified_user() {
let result = new_ucmd!().run();
let exp_result = unwrap_or_return!(expected_result(&[]));
let mut _exp_stdout = exp_result.stdout_str().to_string();
let mut result = scene.ucmd().arg("-u").run();
if result.stderr.contains("cannot find name for user ID") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return;
#[cfg(target_os = "linux")]
{
// NOTE: (SELinux NotImplemented) strip 'context' part from exp_stdout:
if let Some(context_offset) = exp_result.stdout_str().find(" context=") {
_exp_stdout.replace_range(context_offset.._exp_stdout.len() - 1, "");
}
}
assert!(result.success);
let uid = String::from(result.stdout.trim());
result = scene.ucmd().run();
if is_ci() && result.stderr.contains("cannot find name for user ID") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return;
}
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if !result.stderr.contains("Could not find uid") {
// Verify that the id found by --user/-u exists in the list
assert!(result.stdout.contains(&uid));
}
result
.stdout_is(_exp_stdout)
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
#[test]
fn test_id_from_name() {
let username = return_whoami_username();
if username == "" {
// Sometimes, the CI is failing here
return;
}
#[cfg(unix)]
fn test_id_single_user() {
let test_users = [&whoami()[..]];
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg(&username).succeeds();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
let uid = String::from(result.stdout.trim());
let result = scene.ucmd().succeeds();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
// Verify that the id found by --user/-u exists in the list
assert!(result.stdout.contains(&uid));
// Verify that the username found by whoami exists in the list
assert!(result.stdout.contains(&username));
}
let mut exp_result = unwrap_or_return!(expected_result(&test_users));
scene
.ucmd()
.args(&test_users)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
#[test]
fn test_id_name_from_id() {
let mut scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("-u").run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
let uid = String::from(result.stdout.trim());
scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("-nu").arg(uid).run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return;
}
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
let username_id = String::from(result.stdout.trim());
scene = TestScenario::new("whoami");
let result = scene.cmd("whoami").run();
let username_whoami = result.stdout.trim();
assert_eq!(username_id, username_whoami);
}
#[test]
fn test_id_group() {
let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("-g").succeeds();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
let s1 = String::from(result.stdout.trim());
assert!(s1.parse::<f64>().is_ok());
result = scene.ucmd().arg("--group").succeeds();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
let s1 = String::from(result.stdout.trim());
assert!(s1.parse::<f64>().is_ok());
}
#[test]
fn test_id_groups() {
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("-G").succeeds();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
let groups = result.stdout.trim().split_whitespace();
for s in groups {
assert!(s.parse::<f64>().is_ok());
}
let result = scene.ucmd().arg("--groups").succeeds();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
let groups = result.stdout.trim().split_whitespace();
for s in groups {
assert!(s.parse::<f64>().is_ok());
// u/g/G z/n
for &opt in &["--user", "--group", "--groups"] {
let mut args = vec![opt];
args.extend_from_slice(&test_users);
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.push("--zero");
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.push("--name");
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.pop();
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
}
}
#[test]
fn test_id_user() {
let scene = TestScenario::new(util_name!());
#[cfg(unix)]
fn test_id_single_user_non_existing() {
let args = &["hopefully_non_existing_username"];
let result = new_ucmd!().args(args).run();
let exp_result = unwrap_or_return!(expected_result(args));
let mut result = scene.ucmd().arg("-u").succeeds();
assert!(result.success);
let s1 = String::from(result.stdout.trim());
assert!(s1.parse::<f64>().is_ok());
result = scene.ucmd().arg("--user").succeeds();
assert!(result.success);
let s1 = String::from(result.stdout.trim());
assert!(s1.parse::<f64>().is_ok());
// It is unknown why on macOS (and possibly others?) `id` adds "Invalid argument".
// coreutils 8.32: $ LC_ALL=C id foobar
// macOS: stderr: "id: 'foobar': no such user: Invalid argument"
// linux: stderr: "id: 'foobar': no such user"
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
}
#[test]
#[cfg(unix)]
fn test_id_name() {
let scene = TestScenario::new(util_name!());
for &opt in &["--user", "--group", "--groups"] {
let args = [opt, "--name"];
let result = scene.ucmd().args(&args).run();
let exp_result = unwrap_or_return!(expected_result(&args));
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
if opt == "--user" {
assert_eq!(result.stdout_str().trim_end(), whoami());
}
}
}
#[test]
#[cfg(unix)]
fn test_id_real() {
let scene = TestScenario::new(util_name!());
for &opt in &["--user", "--group", "--groups"] {
let args = [opt, "--real"];
let result = scene.ucmd().args(&args).run();
let exp_result = unwrap_or_return!(expected_result(&args));
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
}
#[test]
#[cfg(all(unix, not(target_os = "linux")))]
fn test_id_pretty_print() {
let username = return_whoami_username();
if username == "" {
// Sometimes, the CI is failing here
return;
}
// `-p` is BSD only and not supported on GNU's `id`
let username = whoami();
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("-p").run();
if result.stdout.trim() == "" {
// Sometimes, the CI is failing here with
// old rust versions on Linux
return;
let result = new_ucmd!().arg("-p").run();
if result.stdout_str().trim().is_empty() {
// this fails only on: "MinRustV (ubuntu-latest, feat_os_unix)"
// `rustc 1.40.0 (73528e339 2019-12-16)`
// run: /home/runner/work/coreutils/coreutils/target/debug/coreutils id -p
// thread 'test_id::test_id_pretty_print' panicked at 'Command was expected to succeed.
// stdout =
// stderr = ', tests/common/util.rs:157:13
println!("test skipped:");
} else {
result.success().stdout_contains(username);
}
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
assert!(result.stdout.contains(&username));
}
#[test]
#[cfg(all(unix, not(target_os = "linux")))]
fn test_id_password_style() {
let username = return_whoami_username();
if username == "" {
// Sometimes, the CI is failing here
// `-P` is BSD only and not supported on GNU's `id`
let username = whoami();
let result = new_ucmd!().arg("-P").arg(&username).succeeds();
assert!(result.stdout_str().starts_with(&username));
}
#[test]
#[cfg(unix)]
fn test_id_multiple_users() {
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(all(unix, not(target_os = "linux")))]
let util_name = &format!("g{}", util_name!());
let version_check_string = check_coreutil_version(util_name, VERSION_MIN_MULTIPLE_USERS);
if version_check_string.starts_with(UUTILS_WARNING) {
println!("{}\ntest skipped", version_check_string);
return;
}
// Same typical users that GNU test suite is using.
let test_users = ["root", "man", "postfix", "sshd", &whoami()];
let scene = TestScenario::new(util_name!());
let mut exp_result = unwrap_or_return!(expected_result(&test_users));
scene
.ucmd()
.args(&test_users)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
let result = scene.ucmd().arg("-P").succeeds();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
assert!(result.success);
assert!(result.stdout.starts_with(&username));
// u/g/G z/n
for &opt in &["--user", "--group", "--groups"] {
let mut args = vec![opt];
args.extend_from_slice(&test_users);
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.push("--zero");
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.push("--name");
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.pop();
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
}
}
#[test]
#[cfg(unix)]
fn test_id_multiple_users_non_existing() {
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(all(unix, not(target_os = "linux")))]
let util_name = &format!("g{}", util_name!());
let version_check_string = check_coreutil_version(util_name, VERSION_MIN_MULTIPLE_USERS);
if version_check_string.starts_with(UUTILS_WARNING) {
println!("{}\ntest skipped", version_check_string);
return;
}
let test_users = [
"root",
"hopefully_non_existing_username1",
&whoami(),
"man",
"hopefully_non_existing_username2",
"hopefully_non_existing_username3",
"postfix",
"sshd",
"hopefully_non_existing_username4",
&whoami(),
];
let scene = TestScenario::new(util_name!());
let mut exp_result = unwrap_or_return!(expected_result(&test_users));
scene
.ucmd()
.args(&test_users)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
// u/g/G z/n
for &opt in &["--user", "--group", "--groups"] {
let mut args = vec![opt];
args.extend_from_slice(&test_users);
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.push("--zero");
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.push("--name");
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
args.pop();
exp_result = unwrap_or_return!(expected_result(&args));
scene
.ucmd()
.args(&args)
.run()
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str().replace(": Invalid argument", ""))
.code_is(exp_result.code());
}
}
#[test]
#[cfg(unix)]
fn test_id_default_format() {
let scene = TestScenario::new(util_name!());
for &opt1 in &["--name", "--real"] {
// id: cannot print only names or real IDs in default format
let args = [opt1];
scene
.ucmd()
.args(&args)
.fails()
.stderr_only(unwrap_or_return!(expected_result(&args)).stderr_str());
for &opt2 in &["--user", "--group", "--groups"] {
// u/g/G n/r
let args = [opt2, opt1];
let result = scene.ucmd().args(&args).run();
let exp_result = unwrap_or_return!(expected_result(&args));
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
}
for &opt2 in &["--user", "--group", "--groups"] {
// u/g/G
let args = [opt2];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_only(unwrap_or_return!(expected_result(&args)).stdout_str());
}
}
#[test]
#[cfg(unix)]
fn test_id_zero() {
let scene = TestScenario::new(util_name!());
for z_flag in &["-z", "--zero"] {
// id: option --zero not permitted in default format
scene
.ucmd()
.args(&[z_flag])
.fails()
.stderr_only(unwrap_or_return!(expected_result(&[z_flag])).stderr_str());
for &opt1 in &["--name", "--real"] {
// id: cannot print only names or real IDs in default format
let args = [opt1, z_flag];
scene
.ucmd()
.args(&args)
.fails()
.stderr_only(unwrap_or_return!(expected_result(&args)).stderr_str());
for &opt2 in &["--user", "--group", "--groups"] {
// u/g/G n/r z
let args = [opt2, z_flag, opt1];
let result = scene.ucmd().args(&args).run();
let exp_result = unwrap_or_return!(expected_result(&args));
result
.stdout_is(exp_result.stdout_str())
.stderr_is(exp_result.stderr_str())
.code_is(exp_result.code());
}
}
for &opt2 in &["--user", "--group", "--groups"] {
// u/g/G z
let args = [opt2, z_flag];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_only(unwrap_or_return!(expected_result(&args)).stdout_str());
}
}
}
fn check_coreutil_version(util_name: &str, version_expected: &str) -> String {
// example:
// $ id --version | head -n 1
// id (GNU coreutils) 8.32.162-4eda
let scene = TestScenario::new(util_name);
let version_check = scene
.cmd_keepenv(&util_name)
.env("LC_ALL", "C")
.arg("--version")
.run();
version_check
.stdout_str()
.split('\n')
.collect::<Vec<_>>()
.get(0)
.map_or_else(
|| format!("{}: unexpected output format for reference coreutil: '{} --version'", UUTILS_WARNING, util_name),
|s| {
if s.contains(&format!("(GNU coreutils) {}", version_expected)) {
s.to_string()
} else if s.contains("(GNU coreutils)") {
let version_found = s.split_whitespace().last().unwrap()[..4].parse::<f32>().unwrap_or_default();
let version_expected = version_expected.parse::<f32>().unwrap_or_default();
if version_found > version_expected {
format!("{}: version for the reference coreutil '{}' is higher than expected; expected: {}, found: {}", UUTILS_INFO, util_name, version_expected, version_found)
} else {
format!("{}: version for the reference coreutil '{}' does not match; expected: {}, found: {}", UUTILS_WARNING, util_name, version_expected, version_found) }
} else {
format!("{}: no coreutils version string found for reference coreutils '{} --version'", UUTILS_WARNING, util_name)
}
},
)
}
#[allow(clippy::needless_borrow)]
#[cfg(unix)]
fn expected_result(args: &[&str]) -> Result<CmdResult, String> {
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(all(unix, not(target_os = "linux")))]
let util_name = &format!("g{}", util_name!());
let version_check_string = check_coreutil_version(util_name, VERSION_MIN);
if version_check_string.starts_with(UUTILS_WARNING) {
return Err(version_check_string);
}
println!("{}", version_check_string);
let scene = TestScenario::new(util_name);
let result = scene
.cmd_keepenv(util_name)
.env("LC_ALL", "C")
.args(args)
.run();
let (stdout, stderr): (String, String) = if cfg!(target_os = "linux") {
(
result.stdout_str().to_string(),
result.stderr_str().to_string(),
)
} else {
// strip 'g' prefix from results:
let from = util_name.to_string() + ":";
let to = &from[1..];
(
result.stdout_str().replace(&from, to),
result.stderr_str().replace(&from, to),
)
};
Ok(CmdResult::new(
Some(result.tmpd()),
Some(result.code()),
result.succeeded(),
stdout.as_bytes(),
stderr.as_bytes(),
))
}

View file

@ -1,25 +1,30 @@
// spell-checker:ignore (words) helloworld objdump
use crate::common::util::*;
use filetime::FileTime;
use rust_users::*;
use std::os::unix::fs::PermissionsExt;
#[cfg(not(windows))]
use std::process::Command;
#[cfg(target_os = "linux")]
use std::thread::sleep;
#[test]
fn test_install_help() {
let (_, mut ucmd) = at_and_ucmd!();
assert!(ucmd
.arg("--help")
ucmd.arg("--help")
.succeeds()
.no_stderr()
.stdout
.contains("FLAGS:"));
.stdout_contains("FLAGS:");
}
#[test]
fn test_install_basic() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_a";
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let dir = "target_dir";
let file1 = "source_file1";
let file2 = "source_file2";
at.touch(file1);
at.touch(file2);
@ -34,7 +39,7 @@ fn test_install_basic() {
#[test]
fn test_install_twice_dir() {
let dir = "test_install_target_dir_dir_a";
let dir = "dir";
let scene = TestScenario::new(util_name!());
scene.ucmd().arg("-d").arg(dir).succeeds();
@ -47,67 +52,124 @@ fn test_install_twice_dir() {
#[test]
fn test_install_failing_not_dir() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let file3 = "test_install_target_dir_file_a3";
let file1 = "file1";
let file2 = "file2";
let file3 = "file3";
at.touch(file1);
at.touch(file2);
at.touch(file3);
assert!(ucmd
.arg(file1)
ucmd.arg(file1)
.arg(file2)
.arg(file3)
.fails()
.stderr
.contains("not a directory"));
.stderr_contains("not a directory");
}
#[test]
fn test_install_unimplemented_arg() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_b";
let file = "test_install_target_dir_file_b";
let dir = "target_dir";
let file = "source_file";
let context_arg = "--context";
at.touch(file);
at.mkdir(dir);
assert!(ucmd
.arg(context_arg)
ucmd.arg(context_arg)
.arg(file)
.arg(dir)
.fails()
.stderr
.contains("Unimplemented"));
.stderr_contains("Unimplemented");
assert!(!at.file_exists(&format!("{}/{}", dir, file)));
}
#[test]
fn test_install_component_directories() {
fn test_install_ancestors_directories() {
let (at, mut ucmd) = at_and_ucmd!();
let component1 = "test_install_target_dir_component_c1";
let component2 = "test_install_target_dir_component_c2";
let component3 = "test_install_target_dir_component_c3";
let ancestor1 = "ancestor1";
let ancestor2 = "ancestor1/ancestor2";
let target_dir = "ancestor1/ancestor2/target_dir";
let directories_arg = "-d";
ucmd.args(&[directories_arg, component1, component2, component3])
ucmd.args(&[directories_arg, target_dir])
.succeeds()
.no_stderr();
assert!(at.dir_exists(component1));
assert!(at.dir_exists(component2));
assert!(at.dir_exists(component3));
assert!(at.dir_exists(ancestor1));
assert!(at.dir_exists(ancestor2));
assert!(at.dir_exists(target_dir));
}
#[test]
fn test_install_ancestors_mode_directories() {
let (at, mut ucmd) = at_and_ucmd!();
let ancestor1 = "ancestor1";
let ancestor2 = "ancestor1/ancestor2";
let target_dir = "ancestor1/ancestor2/target_dir";
let directories_arg = "-d";
let mode_arg = "--mode=700";
ucmd.args(&[mode_arg, directories_arg, target_dir])
.succeeds()
.no_stderr();
assert!(at.dir_exists(ancestor1));
assert!(at.dir_exists(ancestor2));
assert!(at.dir_exists(target_dir));
assert_ne!(0o40_700_u32, at.metadata(ancestor1).permissions().mode());
assert_ne!(0o40_700_u32, at.metadata(ancestor2).permissions().mode());
// Expected mode only on the target_dir.
assert_eq!(0o40_700_u32, at.metadata(target_dir).permissions().mode());
}
#[test]
fn test_install_parent_directories() {
let (at, mut ucmd) = at_and_ucmd!();
let ancestor1 = "ancestor1";
let ancestor2 = "ancestor1/ancestor2";
let target_dir = "ancestor1/ancestor2/target_dir";
let directories_arg = "-d";
// Here one of the ancestors already exist and only the target_dir and
// its parent must be created.
at.mkdir(ancestor1);
ucmd.args(&[directories_arg, target_dir])
.succeeds()
.no_stderr();
assert!(at.dir_exists(ancestor2));
assert!(at.dir_exists(target_dir));
}
#[test]
fn test_install_several_directories() {
let (at, mut ucmd) = at_and_ucmd!();
let dir1 = "dir1";
let dir2 = "dir2";
let dir3 = "dir3";
let directories_arg = "-d";
ucmd.args(&[directories_arg, dir1, dir2, dir3])
.succeeds()
.no_stderr();
assert!(at.dir_exists(dir1));
assert!(at.dir_exists(dir2));
assert!(at.dir_exists(dir3));
}
#[test]
fn test_install_mode_numeric() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dir = "test_install_target_dir_dir_e";
let dir2 = "test_install_target_dir_dir_e2";
let dir = "dir1";
let dir2 = "dir2";
let file = "test_install_target_dir_file_e";
let file = "file";
let mode_arg = "--mode=333";
at.touch(file);
@ -124,29 +186,25 @@ fn test_install_mode_numeric() {
assert!(at.file_exists(file));
assert!(at.file_exists(dest_file));
let permissions = at.metadata(dest_file).permissions();
assert_eq!(0o100333 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o100_333_u32, PermissionsExt::mode(&permissions));
let mode_arg = "-m 0333";
at.mkdir(dir2);
let result = scene.ucmd().arg(mode_arg).arg(file).arg(dir2).run();
scene.ucmd().arg(mode_arg).arg(file).arg(dir2).succeeds();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
let dest_file = &format!("{}/{}", dir2, file);
assert!(at.file_exists(file));
assert!(at.file_exists(dest_file));
let permissions = at.metadata(dest_file).permissions();
assert_eq!(0o100333 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o100_333_u32, PermissionsExt::mode(&permissions));
}
#[test]
fn test_install_mode_symbolic() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_f";
let file = "test_install_target_dir_file_f";
let dir = "target_dir";
let file = "source_file";
let mode_arg = "--mode=o+wx";
at.touch(file);
@ -157,25 +215,23 @@ fn test_install_mode_symbolic() {
assert!(at.file_exists(file));
assert!(at.file_exists(dest_file));
let permissions = at.metadata(dest_file).permissions();
assert_eq!(0o100003 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o100_003_u32, PermissionsExt::mode(&permissions));
}
#[test]
fn test_install_mode_failing() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_g";
let file = "test_install_target_dir_file_g";
let dir = "target_dir";
let file = "source_file";
let mode_arg = "--mode=999";
at.touch(file);
at.mkdir(dir);
assert!(ucmd
.arg(file)
ucmd.arg(file)
.arg(dir)
.arg(mode_arg)
.fails()
.stderr
.contains("Invalid mode string: invalid digit found in string"));
.stderr_contains("Invalid mode string: invalid digit found in string");
let dest_file = &format!("{}/{}", dir, file);
assert!(at.file_exists(file));
@ -185,7 +241,7 @@ fn test_install_mode_failing() {
#[test]
fn test_install_mode_directories() {
let (at, mut ucmd) = at_and_ucmd!();
let component = "test_install_target_dir_component_h";
let component = "component";
let directories_arg = "-d";
let mode_arg = "--mode=333";
@ -197,14 +253,14 @@ fn test_install_mode_directories() {
assert!(at.dir_exists(component));
let permissions = at.metadata(component).permissions();
assert_eq!(0o040333 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o040_333_u32, PermissionsExt::mode(&permissions));
}
#[test]
fn test_install_target_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_file_file_i1";
let file2 = "test_install_target_file_file_i2";
let file1 = "source_file";
let file2 = "target_file";
at.touch(file1);
at.touch(file2);
@ -217,8 +273,8 @@ fn test_install_target_file() {
#[test]
fn test_install_target_new_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_install_target_new_filer_file_j";
let dir = "test_install_target_new_file_dir_j";
let file = "file";
let dir = "target_dir";
at.touch(file);
at.mkdir(dir);
@ -234,8 +290,8 @@ fn test_install_target_new_file() {
#[test]
fn test_install_target_new_file_with_group() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_install_target_new_filer_file_j";
let dir = "test_install_target_new_file_dir_j";
let file = "file";
let dir = "target_dir";
let gid = get_effective_gid();
at.touch(file);
@ -247,16 +303,13 @@ fn test_install_target_new_file_with_group() {
.arg(format!("{}/{}", dir, file))
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
if is_ci() && result.stderr.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;
}
assert!(result.success);
result.success();
assert!(at.file_exists(file));
assert!(at.file_exists(&format!("{}/{}", dir, file)));
}
@ -264,8 +317,8 @@ fn test_install_target_new_file_with_group() {
#[test]
fn test_install_target_new_file_with_owner() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_install_target_new_filer_file_j";
let dir = "test_install_target_new_file_dir_j";
let file = "file";
let dir = "target_dir";
let uid = get_effective_uid();
at.touch(file);
@ -277,16 +330,13 @@ fn test_install_target_new_file_with_owner() {
.arg(format!("{}/{}", dir, file))
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
if is_ci() && result.stderr.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;
}
assert!(result.success);
result.success();
assert!(at.file_exists(file));
assert!(at.file_exists(&format!("{}/{}", dir, file)));
}
@ -294,27 +344,49 @@ fn test_install_target_new_file_with_owner() {
#[test]
fn test_install_target_new_file_failing_nonexistent_parent() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_new_file_failing_file_k1";
let file2 = "test_install_target_new_file_failing_file_k2";
let dir = "test_install_target_new_file_failing_dir_k";
let file1 = "source_file";
let file2 = "target_file";
let dir = "target_dir";
at.touch(file1);
let err = ucmd
.arg(file1)
ucmd.arg(file1)
.arg(format!("{}/{}", dir, file2))
.fails()
.stderr;
.stderr_contains(&"No such file or directory");
}
assert!(err.contains("not a directory"))
#[test]
fn test_install_preserve_timestamps() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "source_file";
let file2 = "target_file";
at.touch(file1);
ucmd.arg(file1).arg(file2).arg("-p").succeeds().no_stderr();
assert!(at.file_exists(file1));
assert!(at.file_exists(file2));
let file1_metadata = at.metadata(file1);
let file2_metadata = at.metadata(file2);
assert_eq!(
file1_metadata.accessed().ok(),
file2_metadata.accessed().ok()
);
assert_eq!(
file1_metadata.modified().ok(),
file2_metadata.modified().ok()
);
}
// These two tests are failing but should work
#[test]
fn test_install_copy_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let file1 = "source_file";
let file2 = "target_file";
at.touch(file1);
ucmd.arg(file1).arg(file2).succeeds().no_stderr();
@ -327,9 +399,300 @@ fn test_install_copy_file() {
#[cfg(target_os = "linux")]
fn test_install_target_file_dev_null() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "/dev/null";
let file2 = "test_install_target_file_file_i2";
ucmd.arg(file1).arg(file2).succeeds().no_stderr();
let file1 = "/dev/null";
let file2 = "target_file";
ucmd.arg(file1).arg(file2).succeeds();
assert!(at.file_exists(file2));
}
#[test]
fn test_install_nested_paths_copy_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "source_file";
let dir1 = "source_dir";
let dir2 = "target_dir";
at.mkdir(dir1);
at.mkdir(dir2);
at.touch(&format!("{}/{}", dir1, file1));
ucmd.arg(format!("{}/{}", dir1, file1))
.arg(dir2)
.succeeds()
.no_stderr();
assert!(at.file_exists(&format!("{}/{}", dir2, file1)));
}
#[test]
fn test_install_failing_omitting_directory() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "source_file";
let dir1 = "source_dir";
let dir2 = "target_dir";
at.mkdir(dir1);
at.mkdir(dir2);
at.touch(file1);
ucmd.arg(dir1)
.arg(file1)
.arg(dir2)
.fails()
.code_is(1)
.stderr_contains("omitting directory");
}
#[test]
fn test_install_failing_no_such_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "source_file";
let file2 = "inexistent_file";
let dir1 = "target_dir";
at.mkdir(dir1);
at.touch(file1);
ucmd.arg(file1)
.arg(file2)
.arg(dir1)
.fails()
.code_is(1)
.stderr_contains("No such file or directory");
}
#[test]
fn test_install_copy_then_compare_file() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file1 = "test_install_copy_then_compare_file_a1";
let file2 = "test_install_copy_then_compare_file_a2";
at.touch(file1);
scene
.ucmd()
.arg("-C")
.arg(file1)
.arg(file2)
.succeeds()
.no_stderr();
let mut file2_meta = at.metadata(file2);
let before = FileTime::from_last_modification_time(&file2_meta);
scene
.ucmd()
.arg("-C")
.arg(file1)
.arg(file2)
.succeeds()
.no_stderr();
file2_meta = at.metadata(file2);
let after = FileTime::from_last_modification_time(&file2_meta);
assert!(before == after);
}
#[test]
#[cfg(target_os = "linux")]
fn test_install_copy_then_compare_file_with_extra_mode() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
// XXX: can't tests introspect on their own names?
let file1 = "test_install_copy_then_compare_file_with_extra_mode_a1";
let file2 = "test_install_copy_then_compare_file_with_extra_mode_a2";
at.touch(file1);
scene
.ucmd()
.arg("-C")
.arg(file1)
.arg(file2)
.succeeds()
.no_stderr();
let mut file2_meta = at.metadata(file2);
let before = FileTime::from_last_modification_time(&file2_meta);
sleep(std::time::Duration::from_millis(1000));
scene
.ucmd()
.arg("-C")
.arg(file1)
.arg(file2)
.arg("-m")
.arg("1644")
.succeeds()
.no_stderr();
file2_meta = at.metadata(file2);
let after_install_sticky = FileTime::from_last_modification_time(&file2_meta);
assert!(before != after_install_sticky);
sleep(std::time::Duration::from_millis(1000));
// dest file still 1644, so need_copy ought to return `true`
scene
.ucmd()
.arg("-C")
.arg(file1)
.arg(file2)
.succeeds()
.no_stderr();
file2_meta = at.metadata(file2);
let after_install_sticky_again = FileTime::from_last_modification_time(&file2_meta);
assert!(after_install_sticky != after_install_sticky_again);
}
const STRIP_TARGET_FILE: &str = "helloworld_installed";
const SYMBOL_DUMP_PROGRAM: &str = "objdump";
const STRIP_SOURCE_FILE_SYMBOL: &str = "main";
fn strip_source_file() -> &'static str {
if cfg!(target_os = "macos") {
"helloworld_macos"
} else {
"helloworld_linux"
}
}
#[test]
#[cfg(not(windows))]
fn test_install_and_strip() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene
.ucmd()
.arg("-s")
.arg(strip_source_file())
.arg(STRIP_TARGET_FILE)
.succeeds()
.no_stderr();
let output = Command::new(SYMBOL_DUMP_PROGRAM)
.arg("-t")
.arg(at.plus(STRIP_TARGET_FILE))
.output()
.unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(!stdout.contains(STRIP_SOURCE_FILE_SYMBOL));
}
#[test]
#[cfg(not(windows))]
fn test_install_and_strip_with_program() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene
.ucmd()
.arg("-s")
.arg("--strip-program")
.arg("/usr/bin/strip")
.arg(strip_source_file())
.arg(STRIP_TARGET_FILE)
.succeeds()
.no_stderr();
let output = Command::new(SYMBOL_DUMP_PROGRAM)
.arg("-t")
.arg(at.plus(STRIP_TARGET_FILE))
.output()
.unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(!stdout.contains(STRIP_SOURCE_FILE_SYMBOL));
}
#[test]
#[cfg(not(windows))]
fn test_install_and_strip_with_invalid_program() {
new_ucmd!()
.arg("-s")
.arg("--strip-program")
.arg("/bin/date")
.arg(strip_source_file())
.arg(STRIP_TARGET_FILE)
.fails()
.stderr_contains("strip program failed");
}
#[test]
#[cfg(not(windows))]
fn test_install_and_strip_with_non_existent_program() {
new_ucmd!()
.arg("-s")
.arg("--strip-program")
.arg("/usr/bin/non_existent_program")
.arg(strip_source_file())
.arg(STRIP_TARGET_FILE)
.fails()
.stderr_contains("No such file or directory");
}
#[test]
fn test_install_creating_leading_dirs() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let source = "create_leading_test_file";
let target = "dir1/dir2/dir3/test_file";
at.touch(source);
scene
.ucmd()
.arg("-D")
.arg(source)
.arg(at.plus(target))
.succeeds()
.no_stderr();
}
#[test]
#[cfg(not(windows))]
fn test_install_creating_leading_dir_fails_on_long_name() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let source = "create_leading_test_file";
let target = format!("{}/test_file", "d".repeat(libc::PATH_MAX as usize + 1));
at.touch(source);
scene
.ucmd()
.arg("-D")
.arg(source)
.arg(at.plus(target.as_str()))
.fails()
.stderr_contains("failed to create");
}
#[test]
fn test_install_dir() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "target_dir";
let file1 = "source_file1";
let file2 = "source_file2";
at.touch(file1);
at.touch(file2);
at.mkdir(dir);
ucmd.arg(file1)
.arg(file2)
.arg(&format!("--target-directory={}", dir))
.succeeds()
.no_stderr();
assert!(at.file_exists(file1));
assert!(at.file_exists(file2));
assert!(at.file_exists(&format!("{}/{}", dir, file1)));
assert!(at.file_exists(&format!("{}/{}", dir, file2)));
}

View file

@ -1,3 +1,5 @@
// spell-checker:ignore (words) autoformat
use crate::common::util::*;
#[test]
@ -141,14 +143,14 @@ fn new_line_separated() {
}
#[test]
fn multitab_character() {
fn tab_multi_character() {
new_ucmd!()
.arg("semicolon_fields_1.txt")
.arg("semicolon_fields_2.txt")
.arg("-t")
.arg("э")
.fails()
.stderr_is("join: error: multi-character tab э");
.stderr_is("join: multi-character tab э");
}
#[test]
@ -211,7 +213,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

@ -1 +1,127 @@
// ToDO: add tests
use crate::common::util::*;
use regex::Regex;
use std::os::unix::process::ExitStatusExt;
use std::process::{Child, Command};
// A child process the tests will try to kill.
struct Target {
child: Child,
killed: bool,
}
impl Target {
// Creates a target that will naturally die after some time if not killed
// fast enough.
// This timeout avoids hanging failing tests.
fn new() -> Target {
Target {
child: Command::new("sleep")
.arg("30")
.spawn()
.expect("cannot spawn target"),
killed: false,
}
}
// Waits for the target to complete and returns the signal it received if any.
fn wait_for_signal(&mut self) -> Option<i32> {
let sig = self.child.wait().expect("cannot wait on target").signal();
self.killed = true;
sig
}
fn pid(&self) -> u32 {
self.child.id()
}
}
impl Drop for Target {
// Terminates this target to avoid littering test boxes with zombi processes
// when a test fails after creating a target but before killing it.
fn drop(&mut self) {
if !self.killed {
self.child.kill().expect("cannot kill target");
}
}
}
#[test]
fn test_kill_list_all_signals() {
// Check for a few signals. Do not try to be comprehensive.
new_ucmd!()
.arg("-l")
.succeeds()
.stdout_contains("KILL")
.stdout_contains("TERM")
.stdout_contains("HUP");
}
#[test]
fn test_kill_list_all_signals_as_table() {
// Check for a few signals. Do not try to be comprehensive.
new_ucmd!()
.arg("-t")
.succeeds()
.stdout_contains("KILL")
.stdout_contains("TERM")
.stdout_contains("HUP");
}
#[test]
fn test_kill_list_one_signal_from_name() {
// Use SIGKILL because it is 9 on all unixes.
new_ucmd!()
.arg("-l")
.arg("KILL")
.succeeds()
.stdout_matches(&Regex::new("\\b9\\b").unwrap());
}
#[test]
fn test_kill_set_bad_signal_name() {
// spell-checker:disable-line
new_ucmd!()
.arg("-s")
.arg("IAMNOTASIGNAL") // spell-checker:disable-line
.fails()
.stderr_contains("unknown signal");
}
#[test]
fn test_kill_with_default_signal() {
let mut target = Target::new();
new_ucmd!().arg(format!("{}", target.pid())).succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGTERM));
}
#[test]
fn test_kill_with_signal_number_old_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-9")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(9));
}
#[test]
fn test_kill_with_signal_number_new_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-s")
.arg("9")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(9));
}
#[test]
fn test_kill_with_signal_name_new_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-s")
.arg("KILL")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}

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,29 @@ 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));
}
#[test]
fn test_link_one_argument() {
let (_, mut ucmd) = at_and_ucmd!();
let file = "test_link_argument";
ucmd.args(&[file]).fails().stderr_contains(
"error: The argument '<FILES>...' requires at least 2 values, but only 1 was provide",
);
}
#[test]
fn test_link_three_arguments() {
let (_, mut ucmd) = at_and_ucmd!();
let arguments = vec![
"test_link_argument1",
"test_link_argument2",
"test_link_argument3",
];
ucmd.args(&arguments[..]).fails().stderr_contains(
format!("error: The value '{}' was provided to '<FILES>...', but it wasn't expecting any more values", arguments[2]),
);
}

View file

@ -65,10 +65,10 @@ fn test_symlink_circular() {
}
#[test]
fn test_symlink_dont_overwrite() {
fn test_symlink_do_not_overwrite() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_symlink_dont_overwrite";
let link = "test_symlink_dont_overwrite_link";
let file = "test_symlink_do_not_overwrite";
let link = "test_symlink_do_not_overwrite_link";
at.touch(file);
at.touch(link);
@ -120,7 +120,7 @@ fn test_symlink_interactive() {
scene
.ucmd()
.args(&["-i", "-s", file, link])
.pipe_in("Yesh")
.pipe_in("Yesh") // spell-checker:disable-line
.succeeds()
.no_stderr();
@ -299,13 +299,11 @@ fn test_symlink_overwrite_dir_fail() {
at.touch(path_a);
at.mkdir(path_b);
assert!(
ucmd.args(&["-s", "-T", path_a, path_b])
.fails()
.stderr
.len()
> 0
);
assert!(!ucmd
.args(&["-s", "-T", path_a, path_b])
.fails()
.stderr_str()
.is_empty());
}
#[test]
@ -358,7 +356,11 @@ fn test_symlink_target_only() {
at.mkdir(dir);
assert!(ucmd.args(&["-s", "-t", dir]).fails().stderr.len() > 0);
assert!(!ucmd
.args(&["-s", "-t", dir])
.fails()
.stderr_str()
.is_empty());
}
#[test]
@ -407,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
));
}
@ -426,20 +428,6 @@ fn test_symlink_relative() {
assert_eq!(at.resolve_link(link), file_a);
}
#[test]
fn test_hardlink_relative() {
let (at, mut ucmd) = at_and_ucmd!();
let file_a = "test_hardlink_relative_a";
let link = "test_hardlink_relative_link";
at.touch(file_a);
// relative hardlink
ucmd.args(&["-r", "-v", file_a, link])
.succeeds()
.stdout_only(format!("'{}' -> '{}'\n", link, file_a));
}
#[test]
fn test_symlink_relative_path() {
let (at, mut ucmd) = at_and_ucmd!();
@ -520,10 +508,7 @@ fn test_symlink_no_deref_dir() {
scene.ucmd().args(&["-sn", dir1, link]).fails();
// Try with the no-deref
let result = scene.ucmd().args(&["-sfn", dir1, link]).run();
println!("stdout {}", result.stdout);
println!("stderr {}", result.stderr);
assert!(result.success);
scene.ucmd().args(&["-sfn", dir1, link]).succeeds();
assert!(at.dir_exists(dir1));
assert!(at.dir_exists(dir2));
assert!(at.is_symlink(link));
@ -566,12 +551,40 @@ fn test_symlink_no_deref_file() {
scene.ucmd().args(&["-sn", file1, link]).fails();
// Try with the no-deref
let result = scene.ucmd().args(&["-sfn", file1, link]).run();
println!("stdout {}", result.stdout);
println!("stderr {}", result.stderr);
assert!(result.success);
scene.ucmd().args(&["-sfn", file1, link]).succeeds();
assert!(at.file_exists(file1));
assert!(at.file_exists(file2));
assert!(at.is_symlink(link));
assert_eq!(at.resolve_link(link), file1);
}
#[test]
fn test_relative_requires_symbolic() {
new_ucmd!().args(&["-r", "foo", "bar"]).fails();
}
#[test]
fn test_relative_dst_already_symlink() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("file1");
at.symlink_file("file1", "file2");
ucmd.arg("-srf").arg("file1").arg("file2").succeeds();
at.is_symlink("file2");
}
#[test]
fn test_relative_src_already_symlink() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("file1");
at.symlink_file("file1", "file2");
ucmd.arg("-sr").arg("file2").arg("file3").succeeds();
assert!(at.resolve_link("file3").ends_with("file1"));
}
#[test]
fn test_relative_recursive() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("dir");
ucmd.args(&["-sr", "dir", "dir/recursive"]).succeeds();
assert_eq!(at.resolve_link("dir/recursive"), ".");
}

View file

@ -3,23 +3,19 @@ use std::env;
#[test]
fn test_normal() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let result = new_ucmd!().run();
println!("env::var(CI).is_ok() = {}", env::var("CI").is_ok());
for (key, value) in env::vars() {
println!("{}: {}", key, value);
}
if (is_ci() || is_wsl()) && result.stderr.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
return;
}
assert!(result.success);
assert!(!result.stdout.trim().is_empty());
result.success();
assert!(!result.stdout_str().trim().is_empty());
}

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,12 @@
use crate::common::util::*;
static TEST_DIR1: &'static str = "mkdir_test1";
static TEST_DIR2: &'static str = "mkdir_test2";
static TEST_DIR3: &'static str = "mkdir_test3";
static TEST_DIR4: &'static str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &'static str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &'static str = "mkdir_test6";
static TEST_FILE7: &'static str = "mkdir_test7";
static TEST_DIR1: &str = "mkdir_test1";
static TEST_DIR2: &str = "mkdir_test2";
static TEST_DIR3: &str = "mkdir_test3";
static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &str = "mkdir_test6";
static TEST_FILE7: &str = "mkdir_test7";
#[test]
fn test_mkdir_mkdir() {

View file

@ -1 +1,45 @@
// ToDO: add tests
use crate::common::util::*;
#[test]
fn test_create_fifo_missing_operand() {
new_ucmd!().fails().stderr_is("mkfifo: missing operand");
}
#[test]
fn test_create_one_fifo() {
new_ucmd!().arg("abc").succeeds();
}
#[test]
fn test_create_one_fifo_with_invalid_mode() {
new_ucmd!()
.arg("abcd")
.arg("-m")
.arg("invalid")
.fails()
.stderr_contains("invalid mode");
}
#[test]
fn test_create_multiple_fifos() {
new_ucmd!()
.arg("abcde")
.arg("def")
.arg("sed")
.arg("dum")
.succeeds();
}
#[test]
fn test_create_one_fifo_with_mode() {
new_ucmd!().arg("abcde").arg("-m600").succeeds();
}
#[test]
fn test_create_one_fifo_already_exists() {
new_ucmd!()
.arg("abcdef")
.arg("abcdef")
.fails()
.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

@ -1,21 +1,26 @@
// spell-checker:ignore (words) gpghome
use crate::common::util::*;
use std::path::PathBuf;
use tempfile::tempdir;
static TEST_TEMPLATE1: &'static str = "tempXXXXXX";
static TEST_TEMPLATE2: &'static str = "temp";
static TEST_TEMPLATE3: &'static str = "tempX";
static TEST_TEMPLATE4: &'static str = "tempXX";
static TEST_TEMPLATE5: &'static str = "tempXXX";
static TEST_TEMPLATE6: &'static str = "tempXXXlate";
static TEST_TEMPLATE7: &'static str = "XXXtemplate";
static TEST_TEMPLATE1: &str = "tempXXXXXX";
static TEST_TEMPLATE2: &str = "temp";
static TEST_TEMPLATE3: &str = "tempX";
static TEST_TEMPLATE4: &str = "tempXX";
static TEST_TEMPLATE5: &str = "tempXXX";
static TEST_TEMPLATE6: &str = "tempXXXlate"; // spell-checker:disable-line
static TEST_TEMPLATE7: &str = "XXXtemplate"; // spell-checker:disable-line
#[cfg(unix)]
static TEST_TEMPLATE8: &'static str = "tempXXXl/ate";
static TEST_TEMPLATE8: &str = "tempXXXl/ate";
#[cfg(windows)]
static TEST_TEMPLATE8: &'static str = "tempXXXl\\ate";
static TEST_TEMPLATE8: &str = "tempXXXl\\ate";
const TMPDIR: &'static str = "TMPDIR";
#[cfg(not(windows))]
const TMPDIR: &str = "TMPDIR";
#[cfg(windows)]
const TMPDIR: &str = "TMP";
#[test]
fn test_mktemp_mktemp() {
@ -113,17 +118,15 @@ fn test_mktemp_mktemp_t() {
.arg("-t")
.arg(TEST_TEMPLATE7)
.succeeds();
let result = scene
scene
.ucmd()
.env(TMPDIR, &pathname)
.arg("-t")
.arg(TEST_TEMPLATE8)
.fails();
println!("stdout {}", result.stdout);
println!("stderr {}", result.stderr);
assert!(result
.stderr
.contains("error: suffix cannot contain any path separators"));
.fails()
.no_stdout()
.stderr_contains("invalid suffix")
.stderr_contains("contains directory separator");
}
#[test]
@ -387,14 +390,12 @@ fn test_mktemp_tmpdir_one_arg() {
let scene = TestScenario::new(util_name!());
let result = scene
.ucmd()
.ucmd_keepenv()
.arg("--tmpdir")
.arg("apt-key-gpghome.XXXXXXXXXX")
.succeeds();
println!("stdout {}", result.stdout);
println!("stderr {}", result.stderr);
assert!(result.stdout.contains("apt-key-gpghome."));
assert!(PathBuf::from(result.stdout.trim()).is_file());
result.no_stderr().stdout_contains("apt-key-gpghome.");
assert!(PathBuf::from(result.stdout_str().trim()).is_file());
}
#[test]
@ -402,13 +403,11 @@ fn test_mktemp_directory_tmpdir() {
let scene = TestScenario::new(util_name!());
let result = scene
.ucmd()
.ucmd_keepenv()
.arg("--directory")
.arg("--tmpdir")
.arg("apt-key-gpghome.XXXXXXXXXX")
.succeeds();
println!("stdout {}", result.stdout);
println!("stderr {}", result.stderr);
assert!(result.stdout.contains("apt-key-gpghome."));
assert!(PathBuf::from(result.stdout.trim()).is_dir());
result.no_stderr().stdout_contains("apt-key-gpghome.");
assert!(PathBuf::from(result.stdout_str().trim()).is_dir());
}

View file

@ -2,7 +2,24 @@ use crate::common::util::*;
#[test]
fn test_more_no_arg() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(!result.success);
// Reading from stdin is now supported, so this must succeed
if atty::is(atty::Stream::Stdout) {
new_ucmd!().succeeds();
} else {
}
}
#[test]
fn test_more_dir_arg() {
// Run the test only if there's a valid terminal, else do nothing
// Maybe we could capture the error, i.e. "Device not found" in that case
// but I am leaving this for later
if atty::is(atty::Stream::Stdout) {
let result = new_ucmd!().arg(".").run();
result.failure();
const EXPECTED_ERROR_MESSAGE: &str =
"more: '.' is a directory.\nTry 'more --help' for more information.";
assert_eq!(result.stderr_str().trim(), EXPECTED_ERROR_MESSAGE);
} else {
}
}

View file

@ -82,7 +82,7 @@ fn test_mv_strip_slashes() {
let dir = "test_mv_strip_slashes_dir";
let file = "test_mv_strip_slashes_file";
let mut source = file.to_owned();
source.push_str("/");
source.push('/');
at.mkdir(dir);
at.touch(file);
@ -171,7 +171,7 @@ fn test_mv_interactive() {
.arg("-i")
.arg(file_a)
.arg(file_b)
.pipe_in("Yesh")
.pipe_in("Yesh") // spell-checker:disable-line
.succeeds()
.no_stderr();
@ -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,20 +613,13 @@ 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)"
// GNU: "mv: cannot move a to b: Directory not empty"
// 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
assert!(
ucmd.arg("-vT")
.arg(dir_a)
.arg(dir_b)
.fails()
.no_stdout()
.stderr
.len()
> 0
);
let result = ucmd.arg("-vT").arg(dir_a).arg(dir_b).fails();
result.no_stdout();
assert!(!result.stderr_str().is_empty());
assert!(at.dir_exists(dir_a));
assert!(at.dir_exists(dir_b));
@ -504,7 +638,7 @@ fn test_mv_backup_dir() {
.arg(dir_b)
.succeeds()
.stdout_only(format!(
"{} -> {} (backup: {}~)\n",
"'{}' -> '{}' (backup: '{}~')\n",
dir_a, dir_b, dir_b
));
@ -526,19 +660,19 @@ fn test_mv_errors() {
// $ mv -T -t a b
// mv: cannot combine --target-directory (-t) and --no-target-directory (-T)
let result = scene
scene
.ucmd()
.arg("-T")
.arg("-t")
.arg(dir)
.arg(file_a)
.arg(file_b)
.fails();
assert!(result.stderr.contains("cannot be used with"));
.fails()
.stderr_contains("cannot be used with");
// $ at.touch file && at.mkdir dir
// $ mv -T file dir
// err == mv: cannot overwrite directory dir with non-directory
// err == mv: cannot overwrite directory 'dir' with non-directory
scene
.ucmd()
.arg("-T")
@ -546,14 +680,20 @@ 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
));
// $ at.mkdir dir && at.touch file
// $ mv dir file
// err == mv: cannot overwrite non-directory file with directory dir
assert!(scene.ucmd().arg(dir).arg(file_a).fails().stderr.len() > 0);
// err == mv: cannot overwrite non-directory 'file' with directory 'dir'
assert!(!scene
.ucmd()
.arg(dir)
.arg(file_a)
.fails()
.stderr_str()
.is_empty());
}
#[test]
@ -573,7 +713,7 @@ fn test_mv_verbose() {
.arg(file_a)
.arg(file_b)
.succeeds()
.stdout_only(format!("{} -> {}\n", file_a, file_b));
.stdout_only(format!("'{}' -> '{}'\n", file_a, file_b));
at.touch(file_a);
scene
@ -583,11 +723,29 @@ fn test_mv_verbose() {
.arg(file_b)
.succeeds()
.stdout_only(format!(
"{} -> {} (backup: {}~)\n",
"'{}' -> '{}' (backup: '{}~')\n",
file_a, file_b, file_b
));
}
#[test]
#[cfg(target_os = "linux")] // mkdir does not support -m on windows. Freebsd doesn't return a permission error either.
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
.ccmd("mv")
.arg(folder2)
.arg(folder_to_move)
.fails()
.stderr_contains("Permission denied");
}
// Todo:
// $ at.touch a b
@ -598,5 +756,5 @@ fn test_mv_verbose() {
// -r--r--r-- 1 user user 0 okt 25 11:21 b
// $
// $ mv -v a b
// mv: try to overwrite b, overriding mode 0444 (r--r--r--)? y
// a -> b
// mv: try to overwrite 'b', overriding mode 0444 (r--r--r--)? y
// 'a' -> 'b'

View file

@ -1 +1,58 @@
// ToDO: add tests
use crate::common::util::*;
#[test]
fn test_get_current_niceness() {
// NOTE: this assumes the test suite is being run with a default niceness
// of 0, which may not necessarily be true
new_ucmd!().run().stdout_is("0\n");
}
#[test]
fn test_negative_adjustment() {
// This assumes the test suite is run as a normal (non-root) user, and as
// such attempting to set a negative niceness value will be rejected by
// the OS. If it gets denied, then we know a negative value was parsed
// correctly.
let res = new_ucmd!().args(&["-n", "-1", "true"]).run();
assert!(res
.stderr_str()
.starts_with("nice: warning: setpriority: Permission denied")); // spell-checker:disable-line
}
#[test]
fn test_adjustment_with_no_command_should_error() {
new_ucmd!()
.args(&["-n", "19"])
.run()
.stderr_is("nice: A command must be given with an adjustment.\nTry \"nice --help\" for more information.\n");
}
#[test]
fn test_command_with_no_adjustment() {
new_ucmd!().args(&["echo", "a"]).run().stdout_is("a\n");
}
#[test]
fn test_command_with_no_args() {
new_ucmd!()
.args(&["-n", "19", "echo"])
.run()
.stdout_is("\n");
}
#[test]
fn test_command_with_args() {
new_ucmd!()
.args(&["-n", "19", "echo", "a", "b", "c"])
.run()
.stdout_is("a b c\n");
}
#[test]
fn test_command_where_command_takes_n_flag() {
new_ucmd!()
.args(&["-n", "19", "echo", "-n", "a"])
.run()
.stdout_is("a");
}

View file

@ -1,7 +1,7 @@
use crate::common::util::*;
#[test]
fn test_stdin_nonewline() {
fn test_stdin_no_newline() {
new_ucmd!()
.pipe_in("No Newline")
.run()
@ -23,8 +23,8 @@ fn test_padding_without_overflow() {
.run()
.stdout_is(
"000001xL1\n001001xL2\n002001xL3\n003001xL4\n004001xL5\n005001xL6\n006001xL7\n0070\
01xL8\n008001xL9\n009001xL10\n010001xL11\n011001xL12\n012001xL13\n013001xL14\n014\
001xL15\n",
01xL8\n008001xL9\n009001xL10\n010001xL11\n011001xL12\n012001xL13\n013001xL14\n014\
001xL15\n",
);
}
@ -35,25 +35,26 @@ fn test_padding_with_overflow() {
.run()
.stdout_is(
"0001xL1\n1001xL2\n2001xL3\n3001xL4\n4001xL5\n5001xL6\n6001xL7\n7001xL8\n8001xL9\n\
9001xL10\n10001xL11\n11001xL12\n12001xL13\n13001xL14\n14001xL15\n",
9001xL10\n10001xL11\n11001xL12\n12001xL13\n13001xL14\n14001xL15\n",
);
}
#[test]
fn test_sections_and_styles() {
// spell-checker:disable
for &(fixture, output) in &[
(
"section.txt",
"\nHEADER1\nHEADER2\n\n1 |BODY1\n2 \
|BODY2\n\nFOOTER1\nFOOTER2\n\nNEXTHEADER1\nNEXTHEADER2\n\n1 \
|NEXTBODY1\n2 |NEXTBODY2\n\nNEXTFOOTER1\nNEXTFOOTER2\n",
|BODY2\n\nFOOTER1\nFOOTER2\n\nNEXTHEADER1\nNEXTHEADER2\n\n1 \
|NEXTBODY1\n2 |NEXTBODY2\n\nNEXTFOOTER1\nNEXTFOOTER2\n",
),
(
"joinblanklines.txt",
"1 |Nonempty\n2 |Nonempty\n3 |Followed by 10x empty\n\n\n\n\n4 \
|\n\n\n\n\n5 |\n6 |Followed by 5x empty\n\n\n\n\n7 |\n8 \
|Followed by 4x empty\n\n\n\n\n9 |Nonempty\n10 |Nonempty\n11 \
|Nonempty.\n",
|\n\n\n\n\n5 |\n6 |Followed by 5x empty\n\n\n\n\n7 |\n8 \
|Followed by 4x empty\n\n\n\n\n9 |Nonempty\n10 |Nonempty\n11 \
|Nonempty.\n",
),
] {
new_ucmd!()
@ -63,4 +64,5 @@ fn test_sections_and_styles() {
.run()
.stdout_is(output);
}
// spell-checker:enable
}

View file

@ -1 +1,19 @@
// ToDO: add tests
use crate::common::util::*;
use std::thread::sleep;
// General observation: nohup.out will not be created in tests run by cargo test
// because stdin/stdout is not attached to a TTY.
// All that can be tested is the side-effects.
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_vendor = "apple"))]
fn test_nohup_multiple_args_and_flags() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["touch", "-t", "1006161200", "file1", "file2"])
.succeeds();
sleep(std::time::Duration::from_millis(10));
assert!(at.file_exists("file1"));
assert!(at.file_exists("file2"));
}

View file

@ -2,54 +2,46 @@ use crate::common::util::*;
#[test]
fn test_nproc() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(result.success);
let nproc: u8 = result.stdout.trim().parse().unwrap();
let nproc: u8 = new_ucmd!().succeeds().stdout_str().trim().parse().unwrap();
assert!(nproc > 0);
}
#[test]
fn test_nproc_all_omp() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--all").run();
assert!(result.success);
let nproc: u8 = result.stdout.trim().parse().unwrap();
let result = new_ucmd!().arg("--all").succeeds();
let nproc: u8 = result.stdout_str().trim().parse().unwrap();
assert!(nproc > 0);
let result = TestScenario::new(util_name!())
.ucmd_keepenv()
.env("OMP_NUM_THREADS", "1")
.run();
assert!(result.success);
let nproc_omp: u8 = result.stdout.trim().parse().unwrap();
.succeeds();
let nproc_omp: u8 = result.stdout_str().trim().parse().unwrap();
assert!(nproc - 1 == nproc_omp);
let result = TestScenario::new(util_name!())
.ucmd_keepenv()
.env("OMP_NUM_THREADS", "1") // Has no effect
.arg("--all")
.run();
assert!(result.success);
let nproc_omp: u8 = result.stdout.trim().parse().unwrap();
.succeeds();
let nproc_omp: u8 = result.stdout_str().trim().parse().unwrap();
assert!(nproc == nproc_omp);
}
#[test]
fn test_nproc_ignore() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(result.success);
let nproc: u8 = result.stdout.trim().parse().unwrap();
let result = new_ucmd!().succeeds();
let nproc: u8 = result.stdout_str().trim().parse().unwrap();
if nproc > 1 {
// Ignore all CPU but one
let result = TestScenario::new(util_name!())
.ucmd_keepenv()
.arg("--ignore")
.arg((nproc - 1).to_string())
.run();
assert!(result.success);
let nproc: u8 = result.stdout.trim().parse().unwrap();
.succeeds();
let nproc: u8 = result.stdout_str().trim().parse().unwrap();
assert!(nproc == 1);
}
}

View file

@ -1,3 +1,5 @@
// spell-checker:ignore (paths) gnutest
use crate::common::util::*;
#[test]
@ -33,7 +35,7 @@ fn test_from_iec_i_requires_suffix() {
new_ucmd!()
.args(&["--from=iec-i", "1024"])
.fails()
.stderr_is("numfmt: missing 'i' suffix in input: 1024 (e.g Ki/Mi/Gi)");
.stderr_is("numfmt: missing 'i' suffix in input: '1024' (e.g Ki/Mi/Gi)");
}
#[test]
@ -121,7 +123,7 @@ fn test_header_error_if_non_numeric() {
new_ucmd!()
.args(&["--header=two"])
.run()
.stderr_is("numfmt: invalid header value two");
.stderr_is("numfmt: invalid header value 'two'");
}
#[test]
@ -129,7 +131,7 @@ fn test_header_error_if_0() {
new_ucmd!()
.args(&["--header=0"])
.run()
.stderr_is("numfmt: invalid header value 0");
.stderr_is("numfmt: invalid header value '0'");
}
#[test]
@ -137,7 +139,7 @@ fn test_header_error_if_negative() {
new_ucmd!()
.args(&["--header=-3"])
.run()
.stderr_is("numfmt: invalid header value -3");
.stderr_is("numfmt: invalid header value '-3'");
}
#[test]
@ -185,7 +187,7 @@ fn test_should_report_invalid_empty_number_on_empty_stdin() {
.args(&["--from=auto"])
.pipe_in("\n")
.run()
.stderr_is("numfmt: invalid number: \n");
.stderr_is("numfmt: invalid number: ''\n");
}
#[test]
@ -194,7 +196,7 @@ fn test_should_report_invalid_empty_number_on_blank_stdin() {
.args(&["--from=auto"])
.pipe_in(" \t \n")
.run()
.stderr_is("numfmt: invalid number: \n");
.stderr_is("numfmt: invalid number: ''\n");
}
#[test]
@ -203,14 +205,14 @@ fn test_should_report_invalid_suffix_on_stdin() {
.args(&["--from=auto"])
.pipe_in("1k")
.run()
.stderr_is("numfmt: invalid suffix in input: 1k\n");
.stderr_is("numfmt: invalid suffix in input: '1k'\n");
// GNU numfmt reports this one as “invalid number”
new_ucmd!()
.args(&["--from=auto"])
.pipe_in("NaN")
.run()
.stderr_is("numfmt: invalid suffix in input: NaN\n");
.stderr_is("numfmt: invalid suffix in input: 'NaN'\n");
}
#[test]
@ -220,7 +222,7 @@ fn test_should_report_invalid_number_with_interior_junk() {
.args(&["--from=auto"])
.pipe_in("1x0K")
.run()
.stderr_is("numfmt: invalid number: 1x0K\n");
.stderr_is("numfmt: invalid number: '1x0K'\n");
}
#[test]
@ -231,7 +233,7 @@ fn test_should_skip_leading_space_from_stdin() {
.run()
.stdout_is("2048\n");
// multiline
// multi-line
new_ucmd!()
.args(&["--from=auto"])
.pipe_in("\t1Ki\n 2K")
@ -281,6 +283,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"])
@ -383,3 +386,122 @@ fn test_field_df_example() {
.succeeds()
.stdout_is_fixture("df_expected.txt");
}
#[test]
fn test_delimiter_must_not_be_empty() {
new_ucmd!().args(&["-d"]).fails();
}
#[test]
fn test_delimiter_must_not_be_more_than_one_character() {
new_ucmd!()
.args(&["--delimiter", "sad"])
.fails()
.stderr_is("numfmt: the delimiter must be a single character");
}
#[test]
fn test_delimiter_only() {
new_ucmd!()
.args(&["-d", ","])
.pipe_in("1234,56")
.succeeds()
.stdout_only("1234,56\n");
}
#[test]
fn test_line_is_field_with_no_delimiter() {
new_ucmd!()
.args(&["-d,", "--to=iec"])
.pipe_in("123456")
.succeeds()
.stdout_only("121K\n");
}
#[test]
fn test_delimiter_to_si() {
new_ucmd!()
.args(&["-d=,", "--to=si"])
.pipe_in("1234,56")
.succeeds()
.stdout_only("1.3K,56\n");
}
#[test]
fn test_delimiter_skips_leading_whitespace() {
new_ucmd!()
.args(&["-d=,", "--to=si"])
.pipe_in(" \t 1234,56")
.succeeds()
.stdout_only("1.3K,56\n");
}
#[test]
fn test_delimiter_preserves_leading_whitespace_in_unselected_fields() {
new_ucmd!()
.args(&["-d=|", "--to=si"])
.pipe_in(" 1000| 2000")
.succeeds()
.stdout_only("1.0K| 2000\n");
}
#[test]
fn test_delimiter_from_si() {
new_ucmd!()
.args(&["-d=,", "--from=si"])
.pipe_in("1.2K,56")
.succeeds()
.stdout_only("1200,56\n");
}
#[test]
fn test_delimiter_overrides_whitespace_separator() {
// GNU numfmt reports this as “invalid suffix”
new_ucmd!()
.args(&["-d,"])
.pipe_in("1 234,56")
.fails()
.stderr_is("numfmt: invalid number: '1 234'\n");
}
#[test]
fn test_delimiter_with_padding() {
new_ucmd!()
.args(&["-d=|", "--to=si", "--padding=5"])
.pipe_in("1000|2000")
.succeeds()
.stdout_only(" 1.0K|2000\n");
}
#[test]
fn test_delimiter_with_padding_and_fields() {
new_ucmd!()
.args(&["-d=|", "--to=si", "--padding=5", "--field=-"])
.pipe_in("1000|2000")
.succeeds()
.stdout_only(" 1.0K| 2.0K\n");
}
#[test]
fn test_round() {
for (method, exp) in &[
("from-zero", ["9.1K", "-9.1K", "9.1K", "-9.1K"]),
("towards-zero", ["9.0K", "-9.0K", "9.0K", "-9.0K"]),
("up", ["9.1K", "-9.0K", "9.1K", "-9.0K"]),
("down", ["9.0K", "-9.1K", "9.0K", "-9.1K"]),
("nearest", ["9.0K", "-9.0K", "9.1K", "-9.1K"]),
] {
new_ucmd!()
.args(&[
"--to=si",
&format!("--round={}", method),
"--",
"9001",
"-9001",
"9099",
"-9099",
])
.succeeds()
.stdout_only(exp.join("\n") + "\n");
}
}

View file

@ -1,3 +1,8 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
extern crate unindent;
use self::unindent::*;
@ -8,19 +13,20 @@ use std::fs::File;
use std::io::Write;
use std::path::Path;
// octal dump of 'abcdefghijklmnopqrstuvwxyz\n'
static ALPHA_OUT: &'static str = "
// octal dump of 'abcdefghijklmnopqrstuvwxyz\n' // spell-checker:disable-line
static ALPHA_OUT: &str = "
0000000 061141 062143 063145 064147 065151 066153 067155 070157
0000020 071161 072163 073165 074167 075171 000012
0000033
";
// XXX We could do a better job of ensuring that we have a fresh temp dir to ourselves,
// not a general one full of other proc's leftovers.
// not a general one full of other process leftovers.
// Test that od can read one file and dump with default format
#[test]
fn test_file() {
// TODO: Can this be replaced by AtPath?
use std::env;
let temp = env::temp_dir();
let tmpdir = Path::new(&temp);
@ -28,20 +34,18 @@ fn test_file() {
{
let mut f = File::create(&file).unwrap();
// spell-checker:disable-next-line
if f.write_all(b"abcdefghijklmnopqrstuvwxyz\n").is_err() {
panic!("Test setup failed - could not write file");
}
}
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg(file.as_os_str())
.run();
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, unindent(ALPHA_OUT));
.succeeds()
.no_stderr()
.stdout_is(unindent(ALPHA_OUT));
let _ = remove_file(file);
}
@ -57,6 +61,7 @@ fn test_2files() {
println!("number: {} letter:{}", n, a);
}
// spell-checker:disable-next-line
for &(path, data) in &[(&file1, "abcdefghijklmnop"), (&file2, "qrstuvwxyz\n")] {
let mut f = File::create(&path).unwrap();
if f.write_all(data.as_bytes()).is_err() {
@ -64,16 +69,14 @@ fn test_2files() {
}
}
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg(file1.as_os_str())
.arg(file2.as_os_str())
.run();
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, unindent(ALPHA_OUT));
.succeeds()
.no_stderr()
.stdout_is(unindent(ALPHA_OUT));
// TODO: Handle errors?
let _ = remove_file(file1);
let _ = remove_file(file2);
}
@ -83,24 +86,21 @@ fn test_2files() {
fn test_no_file() {
let temp = env::temp_dir();
let tmpdir = Path::new(&temp);
let file = tmpdir.join("}surely'none'would'thus'a'file'name");
let file = tmpdir.join("}surely'none'would'thus'a'file'name"); // spell-checker:disable-line
let result = new_ucmd!().arg(file.as_os_str()).run();
assert!(!result.success);
new_ucmd!().arg(file.as_os_str()).fails();
}
// Test that od reads from stdin instead of a file
#[test]
fn test_from_stdin() {
let input = "abcdefghijklmnopqrstuvwxyz\n";
let result = new_ucmd!()
let input = "abcdefghijklmnopqrstuvwxyz\n"; // spell-checker:disable-line
new_ucmd!()
.arg("--endian=little")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, unindent(ALPHA_OUT));
.run_piped_stdin(input.as_bytes())
.success()
.no_stderr()
.stdout_is(unindent(ALPHA_OUT));
}
// Test that od reads from stdin and also from files
@ -111,6 +111,7 @@ fn test_from_mixed() {
let file1 = tmpdir.join("test-1");
let file3 = tmpdir.join("test-3");
// spell-checker:disable-next-line
let (data1, data2, data3) = ("abcdefg", "hijklmnop", "qrstuvwxyz\n");
for &(path, data) in &[(&file1, data1), (&file3, data3)] {
let mut f = File::create(&path).unwrap();
@ -119,44 +120,40 @@ fn test_from_mixed() {
}
}
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg(file1.as_os_str())
.arg("-")
.arg(file3.as_os_str())
.run_piped_stdin(data2.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, unindent(ALPHA_OUT));
.run_piped_stdin(data2.as_bytes())
.success()
.no_stderr()
.stdout_is(unindent(ALPHA_OUT));
}
#[test]
fn test_multiple_formats() {
let input = "abcdefghijklmnopqrstuvwxyz\n";
let result = new_ucmd!()
let input = "abcdefghijklmnopqrstuvwxyz\n"; // spell-checker:disable-line
new_ucmd!()
.arg("-c")
.arg("-b")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin(input.as_bytes())
.success()
.no_stderr()
.stdout_is(unindent(
"
0000000 a b c d e f g h i j k l m n o p
141 142 143 144 145 146 147 150 151 152 153 154 155 156 157 160
0000020 q r s t u v w x y z \\n
161 162 163 164 165 166 167 170 171 172 012
0000033
"
)
);
",
));
}
#[test]
fn test_dec() {
// spell-checker:ignore (words) 0xffu8 xffu
let input = [
0u8, 0u8, 1u8, 0u8, 2u8, 0u8, 3u8, 0u8, 0xffu8, 0x7fu8, 0x00u8, 0x80u8, 0x01u8, 0x80u8,
];
@ -166,33 +163,33 @@ fn test_dec() {
0000016
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg("-s")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
fn test_hex16() {
let input: [u8; 9] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff];
// spell-checker:disable
let expected_output = unindent(
"
0000000 2301 6745 ab89 efcd 00ff
0000011
",
);
let result = new_ucmd!()
// spell-checker:enable
new_ucmd!()
.arg("--endian=little")
.arg("-x")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
@ -204,14 +201,13 @@ fn test_hex32() {
0000011
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg("-X")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
@ -232,15 +228,14 @@ fn test_f16() {
0000016
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg("-tf2")
.arg("-w8")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
@ -261,14 +256,13 @@ fn test_f32() {
0000034
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg("-f")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
@ -291,36 +285,31 @@ fn test_f64() {
0000050
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg("-F")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
fn test_multibyte() {
let result = new_ucmd!()
new_ucmd!()
.arg("-c")
.arg("-w12")
.run_piped_stdin("Universität Tübingen \u{1B000}".as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin("Universität Tübingen \u{1B000}".as_bytes()) // spell-checker:disable-line
.success()
.no_stderr()
.stdout_is(unindent(
"
0000000 U n i v e r s i t ä ** t
0000014 T ü ** b i n g e n \u{1B000}
0000030 ** ** **
0000033
"
)
);
",
));
}
#[test]
@ -334,11 +323,13 @@ fn test_width() {
",
);
let result = new_ucmd!().arg("-w4").arg("-v").run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
new_ucmd!()
.arg("-w4")
.arg("-v")
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
@ -352,14 +343,13 @@ fn test_invalid_width() {
",
);
let result = new_ucmd!().arg("-w5").arg("-v").run_piped_stdin(&input[..]);
assert_eq!(
result.stderr,
"od: warning: invalid width 5; using 2 instead\n"
);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
new_ucmd!()
.arg("-w5")
.arg("-v")
.run_piped_stdin(&input[..])
.success()
.stderr_is_bytes("od: warning: invalid width 5; using 2 instead\n".as_bytes())
.stdout_is(expected_output);
}
#[test]
@ -373,14 +363,13 @@ fn test_zero_width() {
",
);
let result = new_ucmd!().arg("-w0").arg("-v").run_piped_stdin(&input[..]);
assert_eq!(
result.stderr,
"od: warning: invalid width 0; using 2 instead\n"
);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
new_ucmd!()
.arg("-w0")
.arg("-v")
.run_piped_stdin(&input[..])
.success()
.stderr_is_bytes("od: warning: invalid width 0; using 2 instead\n".as_bytes())
.stdout_is(expected_output);
}
#[test]
@ -392,11 +381,12 @@ fn test_width_without_value() {
0000050
");
let result = new_ucmd!().arg("-w").run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
new_ucmd!()
.arg("-w")
.run_piped_stdin(&input[..])
.success()
.no_stderr()
.stdout_is(expected_output);
}
#[test]
@ -421,15 +411,14 @@ fn test_suppress_duplicates() {
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("-w4")
.arg("-O")
.arg("-x")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
@ -446,17 +435,16 @@ fn test_big_endian() {
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=big")
.arg("-F")
.arg("-f")
.arg("-X")
.arg("-x")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
@ -474,16 +462,15 @@ fn test_alignment_Xxa() {
);
// in this case the width of the -a (8-bit) determines the alignment for the other fields
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg("-X")
.arg("-x")
.arg("-a")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
@ -500,19 +487,18 @@ fn test_alignment_Fx() {
);
// in this case the width of the -F (64-bit) determines the alignment for the other field
let result = new_ucmd!()
new_ucmd!()
.arg("--endian=little")
.arg("-F")
.arg("-x")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
fn test_maxuint() {
fn test_max_uint() {
let input = [0xFFu8; 8];
let expected_output = unindent(
"
@ -528,16 +514,15 @@ fn test_maxuint() {
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("--format=o8")
.arg("-Oobtu8")
.arg("-Oobtu8") // spell-checker:disable-line
.arg("-Dd")
.arg("--format=u1")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
@ -553,15 +538,14 @@ fn test_hex_offset() {
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("-Ax")
.arg("-X")
.arg("-X")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
@ -577,83 +561,73 @@ fn test_dec_offset() {
",
);
let result = new_ucmd!()
new_ucmd!()
.arg("-Ad")
.arg("-X")
.arg("-X")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
fn test_no_offset() {
let input = [0u8; 31];
const LINE: &'static str = " 00000000 00000000 00000000 00000000\n";
const LINE: &str = " 00000000 00000000 00000000 00000000\n";
let expected_output = [LINE, LINE, LINE, LINE].join("");
let result = new_ucmd!()
new_ucmd!()
.arg("-An")
.arg("-X")
.arg("-X")
.run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, expected_output);
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(expected_output);
}
#[test]
fn test_invalid_offset() {
let result = new_ucmd!().arg("-Ab").run();
assert!(!result.success);
new_ucmd!().arg("-Ab").fails();
}
#[test]
fn test_skip_bytes() {
let input = "abcdefghijklmnopq";
let result = new_ucmd!()
let input = "abcdefghijklmnopq"; // spell-checker:disable-line
new_ucmd!()
.arg("-c")
.arg("--skip-bytes=5")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
"
0000005 f g h i j k l m n o p q
0000021
"
)
);
",
));
}
#[test]
fn test_skip_bytes_error() {
let input = "12345";
let result = new_ucmd!()
new_ucmd!()
.arg("--skip-bytes=10")
.run_piped_stdin(input.as_bytes());
assert!(!result.success);
.run_piped_stdin(input.as_bytes())
.failure();
}
#[test]
fn test_read_bytes() {
let input = "abcdefghijklmnopqrstuvwxyz\n12345678";
let result = new_ucmd!()
let input = "abcdefghijklmnopqrstuvwxyz\n12345678"; // spell-checker:disable-line
new_ucmd!()
.arg("--endian=little")
.arg("--read-bytes=27")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(result.stdout, unindent(ALPHA_OUT));
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(ALPHA_OUT));
}
#[test]
@ -662,13 +636,12 @@ fn test_ascii_dump() {
0x00, 0x01, 0x0a, 0x0d, 0x10, 0x1f, 0x20, 0x61, 0x62, 0x63, 0x7d, 0x7e, 0x7f, 0x80, 0x90,
0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff,
];
let result = new_ucmd!().arg("-tx1zacz").run_piped_stdin(&input[..]);
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
new_ucmd!()
.arg("-tx1zacz") // spell-checker:disable-line
.run_piped_stdin(&input[..])
.no_stderr()
.success()
.stdout_is(unindent(
r"
0000000 00 01 0a 0d 10 1f 20 61 62 63 7d 7e 7f 80 90 a0 >...... abc}~....<
nul soh nl cr dle us sp a b c } ~ del nul dle sp
@ -677,169 +650,145 @@ fn test_ascii_dump() {
0 @ P ` p del
** 300 320 340 360 377 >......<
0000026
"
)
);
",
));
}
#[test]
fn test_filename_parsing() {
// files "a" and "x" both exists, but are no filenames in the commandline below
// files "a" and "x" both exists, but are no filenames in the command line below
// "-f" must be treated as a filename, it contains the text: minus lowercase f
// so "-f" should not be interpreted as a formatting option.
let result = new_ucmd!()
new_ucmd!()
.arg("--format")
.arg("a")
.arg("-A")
.arg("x")
.arg("--")
.arg("-f")
.run();
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.succeeds()
.no_stderr()
.stdout_is(unindent(
"
000000 m i n u s sp l o w e r c a s e sp
000010 f nl
000012
"
)
);
",
));
}
#[test]
fn test_stdin_offset() {
let input = "abcdefghijklmnopq";
let result = new_ucmd!()
let input = "abcdefghijklmnopq"; // spell-checker:disable-line
new_ucmd!()
.arg("-c")
.arg("+5")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
"
0000005 f g h i j k l m n o p q
0000021
"
)
);
",
));
}
#[test]
fn test_file_offset() {
let result = new_ucmd!().arg("-c").arg("--").arg("-f").arg("10").run();
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
new_ucmd!()
.arg("-c")
.arg("--")
.arg("-f")
.arg("10")
.succeeds()
.no_stderr()
.stdout_is(unindent(
r"
0000010 w e r c a s e f \n
0000022
"
)
);
",
));
}
#[test]
fn test_traditional() {
// note gnu od does not align both lines
let input = "abcdefghijklmnopq";
let result = new_ucmd!()
let input = "abcdefghijklmnopq"; // spell-checker:disable-line
new_ucmd!()
.arg("--traditional")
.arg("-a")
.arg("-c")
.arg("-")
.arg("10")
.arg("0")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
r"
0000010 (0000000) i j k l m n o p q
i j k l m n o p q
0000021 (0000011)
"
)
);
",
));
}
#[test]
fn test_traditional_with_skip_bytes_override() {
// --skip-bytes is ignored in this case
let input = "abcdefghijklmnop";
let result = new_ucmd!()
let input = "abcdefghijklmnop"; // spell-checker:disable-line
new_ucmd!()
.arg("--traditional")
.arg("--skip-bytes=10")
.arg("-c")
.arg("0")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
r"
0000000 a b c d e f g h i j k l m n o p
0000020
"
)
);
",
));
}
#[test]
fn test_traditional_with_skip_bytes_non_override() {
// no offset specified in the traditional way, so --skip-bytes is used
let input = "abcdefghijklmnop";
let result = new_ucmd!()
let input = "abcdefghijklmnop"; // spell-checker:disable-line
new_ucmd!()
.arg("--traditional")
.arg("--skip-bytes=10")
.arg("-c")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
r"
0000012 k l m n o p
0000020
"
)
);
",
));
}
#[test]
fn test_traditional_error() {
// file "0" exists - don't fail on that, but --traditional only accepts a single input
let result = new_ucmd!()
new_ucmd!()
.arg("--traditional")
.arg("0")
.arg("0")
.arg("0")
.arg("0")
.run();
assert!(!result.success);
.fails();
}
#[test]
fn test_traditional_only_label() {
let input = "abcdefghijklmnopqrstuvwxyz";
let result = new_ucmd!()
let input = "abcdefghijklmnopqrstuvwxyz"; // spell-checker:disable-line
new_ucmd!()
.arg("-An")
.arg("--traditional")
.arg("-a")
@ -847,20 +796,53 @@ fn test_traditional_only_label() {
.arg("-")
.arg("10")
.arg("0x10")
.run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result);
assert!(result.success);
assert_eq!(
result.stdout,
unindent(
.run_piped_stdin(input.as_bytes())
.no_stderr()
.success()
.stdout_is(unindent(
r"
(0000020) i j k l m n o p q r s t u v w x
i j k l m n o p q r s t u v w x
(0000040) y z
y z
(0000042)
"
)
);
",
));
}
#[test]
fn test_od_invalid_bytes() {
const INVALID_SIZE: &str = "1fb4t";
const BIG_SIZE: &str = "1Y";
// NOTE:
// GNU's od (8.32) with option '--width' does not accept 'Y' as valid suffix.
// According to the man page it should be valid in the same way it is valid for
// '--read-bytes' and '--skip-bytes'.
let options = [
"--read-bytes",
"--skip-bytes",
"--width",
// "--strings", // TODO: consider testing here once '--strings' is implemented
];
for option in &options {
new_ucmd!()
.arg(format!("{}={}", option, INVALID_SIZE))
.arg("file")
.fails()
.code_is(1)
.stderr_only(format!(
"od: invalid {} argument '{}'",
option, INVALID_SIZE
));
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.arg(format!("{}={}", option, BIG_SIZE))
.arg("file")
.fails()
.code_is(1)
.stderr_only(format!("od: {} argument '{}' too large", option, BIG_SIZE));
}
}

View file

@ -7,7 +7,7 @@ struct TestData<'b> {
out: &'b str,
}
static EXAMPLE_DATA: &'static [TestData<'static>] = &[
static EXAMPLE_DATA: &[TestData] = &[
// Ensure that paste properly handles files lacking a final newline.
TestData {
name: "no-nl-1",
@ -64,8 +64,8 @@ static EXAMPLE_DATA: &'static [TestData<'static>] = &[
#[test]
fn test_combine_pairs_of_lines() {
for s in vec!["-s", "--serial"] {
for d in vec!["-d", "--delimiters"] {
for &s in &["-s", "--serial"] {
for &d in &["-d", "--delimiters"] {
new_ucmd!()
.args(&[s, d, "\t\n", "html_colors.txt"])
.run()
@ -76,7 +76,7 @@ fn test_combine_pairs_of_lines() {
#[test]
fn test_multi_stdin() {
for d in vec!["-d", "--delimiters"] {
for &d in &["-d", "--delimiters"] {
new_ucmd!()
.args(&[d, "\t\n", "-", "-"])
.pipe_in_fixture("html_colors.txt")

View file

@ -5,11 +5,152 @@ fn test_default_mode() {
// test the default mode
// accept some reasonable default
new_ucmd!().args(&["abc/def"]).succeeds().no_stdout();
new_ucmd!().args(&["dir/file"]).succeeds().no_stdout();
// fail on long inputs
// accept non-portable chars
new_ucmd!().args(&["dir#/$file"]).succeeds().no_stdout();
// accept empty path
new_ucmd!().args(&[""]).succeeds().no_stdout();
// fail on long path
new_ucmd!()
.args(&[repeat_str("test", 20000)])
.args(&["dir".repeat(libc::PATH_MAX as usize + 1)])
.fails()
.no_stdout();
// fail on long filename
new_ucmd!()
.args(&[format!(
"dir/{}",
"file".repeat(libc::FILENAME_MAX as usize + 1)
)])
.fails()
.no_stdout();
}
#[test]
fn test_posix_mode() {
// test the posix mode
// accept some reasonable default
new_ucmd!().args(&["-p", "dir/file"]).succeeds().no_stdout();
// fail on long path
new_ucmd!()
.args(&["-p", "dir".repeat(libc::PATH_MAX as usize + 1).as_str()])
.fails()
.no_stdout();
// fail on long filename
new_ucmd!()
.args(&[
"-p",
format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
])
.fails()
.no_stdout();
// fail on non-portable chars
new_ucmd!().args(&["-p", "dir#/$file"]).fails().no_stdout();
}
#[test]
fn test_posix_special() {
// test the posix special mode
// accept some reasonable default
new_ucmd!().args(&["-P", "dir/file"]).succeeds().no_stdout();
// accept non-portable chars
new_ucmd!()
.args(&["-P", "dir#/$file"])
.succeeds()
.no_stdout();
// accept non-leading hyphen
new_ucmd!()
.args(&["-P", "dir/file-name"])
.succeeds()
.no_stdout();
// fail on long path
new_ucmd!()
.args(&["-P", "dir".repeat(libc::PATH_MAX as usize + 1).as_str()])
.fails()
.no_stdout();
// fail on long filename
new_ucmd!()
.args(&[
"-P",
format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
])
.fails()
.no_stdout();
// fail on leading hyphen char
new_ucmd!().args(&["-P", "dir/-file"]).fails().no_stdout();
// fail on empty path
new_ucmd!().args(&["-P", ""]).fails().no_stdout();
}
#[test]
fn test_posix_all() {
// test the posix special mode
// accept some reasonable default
new_ucmd!()
.args(&["-p", "-P", "dir/file"])
.succeeds()
.no_stdout();
// accept non-leading hyphen
new_ucmd!()
.args(&["-p", "-P", "dir/file-name"])
.succeeds()
.no_stdout();
// fail on long path
new_ucmd!()
.args(&[
"-p",
"-P",
"dir".repeat(libc::PATH_MAX as usize + 1).as_str(),
])
.fails()
.no_stdout();
// fail on long filename
new_ucmd!()
.args(&[
"-p",
"-P",
format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
])
.fails()
.no_stdout();
// fail on non-portable chars
new_ucmd!()
.args(&["-p", "-P", "dir#/$file"])
.fails()
.no_stdout();
// fail on leading hyphen char
new_ucmd!()
.args(&["-p", "-P", "dir/-file"])
.fails()
.no_stdout();
// fail on empty path
new_ucmd!().args(&["-p", "-P", ""]).fails().no_stdout();
}
#[test]
fn test_args_parsing() {
// fail on no args
let empty_args: [String; 0] = [];
new_ucmd!().args(&empty_args).fails().no_stdout();
}

View file

@ -9,75 +9,105 @@ pub use self::pinky::*;
#[test]
fn test_capitalize() {
assert_eq!("Zbnmasd", "zbnmasd".capitalize());
assert_eq!("Abnmasd", "Abnmasd".capitalize());
assert_eq!("1masd", "1masd".capitalize());
assert_eq!("Zbnmasd", "zbnmasd".capitalize()); // spell-checker:disable-line
assert_eq!("Abnmasd", "Abnmasd".capitalize()); // spell-checker:disable-line
assert_eq!("1masd", "1masd".capitalize()); // spell-checker:disable-line
assert_eq!("", "".capitalize());
}
#[test]
fn test_long_format() {
let ulogin = "root";
let pw: Passwd = Passwd::locate(ulogin).unwrap();
let login = "root";
let pw: Passwd = Passwd::locate(login).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(login)
.succeeds()
.stdout_is(format!(
"Login name: {:<28}In real life: {}\nDirectory: {:<29}Shell: {}\n\n",
login,
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(login)
.succeeds()
.stdout_is(format!(
"Login name: {:<28}In real life: {1}\n\n",
login, real_name
));
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_long_format_multiple_users() {
let args = ["-l", "root", "root", "root"];
new_ucmd!()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
}
#[test]
fn test_long_format_wo_user() {
// "no username specified; at least one must be specified when using -l"
new_ucmd!().arg("-l").fails().code_is(1);
}
#[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)
.run()
.stdout;
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
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)
.run()
.stdout;
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
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")]
fn expected_result(args: &[&str]) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.args(args)
.run()
.stdout
#[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 {
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
// note: clippy::needless_borrow *false positive*
#[allow(clippy::needless_borrow)]
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LC_ALL", "C")
.args(args)
.succeeds()
.stdout_move_str()
}

481
tests/by-util/test_pr.rs Normal file
View file

@ -0,0 +1,481 @@
// spell-checker:ignore (ToDO) Sdivide
use crate::common::util::*;
use chrono::offset::Local;
use chrono::DateTime;
use chrono::Duration;
use std::fs::metadata;
fn file_last_modified_time(ucmd: &UCommand, path: &str) -> String {
let tmp_dir_path = ucmd.get_full_fixture_path(path);
let file_metadata = metadata(tmp_dir_path);
file_metadata
.map(|i| {
i.modified()
.map(|x| {
let date_time: DateTime<Local> = x.into();
date_time.format("%b %d %H:%M %Y").to_string()
})
.unwrap_or_default()
})
.unwrap_or_default()
}
fn all_minutes(from: DateTime<Local>, to: DateTime<Local>) -> Vec<String> {
let to = to + Duration::minutes(1);
const FORMAT: &str = "%b %d %H:%M %Y";
let mut vec = vec![];
let mut current = from;
while current < to {
vec.push(current.format(FORMAT).to_string());
current = current + Duration::minutes(1);
}
vec
}
fn valid_last_modified_template_vars(from: DateTime<Local>) -> Vec<Vec<(String, String)>> {
all_minutes(from, Local::now())
.into_iter()
.map(|time| vec![("{last_modified_time}".to_string(), time)])
.collect()
}
#[test]
fn test_without_any_options() {
let test_file_path = "test_one_page.log";
let expected_test_file_path = "test_one_page.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&[test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_numbering_option_with_number_width() {
let test_file_path = "test_num_page.log";
let expected_test_file_path = "test_num_page_2.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["-n", "2", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_long_header_option() {
let test_file_path = "test_one_page.log";
let expected_test_file_path = "test_one_page_header.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
let header = "new file";
scenario
.args(&["--header=new file", test_file_path])
.succeeds()
.stdout_is_templated_fixture(
expected_test_file_path,
&[("{last_modified_time}", &value), ("{header}", header)],
);
new_ucmd!()
.args(&["-h", header, test_file_path])
.succeeds()
.stdout_is_templated_fixture(
expected_test_file_path,
&[("{last_modified_time}", &value), ("{header}", header)],
);
}
#[test]
fn test_with_double_space_option() {
let test_file_path = "test_one_page.log";
let expected_test_file_path = "test_one_page_double_line.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["-d", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
new_ucmd!()
.args(&["--double-space", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_first_line_number_option() {
let test_file_path = "test_one_page.log";
let expected_test_file_path = "test_one_page_first_line.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["-N", "5", "-n", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_first_line_number_long_option() {
let test_file_path = "test_one_page.log";
let expected_test_file_path = "test_one_page_first_line.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["--first-line-number=5", "-n", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_number_option_with_custom_separator_char() {
let test_file_path = "test_num_page.log";
let expected_test_file_path = "test_num_page_char.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["-nc", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_number_option_with_custom_separator_char_and_width() {
let test_file_path = "test_num_page.log";
let expected_test_file_path = "test_num_page_char_one.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["-nc1", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_valid_page_ranges() {
let test_file_path = "test_num_page.log";
let mut scenario = new_ucmd!();
scenario
.args(&["--pages=20:5", test_file_path])
.fails()
.stderr_is("pr: invalid --pages argument '20:5'")
.stdout_is("");
new_ucmd!()
.args(&["--pages=1:5", test_file_path])
.succeeds();
new_ucmd!().args(&["--pages=1", test_file_path]).succeeds();
new_ucmd!()
.args(&["--pages=-1:5", test_file_path])
.fails()
.stderr_is("pr: invalid --pages argument '-1:5'")
.stdout_is("");
new_ucmd!()
.args(&["--pages=1:-5", test_file_path])
.fails()
.stderr_is("pr: invalid --pages argument '1:-5'")
.stdout_is("");
new_ucmd!()
.args(&["--pages=5:1", test_file_path])
.fails()
.stderr_is("pr: invalid --pages argument '5:1'")
.stdout_is("");
}
#[test]
fn test_with_page_range() {
let test_file_path = "test.log";
let expected_test_file_path = "test_page_range_1.log.expected";
let expected_test_file_path1 = "test_page_range_2.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["--pages=15", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
new_ucmd!()
.args(&["+15", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
new_ucmd!()
.args(&["--pages=15:17", test_file_path])
.succeeds()
.stdout_is_templated_fixture(
expected_test_file_path1,
&[("{last_modified_time}", &value)],
);
new_ucmd!()
.args(&["+15:17", test_file_path])
.succeeds()
.stdout_is_templated_fixture(
expected_test_file_path1,
&[("{last_modified_time}", &value)],
);
}
#[test]
fn test_with_no_header_trailer_option() {
let test_file_path = "test_one_page.log";
let expected_test_file_path = "test_one_page_no_ht.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["-t", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_page_length_option() {
let test_file_path = "test.log";
let expected_test_file_path = "test_page_length.log.expected";
let expected_test_file_path1 = "test_page_length1.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["--pages=2:3", "-l", "100", "-n", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
new_ucmd!()
.args(&["--pages=2:3", "-l", "5", "-n", test_file_path])
.succeeds()
.stdout_is_fixture(expected_test_file_path1);
}
#[test]
fn test_with_suppress_error_option() {
let test_file_path = "test_num_page.log";
let mut scenario = new_ucmd!();
scenario
.args(&["--pages=20:5", "-r", test_file_path])
.fails()
.stderr_is("")
.stdout_is("");
}
#[test]
fn test_with_stdin() {
let expected_file_path = "stdin.log.expected";
let mut scenario = new_ucmd!();
let start = Local::now();
scenario
.pipe_in_fixture("stdin.log")
.args(&["--pages=1:2", "-n", "-"])
.run()
.stdout_is_templated_fixture_any(
expected_file_path,
&valid_last_modified_template_vars(start),
);
}
#[test]
fn test_with_column() {
let test_file_path = "column.log";
let expected_test_file_path = "column.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["--pages=3:5", "--column=3", "-n", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
new_ucmd!()
.args(&["--pages=3:5", "-3", "-n", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_column_across_option() {
let test_file_path = "column.log";
let expected_test_file_path = "column_across.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&["--pages=3:5", "--column=3", "-a", "-n", test_file_path])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_column_across_option_and_column_separator() {
let test_file_path = "column.log";
let expected_test_file_path = "column_across_sep.log.expected";
let expected_test_file_path1 = "column_across_sep1.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&[
"--pages=3:5",
"--column=3",
"-s|",
"-a",
"-n",
test_file_path,
])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
new_ucmd!()
.args(&[
"--pages=3:5",
"--column=3",
"-Sdivide",
"-a",
"-n",
test_file_path,
])
.succeeds()
.stdout_is_templated_fixture(
expected_test_file_path1,
&[("{last_modified_time}", &value)],
);
}
#[test]
fn test_with_mpr() {
let test_file_path = "column.log";
let test_file_path1 = "hosts.log";
let expected_test_file_path = "mpr.log.expected";
let expected_test_file_path1 = "mpr1.log.expected";
let expected_test_file_path2 = "mpr2.log.expected";
let start = Local::now();
new_ucmd!()
.args(&["--pages=1:2", "-m", "-n", test_file_path, test_file_path1])
.succeeds()
.stdout_is_templated_fixture_any(
expected_test_file_path,
&valid_last_modified_template_vars(start),
);
let start = Local::now();
new_ucmd!()
.args(&["--pages=2:4", "-m", "-n", test_file_path, test_file_path1])
.succeeds()
.stdout_is_templated_fixture_any(
expected_test_file_path1,
&valid_last_modified_template_vars(start),
);
let start = Local::now();
new_ucmd!()
.args(&[
"--pages=1:2",
"-l",
"100",
"-n",
"-m",
test_file_path,
test_file_path1,
test_file_path,
])
.succeeds()
.stdout_is_templated_fixture_any(
expected_test_file_path2,
&valid_last_modified_template_vars(start),
);
}
#[test]
fn test_with_mpr_and_column_options() {
let test_file_path = "column.log";
new_ucmd!()
.args(&["--column=2", "-m", "-n", test_file_path])
.fails()
.stderr_is("pr: cannot specify number of columns when printing in parallel")
.stdout_is("");
new_ucmd!()
.args(&["-a", "-m", "-n", test_file_path])
.fails()
.stderr_is("pr: cannot specify both printing across and printing in parallel")
.stdout_is("");
}
#[test]
fn test_with_offset_space_option() {
let test_file_path = "column.log";
let expected_test_file_path = "column_spaces_across.log.expected";
let mut scenario = new_ucmd!();
let value = file_last_modified_time(&scenario, test_file_path);
scenario
.args(&[
"-o",
"5",
"--pages=3:5",
"--column=3",
"-a",
"-n",
test_file_path,
])
.succeeds()
.stdout_is_templated_fixture(expected_test_file_path, &[("{last_modified_time}", &value)]);
}
#[test]
fn test_with_pr_core_utils_tests() {
let test_cases = vec![
("", vec!["0Ft"], vec!["0F"], 0),
("", vec!["0Fnt"], vec!["0F"], 0),
("+3", vec!["0Ft"], vec!["3-0F"], 0),
("+3 -f", vec!["0Ft"], vec!["3f-0F"], 0),
("-a -3", vec!["0Ft"], vec!["a3-0F"], 0),
("-a -3 -f", vec!["0Ft"], vec!["a3f-0F"], 0),
("-a -3 -f", vec!["0Fnt"], vec!["a3f-0F"], 0),
("+3 -a -3 -f", vec!["0Ft"], vec!["3a3f-0F"], 0),
("-l 24", vec!["FnFn"], vec!["l24-FF"], 0),
("-W 20 -l24 -f", vec!["tFFt-ll"], vec!["W20l24f-ll"], 0),
];
for test_case in test_cases {
let (flags, input_file, expected_file, return_code) = test_case;
let mut scenario = new_ucmd!();
let input_file_path = input_file.get(0).unwrap();
let test_file_path = expected_file.get(0).unwrap();
let value = file_last_modified_time(&scenario, test_file_path);
let mut arguments: Vec<&str> = flags
.split(' ')
.into_iter()
.filter(|i| i.trim() != "")
.collect::<Vec<&str>>();
arguments.extend(input_file.clone());
let scenario_with_args = scenario.args(&arguments);
let scenario_with_expected_status = if return_code == 0 {
scenario_with_args.succeeds()
} else {
scenario_with_args.fails()
};
scenario_with_expected_status.stdout_is_templated_fixture(
test_file_path,
&[
("{last_modified_time}", &value),
("{file_name}", input_file_path),
],
);
}
}
#[test]
fn test_with_join_lines_option() {
let test_file_1 = "hosts.log";
let test_file_2 = "test.log";
let expected_file_path = "joined.log.expected";
let mut scenario = new_ucmd!();
let start = Local::now();
scenario
.args(&["+1:2", "-J", "-m", test_file_1, test_file_2])
.run()
.stdout_is_templated_fixture_any(
expected_file_path,
&valid_last_modified_template_vars(start),
);
}

View file

@ -7,10 +7,11 @@ fn test_get_all() {
env::set_var(key, "VALUE");
assert_eq!(env::var(key), Ok("VALUE".to_string()));
let result = TestScenario::new(util_name!()).ucmd_keepenv().run();
assert!(result.success);
assert!(result.stdout.contains("HOME="));
assert!(result.stdout.contains("KEY=VALUE"));
TestScenario::new(util_name!())
.ucmd_keepenv()
.succeeds()
.stdout_contains("HOME=")
.stdout_contains("KEY=VALUE");
}
#[test]
@ -22,9 +23,8 @@ fn test_get_var() {
let result = TestScenario::new(util_name!())
.ucmd_keepenv()
.arg("KEY")
.run();
.succeeds();
assert!(result.success);
assert!(!result.stdout.is_empty());
assert!(result.stdout.trim() == "VALUE");
assert!(!result.stdout_str().is_empty());
assert!(result.stdout_str().trim() == "VALUE");
}

View file

@ -43,12 +43,12 @@ fn escaped_octal() {
}
#[test]
fn escaped_unicode_fourdigit() {
fn escaped_unicode_four_digit() {
new_ucmd!().args(&["\\u0125"]).succeeds().stdout_only("ĥ");
}
#[test]
fn escaped_unicode_eightdigit() {
fn escaped_unicode_eight_digit() {
new_ucmd!()
.args(&["\\U00000125"])
.succeeds()
@ -77,7 +77,7 @@ fn sub_string() {
}
#[test]
fn sub_multifield() {
fn sub_multi_field() {
new_ucmd!()
.args(&["%s %s", "hello", "world"])
.succeeds()
@ -85,7 +85,7 @@ fn sub_multifield() {
}
#[test]
fn sub_repeat_formatstr() {
fn sub_repeat_format_str() {
new_ucmd!()
.args(&["%s.", "hello", "world"])
.succeeds()
@ -101,7 +101,7 @@ fn sub_string_ignore_escapes() {
}
#[test]
fn sub_bstring_handle_escapes() {
fn sub_b_string_handle_escapes() {
new_ucmd!()
.args(&["hello %b", "\\tworld"])
.succeeds()
@ -109,7 +109,7 @@ fn sub_bstring_handle_escapes() {
}
#[test]
fn sub_bstring_ignore_subs() {
fn sub_b_string_ignore_subs() {
new_ucmd!()
.args(&["hello %b", "world %% %i"])
.succeeds()
@ -133,7 +133,7 @@ fn sub_num_int() {
}
#[test]
fn sub_num_int_minwidth() {
fn sub_num_int_min_width() {
new_ucmd!()
.args(&["twenty is %1i", "20"])
.succeeds()
@ -181,11 +181,11 @@ fn sub_num_int_hex_in_neg() {
}
#[test]
fn sub_num_int_charconst_in() {
fn sub_num_int_char_const_in() {
new_ucmd!()
.args(&["ninetyseven is %i", "'a"])
.args(&["ninety seven is %i", "'a"])
.succeeds()
.stdout_only("ninetyseven is 97");
.stdout_only("ninety seven is 97");
}
#[test]
@ -287,7 +287,7 @@ fn sub_num_hex_float_upper() {
}
#[test]
fn sub_minwidth() {
fn sub_min_width() {
new_ucmd!()
.args(&["hello %7s", "world"])
.succeeds()
@ -295,7 +295,7 @@ fn sub_minwidth() {
}
#[test]
fn sub_minwidth_negative() {
fn sub_min_width_negative() {
new_ucmd!()
.args(&["hello %-7s", "world"])
.succeeds()
@ -327,7 +327,7 @@ fn sub_int_leading_zeroes() {
}
#[test]
fn sub_int_leading_zeroes_prio() {
fn sub_int_leading_zeroes_padded() {
new_ucmd!()
.args(&["%5.4i", "11"])
.succeeds()
@ -359,7 +359,7 @@ fn sub_float_no_octal_in() {
}
#[test]
fn sub_any_asterisk_firstparam() {
fn sub_any_asterisk_first_param() {
new_ucmd!()
.args(&["%*i", "3", "11", "4", "12"])
.succeeds()
@ -401,7 +401,7 @@ fn sub_any_asterisk_hex_arg() {
#[test]
fn sub_any_specifiers_no_params() {
new_ucmd!()
.args(&["%ztlhLji", "3"])
.args(&["%ztlhLji", "3"]) //spell-checker:disable-line
.succeeds()
.stdout_only("3");
}
@ -409,7 +409,7 @@ fn sub_any_specifiers_no_params() {
#[test]
fn sub_any_specifiers_after_first_param() {
new_ucmd!()
.args(&["%0ztlhLji", "3"])
.args(&["%0ztlhLji", "3"]) //spell-checker:disable-line
.succeeds()
.stdout_only("3");
}
@ -417,7 +417,7 @@ fn sub_any_specifiers_after_first_param() {
#[test]
fn sub_any_specifiers_after_period() {
new_ucmd!()
.args(&["%0.ztlhLji", "3"])
.args(&["%0.ztlhLji", "3"]) //spell-checker:disable-line
.succeeds()
.stdout_only("3");
}
@ -425,7 +425,7 @@ fn sub_any_specifiers_after_period() {
#[test]
fn sub_any_specifiers_after_second_param() {
new_ucmd!()
.args(&["%0.0ztlhLji", "3"])
.args(&["%0.0ztlhLji", "3"]) //spell-checker:disable-line
.succeeds()
.stdout_only("3");
}

View file

@ -1,43 +1,43 @@
use crate::common::util::*;
#[test]
fn gnu_ext_disabled_roff_no_ref() {
fn gnu_ext_disabled_rightward_no_ref() {
new_ucmd!()
.args(&["-G", "-R", "input"])
.succeeds()
.stdout_only_fixture("gnu_ext_disabled_roff_no_ref.expected");
.stdout_only_fixture("gnu_ext_disabled_rightward_no_ref.expected");
}
#[test]
fn gnu_ext_disabled_roff_no_ref_empty_word_regexp() {
fn gnu_ext_disabled_rightward_no_ref_empty_word_regexp() {
new_ucmd!()
.args(&["-G", "-R", "-W", "", "input"])
.succeeds()
.stdout_only_fixture("gnu_ext_disabled_roff_no_ref.expected");
.stdout_only_fixture("gnu_ext_disabled_rightward_no_ref.expected");
}
#[test]
fn gnu_ext_disabled_roff_no_ref_word_regexp_exc_space() {
fn gnu_ext_disabled_rightward_no_ref_word_regexp_exc_space() {
new_ucmd!()
.args(&["-G", "-R", "-W", "[^\t\n]+", "input"])
.succeeds()
.stdout_only_fixture("gnu_ext_disabled_roff_no_ref_word_regexp_exc_space.expected");
.stdout_only_fixture("gnu_ext_disabled_rightward_no_ref_word_regexp_exc_space.expected");
}
#[test]
fn gnu_ext_disabled_roff_input_ref() {
fn gnu_ext_disabled_rightward_input_ref() {
new_ucmd!()
.args(&["-G", "-r", "-R", "input"])
.succeeds()
.stdout_only_fixture("gnu_ext_disabled_roff_input_ref.expected");
.stdout_only_fixture("gnu_ext_disabled_rightward_input_ref.expected");
}
#[test]
fn gnu_ext_disabled_roff_auto_ref() {
fn gnu_ext_disabled_rightward_auto_ref() {
new_ucmd!()
.args(&["-G", "-A", "-R", "input"])
.succeeds()
.stdout_only_fixture("gnu_ext_disabled_roff_auto_ref.expected");
.stdout_only_fixture("gnu_ext_disabled_rightward_auto_ref.expected");
}
#[test]

View file

@ -9,5 +9,5 @@ fn test_default() {
#[test]
fn test_failed() {
let (_at, mut ucmd) = at_and_ucmd!();
ucmd.arg("willfail").fails();
ucmd.arg("will-fail").fails();
}

View file

@ -1,11 +1,11 @@
use crate::common::util::*;
static GIBBERISH: &'static str = "supercalifragilisticexpialidocious";
static GIBBERISH: &str = "supercalifragilisticexpialidocious";
#[test]
fn test_canonicalize() {
let (at, mut ucmd) = at_and_ucmd!();
let actual = ucmd.arg("-f").arg(".").run().stdout;
let actual = ucmd.arg("-f").arg(".").run().stdout_move_str();
let expect = at.root_dir_resolved() + "\n";
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
@ -15,7 +15,7 @@ fn test_canonicalize() {
#[test]
fn test_canonicalize_existing() {
let (at, mut ucmd) = at_and_ucmd!();
let actual = ucmd.arg("-e").arg(".").run().stdout;
let actual = ucmd.arg("-e").arg(".").run().stdout_move_str();
let expect = at.root_dir_resolved() + "\n";
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
@ -25,7 +25,7 @@ fn test_canonicalize_existing() {
#[test]
fn test_canonicalize_missing() {
let (at, mut ucmd) = at_and_ucmd!();
let actual = ucmd.arg("-m").arg(GIBBERISH).run().stdout;
let actual = ucmd.arg("-m").arg(GIBBERISH).run().stdout_move_str();
let expect = path_concat!(at.root_dir_resolved(), GIBBERISH) + "\n";
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
@ -37,7 +37,7 @@ fn test_long_redirection_to_current_dir() {
let (at, mut ucmd) = at_and_ucmd!();
// Create a 256-character path to current directory
let dir = path_concat!(".", ..128);
let actual = ucmd.arg("-n").arg("-m").arg(dir).run().stdout;
let actual = ucmd.arg("-n").arg("-m").arg(dir).run().stdout_move_str();
let expect = at.root_dir_resolved();
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
@ -48,7 +48,12 @@ fn test_long_redirection_to_current_dir() {
fn test_long_redirection_to_root() {
// Create a 255-character path to root
let dir = path_concat!("..", ..85);
let actual = new_ucmd!().arg("-n").arg("-m").arg(dir).run().stdout;
let actual = new_ucmd!()
.arg("-n")
.arg("-m")
.arg(dir)
.run()
.stdout_move_str();
let expect = get_root_path();
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);

View file

@ -1,106 +1,108 @@
use crate::common::util::*;
#[test]
fn test_current_directory() {
fn test_realpath_current_directory() {
let (at, mut ucmd) = at_and_ucmd!();
let actual = ucmd.arg(".").run().stdout;
let expect = at.root_dir_resolved() + "\n";
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
assert_eq!(actual, expect);
ucmd.arg(".").succeeds().stdout_is(expect);
}
#[test]
fn test_long_redirection_to_current_dir() {
fn test_realpath_long_redirection_to_current_dir() {
let (at, mut ucmd) = at_and_ucmd!();
// Create a 256-character path to current directory
let dir = path_concat!(".", ..128);
let actual = ucmd.arg(dir).run().stdout;
let expect = at.root_dir_resolved() + "\n";
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
assert_eq!(actual, expect);
ucmd.arg(dir).succeeds().stdout_is(expect);
}
#[test]
fn test_long_redirection_to_root() {
fn test_realpath_long_redirection_to_root() {
// Create a 255-character path to root
let dir = path_concat!("..", ..85);
let actual = new_ucmd!().arg(dir).run().stdout;
let expect = get_root_path().to_owned() + "\n";
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
assert_eq!(actual, expect);
new_ucmd!().arg(dir).succeeds().stdout_is(expect);
}
#[test]
fn test_file_and_links() {
fn test_realpath_file_and_links() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("foo");
at.symlink_file("foo", "bar");
let actual = scene.ucmd().arg("foo").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("foo\n"));
let actual = scene.ucmd().arg("bar").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("foo\n"));
scene.ucmd().arg("foo").succeeds().stdout_contains("foo\n");
scene.ucmd().arg("bar").succeeds().stdout_contains("foo\n");
}
#[test]
fn test_file_and_links_zero() {
fn test_realpath_file_and_links_zero() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("foo");
at.symlink_file("foo", "bar");
let actual = scene.ucmd().arg("foo").arg("-z").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("foo"));
assert!(!actual.contains("\n"));
scene
.ucmd()
.arg("foo")
.arg("-z")
.succeeds()
.stdout_contains("foo\u{0}");
let actual = scene.ucmd().arg("bar").arg("-z").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("foo"));
assert!(!actual.contains("\n"));
scene
.ucmd()
.arg("bar")
.arg("-z")
.succeeds()
.stdout_contains("foo\u{0}");
}
#[test]
fn test_file_and_links_strip() {
fn test_realpath_file_and_links_strip() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("foo");
at.symlink_file("foo", "bar");
let actual = scene.ucmd().arg("foo").arg("-s").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("foo\n"));
scene
.ucmd()
.arg("foo")
.arg("-s")
.succeeds()
.stdout_contains("foo\n");
let actual = scene.ucmd().arg("bar").arg("-s").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("bar\n"));
scene
.ucmd()
.arg("bar")
.arg("-s")
.succeeds()
.stdout_contains("bar\n");
}
#[test]
fn test_file_and_links_strip_zero() {
fn test_realpath_file_and_links_strip_zero() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("foo");
at.symlink_file("foo", "bar");
let actual = scene.ucmd().arg("foo").arg("-s").arg("-z").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("foo"));
assert!(!actual.contains("\n"));
scene
.ucmd()
.arg("foo")
.arg("-s")
.arg("-z")
.succeeds()
.stdout_contains("foo\u{0}");
let actual = scene.ucmd().arg("bar").arg("-s").arg("-z").run().stdout;
println!("actual: {:?}", actual);
assert!(actual.contains("bar"));
assert!(!actual.contains("\n"));
scene
.ucmd()
.arg("bar")
.arg("-s")
.arg("-z")
.succeeds()
.stdout_contains("bar\u{0}");
}

View file

@ -1 +1,178 @@
// ToDO: add tests
use crate::common::util::*;
use std::borrow::Cow;
use std::path::Path;
struct TestCase<'a> {
from: &'a str,
to: &'a str,
expected: &'a str,
}
const TESTS: [TestCase; 10] = [
TestCase {
from: "A/B/C",
to: "A",
expected: "../..",
},
TestCase {
from: "A/B/C",
to: "A/B",
expected: "..",
},
TestCase {
from: "A/B/C",
to: "A/B/C",
expected: "",
},
TestCase {
from: "A/B/C",
to: "A/B/C/D",
expected: "D",
},
TestCase {
from: "A/B/C",
to: "A/B/C/D/E",
expected: "D/E",
},
TestCase {
from: "A/B/C",
to: "A/B/D",
expected: "../D",
},
TestCase {
from: "A/B/C",
to: "A/B/D/E",
expected: "../D/E",
},
TestCase {
from: "A/B/C",
to: "A/D",
expected: "../../D",
},
TestCase {
from: "A/B/C",
to: "D/E/F",
expected: "../../../D/E/F",
},
TestCase {
from: "A/B/C",
to: "A/D/E",
expected: "../../D/E",
},
];
#[allow(clippy::needless_lifetimes)]
fn convert_path<'a>(path: &'a str) -> Cow<'a, str> {
#[cfg(windows)]
return path.replace("/", "\\").into();
#[cfg(not(windows))]
return path.into();
}
#[test]
fn test_relpath_with_from_no_d() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
for test in TESTS.iter() {
let from: &str = &convert_path(test.from);
let to: &str = &convert_path(test.to);
let expected: &str = &convert_path(test.expected);
at.mkdir_all(to);
at.mkdir_all(from);
scene
.ucmd()
.arg(to)
.arg(from)
.succeeds()
.stdout_only(&format!("{}\n", expected));
}
}
#[test]
fn test_relpath_with_from_with_d() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
for test in TESTS.iter() {
let from: &str = &convert_path(test.from);
let to: &str = &convert_path(test.to);
let pwd = at.as_string();
at.mkdir_all(to);
at.mkdir_all(from);
// d is part of subpath -> expect relative path
let mut _result_stdout = scene
.ucmd()
.arg(to)
.arg(from)
.arg(&format!("-d{}", pwd))
.succeeds()
.stdout_move_str();
// relax rules for windows test environment
#[cfg(not(windows))]
assert!(Path::new(&_result_stdout).is_relative());
// d is not part of subpath -> expect absolute path
_result_stdout = scene
.ucmd()
.arg(to)
.arg(from)
.arg("-dnon_existing") // spell-checker:disable-line
.succeeds()
.stdout_move_str();
assert!(Path::new(&_result_stdout).is_absolute());
}
}
#[test]
fn test_relpath_no_from_no_d() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
for test in TESTS.iter() {
let to: &str = &convert_path(test.to);
at.mkdir_all(to);
let _result_stdout = scene.ucmd().arg(to).succeeds().stdout_move_str();
#[cfg(not(windows))]
assert_eq!(_result_stdout, format!("{}\n", to));
// relax rules for windows test environment
#[cfg(windows)]
assert!(_result_stdout.ends_with(&format!("{}\n", to)));
}
}
#[test]
fn test_relpath_no_from_with_d() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
for test in TESTS.iter() {
let to: &str = &convert_path(test.to);
let pwd = at.as_string();
at.mkdir_all(to);
// d is part of subpath -> expect relative path
let _result_stdout = scene
.ucmd()
.arg(to)
.arg(&format!("-d{}", pwd))
.succeeds()
.stdout_move_str();
// relax rules for windows test environment
#[cfg(not(windows))]
assert!(Path::new(&_result_stdout).is_relative());
// d is not part of subpath -> expect absolute path
let result_stdout = scene
.ucmd()
.arg(to)
.arg("-dnon_existing") // spell-checker:disable-line
.succeeds()
.stdout_move_str();
assert!(Path::new(&result_stdout).is_absolute());
}
}

View file

@ -15,9 +15,12 @@ fn test_rm_one_file() {
#[test]
fn test_rm_failed() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "test_rm_one_file";
let file = "test_rm_one_file"; // Doesn't exist
ucmd.arg(file).fails(); // Doesn't exist
ucmd.arg(file).fails().stderr_contains(&format!(
"cannot remove '{}': No such file or directory",
file
));
}
#[test]
@ -62,7 +65,7 @@ fn test_rm_interactive() {
.arg("-i")
.arg(file_a)
.arg(file_b)
.pipe_in("Yesh")
.pipe_in("Yesh") // spell-checker:disable-line
.succeeds();
assert!(!at.file_exists(file_a));
@ -115,6 +118,39 @@ fn test_rm_empty_directory() {
assert!(!at.dir_exists(dir));
}
#[test]
fn test_rm_empty_directory_verbose() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_rm_empty_directory_verbose";
at.mkdir(dir);
ucmd.arg("-d")
.arg("-v")
.arg(dir)
.succeeds()
.stdout_only(format!("removed directory '{}'\n", dir));
assert!(!at.dir_exists(dir));
}
#[test]
fn test_rm_non_empty_directory() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_rm_non_empty_dir";
let file_a = &format!("{}/test_rm_non_empty_file_a", dir);
at.mkdir(dir);
at.touch(file_a);
ucmd.arg("-d")
.arg(dir)
.fails()
.stderr_contains(&format!("cannot remove '{}': Directory not empty", dir));
assert!(at.file_exists(file_a));
assert!(at.dir_exists(dir));
}
#[test]
fn test_rm_recursive() {
let (at, mut ucmd) = at_and_ucmd!();
@ -134,22 +170,15 @@ fn test_rm_recursive() {
}
#[test]
fn test_rm_errors() {
fn test_rm_directory_without_flag() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_rm_errors_directory";
let file_a = "test_rm_errors_directory/test_rm_errors_file_a";
let file_b = "test_rm_errors_directory/test_rm_errors_file_b";
let dir = "test_rm_directory_without_flag_dir";
at.mkdir(dir);
at.touch(file_a);
at.touch(file_b);
// $ rm test_rm_errors_directory
// rm: error: could not remove directory 'test_rm_errors_directory' (did you mean to pass '-r'?)
ucmd.arg(dir).fails().stderr_is(
"rm: error: could not remove directory 'test_rm_errors_directory' (did you mean \
to pass '-r'?)\n",
);
ucmd.arg(dir)
.fails()
.stderr_contains(&format!("cannot remove '{}': Is a directory", dir));
}
#[test]
@ -169,10 +198,13 @@ fn test_rm_verbose() {
}
#[test]
fn test_rm_dir_symlink() {
#[cfg(not(windows))]
// on unix symlink_dir is a file
fn test_rm_symlink_dir() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_rm_dir_symlink_dir";
let link = "test_rm_dir_symlink_link";
let dir = "test_rm_symlink_dir_directory";
let link = "test_rm_symlink_dir_link";
at.mkdir(dir);
at.symlink_dir(dir, link);
@ -180,6 +212,30 @@ fn test_rm_dir_symlink() {
ucmd.arg(link).succeeds();
}
#[test]
#[cfg(windows)]
// on windows removing symlink_dir requires "-r" or "-d"
fn test_rm_symlink_dir() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dir = "test_rm_symlink_dir_directory";
let link = "test_rm_symlink_dir_link";
at.mkdir(dir);
at.symlink_dir(dir, link);
scene
.ucmd()
.arg(link)
.fails()
.stderr_contains(&format!("cannot remove '{}': Is a directory", link));
assert!(at.dir_exists(link));
scene.ucmd().arg("-r").arg(link).succeeds();
}
#[test]
fn test_rm_invalid_symlink() {
let (at, mut ucmd) = at_and_ucmd!();
@ -202,5 +258,34 @@ 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]
fn test_rm_verbose_slash() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_rm_verbose_slash_directory";
let file_a = &format!("{}/test_rm_verbose_slash_file_a", dir);
at.mkdir(dir);
at.touch(file_a);
let file_a_normalized = &format!(
"{}{}test_rm_verbose_slash_file_a",
dir,
std::path::MAIN_SEPARATOR
);
ucmd.arg("-r")
.arg("-f")
.arg("-v")
.arg(&format!("{}///", dir))
.succeeds()
.stdout_only(format!(
"removed '{}'\nremoved directory '{}'\n",
file_a_normalized, dir
));
assert!(!at.dir_exists(dir));
assert!(!at.file_exists(file_a));
}

View file

@ -39,8 +39,8 @@ 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 \
empty\n",
"rmdir: failed to remove 'test_rmdir_nonempty_no_parents': Directory not \
empty\n",
);
assert!(at.dir_exists(dir));
@ -59,10 +59,10 @@ 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 \
empty\n",
"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",
);
assert!(at.dir_exists(dir));
@ -108,3 +108,19 @@ fn test_rmdir_ignore_nonempty_directory_with_parents() {
assert!(at.dir_exists(dir));
}
#[test]
fn test_rmdir_remove_symlink_match_gnu_error() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "file";
let fl = "fl";
at.touch(file);
assert!(at.file_exists(file));
at.symlink_file(file, fl);
assert!(at.file_exists(fl));
ucmd.arg("fl/")
.fails()
.stderr_is("rmdir: failed to remove 'fl/': Not a directory");
}

View file

@ -1,5 +1,21 @@
use crate::common::util::*;
#[test]
fn test_rejects_nan() {
new_ucmd!().args(&["NaN"]).fails().stderr_only(
"seq: invalid 'not-a-number' argument: 'NaN'\nTry 'seq --help' for more information.",
);
}
#[test]
fn test_rejects_non_floats() {
new_ucmd!().args(&["foo"]).fails().stderr_only(
"seq: invalid floating point argument: 'foo'\nTry 'seq --help' for more information.",
);
}
// ---- Tests for the big integer based path ----
#[test]
fn test_count_up() {
new_ucmd!()
@ -26,6 +42,18 @@ fn test_separator_and_terminator() {
.args(&["-s", ",", "-t", "!", "2", "6"])
.run()
.stdout_is("2,3,4,5,6!");
new_ucmd!()
.args(&["-s", ",", "2", "6"])
.run()
.stdout_is("2,3,4,5,6\n");
new_ucmd!()
.args(&["-s", "\n", "2", "6"])
.run()
.stdout_is("2\n3\n4\n5\n6\n");
new_ucmd!()
.args(&["-s", "\\n", "2", "6"])
.run()
.stdout_is("2\\n3\\n4\\n5\\n6\n");
}
#[test]
@ -45,3 +73,62 @@ fn test_seq_wrong_arg() {
fn test_zero_step() {
new_ucmd!().args(&["10", "0", "32"]).fails();
}
#[test]
fn test_big_numbers() {
new_ucmd!()
.args(&[
"1000000000000000000000000000",
"1000000000000000000000000001",
])
.succeeds()
.stdout_only("1000000000000000000000000000\n1000000000000000000000000001\n");
}
// ---- Tests for the floating point based path ----
#[test]
fn test_count_up_floats() {
new_ucmd!()
.args(&["10.0"])
.run()
.stdout_is("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}
#[test]
fn test_count_down_floats() {
new_ucmd!()
.args(&["--", "5", "-1.0", "1"])
.run()
.stdout_is("5.0\n4.0\n3.0\n2.0\n1.0\n");
new_ucmd!()
.args(&["5", "-1", "1.0"])
.run()
.stdout_is("5\n4\n3\n2\n1\n");
}
#[test]
fn test_separator_and_terminator_floats() {
new_ucmd!()
.args(&["-s", ",", "-t", "!", "2.0", "6"])
.run()
.stdout_is("2.0,3.0,4.0,5.0,6.0!");
}
#[test]
fn test_equalize_widths_floats() {
new_ucmd!()
.args(&["-w", "5", "10.0"])
.run()
.stdout_is("05\n06\n07\n08\n09\n10\n");
}
#[test]
fn test_seq_wrong_arg_floats() {
new_ucmd!().args(&["-w", "5", "10.0", "33", "32"]).fails();
}
#[test]
fn test_zero_step_floats() {
new_ucmd!().args(&["10.0", "0", "32"]).fails();
}

View file

@ -1 +1,49 @@
// ToDO: add tests
use crate::common::util::*;
#[test]
fn test_shred_remove() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file_a = "test_shred_remove_a";
let file_b = "test_shred_remove_b";
// Create file_a and file_b.
at.touch(file_a);
at.touch(file_b);
// Shred file_a.
scene.ucmd().arg("-u").arg(file_a).run();
// file_a was deleted, file_b exists.
assert!(!at.file_exists(file_a));
assert!(at.file_exists(file_b));
}
#[cfg(not(target_os = "freebsd"))]
#[test]
fn test_shred_force() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file = "test_shred_force";
// Create file_a.
at.touch(file);
assert!(at.file_exists(file));
// Make file_a readonly.
at.set_readonly(file);
// Try shred -u.
scene.ucmd().arg("-u").arg(file).run();
// file_a was not deleted because it is readonly.
assert!(at.file_exists(file));
// Try shred -u -f.
scene.ucmd().arg("-u").arg("-f").arg(file).run();
// file_a was deleted.
assert!(!at.file_exists(file));
}

View file

@ -1 +1,200 @@
// ToDO: add tests
use crate::common::util::*;
#[test]
fn test_output_is_random_permutation() {
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let input = input_seq
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n");
let result = new_ucmd!().pipe_in(input.as_bytes()).succeeds();
result.no_stderr();
let mut result_seq: Vec<i32> = result
.stdout_str()
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort_unstable();
assert_ne!(result.stdout_str(), input, "Output is not randomized");
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
#[test]
fn test_zero_termination() {
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = new_ucmd!().arg("-z").arg("-i1-10").succeeds();
result.no_stderr();
let mut result_seq: Vec<i32> = result
.stdout_str()
.split('\0')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort_unstable();
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
#[test]
fn test_echo() {
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = new_ucmd!()
.arg("-e")
.args(
&input_seq
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>(),
)
.succeeds();
result.no_stderr();
let mut result_seq: Vec<i32> = result
.stdout_str()
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort_unstable();
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
#[test]
fn test_head_count() {
let repeat_limit = 5;
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let input = input_seq
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n");
let result = new_ucmd!()
.args(&["-n", &repeat_limit.to_string()])
.pipe_in(input.as_bytes())
.succeeds();
result.no_stderr();
let mut result_seq: Vec<i32> = result
.stdout_str()
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort_unstable();
assert_eq!(result_seq.len(), repeat_limit, "Output is not limited");
assert!(
result_seq.iter().all(|x| input_seq.contains(x)),
"Output includes element not from input: {}",
result.stdout_str()
)
}
#[test]
fn test_repeat() {
let repeat_limit = 15000;
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let input = input_seq
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n");
let result = new_ucmd!()
.arg("-r")
.args(&["-n", &repeat_limit.to_string()])
.pipe_in(input.as_bytes())
.succeeds();
result.no_stderr();
let result_seq: Vec<i32> = result
.stdout_str()
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
assert_eq!(
result_seq.len(),
repeat_limit,
"Output is not repeating forever"
);
assert!(
result_seq.iter().all(|x| input_seq.contains(x)),
"Output includes element not from input: {:?}",
result_seq
.iter()
.filter(|x| !input_seq.contains(x))
.collect::<Vec<&i32>>()
)
}
#[test]
fn test_file_input() {
let expected_seq = vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
let result = new_ucmd!().arg("file_input.txt").succeeds();
result.no_stderr();
let mut result_seq: Vec<i32> = result
.stdout_str()
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort_unstable();
assert_eq!(result_seq, expected_seq, "Output is not a permutation");
}
#[test]
fn test_shuf_echo_and_input_range_not_allowed() {
new_ucmd!()
.args(&["-e", "0", "-i", "0-2"])
.fails()
.stderr_contains(
"The argument '--input-range <LO-HI>' cannot be used with '--echo <ARG>...'",
);
}
#[test]
fn test_shuf_input_range_and_file_not_allowed() {
new_ucmd!()
.args(&["-i", "0-9", "file"])
.fails()
.stderr_contains("The argument '<file>' cannot be used with '--input-range <LO-HI>'");
}
#[test]
fn test_shuf_invalid_input_range_one() {
new_ucmd!()
.args(&["-i", "0"])
.fails()
.stderr_contains("invalid input range");
}
#[test]
fn test_shuf_invalid_input_range_two() {
new_ucmd!()
.args(&["-i", "a-9"])
.fails()
.stderr_contains("invalid input range: 'a'");
}
#[test]
fn test_shuf_invalid_input_range_three() {
new_ucmd!()
.args(&["-i", "0-b"])
.fails()
.stderr_contains("invalid input range: 'b'");
}
#[test]
fn test_shuf_invalid_input_line_count() {
new_ucmd!()
.args(&["-n", "a"])
.fails()
.stderr_contains("invalid line count: 'a'");
}

View file

@ -1,73 +1,702 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (words) ints
use crate::common::util::*;
fn test_helper(file_name: &str, possible_args: &[&str]) {
for args in possible_args {
new_ucmd!()
.arg(format!("{}.txt", file_name))
.args(&args.split_whitespace().collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected", file_name));
new_ucmd!()
.arg(format!("{}.txt", file_name))
.arg("--debug")
.args(&args.split_whitespace().collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected.debug", file_name));
}
}
#[test]
fn test_buffer_sizes() {
let buffer_sizes = ["0", "50K", "50k", "1M", "100M"];
for buffer_size in &buffer_sizes {
TestScenario::new(util_name!())
.ucmd_keepenv()
.arg("-n")
.arg("-S")
.arg(buffer_size)
.arg("ext_sort.txt")
.succeeds()
.stdout_is_fixture("ext_sort.expected");
#[cfg(not(target_pointer_width = "32"))]
{
let buffer_sizes = ["1000G", "10T"];
for buffer_size in &buffer_sizes {
TestScenario::new(util_name!())
.ucmd_keepenv()
.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()
.code_is(2)
.stderr_only(format!(
"sort: invalid --buffer-size argument '{}'",
invalid_buffer_size
));
}
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.arg("-n")
.arg("-S")
.arg("1Y")
.arg("ext_sort.txt")
.fails()
.code_is(2)
.stderr_only("sort: --buffer-size argument '1Y' too large");
#[cfg(target_pointer_width = "32")]
{
let buffer_sizes = ["1000G", "10T"];
for buffer_size in &buffer_sizes {
new_ucmd!()
.arg("-n")
.arg("-S")
.arg(buffer_size)
.arg("ext_sort.txt")
.fails()
.code_is(2)
.stderr_only(format!(
"sort: --buffer-size argument '{}' too large",
buffer_size
));
}
}
}
#[test]
fn test_ext_sort_stable() {
new_ucmd!()
.arg("-n")
.arg("--stable")
.arg("-S")
.arg("0M")
.arg("ext_stable.txt")
.succeeds()
.stdout_only_fixture("ext_stable.expected");
}
#[test]
fn test_ext_sort_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", "--month-sort", "--sort=month"]);
}
#[test]
fn test_version_empty_lines() {
new_ucmd!()
.arg("-V")
.arg("version-empty-lines.txt")
.succeeds()
.stdout_is("\n\n\n\n\n\n\n1.2.3-alpha\n1.2.3-alpha2\n\t\t\t1.12.4\n11.2.3\n");
}
#[test]
fn test_human_numeric_whitespace() {
test_helper(
"human-numeric-whitespace",
&["-h", "--human-numeric-sort", "--sort=human-numeric"],
);
}
// This tests where serde often fails when reading back JSON
// if it finds a null value
#[test]
fn test_ext_sort_as64_bailout() {
new_ucmd!()
.arg("-g")
.arg("-S 5K")
.arg("multiple_decimals_general.txt")
.succeeds()
.stdout_is_fixture("multiple_decimals_general.expected");
}
#[test]
fn test_multiple_decimals_general() {
test_helper(
"multiple_decimals_general",
&["-g", "--general-numeric-sort", "--sort=general-numeric"],
)
}
#[test]
fn test_multiple_decimals_numeric() {
test_helper(
"multiple_decimals_numeric",
&["-n", "--numeric-sort", "--sort=numeric"],
)
}
#[test]
fn test_numeric_with_trailing_invalid_chars() {
test_helper(
"numeric_trailing_chars",
&["-n", "--numeric-sort", "--sort=numeric"],
)
}
#[test]
fn test_check_zero_terminated_failure() {
new_ucmd!()
.arg("-z")
.arg("-c")
.arg("zero-terminated.txt")
.fails()
.stdout_is("sort: zero-terminated.txt:2: disorder: ../../fixtures/du\n");
}
#[test]
fn test_check_zero_terminated_success() {
new_ucmd!()
.arg("-z")
.arg("-c")
.arg("zero-terminated.expected")
.succeeds();
}
#[test]
fn test_random_shuffle_len() {
// check whether output is the same length as the input
const FILE: &str = "default_unsorted_ints.expected";
let (at, _ucmd) = at_and_ucmd!();
let result = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str();
let expected = at.read(FILE);
assert_ne!(result, expected);
assert_eq!(result.len(), expected.len());
}
#[test]
fn test_random_shuffle_contains_all_lines() {
// check whether lines of input are all in output
const FILE: &str = "default_unsorted_ints.expected";
let (at, _ucmd) = at_and_ucmd!();
let result = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str();
let expected = at.read(FILE);
let result_sorted = new_ucmd!().pipe_in(result.clone()).run().stdout_move_str();
assert_ne!(result, expected);
assert_eq!(result_sorted, expected);
}
#[test]
fn test_random_shuffle_two_runs_not_the_same() {
// check to verify that two random shuffles are not equal; this has the
// potential to fail in the very unlikely event that the random order is the same
// as the starting order, or if both random sorts end up having the same order.
const FILE: &str = "default_unsorted_ints.expected";
let (at, _ucmd) = at_and_ucmd!();
let result = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str();
let expected = at.read(FILE);
let unexpected = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str();
assert_ne!(result, expected);
assert_ne!(result, unexpected);
}
#[test]
fn test_random_shuffle_contains_two_runs_not_the_same() {
// check to verify that two random shuffles are not equal; this has the
// potential to fail in the unlikely event that random order is the same
// as the starting order, or if both random sorts end up having the same order.
const FILE: &str = "default_unsorted_ints.expected";
let (at, _ucmd) = at_and_ucmd!();
let result = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str();
let expected = at.read(FILE);
let unexpected = new_ucmd!().arg("-R").arg(FILE).run().stdout_move_str();
assert_ne!(result, expected);
assert_ne!(result, unexpected);
}
#[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]
fn test_dictionary_order2() {
for non_dictionary_order2_param in &["-d"] {
new_ucmd!()
.pipe_in("a👦🏻aa b\naaaa b") // spell-checker:disable-line
.arg(non_dictionary_order2_param) // spell-checker:disable-line
.succeeds()
.stdout_only("a👦🏻aa b\naaaa b\n"); // spell-checker:disable-line
}
}
#[test]
fn test_non_printing_chars() {
for non_printing_chars_param in &["-i"] {
new_ucmd!()
.pipe_in("a👦🏻aa\naaaa") // spell-checker:disable-line
.arg(non_printing_chars_param) // spell-checker:disable-line
.succeeds()
.stdout_only("a👦🏻aa\naaaa\n"); // spell-checker:disable-line
}
}
#[test]
fn test_exponents_positive_general_fixed() {
test_helper("exponents_general", &["-g"]);
}
#[test]
fn test_exponents_positive_numeric() {
test_helper(
"exponents-positive-numeric",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_months_dedup() {
test_helper("months-dedup", &["-Mu"]);
}
#[test]
fn test_mixed_floats_ints_chars_numeric() {
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]
fn test_words_unique() {
test_helper("words_unique", &["-u"]);
}
#[test]
fn test_numeric_unique() {
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]
fn test_mixed_floats_ints_chars_numeric_stable() {
test_helper("mixed_floats_ints_chars_numeric_stable", &["-ns"]);
}
#[test]
fn test_numeric_floats_and_ints2() {
for numeric_sort_param in &["-n", "--numeric-sort"] {
let input = "1.444\n8.013\n1\n-8\n1.04\n-1";
new_ucmd!()
.arg(numeric_sort_param)
.pipe_in(input)
.succeeds()
.stdout_only("-8\n-1\n1\n1.04\n1.444\n8.013\n");
}
}
#[test]
fn test_numeric_floats2() {
for numeric_sort_param in &["-n", "--numeric-sort"] {
let input = "1.444\n8.013\n1.58590\n-8.90880\n1.040000000\n-.05";
new_ucmd!()
.arg(numeric_sort_param)
.pipe_in(input)
.succeeds()
.stdout_only("-8.90880\n-.05\n1.040000000\n1.444\n1.58590\n8.013\n");
}
}
#[test]
fn test_numeric_floats_with_nan2() {
test_helper(
"numeric-floats-with-nan2",
&["-n", "--numeric-sort", "--sort=numeric"],
);
}
#[test]
fn test_human_block_sizes2() {
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)
.pipe_in(input)
.succeeds()
.stdout_only("-8T\n8981K\n0.8M\n909991M\n21G\n");
}
}
#[test]
fn test_human_numeric_zero_stable() {
let input = "0M\n0K\n-0K\n-P\n-0M\n";
new_ucmd!()
.arg("-hs")
.pipe_in(input)
.succeeds()
.stdout_only(input);
}
#[test]
fn test_month_default2() {
for month_sort_param in &["-M", "--month-sort", "--sort=month"] {
let input = "JAn\nMAY\n000may\nJun\nFeb";
new_ucmd!()
.arg(month_sort_param)
.pipe_in(input)
.succeeds()
.stdout_only("000may\nJAn\nFeb\nMAY\nJun\n");
}
}
#[test]
fn test_default_unsorted_ints2() {
let input = "9\n1909888\n000\n1\n2";
new_ucmd!()
.pipe_in(input)
.succeeds()
.stdout_only("000\n1\n1909888\n2\n9\n");
}
#[test]
fn test_numeric_unique_ints2() {
for numeric_unique_sort_param in &["-nu"] {
let input = "9\n9\n8\n1\n";
new_ucmd!()
.arg(numeric_unique_sort_param)
.pipe_in(input)
.succeeds()
.stdout_only("1\n8\n9\n");
}
}
#[test]
fn test_keys_open_ended() {
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]
fn test_keys_multiple_ranges() {
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]
fn test_keys_no_char_match() {
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]
fn test_keys_invalid_field() {
new_ucmd!()
.args(&["-k", "1."])
.fails()
.stderr_only("sort: failed to parse key `1.`: failed to parse character index ``: cannot parse integer from empty string");
}
#[test]
fn test_keys_invalid_field_option() {
new_ucmd!()
.args(&["-k", "1.1x"])
.fails()
.stderr_only("sort: failed to parse key `1.1x`: invalid option: `x`");
}
#[test]
fn test_keys_invalid_field_zero() {
new_ucmd!()
.args(&["-k", "0.1"])
.fails()
.stderr_only("sort: failed to parse key `0.1`: field index can not be 0");
}
#[test]
fn test_keys_invalid_char_zero() {
new_ucmd!()
.args(&["-k", "1.0"])
.fails()
.stderr_only("sort: failed to parse key `1.0`: invalid character index 0 for the start position of a field");
}
#[test]
fn test_keys_with_options() {
let input = "aa 3 cc\ndd 1 ff\ngg 2 cc\n";
for param in &[
&["-k", "2,2n"][..],
&["-k", "2n,2"][..],
&["-k", "2,2", "-n"][..],
] {
new_ucmd!()
.args(param)
.pipe_in(input)
.succeeds()
.stdout_only("dd 1 ff\ngg 2 cc\naa 3 cc\n");
}
}
#[test]
fn test_keys_with_options_blanks_start() {
let input = "aa 3 cc\ndd 1 ff\ngg 2 cc\n";
for param in &[&["-k", "2b,2"][..], &["-k", "2,2", "-b"][..]] {
new_ucmd!()
.args(param)
.pipe_in(input)
.succeeds()
.stdout_only("dd 1 ff\ngg 2 cc\naa 3 cc\n");
}
}
#[test]
fn test_keys_blanks_with_char_idx() {
test_helper("keys_blanks", &["-k 1.2b"])
}
#[test]
fn test_keys_with_options_blanks_end() {
let input = "a b
a b
a b
";
new_ucmd!()
.args(&["-k", "1,2.1b", "-s"])
.pipe_in(input)
.succeeds()
.stdout_only(
"a b
a b
a b
",
);
}
#[test]
fn test_keys_stable() {
let input = "a b
a b
a b
";
new_ucmd!()
.args(&["-k", "1,2.1", "-s"])
.pipe_in(input)
.succeeds()
.stdout_only(
"a b
a b
a b
",
);
}
#[test]
fn test_keys_empty_match() {
let input = "a a a a
aaaa
";
new_ucmd!()
.args(&["-k", "1,1", "-t", "a"])
.pipe_in(input)
.succeeds()
.stdout_only(input);
}
#[test]
fn test_keys_negative_size_match() {
// If the end of a field is before its start, we should not crash.
// Debug output should report "no match for key" at the start position (i.e. the later position).
test_helper("keys_negative_size", &["-k 3,1"]);
}
#[test]
fn test_keys_ignore_flag() {
test_helper("keys_ignore_flag", &["-k 1n -b"])
}
#[test]
fn test_does_not_inherit_key_settings() {
let input = " 1
2
10
";
new_ucmd!()
.args(&["-k", "1b", "-n"])
.pipe_in(input)
.succeeds()
.stdout_only(
" 1
10
2
",
);
}
#[test]
fn test_inherits_key_settings() {
let input = " 1
2
10
";
new_ucmd!()
.args(&["-k", "1", "-n"])
.pipe_in(input)
.succeeds()
.stdout_only(
" 1
2
10
",
);
}
#[test]
fn test_zero_terminated() {
test_helper("zero-terminated", &["-z"]);
}
#[test]
@ -106,6 +735,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!()
@ -133,23 +774,170 @@ fn test_pipe() {
#[test]
fn test_check() {
for diagnose_arg in &["-c", "--check", "--check=diagnose-first"] {
new_ucmd!()
.arg(diagnose_arg)
.arg("check_fail.txt")
.fails()
.stdout_is("sort: check_fail.txt:6: disorder: 5\n");
new_ucmd!()
.arg(diagnose_arg)
.arg("multiple_files.expected")
.succeeds()
.stdout_is("");
}
}
#[test]
fn test_check_silent() {
for silent_arg in &["-C", "--check=silent", "--check=quiet"] {
new_ucmd!()
.arg(silent_arg)
.arg("check_fail.txt")
.fails()
.stdout_is("");
}
}
#[test]
fn test_dictionary_and_nonprinting_conflicts() {
let conflicting_args = ["n", "h", "g", "M"];
for restricted_arg in &["d", "i"] {
for conflicting_arg in &conflicting_args {
new_ucmd!()
.arg(&format!("-{}{}", restricted_arg, conflicting_arg))
.fails();
}
for conflicting_arg in &conflicting_args {
new_ucmd!()
.args(&[
format!("-{}", restricted_arg).as_str(),
"-k",
&format!("1,1{}", conflicting_arg),
])
.succeeds();
}
for conflicting_arg in &conflicting_args {
new_ucmd!()
.args(&["-k", &format!("1{},1{}", restricted_arg, conflicting_arg)])
.fails();
}
}
}
#[test]
fn test_trailing_separator() {
new_ucmd!()
.arg("-c")
.arg("check_fail.txt")
.args(&["-t", "x", "-k", "1,1"])
.pipe_in("aax\naaa\n")
.succeeds()
.stdout_is("aax\naaa\n");
}
#[test]
fn test_nonexistent_file() {
new_ucmd!()
.arg("nonexistent.txt")
.fails()
.stdout_is("sort: disorder in line 4\n");
new_ucmd!()
.arg("-c")
.arg("multiple_files.expected")
.succeeds()
.stdout_is("");
.status_code(2)
.stderr_only(
#[cfg(not(windows))]
"sort: cannot read: \"nonexistent.txt\": No such file or directory (os error 2)",
#[cfg(windows)]
"sort: cannot read: \"nonexistent.txt\": The system cannot find the file specified. (os error 2)",
);
}
fn test_helper(file_name: &str, args: &str) {
new_ucmd!()
.arg(args)
.arg(format!("{}{}", file_name, ".txt"))
.succeeds()
.stdout_is_fixture(format!("{}{}", file_name, ".expected"));
#[test]
fn test_blanks() {
test_helper("blanks", &["-b", "--ignore-leading-blanks"]);
}
#[test]
fn sort_multiple() {
new_ucmd!()
.args(&["no_trailing_newline1.txt", "no_trailing_newline2.txt"])
.succeeds()
.stdout_is("a\nb\nb\n");
}
#[test]
fn sort_empty_chunk() {
new_ucmd!()
.args(&["-S", "40b"])
.pipe_in("a\na\n")
.succeeds()
.stdout_is("a\na\n");
}
#[test]
#[cfg(target_os = "linux")]
fn test_compress() {
new_ucmd!()
.args(&[
"ext_sort.txt",
"-n",
"--compress-program",
"gzip",
"-S",
"10",
])
.succeeds()
.stdout_only_fixture("ext_sort.expected");
}
#[test]
fn test_compress_fail() {
TestScenario::new(util_name!())
.ucmd_keepenv()
.args(&[
"ext_sort.txt",
"-n",
"--compress-program",
"nonexistent-program",
"-S",
"10",
])
.fails()
.stderr_only("sort: couldn't execute compress program: errno 2");
}
#[test]
fn test_merge_batches() {
TestScenario::new(util_name!())
.ucmd_keepenv()
.args(&["ext_sort.txt", "-n", "-S", "150b"])
.succeeds()
.stdout_only_fixture("ext_sort.expected");
}
#[test]
fn test_merge_batch_size() {
TestScenario::new(util_name!())
.ucmd_keepenv()
.arg("--batch-size=2")
.arg("-m")
.arg("--unique")
.arg("merge_ints_interleaved_1.txt")
.arg("merge_ints_interleaved_2.txt")
.arg("merge_ints_interleaved_3.txt")
.arg("merge_ints_interleaved_3.txt")
.arg("merge_ints_interleaved_2.txt")
.arg("merge_ints_interleaved_1.txt")
.succeeds()
.stdout_only_fixture("merge_ints_interleaved.expected");
}
#[test]
fn test_sigpipe_panic() {
let mut cmd = new_ucmd!();
let mut child = cmd.args(&["ext_sort.txt"]).run_no_wait();
// Dropping the stdout should not lead to an error.
// The "Broken pipe" error should be silently ignored.
drop(child.stdout.take());
assert_eq!(
String::from_utf8(child.wait_with_output().unwrap().stderr),
Ok(String::new())
);
}

View file

@ -1,14 +1,23 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
extern crate rand;
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 +67,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 +90,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_all(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 +123,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 +142,90 @@ 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();
#[allow(clippy::needless_range_loop)]
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,21 +239,22 @@ 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)
// (assert that command succeeded)
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
assert!(
glob.collate().iter().find(|&&c| {
// is not i
c != ('i' as u8)
c != (b'i')
// is not newline
&& c != ('\n' as u8)
&& c != (b'\n')
}) == None
);
}
@ -209,16 +267,17 @@ 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);
let env_var_value = "somevalue";
let env_var_value = "some-value";
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());
assert!(env::var("FILE").unwrap_or("var was unset".to_owned()) == env_var_value);
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
assert_eq!(glob.collate(), at.read_bytes(name));
assert!(env::var("FILE").unwrap_or_else(|_| "var was unset".to_owned()) == env_var_value);
}
#[test]
@ -231,3 +290,53 @@ fn test_filter_command_fails() {
ucmd.args(&["--filter=/a/path/that/totally/does/not/exist", name])
.fails();
}
#[test]
fn test_split_lines_number() {
// Test if stdout/stderr for '--lines' option is correct
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("file");
scene
.ucmd()
.args(&["--lines", "2", "file"])
.succeeds()
.no_stderr()
.no_stdout();
scene
.ucmd()
.args(&["--lines", "2fb", "file"])
.fails()
.code_is(1)
.stderr_only("split: invalid number of lines: '2fb'");
}
#[test]
fn test_split_invalid_bytes_size() {
new_ucmd!()
.args(&["-b", "1024R"])
.fails()
.code_is(1)
.stderr_only("split: invalid number of bytes: '1024R'");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["-b", "1Y"])
.fails()
.code_is(1)
.stderr_only("split: invalid number of bytes: '1Y': Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["-b", size])
.fails()
.code_is(1)
.stderr_only(format!(
"split: invalid number of bytes: '{}': Value too large for defined data type",
size
));
}
}
}

View file

@ -5,84 +5,21 @@ 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() {
fn test_scanners() {
assert_eq!(Some((-5, 2)), "-5zxc".scan_num::<i32>());
assert_eq!(Some((51, 2)), "51zxc".scan_num::<u32>());
assert_eq!(Some((192, 4)), "+192zxc".scan_num::<i32>());
assert_eq!(None, "z192zxc".scan_num::<i32>());
assert_eq!(Some(('a', 3)), "141zxc".scan_char(8));
assert_eq!(Some(('\n', 2)), "12qzxc".scan_char(8));
assert_eq!(Some(('\r', 1)), "dqzxc".scan_char(16));
assert_eq!(None, "z2qzxc".scan_char(8));
assert_eq!(Some(('\n', 2)), "12qzxc".scan_char(8)); // spell-checker:disable-line
assert_eq!(Some(('\r', 1)), "dqzxc".scan_char(16)); // spell-checker:disable-line
assert_eq!(None, "z2qzxc".scan_char(8)); // spell-checker:disable-line
}
#[test]
fn test_groupnum() {
fn test_group_num() {
assert_eq!("12,379,821,234", group_num("12379821234"));
assert_eq!("21,234", group_num("21234"));
assert_eq!("821,234", group_num("821234"));
@ -159,14 +96,14 @@ fn test_invalid_option() {
new_ucmd!().arg("-w").arg("-q").arg("/").fails();
}
#[cfg(target_os = "linux")]
const NORMAL_FMTSTR: &'static str =
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
const NORMAL_FORMAT_STR: &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")]
const DEV_FMTSTR: &'static str =
#[cfg(any(target_os = "linux"))]
const DEV_FORMAT_STR: &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")]
const FS_FMTSTR: &'static str = "%b %c %i %l %n %s %S %t %T"; // avoid "%a %d %f" which can cause test failure due to race conditions
const FS_FORMAT_STR: &str = "%b %c %i %l %n %s %S %t %T"; // avoid "%a %d %f" which can cause test failure due to race conditions
#[test]
#[cfg(target_os = "linux")]
@ -181,26 +118,33 @@ fn test_terse_fs_format() {
#[test]
#[cfg(target_os = "linux")]
fn test_fs_format() {
let args = ["-f", "-c", FS_FMTSTR, "/dev/shm"];
let args = ["-f", "-c", FS_FORMAT_STR, "/dev/shm"];
new_ucmd!()
.args(&args)
.run()
.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
let args = ["-t", "/"];
let actual = new_ucmd!().args(&args).run().stdout;
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
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,11 +156,11 @@ 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 actual = new_ucmd!().args(&args).run().stdout;
let args = ["-c", "%w", "/bin"];
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
@ -236,11 +180,11 @@ 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 actual = new_ucmd!().args(&args).run().stdout;
let args = ["-c", "%W", "/bin"];
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
@ -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_FORMAT_STR, "/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 &[
"/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_FORMAT_STR, file];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
// -L, --dereference follow links
let args = ["-L", "-c", NORMAL_FORMAT_STR, 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_FORMAT_STR,
#[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,
NORMAL_FORMAT_STR,
"/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,23 @@ 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!())
.env("LANGUAGE", "C")
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
// note: clippy::needless_borrow *false positive*
#[allow(clippy::needless_borrow)]
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LC_ALL", "C")
.args(args)
.run()
.stdout
.succeeds()
.stdout_move_str()
}

View file

@ -1,13 +1,74 @@
#[cfg(not(target_os = "windows"))]
use crate::common::util::*;
#[cfg(not(target_os = "windows"))]
#[test]
fn test_stdbuf_unbuffered_stdout() {
if cfg!(target_os = "linux") {
// This is a basic smoke test
// This is a basic smoke test
new_ucmd!()
.args(&["-o0", "head"])
.pipe_in("The quick brown fox jumps over the lazy dog.")
.run()
.stdout_is("The quick brown fox jumps over the lazy dog.");
}
#[cfg(not(target_os = "windows"))]
#[test]
fn test_stdbuf_line_buffered_stdout() {
new_ucmd!()
.args(&["-oL", "head"])
.pipe_in("The quick brown fox jumps over the lazy dog.")
.run()
.stdout_is("The quick brown fox jumps over the lazy dog.");
}
#[cfg(not(target_os = "windows"))]
#[test]
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",
);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn test_stdbuf_trailing_var_arg() {
new_ucmd!()
.args(&["-i", "1024", "tail", "-1"])
.pipe_in("The quick brown fox\njumps over the lazy dog.")
.run()
.stdout_is("jumps over the lazy dog.");
}
#[cfg(not(target_os = "windows"))]
#[test]
fn test_stdbuf_line_buffering_stdin_fails() {
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"))]
#[test]
fn test_stdbuf_invalid_mode_fails() {
let options = ["--input", "--output", "--error"];
for option in &options {
new_ucmd!()
.args(&["-o0", "head"])
.pipe_in("The quick brown fox jumps over the lazy dog.")
.run()
.stdout_is("The quick brown fox jumps over the lazy dog.");
.args(&[*option, "1024R", "head"])
.fails()
.code_is(125)
.stderr_only("stdbuf: invalid mode '1024R'");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&[*option, "1Y", "head"])
.fails()
.code_is(125)
.stderr_contains("stdbuf: invalid mode '1Y': Value too large for defined data type");
}
}

View file

@ -52,3 +52,21 @@ fn test_sysv_stdin() {
.succeeds()
.stdout_only_fixture("sysv_stdin.expected");
}
#[test]
fn test_invalid_file() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
ucmd.arg("a").fails().stderr_is("sum: 'a' Is a directory");
}
#[test]
fn test_invalid_metadata() {
let (_, mut ucmd) = at_and_ucmd!();
ucmd.arg("b")
.fails()
.stderr_is("sum: 'b' No such file or directory");
}

View file

@ -5,8 +5,7 @@ use tempfile::tempdir;
#[test]
fn test_sync_default() {
let result = new_ucmd!().run();
assert!(result.success);
new_ucmd!().succeeds();
}
#[test]
@ -18,8 +17,10 @@ fn test_sync_incorrect_arg() {
fn test_sync_fs() {
let temporary_directory = tempdir().unwrap();
let temporary_path = fs::canonicalize(temporary_directory.path()).unwrap();
let result = new_ucmd!().arg("--file-system").arg(&temporary_path).run();
assert!(result.success);
new_ucmd!()
.arg("--file-system")
.arg(&temporary_path)
.succeeds();
}
#[test]
@ -27,12 +28,14 @@ fn test_sync_data() {
// Todo add a second arg
let temporary_directory = tempdir().unwrap();
let temporary_path = fs::canonicalize(temporary_directory.path()).unwrap();
let result = new_ucmd!().arg("--data").arg(&temporary_path).run();
assert!(result.success);
new_ucmd!().arg("--data").arg(&temporary_path).succeeds();
}
#[test]
fn test_sync_no_existing_files() {
let result = new_ucmd!().arg("--data").arg("do-no-exist").fails();
assert!(result.stderr.contains("error: cannot stat"));
new_ucmd!()
.arg("--data")
.arg("do-no-exist")
.fails()
.stderr_contains("cannot stat");
}

View file

@ -49,3 +49,22 @@ fn test_single_non_newline_separator_before() {
.run()
.stdout_is_fixture("delimited_primes_before.expected");
}
#[test]
fn test_invalid_input() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene
.ucmd()
.arg("b")
.fails()
.stderr_contains("failed to open 'b' for reading: No such file or directory");
at.mkdir("a");
scene
.ucmd()
.arg("a")
.fails()
.stderr_contains("dir: read error: Invalid argument");
}

View file

@ -1,13 +1,19 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (ToDO) abcdefghijklmnopqrstuvwxyz efghijklmnopqrstuvwxyz vwxyz emptyfile
extern crate tail;
use self::tail::parse_size;
use crate::common::util::*;
use std::char::from_digit;
use std::io::Write;
static FOOBAR_TXT: &'static str = "foobar.txt";
static FOOBAR_2_TXT: &'static str = "foobar2.txt";
static FOOBAR_WITH_NULL_TXT: &'static str = "foobar_with_null.txt";
static FOOBAR_TXT: &str = "foobar.txt";
static FOOBAR_2_TXT: &str = "foobar2.txt";
static FOOBAR_WITH_NULL_TXT: &str = "foobar_with_null.txt";
#[test]
fn test_stdin_default() {
@ -78,7 +84,7 @@ fn test_follow_multiple() {
at.append(FOOBAR_2_TXT, first_append);
assert_eq!(read_size(&mut child, first_append.len()), first_append);
let second_append = "doce\ntrece\n";
let second_append = "twenty\nthirty\n";
let expected = at.read("foobar_follow_multiple_appended.expected");
at.append(FOOBAR_TXT, second_append);
assert_eq!(read_size(&mut child, expected.len()), expected);
@ -95,7 +101,7 @@ fn test_follow_stdin() {
.stdout_is_fixture("follow_stdin.expected");
}
// FixME: test PASSES for usual windows builds, but fails for coverage testing builds (likely related to the specific RUSTFLAGS '-Zpanic_abort_tests -Cpanic=abort') This test also breaks tty settings under bash requiring a 'stty sane' or reset.
// FixME: test PASSES for usual windows builds, but fails for coverage testing builds (likely related to the specific RUSTFLAGS '-Zpanic_abort_tests -Cpanic=abort') This test also breaks tty settings under bash requiring a 'stty sane' or reset. // spell-checker:disable-line
#[cfg(disable_until_fixed)]
#[test]
fn test_follow_with_pid() {
@ -130,7 +136,7 @@ fn test_follow_with_pid() {
at.append(FOOBAR_2_TXT, first_append);
assert_eq!(read_size(&mut child, first_append.len()), first_append);
let second_append = "doce\ntrece\n";
let second_append = "twenty\nthirty\n";
let expected = at.read("foobar_follow_multiple_appended.expected");
at.append(FOOBAR_TXT, second_append);
assert_eq!(read_size(&mut child, expected.len()), expected);
@ -153,8 +159,8 @@ fn test_follow_with_pid() {
#[test]
fn test_single_big_args() {
const FILE: &'static str = "single_big_args.txt";
const EXPECTED_FILE: &'static str = "single_big_args_expected.txt";
const FILE: &str = "single_big_args.txt";
const EXPECTED_FILE: &str = "single_big_args_expected.txt";
const LINES: usize = 1_000_000;
const N_ARG: usize = 100_000;
@ -162,13 +168,13 @@ fn test_single_big_args() {
let mut big_input = at.make_file(FILE);
for i in 0..LINES {
write!(&mut big_input, "Line {}\n", i).expect("Could not write to FILE");
writeln!(&mut big_input, "Line {}", i).expect("Could not write to FILE");
}
big_input.flush().expect("Could not flush FILE");
let mut big_expected = at.make_file(EXPECTED_FILE);
for i in (LINES - N_ARG)..LINES {
write!(&mut big_expected, "Line {}\n", i).expect("Could not write to EXPECTED_FILE");
writeln!(&mut big_expected, "Line {}", i).expect("Could not write to EXPECTED_FILE");
}
big_expected.flush().expect("Could not flush EXPECTED_FILE");
@ -201,8 +207,8 @@ fn test_bytes_stdin() {
#[test]
fn test_bytes_big() {
const FILE: &'static str = "test_bytes_big.txt";
const EXPECTED_FILE: &'static str = "test_bytes_big_expected.txt";
const FILE: &str = "test_bytes_big.txt";
const EXPECTED_FILE: &str = "test_bytes_big_expected.txt";
const BYTES: usize = 1_000_000;
const N_ARG: usize = 100_000;
@ -226,8 +232,8 @@ fn test_bytes_big() {
.arg(FILE)
.arg("-c")
.arg(format!("{}", N_ARG))
.run()
.stdout;
.succeeds()
.stdout_move_str();
let expected = at.read(EXPECTED_FILE);
assert_eq!(result.len(), expected.len());
@ -236,45 +242,10 @@ fn test_bytes_big() {
}
}
#[test]
fn test_parse_size() {
// No suffix.
assert_eq!(Ok(1234), parse_size("1234"));
// kB is 1000
assert_eq!(Ok(9 * 1000), parse_size("9kB"));
// K is 1024
assert_eq!(Ok(2 * 1024), parse_size("2K"));
let suffixes = [
('M', 2u32),
('G', 3u32),
('T', 4u32),
('P', 5u32),
('E', 6u32),
];
for &(c, exp) in &suffixes {
let s = format!("2{}B", c);
assert_eq!(Ok(2 * (1000 as u64).pow(exp)), parse_size(&s));
let s = format!("2{}", c);
assert_eq!(Ok(2 * (1024 as u64).pow(exp)), parse_size(&s));
}
// Sizes that are too big.
assert!(parse_size("1Z").is_err());
assert!(parse_size("1Y").is_err());
// Bad number
assert!(parse_size("328hdsf3290").is_err());
}
#[test]
fn test_lines_with_size_suffix() {
const FILE: &'static str = "test_lines_with_size_suffix.txt";
const EXPECTED_FILE: &'static str = "test_lines_with_size_suffix_expected.txt";
const FILE: &str = "test_lines_with_size_suffix.txt";
const EXPECTED_FILE: &str = "test_lines_with_size_suffix_expected.txt";
const LINES: usize = 3_000;
const N_ARG: usize = 2 * 1024;
@ -320,12 +291,128 @@ fn test_multiple_input_files_with_suppressed_headers() {
#[test]
fn test_multiple_input_quiet_flag_overrides_verbose_flag_for_suppressing_headers() {
// TODO: actually the later one should win, i.e. -qv should lead to headers being printed, -vq to them being suppressed
new_ucmd!()
.arg(FOOBAR_TXT)
.arg(FOOBAR_2_TXT)
.arg("-q")
.arg("-v")
.arg("-q")
.run()
.stdout_is_fixture("foobar_multiple_quiet.expected");
}
#[test]
fn test_negative_indexing() {
let positive_lines_index = new_ucmd!().arg("-n").arg("5").arg(FOOBAR_TXT).run();
let negative_lines_index = new_ucmd!().arg("-n").arg("-5").arg(FOOBAR_TXT).run();
let positive_bytes_index = new_ucmd!().arg("-c").arg("20").arg(FOOBAR_TXT).run();
let negative_bytes_index = new_ucmd!().arg("-c").arg("-20").arg(FOOBAR_TXT).run();
assert_eq!(positive_lines_index.stdout(), negative_lines_index.stdout());
assert_eq!(positive_bytes_index.stdout(), negative_bytes_index.stdout());
}
#[test]
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");
}
#[test]
fn test_tail_invalid_num() {
new_ucmd!()
.args(&["-c", "1024R", "emptyfile.txt"])
.fails()
.stderr_is("tail: invalid number of bytes: '1024R'");
new_ucmd!()
.args(&["-n", "1024R", "emptyfile.txt"])
.fails()
.stderr_is("tail: invalid number of lines: '1024R'");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["-c", "1Y", "emptyfile.txt"])
.fails()
.stderr_is("tail: invalid number of bytes: '1Y': Value too large for defined data type");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["-n", "1Y", "emptyfile.txt"])
.fails()
.stderr_is("tail: invalid number of lines: '1Y': Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["-c", size])
.fails()
.code_is(1)
.stderr_only(format!(
"tail: invalid number of bytes: '{}': Value too large for defined data type",
size
));
}
}
}
#[test]
fn test_tail_num_with_undocumented_sign_bytes() {
// tail: '-' is not documented (8.32 man pages)
// head: '+' is not documented (8.32 man pages)
const ALPHABET: &str = "abcdefghijklmnopqrstuvwxyz";
new_ucmd!()
.args(&["-c", "5"])
.pipe_in(ALPHABET)
.succeeds()
.stdout_is("vwxyz");
new_ucmd!()
.args(&["-c", "-5"])
.pipe_in(ALPHABET)
.succeeds()
.stdout_is("vwxyz");
new_ucmd!()
.args(&["-c", "+5"])
.pipe_in(ALPHABET)
.succeeds()
.stdout_is("efghijklmnopqrstuvwxyz");
}

View file

@ -1 +1,104 @@
// ToDO: add tests
use crate::common::util::*;
// tests for basic tee functionality.
// inspired by:
// https://github.com/coreutils/coreutils/tests/misc/tee.sh
#[test]
fn test_tee_processing_multiple_operands() {
// POSIX says: "Processing of at least 13 file operands shall be supported."
let content = "tee_sample_content";
for &n in [1, 2, 12, 13].iter() {
let files = (1..=n).map(|x| x.to_string()).collect::<Vec<_>>();
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&files)
.pipe_in(content)
.succeeds()
.stdout_is(content);
for file in files.iter() {
assert!(at.file_exists(file));
assert_eq!(at.read(file), content);
}
}
}
#[test]
fn test_tee_treat_minus_as_filename() {
// Ensure tee treats '-' as the name of a file, as mandated by POSIX.
let (at, mut ucmd) = at_and_ucmd!();
let content = "tee_sample_content";
let file = "-";
ucmd.arg("-").pipe_in(content).succeeds().stdout_is(content);
assert!(at.file_exists(file));
assert_eq!(at.read(file), content);
}
#[test]
fn test_tee_append() {
let (at, mut ucmd) = at_and_ucmd!();
let content = "tee_sample_content";
let file = "tee_out";
at.touch(file);
at.write(file, content);
assert_eq!(at.read(file), content);
ucmd.arg("-a")
.arg(file)
.pipe_in(content)
.succeeds()
.stdout_is(content);
assert!(at.file_exists(file));
assert_eq!(at.read(file), content.repeat(2));
}
#[test]
#[cfg(target_os = "linux")]
fn test_tee_no_more_writeable_1() {
// equals to 'tee /dev/full out2 <multi_read' call
let (at, mut ucmd) = at_and_ucmd!();
let content = (1..=10)
.map(|x| format!("{}\n", x.to_string()))
.collect::<String>();
let file_out = "tee_file_out";
ucmd.arg("/dev/full")
.arg(file_out)
.pipe_in(&content[..])
.fails()
.stdout_contains(&content)
.stderr_contains(&"No space left on device");
assert_eq!(at.read(file_out), content);
}
#[test]
#[cfg(target_os = "linux")]
fn test_tee_no_more_writeable_2() {
// should be equals to 'tee out1 out2 >/dev/full <multi_read' call
// but currently there is no way to redirect stdout to /dev/full
// so this test is disabled
let (_at, mut ucmd) = at_and_ucmd!();
let _content = (1..=10)
.map(|x| format!("{}\n", x.to_string()))
.collect::<String>();
let file_out_a = "tee_file_out_a";
let file_out_b = "tee_file_out_b";
let _result = ucmd
.arg(file_out_a)
.arg(file_out_b)
.pipe_in("/dev/full")
.succeeds(); // TODO: expected to succeed currently; change to fails() when required
// TODO: comment in after https://github.com/uutils/coreutils/issues/1805 is fixed
// assert_eq!(at.read(file_out_a), content);
// assert_eq!(at.read(file_out_b), content);
// assert!(result.stderr.contains("No space left on device"));
}

View file

@ -2,26 +2,719 @@
// This file is part of the uutils coreutils package.
//
// (c) mahkoh (ju.orth [at] gmail [dot] com)
// (c) Daniel Rocco <drocco@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
// spell-checker:ignore (words) egid euid pseudofloat
use crate::common::util::*;
#[test]
fn test_op_prec_and_or_1() {
fn test_empty_test_equivalent_to_false() {
new_ucmd!().run().status_code(1);
}
#[test]
fn test_empty_string_is_false() {
new_ucmd!().arg("").run().status_code(1);
}
#[test]
fn test_solo_not() {
new_ucmd!().arg("!").succeeds();
}
#[test]
fn test_solo_and_or_or_is_a_literal() {
// /bin/test '' -a '' => 1; so test(1) must interpret `-a` by itself as
// a literal string
new_ucmd!().arg("-a").succeeds();
new_ucmd!().arg("-o").succeeds();
}
#[test]
fn test_double_not_is_false() {
new_ucmd!().args(&["!", "!"]).run().status_code(1);
}
#[test]
fn test_and_not_is_false() {
new_ucmd!().args(&["-a", "!"]).run().status_code(1);
}
#[test]
fn test_not_and_is_false() {
// `-a` is a literal here & has nonzero length
new_ucmd!().args(&["!", "-a"]).run().status_code(1);
}
#[test]
fn test_not_and_not_succeeds() {
new_ucmd!().args(&["!", "-a", "!"]).succeeds();
}
#[test]
fn test_simple_or() {
new_ucmd!().args(&["foo", "-o", ""]).succeeds();
}
#[test]
fn test_negated_or() {
new_ucmd!()
.args(&["!", "foo", "-o", "bar"])
.run()
.status_code(1);
new_ucmd!().args(&["foo", "-o", "!", "bar"]).succeeds();
new_ucmd!()
.args(&["!", "foo", "-o", "!", "bar"])
.run()
.status_code(1);
}
#[test]
fn test_string_length_of_nothing() {
// odd but matches GNU, which must interpret -n as a literal here
new_ucmd!().arg("-n").succeeds();
}
#[test]
fn test_string_length_of_empty() {
new_ucmd!().args(&["-n", ""]).run().status_code(1);
// STRING equivalent to -n STRING
new_ucmd!().arg("").run().status_code(1);
}
#[test]
fn test_nothing_is_empty() {
// -z is a literal here and has nonzero length
new_ucmd!().arg("-z").succeeds();
}
#[test]
fn test_zero_len_of_empty() {
new_ucmd!().args(&["-z", ""]).succeeds();
}
#[test]
fn test_solo_parenthesis_is_literal() {
let scenario = TestScenario::new(util_name!());
let tests = [["("], [")"]];
for test in &tests {
scenario.ucmd().args(&test[..]).succeeds();
}
}
#[test]
fn test_solo_empty_parenthetical_is_error() {
new_ucmd!().args(&["(", ")"]).run().status_code(2);
}
#[test]
fn test_zero_len_equals_zero_len() {
new_ucmd!().args(&["", "=", ""]).succeeds();
}
#[test]
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!());
let tests = [
["foo", "!=", "bar"],
["contained\nnewline", "=", "contained\nnewline"],
["(", "=", "("],
["(", "!=", ")"],
["!", "=", "!"],
["=", "=", "="],
];
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]
#[ignore = "fixme: error reporting"]
fn test_dangling_string_comparison_is_error() {
new_ucmd!()
.args(&["missing_something", "="])
.run()
.status_code(2)
.stderr_is("test: missing argument after '='");
}
#[test]
fn test_string_operator_is_literal_after_bang() {
let scenario = TestScenario::new(util_name!());
let tests = [
["!", "="],
["!", "!="],
["!", "-eq"],
["!", "-ne"],
["!", "-lt"],
["!", "-le"],
["!", "-gt"],
["!", "-ge"],
["!", "-ef"],
["!", "-nt"],
["!", "-ot"],
];
for test in &tests {
scenario.ucmd().args(&test[..]).run().status_code(1);
}
}
#[test]
fn test_a_bunch_of_not() {
new_ucmd!()
.args(&["!", "", "!=", "", "-a", "!", "", "!=", ""])
.succeeds();
}
#[test]
fn test_pseudofloat_equal() {
new_ucmd!().args(&["123.45", "=", "123.45"]).succeeds();
}
#[test]
fn test_pseudofloat_not_equal() {
new_ucmd!().args(&["123.45", "!=", "123.450"]).succeeds();
}
#[test]
fn test_negative_arg_is_a_string() {
new_ucmd!().arg("-12345").succeeds();
new_ucmd!().arg("--qwert").succeeds(); // spell-checker:disable-line
}
#[test]
fn test_some_int_compares() {
let scenario = TestScenario::new(util_name!());
let tests = [
["0", "-eq", "0"],
["0", "-ne", "1"],
["421", "-lt", "3720"],
["0", "-le", "0"],
["11", "-gt", "10"],
["1024", "-ge", "512"],
["9223372036854775806", "-le", "9223372036854775807"],
];
for test in &tests {
scenario.ucmd().args(&test[..]).succeeds();
}
}
#[test]
#[ignore = "fixme: evaluation error (code 1); GNU returns 0"]
fn test_values_greater_than_i64_allowed() {
new_ucmd!()
.args(&["9223372036854775808", "-gt", "0"])
.succeeds();
}
#[test]
fn test_negative_int_compare() {
let scenario = TestScenario::new(util_name!());
let tests = [
["-1", "-eq", "-1"],
["-1", "-ne", "-2"],
["-3720", "-lt", "-421"],
["-10", "-le", "-10"],
["-21", "-gt", "-22"],
["-128", "-ge", "-256"],
["-9223372036854775808", "-le", "-9223372036854775807"],
];
for test in &tests {
scenario.ucmd().args(&test[..]).succeeds();
}
}
#[test]
fn test_float_inequality_is_error() {
new_ucmd!()
.args(&["123.45", "-ge", "6"])
.run()
.status_code(2)
.stderr_is("test: invalid integer '123.45'");
}
#[test]
#[cfg(not(windows))]
fn test_invalid_utf8_integer_compare() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
let source = [0x66, 0x6f, 0x80, 0x6f];
let arg = OsStr::from_bytes(&source[..]);
let mut cmd = new_ucmd!();
cmd.arg("123").arg("-ne");
cmd.raw.arg(arg);
cmd.run()
.status_code(2)
.stderr_is("test: invalid integer 'fo<66>o'");
let mut cmd = new_ucmd!();
cmd.raw.arg(arg);
cmd.arg("-eq").arg("456");
cmd.run()
.status_code(2)
.stderr_is("test: invalid integer 'fo<66>o'");
}
#[test]
#[ignore = "fixme: parse/evaluation error (code 2); GNU returns 1"]
fn test_file_is_itself() {
new_ucmd!()
.args(&["regular_file", "-ef", "regular_file"])
.succeeds();
}
#[test]
#[ignore = "fixme: parse/evaluation error (code 2); GNU returns 1"]
fn test_file_is_newer_than_and_older_than_itself() {
// odd but matches GNU
new_ucmd!()
.args(&["regular_file", "-nt", "regular_file"])
.run()
.status_code(1);
new_ucmd!()
.args(&["regular_file", "-ot", "regular_file"])
.run()
.status_code(1);
}
#[test]
#[ignore = "todo: implement these"]
fn test_newer_file() {
let scenario = TestScenario::new(util_name!());
scenario.cmd("touch").arg("newer_file").succeeds();
scenario
.cmd("touch")
.args(&["-m", "-d", "last Thursday", "regular_file"])
.succeeds();
scenario
.ucmd()
.args(&["newer_file", "-nt", "regular_file"])
.succeeds();
scenario
.ucmd()
.args(&["regular_file", "-ot", "newer_file"])
.succeeds();
}
#[test]
fn test_file_exists() {
new_ucmd!().args(&["-e", "regular_file"]).succeeds();
}
#[test]
fn test_nonexistent_file_does_not_exist() {
new_ucmd!()
.args(&["-e", "nonexistent_file"])
.run()
.status_code(1);
}
#[test]
fn test_nonexistent_file_is_not_regular() {
new_ucmd!()
.args(&["-f", "nonexistent_file"])
.run()
.status_code(1);
}
#[test]
fn test_file_exists_and_is_regular() {
new_ucmd!().args(&["-f", "regular_file"]).succeeds();
}
#[test]
#[cfg(not(windows))] // FIXME: implement on Windows
fn test_file_is_readable() {
new_ucmd!().args(&["-r", "regular_file"]).succeeds();
}
#[test]
#[cfg(not(windows))] // FIXME: implement on Windows
fn test_file_is_not_readable() {
let scenario = TestScenario::new(util_name!());
let mut ucmd = scenario.ucmd();
let mut chmod = scenario.cmd("chmod");
scenario.fixtures.touch("crypto_file");
chmod.args(&["u-r", "crypto_file"]).succeeds();
ucmd.args(&["!", "-r", "crypto_file"]).succeeds();
}
#[test]
#[cfg(not(windows))] // FIXME: implement on Windows
fn test_file_is_writable() {
new_ucmd!().args(&["-w", "regular_file"]).succeeds();
}
#[test]
#[cfg(not(windows))] // FIXME: implement on Windows
fn test_file_is_not_writable() {
let scenario = TestScenario::new(util_name!());
let mut ucmd = scenario.ucmd();
let mut chmod = scenario.cmd("chmod");
scenario.fixtures.touch("immutable_file");
chmod.args(&["u-w", "immutable_file"]).succeeds();
ucmd.args(&["!", "-w", "immutable_file"]).succeeds();
}
#[test]
fn test_file_is_not_executable() {
new_ucmd!().args(&["!", "-x", "regular_file"]).succeeds();
}
#[test]
#[cfg(not(windows))] // FIXME: implement on Windows
fn test_file_is_executable() {
let scenario = TestScenario::new(util_name!());
let mut chmod = scenario.cmd("chmod");
chmod.args(&["u+x", "regular_file"]).succeeds();
scenario.ucmd().args(&["-x", "regular_file"]).succeeds();
}
#[test]
fn test_is_not_empty() {
new_ucmd!().args(&["-s", "non_empty_file"]).succeeds();
}
#[test]
fn test_nonexistent_file_size_test_is_false() {
new_ucmd!()
.args(&["-s", "nonexistent_file"])
.run()
.status_code(1);
}
#[test]
fn test_not_is_not_empty() {
new_ucmd!().args(&["!", "-s", "regular_file"]).succeeds();
}
#[test]
#[cfg(not(windows))]
fn test_symlink_is_symlink() {
let scenario = TestScenario::new(util_name!());
let at = &scenario.fixtures;
at.symlink_file("regular_file", "symlink");
// FIXME: implement on Windows
scenario.ucmd().args(&["-h", "symlink"]).succeeds();
scenario.ucmd().args(&["-L", "symlink"]).succeeds();
}
#[test]
fn test_file_is_not_symlink() {
let scenario = TestScenario::new(util_name!());
scenario
.ucmd()
.args(&["!", "-h", "regular_file"])
.succeeds();
scenario
.ucmd()
.args(&["!", "-L", "regular_file"])
.succeeds();
}
#[test]
fn test_nonexistent_file_is_not_symlink() {
let scenario = TestScenario::new(util_name!());
scenario
.ucmd()
.args(&["!", "-h", "nonexistent_file"])
.succeeds();
scenario
.ucmd()
.args(&["!", "-L", "nonexistent_file"])
.succeeds();
}
#[test]
#[cfg(not(windows))] // Windows has no concept of sticky bit
fn test_file_is_sticky() {
let scenario = TestScenario::new(util_name!());
let mut ucmd = scenario.ucmd();
let mut chmod = scenario.cmd("chmod");
scenario.fixtures.touch("sticky_file");
chmod.args(&["+t", "sticky_file"]).succeeds();
ucmd.args(&["-k", "sticky_file"]).succeeds();
}
#[test]
fn test_file_is_not_sticky() {
new_ucmd!()
.args(&["-k", "regular_file"])
.run()
.status_code(1);
}
#[test]
#[cfg(not(windows))]
fn test_file_owned_by_euid() {
new_ucmd!().args(&["-O", "regular_file"]).succeeds();
}
#[test]
#[cfg(not(windows))]
fn test_nonexistent_file_not_owned_by_euid() {
new_ucmd!()
.args(&["-O", "nonexistent_file"])
.run()
.status_code(1);
}
#[test]
#[cfg(all(not(windows), not(target_os = "freebsd")))]
fn test_file_not_owned_by_euid() {
new_ucmd!()
.args(&["-f", "/bin/sh", "-a", "!", "-O", "/bin/sh"])
.succeeds();
}
#[test]
#[cfg(not(windows))]
fn test_file_owned_by_egid() {
new_ucmd!().args(&["-G", "regular_file"]).succeeds();
}
#[test]
#[cfg(not(windows))]
fn test_nonexistent_file_not_owned_by_egid() {
new_ucmd!()
.args(&["-G", "nonexistent_file"])
.run()
.status_code(1);
}
#[test]
#[cfg(all(not(windows), not(target_os = "freebsd")))]
fn test_file_not_owned_by_egid() {
new_ucmd!()
.args(&["-f", "/bin/sh", "-a", "!", "-G", "/bin/sh"])
.succeeds();
}
#[test]
fn test_op_precedence_and_or_1() {
new_ucmd!().args(&[" ", "-o", "", "-a", ""]).succeeds();
}
#[test]
fn test_op_prec_and_or_2() {
fn test_op_precedence_and_or_1_overridden_by_parentheses() {
new_ucmd!()
.args(&["(", " ", "-o", "", ")", "-a", ""])
.run()
.status_code(1);
}
#[test]
fn test_op_precedence_and_or_2() {
new_ucmd!()
.args(&["", "-a", "", "-o", " ", "-a", " "])
.succeeds();
}
#[test]
fn test_or_as_filename() {
new_ucmd!().args(&["x", "-a", "-z", "-o"]).fails();
fn test_op_precedence_and_or_2_overridden_by_parentheses() {
new_ucmd!()
.args(&["", "-a", "(", "", "-o", " ", ")", "-a", " "])
.run()
.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_bool_op_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_bool_op_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() {
new_ucmd!()
.args(&["(", "(", "a", "!=", "b", ")", "-o", "-n", "c"])
.run()
.status_code(2);
new_ucmd!()
.args(&["(", "(", "a", "!=", "b", ")", "-o", "-n", "c", ")"])
.succeeds();
}
#[test]
fn test_complicated_parenthesized_expression() {
new_ucmd!()
.args(&[
"(", "(", "!", "(", "a", "=", "b", ")", "-o", "c", "=", "d", ")", "-a", "(", "q", "!=",
"r", ")", ")",
])
.succeeds();
}
#[test]
fn test_erroneous_parenthesized_expression() {
new_ucmd!()
.args(&["a", "!=", "(", "b", "-a", "b", ")", "!=", "c"])
.run()
.status_code(2)
.stderr_is("test: extra argument 'b'");
}
#[test]
fn test_or_as_filename() {
new_ucmd!()
.args(&["x", "-a", "-z", "-o"])
.run()
.status_code(1);
}
#[test]
#[ignore = "GNU considers this an error"]
fn test_string_length_and_nothing() {
new_ucmd!().args(&["-n", "a", "-a"]).run().status_code(2);
}
#[test]
fn test_bracket_syntax_success() {
let scenario = TestScenario::new("[");
let mut ucmd = scenario.ucmd();
ucmd.args(&["1", "-eq", "1", "]"]).succeeds();
}
#[test]
fn test_bracket_syntax_failure() {
let scenario = TestScenario::new("[");
let mut ucmd = scenario.ucmd();
ucmd.args(&["1", "-eq", "2", "]"]).run().status_code(1);
}
#[test]
fn test_bracket_syntax_missing_right_bracket() {
let scenario = TestScenario::new("[");
let mut ucmd = scenario.ucmd();
// Missing closing bracket takes precedence over other possible errors.
ucmd.args(&["1", "-eq"])
.run()
.status_code(2)
.stderr_is("[: missing ']'");
}

View file

@ -4,8 +4,44 @@ use crate::common::util::*;
// the best solution is probably to generate some test binaries that we can call for any
// utility that requires executing another program (kill, for instance)
#[test]
fn test_subcommand_retcode() {
fn test_subcommand_return_code() {
new_ucmd!().arg("1").arg("true").succeeds();
new_ucmd!().arg("1").arg("false").run().status_code(1);
}
#[test]
fn test_command_with_args() {
new_ucmd!()
.args(&["1700", "echo", "-n", "abcd"])
.succeeds()
.stdout_only("abcd");
}
#[test]
fn test_verbose() {
for &verbose_flag in &["-v", "--verbose"] {
new_ucmd!()
.args(&[verbose_flag, ".1", "sleep", "10"])
.fails()
.stderr_only("timeout: sending signal TERM to command 'sleep'");
new_ucmd!()
.args(&[verbose_flag, "-s0", "-k.1", ".1", "sleep", "10"])
.fails()
.stderr_only("timeout: sending signal EXIT to command 'sleep'\ntimeout: sending signal KILL to command 'sleep'");
}
}
#[test]
fn test_zero_timeout() {
new_ucmd!()
.args(&["-v", "0", "sleep", ".1"])
.succeeds()
.no_stderr()
.no_stdout();
new_ucmd!()
.args(&["-v", "0", "-s0", "-k0", "sleep", ".1"])
.succeeds()
.no_stderr()
.no_stdout();
}

View file

@ -1,9 +1,12 @@
// spell-checker:ignore (formats) cymdhm cymdhms mdhm mdhms ymdhm ymdhms
extern crate touch;
use self::touch::filetime::{self, FileTime};
extern crate time;
use crate::common::util::*;
use std::path::PathBuf;
fn get_file_times(at: &AtPath, path: &str) -> (FileTime, FileTime) {
let m = at.metadata(path);
@ -29,6 +32,7 @@ fn set_file_times(at: &AtPath, path: &str, atime: FileTime, mtime: FileTime) {
fn str_to_filetime(format: &str, s: &str) -> FileTime {
let mut tm = time::strptime(s, format).unwrap();
tm.tm_utcoff = time::now().tm_utcoff;
tm.tm_isdst = -1; // Unknown flag DST
let ts = tm.to_timespec();
FileTime::from_unix_time(ts.sec as i64, ts.nsec as u32)
}
@ -352,3 +356,166 @@ fn test_touch_set_date() {
assert_eq!(atime, start_of_year);
assert_eq!(mtime, start_of_year);
}
#[test]
fn test_touch_set_date2() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_date";
ucmd.args(&["-d", "2000-01-23", file])
.succeeds()
.no_stderr();
assert!(at.file_exists(file));
let start_of_year = str_to_filetime("%Y%m%d%H%M", "200001230000");
let (atime, mtime) = get_file_times(&at, file);
assert_eq!(atime, mtime);
assert_eq!(atime, start_of_year);
assert_eq!(mtime, start_of_year);
}
#[test]
fn test_touch_set_date3() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_date";
ucmd.args(&["-d", "@1623786360", file])
.succeeds()
.no_stderr();
assert!(at.file_exists(file));
let expected = FileTime::from_unix_time(1623786360, 0);
let (atime, mtime) = get_file_times(&at, file);
assert_eq!(atime, mtime);
assert_eq!(atime, expected);
assert_eq!(mtime, expected);
}
#[test]
fn test_touch_set_date_wrong_format() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_date_wrong_format";
ucmd.args(&["-d", "2005-43-21", file])
.fails()
.stderr_contains("Unable to parse date: 2005-43-21");
}
#[test]
fn test_touch_mtime_dst_succeeds() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_mtime_dst_succeeds";
ucmd.args(&["-m", "-t", "202103140300", file])
.succeeds()
.no_stderr();
assert!(at.file_exists(file));
let target_time = str_to_filetime("%Y%m%d%H%M", "202103140300");
let (_, mtime) = get_file_times(&at, file);
assert!(target_time == mtime);
}
// is_dst_switch_hour returns true if timespec ts is just before the switch
// to Daylight Saving Time.
// For example, in EST (UTC-5), Timespec { sec: 1583647200, nsec: 0 }
// for March 8 2020 01:00:00 AM
// is just before the switch because on that day clock jumps by 1 hour,
// so 1 minute after 01:59:00 is 03:00:00.
fn is_dst_switch_hour(ts: time::Timespec) -> bool {
let ts_after = ts + time::Duration::hours(1);
let tm = time::at(ts);
let tm_after = time::at(ts_after);
tm_after.tm_hour == tm.tm_hour + 2
}
// get_dst_switch_hour returns date string for which touch -m -t fails.
// For example, in EST (UTC-5), that will be "202003080200" so
// touch -m -t 202003080200 file
// fails (that date/time does not exist).
// In other locales it will be a different date/time, and in some locales
// it doesn't exist at all, in which case this function will return None.
fn get_dst_switch_hour() -> Option<String> {
let now = time::now();
// Start from January 1, 2020, 00:00.
let mut tm = time::strptime("20200101-0000", "%Y%m%d-%H%M").unwrap();
tm.tm_isdst = -1;
tm.tm_utcoff = now.tm_utcoff;
let mut ts = tm.to_timespec();
// Loop through all hours in year 2020 until we find the hour just
// before the switch to DST.
for _i in 0..(366 * 24) {
if is_dst_switch_hour(ts) {
let mut tm = time::at(ts);
tm.tm_hour += 1;
let s = time::strftime("%Y%m%d%H%M", &tm).unwrap();
return Some(s);
}
ts = ts + time::Duration::hours(1);
}
None
}
#[test]
fn test_touch_mtime_dst_fails() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_mtime_dst_fails";
if let Some(s) = get_dst_switch_hour() {
ucmd.args(&["-m", "-t", &s, file]).fails();
}
}
#[test]
#[cfg(unix)]
fn test_touch_system_fails() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "/";
ucmd.args(&[file])
.fails()
.stderr_contains("setting times of '/'");
}
#[test]
fn test_touch_trailing_slash() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "no-file/";
ucmd.args(&[file]).fails();
}
#[test]
fn test_touch_no_such_file_error_msg() {
let dirname = "nonexistent";
let filename = "file";
let path = PathBuf::from(dirname).join(filename);
let path_str = path.to_str().unwrap();
new_ucmd!().arg(&path).fails().stderr_only(format!(
"touch: cannot touch '{}': No such file or directory",
path_str
));
}
#[test]
#[cfg(unix)]
fn test_touch_permission_denied_error_msg() {
let (at, mut ucmd) = at_and_ucmd!();
let dirname = "dir_with_read_only_access";
let filename = "file";
let path = PathBuf::from(dirname).join(filename);
let path_str = path.to_str().unwrap();
// create dest without write permissions
at.mkdir(dirname);
at.set_readonly(dirname);
let full_path = at.plus_as_string(path_str);
ucmd.arg(&full_path).fails().stderr_only(format!(
"touch: cannot touch '{}': Permission denied",
&full_path
));
}

View file

@ -1,7 +1,7 @@
use crate::common::util::*;
#[test]
fn test_toupper() {
fn test_to_upper() {
new_ucmd!()
.args(&["a-z", "A-Z"])
.pipe_in("!abcd!")
@ -45,6 +45,70 @@ fn test_delete_complement() {
.stdout_is("ac");
}
#[test]
fn test_delete_complement_2() {
new_ucmd!()
.args(&["-d", "-C", "0-9"])
.pipe_in("Phone: 01234 567890")
.succeeds()
.stdout_is("01234567890");
new_ucmd!()
.args(&["-d", "--complement", "0-9"])
.pipe_in("Phone: 01234 567890")
.succeeds()
.stdout_is("01234567890");
}
#[test]
fn test_complement1() {
new_ucmd!()
.args(&["-c", "a", "X"])
.pipe_in("ab")
.run()
.stdout_is("aX");
}
#[test]
fn test_complement2() {
new_ucmd!()
.args(&["-c", "0-9", "x"])
.pipe_in("Phone: 01234 567890")
.run()
.stdout_is("xxxxxxx01234x567890");
}
#[test]
fn test_complement3() {
new_ucmd!()
.args(&["-c", "abcdefgh", "123"]) // spell-checker:disable-line
.pipe_in("the cat and the bat")
.run()
.stdout_is("3he3ca33a3d33he3ba3"); // spell-checker:disable-line
}
#[test]
fn test_complement4() {
// $ echo -n '0x1y2z3' | tr -c '0-@' '*-~'
// 0~1~2~3
new_ucmd!()
.args(&["-c", "0-@", "*-~"])
.pipe_in("0x1y2z3")
.run()
.stdout_is("0~1~2~3");
}
#[test]
#[ignore = "fixme: GNU tr returns '0a1b2c3' instead of '0~1~2~3', see #2158"]
fn test_complement5() {
// $ echo '0x1y2z3' | tr -c '\0-@' '*-~'
// 0a1b2c3
new_ucmd!()
.args(&["-c", "\\0-@", "*-~"])
.pipe_in("0x1y2z3")
.run()
.stdout_is("0a1b2c3");
}
#[test]
fn test_squeeze() {
new_ucmd!()
@ -63,6 +127,24 @@ fn test_squeeze_complement() {
.stdout_is("aaBcDcc");
}
#[test]
fn test_translate_and_squeeze() {
new_ucmd!()
.args(&["-s", "x", "y"])
.pipe_in("xx")
.run()
.stdout_is("y");
}
#[test]
fn test_translate_and_squeeze_multiple_lines() {
new_ucmd!()
.args(&["-s", "x", "y"])
.pipe_in("xxaax\nxaaxx") // spell-checker:disable-line
.run()
.stdout_is("yaay\nyaay"); // spell-checker:disable-line
}
#[test]
fn test_delete_and_squeeze() {
new_ucmd!()
@ -87,7 +169,7 @@ fn test_set1_longer_than_set2() {
.args(&["abc", "xy"])
.pipe_in("abcde")
.run()
.stdout_is("xyyde");
.stdout_is("xyyde"); // spell-checker:disable-line
}
#[test]
@ -96,7 +178,7 @@ fn test_set1_shorter_than_set2() {
.args(&["ab", "xyz"])
.pipe_in("abcde")
.run()
.stdout_is("xycde");
.stdout_is("xycde"); // spell-checker:disable-line
}
#[test]
@ -105,7 +187,7 @@ fn test_truncate() {
.args(&["-t", "abc", "xy"])
.pipe_in("abcde")
.run()
.stdout_is("xycde");
.stdout_is("xycde"); // spell-checker:disable-line
}
#[test]
@ -114,23 +196,82 @@ fn test_truncate_with_set1_shorter_than_set2() {
.args(&["-t", "ab", "xyz"])
.pipe_in("abcde")
.run()
.stdout_is("xycde");
.stdout_is("xycde"); // spell-checker:disable-line
}
#[test]
fn missing_args_fails() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(!result.success);
assert!(result.stderr.contains("missing operand"));
ucmd.fails().stderr_contains("missing operand");
}
#[test]
fn missing_required_second_arg_fails() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.args(&["foo"]).run();
assert!(!result.success);
assert!(result.stderr.contains("missing operand after"));
ucmd.args(&["foo"])
.fails()
.stderr_contains("missing operand after");
}
#[test]
fn test_interpret_backslash_escapes() {
new_ucmd!()
.args(&["abfnrtv", r"\a\b\f\n\r\t\v"]) // spell-checker:disable-line
.pipe_in("abfnrtv") // spell-checker:disable-line
.succeeds()
.stdout_is("\u{7}\u{8}\u{c}\n\r\t\u{b}");
}
#[test]
fn test_interpret_unrecognized_backslash_escape_as_character() {
new_ucmd!()
.args(&["qcz+=~-", r"\q\c\z\+\=\~\-"])
.pipe_in("qcz+=~-")
.succeeds()
.stdout_is("qcz+=~-");
}
#[test]
fn test_interpret_single_octal_escape() {
new_ucmd!()
.args(&["X", r"\015"])
.pipe_in("X")
.succeeds()
.stdout_is("\r");
}
#[test]
fn test_interpret_one_and_two_digit_octal_escape() {
new_ucmd!()
.args(&["XYZ", r"\0\11\77"])
.pipe_in("XYZ")
.succeeds()
.stdout_is("\0\t?");
}
#[test]
fn test_octal_escape_is_at_most_three_digits() {
new_ucmd!()
.args(&["XY", r"\0156"])
.pipe_in("XY")
.succeeds()
.stdout_is("\r6");
}
#[test]
fn test_non_octal_digit_ends_escape() {
new_ucmd!()
.args(&["rust", r"\08\11956"])
.pipe_in("rust")
.succeeds()
.stdout_is("\08\t9");
}
#[test]
fn test_interpret_backslash_at_eol_literally() {
new_ucmd!()
.args(&["X", r"\"])
.pipe_in("X")
.succeeds()
.stdout_is("\\");
}

View file

@ -1,56 +1,112 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (words) RFILE
use crate::common::util::*;
use std::io::{Seek, SeekFrom, Write};
static TFILE1: &'static str = "truncate_test_1";
static TFILE2: &'static str = "truncate_test_2";
static FILE1: &str = "truncate_test_1";
static FILE2: &str = "truncate_test_2";
#[test]
fn test_increase_file_size() {
let expected = 5 * 1024;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(TFILE1);
ucmd.args(&["-s", "+5K", TFILE1]).succeeds();
let mut file = at.make_file(FILE1);
ucmd.args(&["-s", "+5K", FILE1]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
assert!(file.seek(SeekFrom::Current(0)).unwrap() == 5 * 1024);
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_increase_file_size_kb() {
let expected = 5 * 1000;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(TFILE1);
ucmd.args(&["-s", "+5KB", TFILE1]).succeeds();
let mut file = at.make_file(FILE1);
ucmd.args(&["-s", "+5KB", FILE1]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
assert!(file.seek(SeekFrom::Current(0)).unwrap() == 5 * 1000);
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_reference() {
let expected = 5 * 1000;
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let mut file = at.make_file(TFILE2);
let mut file = at.make_file(FILE2);
scene.ucmd().arg("-s").arg("+5KB").arg(TFILE1).run();
// manpage: "A FILE argument that does not exist is created."
// TODO: 'truncate' does not create the file in this case,
// but should because '--no-create' wasn't specified.
at.touch(FILE1); // TODO: remove this when 'no-create' is fixed
scene.ucmd().arg("-s").arg("+5KB").arg(FILE1).succeeds();
scene
.ucmd()
.arg("--reference")
.arg(TFILE1)
.arg(TFILE2)
.run();
.arg(FILE1)
.arg(FILE2)
.succeeds();
file.seek(SeekFrom::End(0)).unwrap();
assert!(file.seek(SeekFrom::Current(0)).unwrap() == 5 * 1000);
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_decrease_file_size() {
let expected = 6;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(TFILE2);
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size=-4", TFILE2]).succeeds();
ucmd.args(&["--size=-4", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
assert!(file.seek(SeekFrom::Current(0)).unwrap() == 6);
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_space_in_size() {
let expected = 4;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", " 4", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
@ -61,11 +117,207 @@ fn test_failed() {
#[test]
fn test_failed_2() {
let (_at, mut ucmd) = at_and_ucmd!();
ucmd.args(&[TFILE1]).fails();
ucmd.args(&[FILE1]).fails();
}
#[test]
fn test_failed_incorrect_arg() {
let (_at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["-s", "+5A", TFILE1]).fails();
ucmd.args(&["-s", "+5A", FILE1]).fails();
}
#[test]
fn test_at_most_shrinks() {
let expected = 4;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", "<4", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_at_most_no_change() {
let expected = 10;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", "<40", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_at_least_grows() {
let expected = 15;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", ">15", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_at_least_no_change() {
let expected = 10;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", ">4", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_round_down() {
let expected = 8;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", "/4", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_round_up() {
let expected = 12;
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(FILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", "%4", FILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
let actual = file.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_size_and_reference() {
let expected = 15;
let (at, mut ucmd) = at_and_ucmd!();
let mut file1 = at.make_file(FILE1);
let mut file2 = at.make_file(FILE2);
file1.write_all(b"1234567890").unwrap();
ucmd.args(&["--reference", FILE1, "--size", "+5", FILE2])
.succeeds();
file2.seek(SeekFrom::End(0)).unwrap();
let actual = file2.seek(SeekFrom::Current(0)).unwrap();
assert!(
expected == actual,
"expected '{}' got '{}'",
expected,
actual
);
}
#[test]
fn test_error_filename_only() {
// truncate: you must specify either '--size' or '--reference'
new_ucmd!().args(&["file"]).fails().stderr_contains(
"error: The following required arguments were not provided:
--reference <RFILE>
--size <SIZE>",
);
}
#[test]
fn test_invalid_numbers() {
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");
}
#[test]
fn test_reference_with_size_file_not_found() {
new_ucmd!()
.args(&["-r", "a", "-s", "+1", "b"])
.fails()
.stderr_contains("cannot stat 'a': No such file or directory");
}
#[test]
fn test_truncate_bytes_size() {
// TODO: this should succeed without error, uncomment when '--no-create' is fixed
// new_ucmd!()
// .args(&["--no-create", "--size", "K", "file"])
// .succeeds();
new_ucmd!()
.args(&["--size", "1024R", "file"])
.fails()
.code_is(1)
.stderr_only("truncate: Invalid number: '1024R'");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["--size", "1Y", "file"])
.fails()
.code_is(1)
.stderr_only("truncate: Invalid number: '1Y': Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["--size", size, "file"])
.fails()
.code_is(1)
.stderr_only(format!(
"truncate: Invalid number: '{}': Value too large for defined data type",
size
));
}
}
}

View file

@ -15,3 +15,38 @@ fn test_sort_self_loop() {
.succeeds()
.stdout_only("first\nsecond\n");
}
#[test]
fn test_no_such_file() {
new_ucmd!()
.arg("invalid_file_txt")
.fails()
.stderr_contains("No such file or directory");
}
#[test]
fn test_version_flag() {
let version_short = new_ucmd!().arg("-V").succeeds();
let version_long = new_ucmd!().arg("--version").succeeds();
assert_eq!(version_short.stdout_str(), version_long.stdout_str());
}
#[test]
fn test_help_flag() {
let help_short = new_ucmd!().arg("-h").succeeds();
let help_long = new_ucmd!().arg("--help").succeeds();
assert_eq!(help_short.stdout_str(), help_long.stdout_str());
}
#[test]
fn test_multiple_arguments() {
new_ucmd!()
.arg("call_graph.txt")
.arg("invalid_file")
.fails()
.stderr_contains(
"Found argument 'invalid_file' which wasn't expected, or isn't valid in this context",
);
}

View file

@ -1 +1,74 @@
// ToDO: add tests
use std::fs::File;
use crate::common::util::*;
#[test]
#[cfg(not(windows))]
fn test_dev_null() {
new_ucmd!()
.set_stdin(File::open("/dev/null").unwrap())
.fails()
.code_is(1)
.stdout_is("not a tty\n");
}
#[test]
#[cfg(not(windows))]
fn test_dev_null_silent() {
new_ucmd!()
.args(&["-s"])
.set_stdin(File::open("/dev/null").unwrap())
.fails()
.code_is(1)
.stdout_is("");
}
#[test]
fn test_close_stdin() {
let mut child = new_ucmd!().run_no_wait();
drop(child.stdin.take());
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(1));
assert_eq!(std::str::from_utf8(&output.stdout), Ok("not a tty\n"));
}
#[test]
fn test_close_stdin_silent() {
let mut child = new_ucmd!().arg("-s").run_no_wait();
drop(child.stdin.take());
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(1));
assert!(output.stdout.is_empty());
}
#[test]
fn test_close_stdin_silent_long() {
let mut child = new_ucmd!().arg("--silent").run_no_wait();
drop(child.stdin.take());
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(1));
assert!(output.stdout.is_empty());
}
#[test]
fn test_close_stdin_silent_alias() {
let mut child = new_ucmd!().arg("--quiet").run_no_wait();
drop(child.stdin.take());
let output = child.wait_with_output().unwrap();
assert_eq!(output.status.code(), Some(1));
assert!(output.stdout.is_empty());
}
#[test]
fn test_wrong_argument() {
new_ucmd!().args(&["a"]).fails().code_is(2);
}
#[test]
#[cfg(not(windows))]
fn test_stdout_fail() {
let mut child = new_ucmd!().run_no_wait();
drop(child.stdout.take());
let status = child.wait().unwrap();
assert_eq!(status.code(), Some(3));
}

View file

@ -2,60 +2,46 @@ use crate::common::util::*;
#[test]
fn test_uname_compatible() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-a").run();
assert!(result.success);
new_ucmd!().arg("-a").succeeds();
}
#[test]
fn test_uname_name() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-n").run();
assert!(result.success);
new_ucmd!().arg("-n").succeeds();
}
#[test]
fn test_uname_processor() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-p").run();
assert!(result.success);
assert_eq!(result.stdout.trim_end(), "unknown");
let result = new_ucmd!().arg("-p").succeeds();
assert_eq!(result.stdout_str().trim_end(), "unknown");
}
#[test]
fn test_uname_hwplatform() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-i").run();
assert!(result.success);
assert_eq!(result.stdout.trim_end(), "unknown");
fn test_uname_hardware_platform() {
let result = new_ucmd!().arg("-i").succeeds();
assert_eq!(result.stdout_str().trim_end(), "unknown");
}
#[test]
fn test_uname_machine() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-m").run();
assert!(result.success);
new_ucmd!().arg("-m").succeeds();
}
#[test]
fn test_uname_kernel_version() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-v").run();
assert!(result.success);
new_ucmd!().arg("-v").succeeds();
}
#[test]
fn test_uname_kernel() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("-o").run();
assert!(result.success);
#[cfg(target_os = "linux")]
assert!(result.stdout.to_lowercase().contains("linux"));
{
let result = ucmd.arg("-o").succeeds();
assert!(result.stdout_str().to_lowercase().contains("linux"));
}
#[cfg(not(target_os = "linux"))]
ucmd.arg("-o").succeeds();
}

View file

@ -38,7 +38,7 @@ fn unexpand_init_list_1() {
}
#[test]
fn unexpand_aflag_0() {
fn unexpand_flag_a_0() {
new_ucmd!()
.args(&["--"])
.pipe_in("e E\nf F\ng G\nh H\n")
@ -47,7 +47,7 @@ fn unexpand_aflag_0() {
}
#[test]
fn unexpand_aflag_1() {
fn unexpand_flag_a_1() {
new_ucmd!()
.args(&["-a"])
.pipe_in("e E\nf F\ng G\nh H\n")
@ -56,7 +56,7 @@ fn unexpand_aflag_1() {
}
#[test]
fn unexpand_aflag_2() {
fn unexpand_flag_a_2() {
new_ucmd!()
.args(&["-t8"])
.pipe_in("e E\nf F\ng G\nh H\n")
@ -136,3 +136,22 @@ fn unexpand_spaces_after_fields() {
.run()
.stdout_is("\t\tA B C D\t\t A\t\n");
}
#[test]
fn unexpand_read_from_file() {
new_ucmd!()
.arg("with_spaces.txt")
.arg("-t4")
.run()
.success();
}
#[test]
fn unexpand_read_from_two_file() {
new_ucmd!()
.arg("with_spaces.txt")
.arg("with_spaces.txt")
.arg("-t4")
.run()
.success();
}

View file

@ -1,10 +1,10 @@
use crate::common::util::*;
static INPUT: &'static str = "sorted.txt";
static OUTPUT: &'static str = "sorted-output.txt";
static SKIP_CHARS: &'static str = "skip-chars.txt";
static SKIP_FIELDS: &'static str = "skip-fields.txt";
static SORTED_ZERO_TERMINATED: &'static str = "sorted-zero-terminated.txt";
static INPUT: &str = "sorted.txt";
static OUTPUT: &str = "sorted-output.txt";
static SKIP_CHARS: &str = "skip-chars.txt";
static SKIP_FIELDS: &str = "skip-fields.txt";
static SORTED_ZERO_TERMINATED: &str = "sorted-zero-terminated.txt";
#[test]
fn test_stdin_default() {
@ -145,5 +145,50 @@ 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]
fn test_group() {
new_ucmd!()
.args(&["--group"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group.expected");
}
#[test]
fn test_group_prepend() {
new_ucmd!()
.args(&["--group=prepend"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group-prepend.expected");
}
#[test]
fn test_group_append() {
new_ucmd!()
.args(&["--group=append"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group-append.expected");
}
#[test]
fn test_group_both() {
new_ucmd!()
.args(&["--group=both"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group-both.expected");
}
#[test]
fn test_group_separate() {
new_ucmd!()
.args(&["--group=separate"])
.pipe_in_fixture(INPUT)
.run()
.stdout_is_fixture("group.expected");
}

View file

@ -22,8 +22,8 @@ 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' \
for more information.\n",
"unlink: extra operand: 'test_unlink_multiple_file_b'\nTry 'unlink --help' \
for more information.\n",
);
}
@ -35,8 +35,8 @@ 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 \
or symlink\n",
"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 \
(os error 2)\n",
"unlink: Cannot stat 'test_unlink_nonexistent': No such file or directory \
(os error 2)\n",
);
}

View file

@ -4,33 +4,23 @@ use crate::common::util::*;
#[test]
fn test_uptime() {
let result = TestScenario::new(util_name!()).ucmd_keepenv().run();
TestScenario::new(util_name!())
.ucmd_keepenv()
.succeeds()
.stdout_contains("load average:")
.stdout_contains(" up ");
println!("stdout = {}", result.stdout);
println!("stderr = {}", result.stderr);
assert!(result.success);
assert!(result.stdout.contains("load average:"));
assert!(result.stdout.contains(" up "));
// Don't check for users as it doesn't show in some CI
}
#[test]
fn test_uptime_since() {
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("--since").succeeds();
println!("stdout = {}", result.stdout);
println!("stderr = {}", result.stderr);
assert!(result.success);
let re = Regex::new(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}").unwrap();
assert!(re.is_match(&result.stdout.trim()));
new_ucmd!().arg("--since").succeeds().stdout_matches(&re);
}
#[test]
fn test_failed() {
let (_at, mut ucmd) = at_and_ucmd!();
ucmd.arg("willfail").fails();
new_ucmd!().arg("will-fail").fails();
}

View file

@ -1,30 +1,25 @@
use crate::common::util::*;
use std::env;
#[test]
fn test_users_noarg() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
assert!(result.success);
fn test_users_no_arg() {
new_ucmd!().succeeds();
}
#[test]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn test_users_check_name() {
let result = TestScenario::new(util_name!()).ucmd_keepenv().run();
assert!(result.success);
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
// Expectation: USER is often set
let key = "USER";
// note: clippy::needless_borrow *false positive*
#[allow(clippy::needless_borrow)]
let expected = TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LC_ALL", "C")
.succeeds()
.stdout_move_str();
match env::var(key) {
Err(e) => println!("Key {} isn't set. Found {}", &key, e),
Ok(username) =>
// Check if "users" contains the name of the user
{
println!("username found {}", &username);
println!("result.stdout {}", &result.stdout);
if !&result.stdout.is_empty() {
assert!(result.stdout.contains(&username))
}
}
}
new_ucmd!().succeeds().stdout_is(&expected);
}

View file

@ -1,11 +1,50 @@
use crate::common::util::*;
// spell-checker:ignore (flags) lwmcL clmwL ; (path) bogusfile emptyfile manyemptylines moby notrailingnewline onelongemptyline onelongword
#[test]
fn test_count_bytes_large_stdin() {
for &n in &[
0,
1,
42,
16 * 1024 - 7,
16 * 1024 - 1,
16 * 1024,
16 * 1024 + 1,
16 * 1024 + 3,
32 * 1024,
64 * 1024,
80 * 1024,
96 * 1024,
112 * 1024,
128 * 1024,
] {
let data = vec_of_size(n);
let expected = format!("{}\n", n);
new_ucmd!()
.args(&["-c"])
.pipe_in(data)
.succeeds()
.stdout_is_bytes(&expected.as_bytes());
}
}
#[test]
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]
@ -14,9 +53,11 @@ fn test_utf8() {
.args(&["-lwmcL"])
.pipe_in_fixture("UTF_8_test.txt")
.run()
.stdout_is(" 0 0 0 0 0\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]
@ -25,7 +66,7 @@ fn test_stdin_line_len_regression() {
.args(&["-L"])
.pipe_in("\n123456")
.run()
.stdout_is(" 6\n");
.stdout_is("6\n");
}
#[test]
@ -34,7 +75,7 @@ fn test_stdin_only_bytes() {
.args(&["-c"])
.pipe_in_fixture("lorem_ipsum.txt")
.run()
.stdout_is(" 772\n");
.stdout_is("772\n");
}
#[test]
@ -43,7 +84,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]
@ -51,7 +92,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]
@ -59,7 +100,7 @@ fn test_single_only_lines() {
new_ucmd!()
.args(&["-l", "moby_dick.txt"])
.run()
.stdout_is(" 18 moby_dick.txt\n");
.stdout_is("18 moby_dick.txt\n");
}
#[test]
@ -67,7 +108,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]
@ -80,7 +121,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() {
new_ucmd!()
.args(&["-clmwL", "emptyfile.txt"])
.run()
.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() {
new_ucmd!()
.args(&["-clmwL", "notrailingnewline.txt"])
.run()
.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() {
new_ucmd!()
.args(&["-clmwL", "manyemptylines.txt"])
.run()
.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() {
new_ucmd!()
.args(&["-clmwL", "onelongemptyline.txt"])
.run()
.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() {
new_ucmd!()
.args(&["-clmwL", "onelongword.txt"])
.run()
.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,30 +1,38 @@
#[cfg(target_os = "linux")]
use crate::common::util::*;
#[cfg(target_os = "linux")]
// spell-checker:ignore (flags) runlevel mesg
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_count() {
for opt in vec!["-q", "--count"] {
new_ucmd!().arg(opt).run().stdout_is(expected_result(opt));
for opt in &["-q", "--count"] {
new_ucmd!()
.arg(opt)
.succeeds()
.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).run().stdout_is(expected_result(opt));
for opt in &["-b", "--boot"] {
new_ucmd!()
.arg(opt)
.succeeds()
.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"] {
for opt in &["-H", "--heading"] {
// allow whitespace variation
// * 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).run().stdout;
let expect = expected_result(opt);
// * 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]);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
@ -33,52 +41,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).run().stdout_is(expected_result(opt));
for opt in &["-s", "--short"] {
new_ucmd!()
.arg(opt)
.succeeds()
.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).run().stdout_is(expected_result(opt));
for opt in &["-l", "--login"] {
new_ucmd!()
.arg(opt)
.succeeds()
.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).run().stdout_is(expected_result(opt));
for opt in &["-m"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_process() {
for opt in &["-p", "--process"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
}
#[test]
fn test_runlevel() {
for opt in &["-r", "--runlevel"] {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
#[cfg(not(target_os = "linux"))]
new_ucmd!().arg(opt).succeeds().stdout_is("");
}
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_time() {
for opt in &["-t", "--time"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_mesg() {
// -T, -w, --mesg
// add user's message status as +, - or ?
// --message
// same as -T
// --writable
// same as -T
for opt in &["-T", "-w", "--mesg", "--message", "--writable"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
}
#[test]
fn test_arg1_arg2() {
let args = ["am", "i"];
new_ucmd!()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
}
#[test]
fn test_too_many_args() {
const EXPECTED: &str =
"error: The value 'u' was provided to '<FILE>...', but it wasn't expecting any more values";
let args = ["am", "i", "u"];
new_ucmd!().args(&args).fails().stderr_contains(EXPECTED);
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_users() {
for opt in &["-u", "--users"] {
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` sometimes differs from GNU's output on macOS (race condition?)
// actual: "runner console Jun 23 06:37 00:34 196\n"
// expect: "runner console Jun 23 06:37 old 196\n"
if cfg!(target_os = "macos") {
v_actual.remove(5);
v_expect.remove(5);
}
assert_eq!(v_actual, v_expect);
}
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_lookup() {
let opt = "--lookup";
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_dead() {
for opt in vec!["-d", "--dead"] {
new_ucmd!().arg(opt).run().stdout_is(expected_result(opt));
for opt in &["-d", "--dead"] {
new_ucmd!()
.arg(opt)
.succeeds()
.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!());
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
scene
.ucmd()
.arg("--all")
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_all() {
for opt in vec!["-a", "--all"] {
new_ucmd!().arg(opt).run().stdout_is(expected_result(opt));
if cfg!(target_os = "macos") {
// TODO: fix `-u`, see: test_users
return;
}
for opt in &["-a", "--all"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
fn expected_result(arg: &str) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.args(&[arg])
.run()
.stdout
#[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!());
// note: clippy::needless_borrow *false positive*
#[allow(clippy::needless_borrow)]
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LC_ALL", "C")
.args(args)
.succeeds()
.stdout_move_str()
}

View file

@ -1,50 +1,63 @@
use crate::common::util::*;
use std::env;
// Apparently some CI environments have configuration issues, e.g. with 'whoami' and 'id'.
// If we are running inside the CI and "needle" is in "stderr" skipping this test is
// 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: 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() {
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
if is_ci() && result.stderr_str().contains(needle) {
println!("test skipped:");
return true;
} else {
result.success();
}
}
false
}
#[test]
fn test_normal() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
println!("env::var(CI).is_ok() = {}", env::var("CI").is_ok());
for (key, value) in env::vars() {
println!("{}: {}", key, value);
}
if is_ci() && result.stderr.contains("failed to get username") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
// use std::env;
// println!("env::var(CI).is_ok() = {}", env::var("CI").is_ok());
// for (key, value) in env::vars() {
// println!("{}: {}", key, value);
// }
if skipping_test_is_okay(&result, "failed to get username") {
return;
}
assert!(result.success);
assert!(!result.stdout.trim().is_empty());
result.no_stderr();
assert!(!result.stdout_str().trim().is_empty());
}
#[test]
#[cfg(not(windows))]
fn test_normal_compare_id() {
let (_, mut ucmd) = at_and_ucmd!();
let scene = TestScenario::new(util_name!());
let result = ucmd.run();
println!("result.stdout = {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("failed to get username") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
let result_ucmd = scene.ucmd().run();
if skipping_test_is_okay(&result_ucmd, "failed to get username") {
return;
}
assert!(result.success);
let ts = TestScenario::new("id");
let id = ts.cmd("id").arg("-un").run();
if is_ci() && id.stderr.contains("cannot find name for user ID") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
let result_cmd = scene.cmd("id").arg("-un").run();
if skipping_test_is_okay(&result_cmd, "cannot find name for user ID") {
return;
}
assert_eq!(result.stdout.trim(), id.stdout.trim());
assert_eq!(
result_ucmd.stdout_str().trim(),
result_cmd.stdout_str().trim()
);
}

View file

@ -1,31 +1,4 @@
#[macro_export]
macro_rules! assert_empty_stderr(
($cond:expr) => (
if $cond.stderr.len() > 0 {
panic!(format!("stderr: {}", $cond.stderr))
}
);
);
#[macro_export]
macro_rules! assert_empty_stdout(
($cond:expr) => (
if $cond.stdout.len() > 0 {
panic!(format!("stdout: {}", $cond.stdout))
}
);
);
#[macro_export]
macro_rules! assert_no_error(
($cond:expr) => (
assert!($cond.success);
if $cond.stderr.len() > 0 {
panic!(format!("stderr: {}", $cond.stderr))
}
);
);
/// Platform-independent helper for constructing a PathBuf from individual elements
#[macro_export]
macro_rules! path_concat {
($e:expr, ..$n:expr) => {{
@ -47,6 +20,9 @@ macro_rules! path_concat {
}};
}
/// Deduce the name of the test binary from the test filename.
///
/// e.g.: `tests/by-util/test_cat.rs` -> `cat`
#[macro_export]
macro_rules! util_name {
() => {
@ -54,6 +30,16 @@ macro_rules! util_name {
};
}
/// Convenience macro for acquiring a [`UCommand`] builder.
///
/// Returns the following:
/// - a [`UCommand`] builder for invoking the binary to be tested
///
/// This macro is intended for quick, single-call tests. For more complex tests
/// that require multiple invocations of the tested binary, see [`TestScenario`]
///
/// [`UCommand`]: crate::tests::common::util::UCommand
/// [`TestScenario]: crate::tests::common::util::TestScenario
#[macro_export]
macro_rules! new_ucmd {
() => {
@ -61,6 +47,18 @@ macro_rules! new_ucmd {
};
}
/// Convenience macro for acquiring a [`UCommand`] builder and a test path.
///
/// Returns a tuple containing the following:
/// - an [`AtPath`] that points to a unique temporary test directory
/// - a [`UCommand`] builder for invoking the binary to be tested
///
/// This macro is intended for quick, single-call tests. For more complex tests
/// that require multiple invocations of the tested binary, see [`TestScenario`]
///
/// [`UCommand`]: crate::tests::common::util::UCommand
/// [`AtPath`]: crate::tests::common::util::AtPath
/// [`TestScenario]: crate::tests::common::util::TestScenario
#[macro_export]
macro_rules! at_and_ucmd {
() => {{

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
Hello, World!

View file

@ -0,0 +1 @@
Hello, World!

Some files were not shown because too many files have changed in this diff Show more