From c3912d53ac1430b718cebbcf724de3af59a06e2e Mon Sep 17 00:00:00 2001 From: Daniel Rocco Date: Thu, 22 Apr 2021 20:29:04 -0400 Subject: [PATCH] test: add tests for basic tests & edge cases Some edge cases covered: - no args - operator by itself (!, -a, etc.) - string, file tests of nothing - compound negations --- tests/by-util/test_test.rs | 442 ++++++++++++++++++++++++++++- tests/fixtures/test/non_empty_file | 1 + tests/fixtures/test/regular_file | 0 3 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/test/non_empty_file create mode 100644 tests/fixtures/test/regular_file diff --git a/tests/by-util/test_test.rs b/tests/by-util/test_test.rs index 4b9a9ff55..2173371fc 100644 --- a/tests/by-util/test_test.rs +++ b/tests/by-util/test_test.rs @@ -2,6 +2,7 @@ // This file is part of the uutils coreutils package. // // (c) mahkoh (ju.orth [at] gmail [dot] com) +// (c) Daniel Rocco // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. @@ -9,6 +10,436 @@ use crate::common::util::*; +#[test] +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] +#[ignore = "fixme: parse/evaluation error (code 2); GNU returns 1"] +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] +#[ignore = "fixme: parse/evaluation error (code 2); GNU returns 0"] +fn test_not_and_not_succeeds() { + new_ucmd!().args(&["!", "-a", "!"]).succeeds(); +} + +#[test] +fn test_simple_or() { + new_ucmd!().args(&["foo", "-o", ""]).succeeds(); +} + +#[test] +#[ignore = "fixme: parse/evaluation error (code 0); GNU returns 1"] +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_strlen_of_nothing() { + // odd but matches GNU, which must interpret -n as a literal here + new_ucmd!().arg("-n").succeeds(); +} + +#[test] +fn test_strlen_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_paren_is_literal() { + let scenario = TestScenario::new(util_name!()); + let tests = [["("], [")"]]; + + for test in &tests { + scenario.ucmd().args(&test[..]).succeeds(); + } +} + +#[test] +#[ignore = "GNU considers this an error"] +fn test_solo_empty_parenthetical_is_error() { + new_ucmd!() + .args(&["(", ")"]) + .run() + .status_code(2) + .stderr_is("test: missing argument after ‘)’"); +} + +#[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_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(); + } +} + +#[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_stringop_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(); +} + +#[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] +#[ignore = "fixme: error reporting"] +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] +#[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 mut ln = scenario.cmd("ln"); + + // creating symlinks requires admin on Windows + ln.args(&["-s", "regular_file", "symlink"]).succeeds(); + + // 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] fn test_op_prec_and_or_1() { new_ucmd!().args(&[" ", "-o", "", "-a", ""]).succeeds(); @@ -23,5 +454,14 @@ fn test_op_prec_and_or_2() { #[test] fn test_or_as_filename() { - new_ucmd!().args(&["x", "-a", "-z", "-o"]).fails(); + new_ucmd!() + .args(&["x", "-a", "-z", "-o"]) + .run() + .status_code(1); +} + +#[test] +#[ignore = "GNU considers this an error"] +fn test_strlen_and_nothing() { + new_ucmd!().args(&["-n", "a", "-a"]).run().status_code(2); } diff --git a/tests/fixtures/test/non_empty_file b/tests/fixtures/test/non_empty_file new file mode 100644 index 000000000..34425caf6 --- /dev/null +++ b/tests/fixtures/test/non_empty_file @@ -0,0 +1 @@ +Not empty! diff --git a/tests/fixtures/test/regular_file b/tests/fixtures/test/regular_file new file mode 100644 index 000000000..e69de29bb