coreutils/tests/by-util/test_test.rs
Sylvestre Ledru 6b70dc232e
test: -o arg should trigger an error (#6642)
* test: -o arg should trigger an error

Should fix tests/test/test-diag.pl

* test: use var directly in string

---------

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
2024-08-13 11:05:35 +02:00

991 lines
24 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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) egid euid pseudofloat
use crate::common::util::TestScenario;
use std::thread::sleep;
#[test]
fn test_empty_test_equivalent_to_false() {
new_ucmd!().run().code_is(1);
}
#[test]
fn test_empty_string_is_false() {
new_ucmd!().arg("").run().code_is(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_some_literals() {
let scenario = TestScenario::new(util_name!());
let tests = [
"a string",
"(",
")",
"-",
"--",
"-0",
"-f",
"--help",
"--version",
"-eq",
"-lt",
"-ef",
"[",
];
for test in &tests {
scenario.ucmd().arg(test).succeeds();
}
// run the inverse of all these tests
for test in &tests {
scenario.ucmd().arg("!").arg(test).run().code_is(1);
}
}
#[test]
fn test_double_not_is_false() {
new_ucmd!().args(&["!", "!"]).run().code_is(1);
}
#[test]
fn test_and_not_is_false() {
new_ucmd!().args(&["-a", "!"]).run().code_is(2);
}
#[test]
fn test_not_and_is_false() {
// `-a` is a literal here & has nonzero length
new_ucmd!().args(&["!", "-a"]).run().code_is(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_errors_miss_and_or() {
new_ucmd!()
.args(&["-o", "arg"])
.fails()
.stderr_contains("'-o': unary operator expected");
new_ucmd!()
.args(&["-a", "arg"])
.fails()
.stderr_contains("'-a': unary operator expected");
}
#[test]
fn test_negated_or() {
new_ucmd!()
.args(&["!", "foo", "-o", "bar"])
.run()
.code_is(1);
new_ucmd!().args(&["foo", "-o", "!", "bar"]).succeeds();
new_ucmd!()
.args(&["!", "foo", "-o", "!", "bar"])
.run()
.code_is(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().code_is(1);
// STRING equivalent to -n STRING
new_ucmd!().arg("").run().code_is(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_zero_len_equals_zero_len() {
new_ucmd!().args(&["", "=", ""]).succeeds();
}
#[test]
fn test_zero_len_not_equals_zero_len_is_false() {
new_ucmd!().args(&["", "!=", ""]).run().code_is(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().code_is(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().code_is(1);
}
}
#[test]
#[ignore = "fixme: error reporting"]
fn test_dangling_string_comparison_is_error() {
new_ucmd!()
.args(&["missing_something", "="])
.run()
.code_is(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().code_is(1);
}
}
#[test]
fn test_a_bunch_of_not() {
new_ucmd!()
.args(&["!", "", "!=", "", "-a", "!", "", "!=", ""])
.succeeds();
}
#[test]
fn test_pseudofloat_equal() {
// string comparison; test(1) doesn't support comparison of actual floats
new_ucmd!().args(&["123.45", "=", "123.45"]).succeeds();
}
#[test]
fn test_pseudofloat_not_equal() {
// string comparison; test(1) doesn't support comparison of actual floats
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();
}
// run the inverse of all these tests
for test in &tests {
scenario.ucmd().arg("!").args(&test[..]).run().code_is(1);
}
}
#[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();
}
// run the inverse of all these tests
for test in &tests {
scenario.ucmd().arg("!").args(&test[..]).run().code_is(1);
}
}
#[test]
fn test_float_inequality_is_error() {
new_ucmd!()
.args(&["123.45", "-ge", "6"])
.run()
.code_is(2)
.stderr_is("test: invalid integer '123.45'\n");
}
#[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[..]);
new_ucmd!()
.args(&[OsStr::new("123"), OsStr::new("-ne"), arg])
.run()
.code_is(2)
.stderr_is("test: invalid integer $'fo\\x80o'\n");
new_ucmd!()
.args(&[arg, OsStr::new("-eq"), OsStr::new("456")])
.run()
.code_is(2)
.stderr_is("test: invalid integer $'fo\\x80o'\n");
}
#[test]
#[cfg(unix)]
fn test_file_is_itself() {
new_ucmd!()
.args(&["regular_file", "-ef", "regular_file"])
.succeeds();
}
#[test]
#[cfg(not(target_os = "android"))]
fn test_file_is_newer_than_and_older_than_itself() {
// odd but matches GNU
new_ucmd!()
.args(&["regular_file", "-nt", "regular_file"])
.run()
.code_is(1);
new_ucmd!()
.args(&["regular_file", "-ot", "regular_file"])
.run()
.code_is(1);
}
#[test]
fn test_non_existing_files() {
let scenario = TestScenario::new(util_name!());
let result = scenario
.ucmd()
.args(&["newer_file", "-nt", "regular_file"])
.fails();
assert!(result.stderr().is_empty());
}
#[test]
#[cfg(unix)]
fn test_same_device_inode() {
let scenario = TestScenario::new(util_name!());
let at = &scenario.fixtures;
scenario.cmd("touch").arg("regular_file").succeeds();
scenario.cmd("touch").arg("regular_file_second").succeeds();
at.symlink_file("regular_file", "symlink");
scenario
.ucmd()
.args(&["regular_file", "-ef", "regular_file_second"])
.fails();
scenario
.ucmd()
.args(&["regular_file", "-ef", "symlink"])
.succeeds();
}
#[test]
#[cfg(not(target_os = "android"))]
fn test_newer_file() {
let scenario = TestScenario::new(util_name!());
scenario.fixtures.touch("regular_file");
sleep(std::time::Duration::from_millis(1000));
scenario.fixtures.touch("newer_file");
scenario
.ucmd()
.args(&["newer_file", "-nt", "regular_file"])
.succeeds();
scenario
.ucmd()
.args(&["regular_file", "-nt", "newer_file"])
.fails();
scenario
.ucmd()
.args(&["regular_file", "-ot", "newer_file"])
.succeeds();
scenario
.ucmd()
.args(&["newer_file", "-ot", "regular_file"])
.fails();
}
#[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()
.code_is(1);
}
#[test]
fn test_nonexistent_file_is_not_regular() {
new_ucmd!()
.args(&["-f", "nonexistent_file"])
.run()
.code_is(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() {
#[cfg(unix)]
let (at, mut ucmd) = at_and_ucmd!();
#[cfg(not(unix))]
let (_, mut ucmd) = at_and_ucmd!();
// WSL creates executable files by default, so if we are on unix, make sure
// to set make it non-executable.
// Files on other targets are non-executable by default.
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = std::fs::metadata(at.plus("regular_file")).unwrap();
let mut permissions = metadata.permissions();
// The conversion is useless on some platforms and casts from u16 to
// u32 on others
#[allow(clippy::useless_conversion)]
permissions.set_mode(permissions.mode() & !u32::from(libc::S_IXUSR));
std::fs::set_permissions(at.plus("regular_file"), permissions).unwrap();
}
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()
.code_is(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]
// Only the superuser is allowed to set the sticky bit on files on FreeBSD/OpenBSD.
// Windows has no concept of sticky bit
#[cfg(not(any(windows, target_os = "freebsd", target_os = "openbsd")))]
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().code_is(1);
}
#[test]
fn test_solo_empty_parenthetical_is_error() {
new_ucmd!().args(&["(", ")"]).run().code_is(2);
}
#[test]
fn test_parenthesized_literal() {
let scenario = TestScenario::new(util_name!());
let tests = [
"a string",
"(",
")",
"-",
"--",
"-0",
"-f",
"--help",
"--version",
"-e",
"-t",
"!",
"-n",
"-z",
"[",
"-a",
"-o",
];
for test in &tests {
scenario.ucmd().arg("(").arg(test).arg(")").succeeds();
}
// run the inverse of all these tests
for test in &tests {
scenario
.ucmd()
.arg("!")
.arg("(")
.arg(test)
.arg(")")
.run()
.code_is(1);
}
}
#[test]
fn test_parenthesized_op_compares_literal_parenthesis() {
// ensure we arent treating this case as “string length of literal equal
// sign”
new_ucmd!().args(&["(", "=", ")"]).run().code_is(1);
}
#[test]
fn test_parenthesized_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().code_is(1);
}
}
#[test]
fn test_parenthesized_right_parenthesis_as_literal() {
new_ucmd!().args(&["(", "-f", ")", ")"]).run().code_is(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()
.code_is(1);
}
#[test]
#[cfg(not(windows))]
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() {
// On some platforms (mostly the BSDs) the test fixture files copied to the
// /tmp directory will have a different gid than the current egid (due to
// the sticky bit set on the /tmp directory). Fix this before running the
// test command.
use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use uucore::process::getegid;
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let metadata = at.metadata("regular_file");
let file_gid = metadata.gid();
let user_gid = getegid();
if user_gid != file_gid {
let file_metadata_uid = metadata.uid();
let path = CString::new(at.plus("regular_file").as_os_str().as_bytes()).expect("bad path");
let r = unsafe { libc::chown(path.as_ptr(), file_metadata_uid, user_gid) };
assert_ne!(r, -1);
}
scene.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()
.code_is(1);
}
#[test]
#[cfg(not(windows))]
fn test_file_not_owned_by_egid() {
let target_file = if cfg!(target_os = "freebsd") {
// The coreutils test runner user has a primary group id of "wheel",
// which matches the gid of /bin/sh, so use /sbin/shutdown which has gid
// of "operator".
"/sbin/shutdown"
} else {
"/bin/sh"
};
new_ucmd!()
.args(&["-f", target_file, "-a", "!", "-G", target_file])
.succeeds();
}
#[test]
fn test_op_precedence_and_or_1() {
new_ucmd!().args(&[" ", "-o", "", "-a", ""]).succeeds();
}
#[test]
fn test_op_precedence_and_or_1_overridden_by_parentheses() {
new_ucmd!()
.args(&["(", " ", "-o", "", ")", "-a", ""])
.run()
.code_is(1);
}
#[test]
fn test_op_precedence_and_or_2() {
new_ucmd!()
.args(&["", "-a", "", "-o", " ", "-a", " "])
.succeeds();
}
#[test]
fn test_op_precedence_and_or_2_overridden_by_parentheses() {
new_ucmd!()
.args(&["", "-a", "(", "", "-o", " ", ")", "-a", " "])
.run()
.code_is(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().code_is(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()
.code_is(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()
.code_is(1);
new_ucmd!()
.args(&["!", "", "-a", "-n", ""])
.run()
.code_is(1);
// for compound Boolean expressions, bang inverts the _next_ expression
// only, not the entire compound expression
new_ucmd!()
.args(&["!", "", "-a", "", "-a", ""])
.run()
.code_is(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()
.code_is(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()
.code_is(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()
.code_is(2)
.stderr_is("test: extra argument 'b'\n");
}
#[test]
fn test_or_as_filename() {
new_ucmd!().args(&["x", "-a", "-z", "-o"]).run().code_is(1);
}
#[test]
#[ignore = "TODO: Busybox has this working"]
fn test_filename_or_with_equal() {
new_ucmd!()
.args(&["-f", "=", "a", "-o", "b"])
.run()
.code_is(0);
}
#[test]
#[ignore = "GNU considers this an error"]
fn test_string_length_and_nothing() {
new_ucmd!().args(&["-n", "a", "-a"]).run().code_is(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().code_is(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()
.code_is(2)
.stderr_is("[: missing ']'\n");
}
#[test]
fn test_bracket_syntax_help() {
let scenario = TestScenario::new("[");
let mut ucmd = scenario.ucmd();
ucmd.arg("--help").succeeds().stdout_contains("Usage:");
}
#[test]
fn test_bracket_syntax_version() {
let scenario = TestScenario::new("[");
let mut ucmd = scenario.ucmd();
ucmd.arg("--version")
.succeeds()
.stdout_matches(&r"\[ \d+\.\d+\.\d+".parse().unwrap());
}
#[test]
#[allow(non_snake_case)]
#[cfg(unix)]
fn test_file_N() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene.ucmd().args(&["-N", "regular_file"]).fails();
// The file will have different create/modified data
// so, test -N will return 0
sleep(std::time::Duration::from_millis(1000));
at.touch("regular_file");
scene.ucmd().args(&["-N", "regular_file"]).succeeds();
}
#[test]
fn test_long_integer() {
let scene = TestScenario::new(util_name!());
scene
.ucmd()
.args(&["18446744073709551616", "-eq", "0"])
.fails();
scene
.ucmd()
.args(&["-9223372036854775809", "-ge", "18446744073709551616"])
.fails();
scene
.ucmd()
.args(&[
"'('",
"-9223372036854775809",
"-ge",
"18446744073709551616",
"')'",
])
.fails();
}
#[test]
fn test_missing_argument_after() {
let mut ucmd = new_ucmd!();
let result = ucmd.args(&["(", "foo"]).fails();
result.no_stdout();
assert_eq!(result.exit_status().code().unwrap(), 2);
assert_eq!(
result.stderr_str().trim(),
"test: missing argument after 'foo'"
);
}