diff --git a/build.rs b/build.rs index ae38177b0..2ed8e1345 100644 --- a/build.rs +++ b/build.rs @@ -54,6 +54,29 @@ pub fn main() { for krate in crates { match krate.as_ref() { + // 'test' is named uu_test to avoid collision with rust core crate 'test'. + // It can also be invoked by name '[' for the '[ expr ] syntax'. + "uu_test" => { + mf.write_all( + format!( + "\ + \tmap.insert(\"test\", {krate}::uumain);\n\ + \t\tmap.insert(\"[\", {krate}::uumain);\n\ + ", + krate = krate + ) + .as_bytes(), + ) + .unwrap(); + tf.write_all( + format!( + "#[path=\"{dir}/test_test.rs\"]\nmod test_test;\n", + dir = util_tests_dir, + ) + .as_bytes(), + ) + .unwrap() + } k if k.starts_with(override_prefix) => { mf.write_all( format!( diff --git a/src/uu/test/src/test.rs b/src/uu/test/src/test.rs index 5f20b95f0..97a244cdc 100644 --- a/src/uu/test/src/test.rs +++ b/src/uu/test/src/test.rs @@ -12,10 +12,24 @@ mod parser; use parser::{parse, Symbol}; use std::ffi::{OsStr, OsString}; +use std::path::Path; -pub fn uumain(args: impl uucore::Args) -> i32 { - // TODO: handle being called as `[` - let args: Vec<_> = args.skip(1).collect(); +pub fn uumain(mut args: impl uucore::Args) -> i32 { + let program = args.next().unwrap_or_else(|| OsString::from("test")); + let binary_name = Path::new(&program) + .file_name() + .unwrap_or_else(|| OsStr::new("test")) + .to_string_lossy(); + let mut args: Vec<_> = args.collect(); + + // If invoked via name '[', matching ']' must be in the last arg + if binary_name == "[" { + let last = args.pop(); + if last != Some(OsString::from("]")) { + eprintln!("[: missing ']'"); + return 2; + } + } let result = parse(args).and_then(|mut stack| eval(&mut stack)); diff --git a/tests/by-util/test_test.rs b/tests/by-util/test_test.rs index c4964d6bf..36e825f2d 100644 --- a/tests/by-util/test_test.rs +++ b/tests/by-util/test_test.rs @@ -690,3 +690,31 @@ fn test_or_as_filename() { 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 ']'"); +}