From 50fe62344729fb2194cd81159c605a10d3d6651a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 28 Mar 2025 09:22:03 +0100 Subject: [PATCH 1/3] Create the uutest crate + adjust the code + move some of the tests into the program test --- .../workspace.wordlist.txt | 2 + Cargo.lock | 63 ++++++++++- Cargo.toml | 3 + tests/test_util_name.rs | 107 ++++++++++-------- tests/tests.rs | 15 ++- tests/uutests/Cargo.toml | 45 ++++++++ .../{common/mod.rs => uutests/src/lib/lib.rs} | 0 tests/{common => uutests/src/lib}/macros.rs | 0 tests/uutests/src/lib/mod.rs | 8 ++ tests/{common => uutests/src/lib}/random.rs | 0 tests/{common => uutests/src/lib}/util.rs | 32 ++++-- 11 files changed, 211 insertions(+), 64 deletions(-) create mode 100644 tests/uutests/Cargo.toml rename tests/{common/mod.rs => uutests/src/lib/lib.rs} (100%) rename tests/{common => uutests/src/lib}/macros.rs (100%) create mode 100644 tests/uutests/src/lib/mod.rs rename tests/{common => uutests/src/lib}/random.rs (100%) rename tests/{common => uutests/src/lib}/util.rs (99%) diff --git a/.vscode/cspell.dictionaries/workspace.wordlist.txt b/.vscode/cspell.dictionaries/workspace.wordlist.txt index 45373d95c72..43f56dfc2eb 100644 --- a/.vscode/cspell.dictionaries/workspace.wordlist.txt +++ b/.vscode/cspell.dictionaries/workspace.wordlist.txt @@ -325,6 +325,7 @@ libc libstdbuf musl tmpd +uchild ucmd ucommand utmpx @@ -333,6 +334,7 @@ uucore_procs uudoc uumain uutil +uutests uutils # * function names diff --git a/Cargo.lock b/Cargo.lock index b0f54656ca7..086aace1ca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,6 +448,7 @@ dependencies = [ "clap", "clap_complete", "clap_mangen", + "ctor", "filetime", "glob", "hex-literal", @@ -574,6 +575,7 @@ dependencies = [ "uu_yes", "uucore", "uuhelp_parser", + "uutests", "walkdir", "xattr", "zip", @@ -724,6 +726,22 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e9666f4a9a948d4f1dff0c08a4512b0f7c86414b23960104c243c10d79f4c3" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" + [[package]] name = "ctrlc" version = "3.4.5" @@ -817,6 +835,21 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dtor" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222ef136a1c687d4aa0395c175f2c4586e379924c352fd02f7870cf7de783c23" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" + [[package]] name = "dunce" version = "1.0.5" @@ -848,7 +881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1259,7 +1292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -1989,7 +2022,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2002,7 +2035,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.3", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2246,7 +2279,7 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix 1.0.1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3531,6 +3564,24 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" +[[package]] +name = "uutests" +version = "0.0.30" +dependencies = [ + "ctor", + "glob", + "libc", + "nix", + "pretty_assertions", + "rand 0.9.0", + "regex", + "rlimit", + "tempfile", + "time", + "uucore", + "xattr", +] + [[package]] name = "uutils_term_grid" version = "0.6.0" @@ -3670,7 +3721,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a0fd3f19d44..226bd28d0e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -366,6 +366,7 @@ uucore = { version = "0.0.30", package = "uucore", path = "src/uucore" } uucore_procs = { version = "0.0.30", package = "uucore_procs", path = "src/uucore_procs" } uu_ls = { version = "0.0.30", path = "src/uu/ls" } uu_base32 = { version = "0.0.30", path = "src/uu/base32" } +uutests = { version = "0.0.30", package = "uutests", path = "tests/uutests/" } [dependencies] clap = { workspace = true } @@ -505,6 +506,7 @@ sha1 = { workspace = true, features = ["std"] } tempfile = { workspace = true } time = { workspace = true, features = ["local-offset"] } unindent = "0.2.3" +uutests = { workspace = true } uucore = { workspace = true, features = [ "mode", "entries", @@ -515,6 +517,7 @@ uucore = { workspace = true, features = [ walkdir = { workspace = true } hex-literal = "1.0.0" rstest = { workspace = true } +ctor = "0.4.1" [target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies] procfs = { version = "0.17", default-features = false } diff --git a/tests/test_util_name.rs b/tests/test_util_name.rs index dd8cd93592f..789077b5caa 100644 --- a/tests/test_util_name.rs +++ b/tests/test_util_name.rs @@ -2,15 +2,26 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#![allow(unused_imports)] -mod common; - -use common::util::TestScenario; +use uutests::util::TestScenario; #[cfg(unix)] use std::os::unix::fs::symlink as symlink_file; -#[cfg(windows)] -use std::os::windows::fs::symlink_file; + +use std::env; +pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_coreutils"); + +// Set the environment variable for any tests + +// Use the ctor attribute to run this function before any tests +#[ctor::ctor] +fn init() { + // No need for unsafe here + unsafe { + std::env::set_var("UUTESTS_BINARY_PATH", TESTS_BINARY); + } + // Print for debugging + eprintln!("Setting UUTESTS_BINARY_PATH={}", TESTS_BINARY); +} #[test] #[cfg(feature = "ls")] @@ -18,6 +29,10 @@ fn execution_phrase_double() { use std::process::Command; let scenario = TestScenario::new("ls"); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } let output = Command::new(&scenario.bin_path) .arg("ls") .arg("--some-invalid-arg") @@ -30,25 +45,6 @@ fn execution_phrase_double() { ); } -#[test] -#[cfg(feature = "ls")] -#[cfg(any(unix, windows))] -fn execution_phrase_single() { - use std::process::Command; - - let scenario = TestScenario::new("ls"); - symlink_file(&scenario.bin_path, scenario.fixtures.plus("uu-ls")).unwrap(); - let output = Command::new(scenario.fixtures.plus("uu-ls")) - .arg("--some-invalid-arg") - .output() - .unwrap(); - dbg!(String::from_utf8(output.stderr.clone()).unwrap()); - assert!(String::from_utf8(output.stderr).unwrap().contains(&format!( - "Usage: {}", - scenario.fixtures.plus("uu-ls").display() - ))); -} - #[test] #[cfg(feature = "sort")] fn util_name_double() { @@ -58,6 +54,10 @@ fn util_name_double() { }; let scenario = TestScenario::new("sort"); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } let mut child = Command::new(&scenario.bin_path) .arg("sort") .stdin(Stdio::piped()) @@ -72,7 +72,7 @@ fn util_name_double() { #[test] #[cfg(feature = "sort")] -#[cfg(any(unix, windows))] +#[cfg(unix)] fn util_name_single() { use std::{ io::Write, @@ -80,6 +80,11 @@ fn util_name_single() { }; let scenario = TestScenario::new("sort"); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } + symlink_file(&scenario.bin_path, scenario.fixtures.plus("uu-sort")).unwrap(); let mut child = Command::new(scenario.fixtures.plus("uu-sort")) .stdin(Stdio::piped()) @@ -96,14 +101,15 @@ fn util_name_single() { } #[test] -#[cfg(any(unix, windows))] +#[cfg(unix)] fn util_invalid_name_help() { - use std::{ - io::Write, - process::{Command, Stdio}, - }; + use std::process::{Command, Stdio}; let scenario = TestScenario::new("invalid_name"); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } symlink_file(&scenario.bin_path, scenario.fixtures.plus("invalid_name")).unwrap(); let child = Command::new(scenario.fixtures.plus("invalid_name")) .arg("--help") @@ -132,14 +138,17 @@ fn util_non_utf8_name_help() { // Make sure we don't crash even if the util name is invalid UTF-8. use std::{ ffi::OsStr, - io::Write, os::unix::ffi::OsStrExt, - path::Path, process::{Command, Stdio}, }; let scenario = TestScenario::new("invalid_name"); let non_utf8_path = scenario.fixtures.plus(OsStr::from_bytes(b"\xff")); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } + symlink_file(&scenario.bin_path, &non_utf8_path).unwrap(); let child = Command::new(&non_utf8_path) .arg("--help") @@ -160,15 +169,17 @@ fn util_non_utf8_name_help() { } #[test] -#[cfg(any(unix, windows))] +#[cfg(unix)] fn util_invalid_name_invalid_command() { - use std::{ - io::Write, - process::{Command, Stdio}, - }; + use std::process::{Command, Stdio}; let scenario = TestScenario::new("invalid_name"); symlink_file(&scenario.bin_path, scenario.fixtures.plus("invalid_name")).unwrap(); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } + let child = Command::new(scenario.fixtures.plus("invalid_name")) .arg("definitely_invalid") .stdin(Stdio::piped()) @@ -188,12 +199,14 @@ fn util_invalid_name_invalid_command() { #[test] #[cfg(feature = "true")] fn util_completion() { - use std::{ - io::Write, - process::{Command, Stdio}, - }; + use std::process::{Command, Stdio}; let scenario = TestScenario::new("completion"); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } + let child = Command::new(&scenario.bin_path) .arg("completion") .arg("true") @@ -216,12 +229,14 @@ fn util_completion() { #[test] #[cfg(feature = "true")] fn util_manpage() { - use std::{ - io::Write, - process::{Command, Stdio}, - }; + use std::process::{Command, Stdio}; let scenario = TestScenario::new("completion"); + if !scenario.bin_path.exists() { + println!("Skipping test: Binary not found at {:?}", scenario.bin_path); + return; + } + let child = Command::new(&scenario.bin_path) .arg("manpage") .arg("true") diff --git a/tests/tests.rs b/tests/tests.rs index 1fb5735eb71..a4eaaacff51 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,8 +2,19 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#[macro_use] -mod common; + +// Then override the macro with your constant +use std::env; + +pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_coreutils"); + +// Use the ctor attribute to run this function before any tests +#[ctor::ctor] +fn init() { + unsafe { + std::env::set_var("UUTESTS_BINARY_PATH", TESTS_BINARY); + } +} #[cfg(feature = "arch")] #[path = "by-util/test_arch.rs"] diff --git a/tests/uutests/Cargo.toml b/tests/uutests/Cargo.toml new file mode 100644 index 00000000000..2ede0214840 --- /dev/null +++ b/tests/uutests/Cargo.toml @@ -0,0 +1,45 @@ +# spell-checker:ignore (features) zerocopy serde + +[package] +name = "uutests" +version = "0.0.30" +authors = ["uutils developers"] +license = "MIT" +description = "uutils ~ 'core' uutils test library (cross-platform)" + +homepage = "https://github.com/uutils/coreutils" +repository = "https://github.com/uutils/coreutils/tree/main/src/tests/common" +# readme = "README.md" +keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"] +categories = ["command-line-utilities"] +edition = "2024" + +[package.metadata.docs.rs] +all-features = true + +[lib] +path = "src/lib/lib.rs" + +[dependencies] +glob = { workspace = true } +libc = { workspace = true } +pretty_assertions = "1.4.0" +rand = { workspace = true } +regex = { workspace = true } +tempfile = { workspace = true } +time = { workspace = true, features = ["local-offset"] } +uucore = { workspace = true, features = [ + "mode", + "entries", + "process", + "signals", + "utmpx", +] } +ctor = "0.4.1" + +[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] + +[target.'cfg(unix)'.dependencies] +nix = { workspace = true, features = ["process", "signal", "user", "term"] } +rlimit = "0.10.1" +xattr = { workspace = true } diff --git a/tests/common/mod.rs b/tests/uutests/src/lib/lib.rs similarity index 100% rename from tests/common/mod.rs rename to tests/uutests/src/lib/lib.rs diff --git a/tests/common/macros.rs b/tests/uutests/src/lib/macros.rs similarity index 100% rename from tests/common/macros.rs rename to tests/uutests/src/lib/macros.rs diff --git a/tests/uutests/src/lib/mod.rs b/tests/uutests/src/lib/mod.rs new file mode 100644 index 00000000000..05e2b13824c --- /dev/null +++ b/tests/uutests/src/lib/mod.rs @@ -0,0 +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. +#[macro_use] +pub mod macros; +pub mod random; +pub mod util; diff --git a/tests/common/random.rs b/tests/uutests/src/lib/random.rs similarity index 100% rename from tests/common/random.rs rename to tests/uutests/src/lib/random.rs diff --git a/tests/common/util.rs b/tests/uutests/src/lib/util.rs similarity index 99% rename from tests/common/util.rs rename to tests/uutests/src/lib/util.rs index 4b7acaa5430..79c5ac96b4c 100644 --- a/tests/common/util.rs +++ b/tests/uutests/src/lib/util.rs @@ -2,7 +2,6 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. - //spell-checker: ignore (linux) rlimit prlimit coreutil ggroups uchild uncaptured scmd SHLVL canonicalized openpty //spell-checker: ignore (linux) winsize xpixel ypixel setrlimit FSIZE SIGBUS SIGSEGV sigbus tmpfs @@ -22,8 +21,6 @@ use nix::sys; use pretty_assertions::assert_eq; #[cfg(unix)] use rlimit::setrlimit; -#[cfg(feature = "sleep")] -use rstest::rstest; use std::borrow::Cow; use std::collections::VecDeque; #[cfg(not(windows))] @@ -63,7 +60,21 @@ static MULTIPLE_STDIN_MEANINGLESS: &str = "Ucommand is designed around a typical static NO_STDIN_MEANINGLESS: &str = "Setting this flag has no effect if there is no stdin"; static END_OF_TRANSMISSION_SEQUENCE: &[u8] = b"\n\x04"; -pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_coreutils"); +// we can't use +// pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_coreutils"); +// as we are in a library, not a binary +pub fn get_tests_binary() -> String { + std::env::var("CARGO_BIN_EXE_coreutils").unwrap_or_else(|_| { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let debug_or_release = if cfg!(debug_assertions) { + "debug" + } else { + "release" + }; + format!("{manifest_dir}/../../target/{debug_or_release}/coreutils") + }) +} + pub const PATH: &str = env!("PATH"); /// Default environment variables to run the commands with @@ -1178,8 +1189,9 @@ impl TestScenario { T: AsRef, { let tmpd = Rc::new(TempDir::new().unwrap()); + println!("bin: {:?}", get_tests_binary()); let ts = Self { - bin_path: PathBuf::from(TESTS_BINARY), + bin_path: PathBuf::from(get_tests_binary()), util_name: util_name.as_ref().into(), fixtures: AtPath::new(tmpd.as_ref().path()), tmpd, @@ -1343,7 +1355,7 @@ impl UCommand { { let mut ucmd = Self::new(); ucmd.util_name = Some(util_name.as_ref().into()); - ucmd.bin_path(TESTS_BINARY).temp_dir(tmpd); + ucmd.bin_path(&*get_tests_binary()).temp_dir(tmpd); ucmd } @@ -1604,7 +1616,7 @@ impl UCommand { self.args.push_front(util_name.into()); } } else if let Some(util_name) = &self.util_name { - self.bin_path = Some(PathBuf::from(TESTS_BINARY)); + self.bin_path = Some(PathBuf::from(&*get_tests_binary())); self.args.push_front(util_name.into()); // neither `bin_path` nor `util_name` was set so we apply the default to run the arguments // in a platform specific shell @@ -2762,7 +2774,7 @@ const UUTILS_INFO: &str = "uutils-tests-info"; /// Example: /// /// ```no_run -/// use crate::common::util::*; +/// use uutests::util::*; /// const VERSION_MIN_MULTIPLE_USERS: &str = "8.31"; /// /// #[test] @@ -2838,7 +2850,7 @@ fn parse_coreutil_version(version_string: &str) -> f32 { /// Example: /// /// ```no_run -/// use crate::common::util::*; +/// use uutests::util::*; /// #[test] /// fn test_xyz() { /// let ts = TestScenario::new(util_name!()); @@ -2901,7 +2913,7 @@ pub fn expected_result(ts: &TestScenario, args: &[&str]) -> std::result::Result< /// Example: /// /// ```no_run -/// use crate::common::util::*; +/// use uutests::util::*; /// #[test] /// fn test_xyz() { /// let ts = TestScenario::new("whoami"); From ccfcda531eef4aeab14659773a7c618e391ca931 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 28 Mar 2025 09:47:40 +0100 Subject: [PATCH 2/3] uutests: improve the docs --- tests/uutests/src/lib/macros.rs | 10 +- tests/uutests/src/lib/util.rs | 844 +++++--------------------------- 2 files changed, 124 insertions(+), 730 deletions(-) diff --git a/tests/uutests/src/lib/macros.rs b/tests/uutests/src/lib/macros.rs index 4902ca49b4d..cc245a2082e 100644 --- a/tests/uutests/src/lib/macros.rs +++ b/tests/uutests/src/lib/macros.rs @@ -47,8 +47,8 @@ macro_rules! util_name { /// 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 +/// [`UCommand`]: crate::util::UCommand +/// [`TestScenario`]: crate::util::TestScenario #[macro_export] macro_rules! new_ucmd { () => { @@ -65,9 +65,9 @@ macro_rules! new_ucmd { /// 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 +/// [`UCommand`]: crate::util::UCommand +/// [`AtPath`]: crate::util::AtPath +/// [`TestScenario`]: crate::util::TestScenario #[macro_export] macro_rules! at_and_ucmd { () => {{ diff --git a/tests/uutests/src/lib/util.rs b/tests/uutests/src/lib/util.rs index 79c5ac96b4c..bef500f5ce2 100644 --- a/tests/uutests/src/lib/util.rs +++ b/tests/uutests/src/lib/util.rs @@ -49,6 +49,8 @@ use std::time::{Duration, Instant}; use std::{env, hint, mem, thread}; use tempfile::{Builder, TempDir}; +use std::sync::OnceLock; + static TESTS_DIR: &str = "tests"; static FIXTURES_DIR: &str = "fixtures"; @@ -60,19 +62,26 @@ static MULTIPLE_STDIN_MEANINGLESS: &str = "Ucommand is designed around a typical static NO_STDIN_MEANINGLESS: &str = "Setting this flag has no effect if there is no stdin"; static END_OF_TRANSMISSION_SEQUENCE: &[u8] = b"\n\x04"; -// we can't use -// pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_coreutils"); -// as we are in a library, not a binary -pub fn get_tests_binary() -> String { - std::env::var("CARGO_BIN_EXE_coreutils").unwrap_or_else(|_| { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let debug_or_release = if cfg!(debug_assertions) { - "debug" - } else { - "release" - }; - format!("{manifest_dir}/../../target/{debug_or_release}/coreutils") +static TESTS_BINARY_PATH: OnceLock = OnceLock::new(); +/// This function needs the env variable UUTESTS_BINARY_PATH +/// which will very probably be env!("`CARGO_BIN_EXE_`") +/// because here, we are in a crate but we need the name of the final binary +pub fn get_tests_binary() -> &'static str { + TESTS_BINARY_PATH.get_or_init(|| { + if let Ok(path) = env::var("UUTESTS_BINARY_PATH") { + return PathBuf::from(path); + } + panic!("Could not determine coreutils binary path. Please set UUTESTS_BINARY_PATH environment variable"); }) + .to_str() + .unwrap() +} + +#[macro_export] +macro_rules! get_tests_binary { + () => { + $crate::util::get_tests_binary() + }; } pub const PATH: &str = env!("PATH"); @@ -394,6 +403,13 @@ impl CmdResult { self.exit_status().code().unwrap() } + /// Verify the exit code of the program + /// + /// # Examples + /// + /// ```rust,ignore + /// new_ucmd!().arg("--definitely-invalid").fails().code_is(1); + /// ``` #[track_caller] pub fn code_is(&self, expected_code: i32) -> &Self { let fails = self.code() != expected_code; @@ -452,6 +468,12 @@ impl CmdResult { /// but you might find yourself using this function if /// 1. you can not know exactly what stdout will be or /// 2. you know that stdout will also be empty + /// + /// # Examples + /// + /// ```rust,ignore + /// scene.ucmd().fails().no_stderr(); + /// ``` #[track_caller] pub fn no_stderr(&self) -> &Self { assert!( @@ -468,6 +490,13 @@ impl CmdResult { /// but you might find yourself using this function if /// 1. you can not know exactly what stderr will be or /// 2. you know that stderr will also be empty + /// new_ucmd!() + /// + /// # Examples + /// + /// ```rust,ignore + /// scene.ucmd().fails().no_stdout(); + /// ``` #[track_caller] pub fn no_stdout(&self) -> &Self { assert!( @@ -690,6 +719,16 @@ impl CmdResult { )) } + /// Verify if stdout contains a specific string + /// + /// # Examples + /// + /// ```rust,ignore + /// new_ucmd!() + /// .arg("--help") + /// .succeeds() + /// .stdout_contains("Options:"); + /// ``` #[track_caller] pub fn stdout_contains>(&self, cmp: T) -> &Self { assert!( @@ -701,6 +740,16 @@ impl CmdResult { self } + /// Verify if stdout contains a specific line + /// + /// # Examples + /// + /// ```rust,ignore + /// new_ucmd!() + /// .arg("--help") + /// .succeeds() + /// .stdout_contains_line("Options:"); + /// ``` #[track_caller] pub fn stdout_contains_line>(&self, cmp: T) -> &Self { assert!( @@ -712,6 +761,17 @@ impl CmdResult { self } + /// Verify if stderr contains a specific string + /// + /// # Examples + /// + /// ```rust,ignore + /// new_ucmd!() + /// .arg("-l") + /// .arg("IaMnOtAsIgNaL") + /// .fails() + /// .stderr_contains("IaMnOtAsIgNaL"); + /// ``` #[track_caller] pub fn stderr_contains>(&self, cmp: T) -> &Self { assert!( @@ -723,6 +783,17 @@ impl CmdResult { self } + /// Verify if stdout does not contain a specific string + /// + /// # Examples + /// + /// ```rust,ignore + /// new_ucmd!() + /// .arg("-l") + /// .arg("IaMnOtAsIgNaL") + /// .fails() + /// .stdout_does_not_contain("Valid-signal"); + /// ``` #[track_caller] pub fn stdout_does_not_contain>(&self, cmp: T) -> &Self { assert!( @@ -734,6 +805,17 @@ impl CmdResult { self } + /// Verify if st stderr does not contain a specific string + /// + /// # Examples + /// + /// ```rust,ignore + /// new_ucmd!() + /// .arg("-l") + /// .arg("IaMnOtAsIgNaL") + /// .fails() + /// .stderr_does_not_contain("Valid-signal"); + /// ``` #[track_caller] pub fn stderr_does_not_contain>(&self, cmp: T) -> &Self { assert!(!self.stderr_str().contains(cmp.as_ref())); @@ -1189,9 +1271,9 @@ impl TestScenario { T: AsRef, { let tmpd = Rc::new(TempDir::new().unwrap()); - println!("bin: {:?}", get_tests_binary()); + println!("bin: {:?}", get_tests_binary!()); let ts = Self { - bin_path: PathBuf::from(get_tests_binary()), + bin_path: PathBuf::from(get_tests_binary!()), util_name: util_name.as_ref().into(), fixtures: AtPath::new(tmpd.as_ref().path()), tmpd, @@ -1284,10 +1366,10 @@ impl Drop for TestScenario { #[cfg(unix)] #[derive(Debug, Default)] pub struct TerminalSimulation { - size: Option, - stdin: bool, - stdout: bool, - stderr: bool, + pub size: Option, + pub stdin: bool, + pub stdout: bool, + pub stderr: bool, } /// A `UCommand` is a builder wrapping an individual Command that provides several additional features: @@ -1355,7 +1437,7 @@ impl UCommand { { let mut ucmd = Self::new(); ucmd.util_name = Some(util_name.as_ref().into()); - ucmd.bin_path(&*get_tests_binary()).temp_dir(tmpd); + ucmd.bin_path(&*get_tests_binary!()).temp_dir(tmpd); ucmd } @@ -1392,7 +1474,8 @@ impl UCommand { /// Set the working directory for this [`UCommand`] /// - /// Per default the working directory is set to the [`UCommands`] temporary directory. + /// Per default the working directory is set to the [`UCommand`] temporary directory. + /// pub fn current_dir(&mut self, current_dir: T) -> &mut Self where T: Into, @@ -1505,7 +1588,7 @@ impl UCommand { /// /// After the timeout elapsed these `run` methods (besides [`UCommand::run_no_wait`]) will /// panic. When [`UCommand::run_no_wait`] is used, this timeout is applied to - /// [`UChild::wait_with_output`] including all other waiting methods in [`UChild`] implicitly + /// `wait_with_output` including all other waiting methods in [`UChild`] implicitly /// using `wait_with_output()` and additionally [`UChild::kill`]. The default timeout of `kill` /// will be overwritten by this `timeout`. pub fn timeout(&mut self, timeout: Duration) -> &mut Self { @@ -1616,7 +1699,7 @@ impl UCommand { self.args.push_front(util_name.into()); } } else if let Some(util_name) = &self.util_name { - self.bin_path = Some(PathBuf::from(&*get_tests_binary())); + self.bin_path = Some(PathBuf::from(&*get_tests_binary!())); self.args.push_front(util_name.into()); // neither `bin_path` nor `util_name` was set so we apply the default to run the arguments // in a platform specific shell @@ -1811,7 +1894,6 @@ impl UCommand { let (mut command, captured_stdout, captured_stderr, stdin_pty) = self.build(); log_info("run", self.to_string()); - let child = command.spawn().unwrap(); let mut child = UChild::from(self, child, captured_stdout, captured_stderr, stdin_pty); @@ -2307,12 +2389,12 @@ impl UChild { /// Wait for the child process to terminate and return a [`CmdResult`]. /// - /// See [`UChild::wait_with_output`] for details on timeouts etc. This method can also be run if + /// See `wait_with_output` for details on timeouts etc. This method can also be run if /// the child process was killed with [`UChild::kill`]. /// /// # Errors /// - /// Returns the error from the call to [`UChild::wait_with_output`] if any + /// Returns the error from the call to `wait_with_output` if any pub fn wait(self) -> io::Result { let (bin_path, util_name, tmpd) = ( self.bin_path.clone(), @@ -2591,9 +2673,7 @@ impl UChild { /// the methods below when exiting the child process. /// /// * [`UChild::wait`] - /// * [`UChild::wait_with_output`] /// * [`UChild::pipe_in_and_wait`] - /// * [`UChild::pipe_in_and_wait_with_output`] /// /// Usually, there's no need to join manually but if needed, the [`UChild::join`] method can be /// used . @@ -2650,7 +2730,7 @@ impl UChild { /// [`UChild::pipe_in`]. /// /// # Errors - /// If [`ChildStdin::write_all`] or [`ChildStdin::flush`] returned an error + /// If [`std::process::ChildStdin::write_all`] or [`std::process::ChildStdin::flush`] returned an error pub fn try_write_in>>(&mut self, data: T) -> io::Result<()> { let ignore_stdin_write_error = self.ignore_stdin_write_error; let mut writer = self.access_stdin_as_writer(); @@ -2672,7 +2752,7 @@ impl UChild { /// Close the child process stdout. /// - /// Note this will have no effect if the output was captured with [`CapturedOutput`] which is the + /// Note this will have no effect if the output was captured with CapturedOutput which is the /// default if [`UCommand::set_stdout`] wasn't called. pub fn close_stdout(&mut self) -> &mut Self { self.raw.stdout.take(); @@ -2681,7 +2761,7 @@ impl UChild { /// Close the child process stderr. /// - /// Note this will have no effect if the output was captured with [`CapturedOutput`] which is the + /// Note this will have no effect if the output was captured with CapturedOutput which is the /// default if [`UCommand::set_stderr`] wasn't called. pub fn close_stderr(&mut self) -> &mut Self { self.raw.stderr.take(); @@ -2986,6 +3066,15 @@ mod tests { // spell-checker:ignore (tests) asdfsadfa use super::*; + // Create a init for the test with a fake value (not needed) + #[cfg(test)] + #[ctor::ctor] + fn init() { + unsafe { + std::env::set_var("UUTESTS_BINARY_PATH", ""); + } + } + pub fn run_cmd>(cmd: T) -> CmdResult { UCommand::new().arg(cmd).run() } @@ -3193,168 +3282,6 @@ mod tests { res.stdout_does_not_match(&positive); } - #[cfg(feature = "echo")] - #[test] - fn test_normalized_newlines_stdout_is() { - let ts = TestScenario::new("echo"); - let res = ts.ucmd().args(&["-ne", "A\r\nB\nC"]).run(); - - res.normalized_newlines_stdout_is("A\r\nB\nC"); - res.normalized_newlines_stdout_is("A\nB\nC"); - res.normalized_newlines_stdout_is("A\nB\r\nC"); - } - - #[cfg(feature = "echo")] - #[test] - #[should_panic] - fn test_normalized_newlines_stdout_is_fail() { - let ts = TestScenario::new("echo"); - let res = ts.ucmd().args(&["-ne", "A\r\nB\nC"]).run(); - - res.normalized_newlines_stdout_is("A\r\nB\nC\n"); - } - - #[cfg(feature = "echo")] - #[test] - fn test_cmd_result_stdout_check_and_stdout_str_check() { - let result = TestScenario::new("echo").ucmd().arg("Hello world").run(); - - result.stdout_str_check(|stdout| stdout.ends_with("world\n")); - result.stdout_check(|stdout| stdout.get(0..2).unwrap().eq(b"He")); - result.no_stderr(); - } - - #[cfg(feature = "echo")] - #[test] - fn test_cmd_result_stderr_check_and_stderr_str_check() { - let ts = TestScenario::new("echo"); - let result = run_cmd(format!( - "{} {} Hello world >&2", - ts.bin_path.display(), - ts.util_name - )); - - result.stderr_str_check(|stderr| stderr.ends_with("world\n")); - result.stderr_check(|stdout| stdout.get(0..2).unwrap().eq(b"He")); - result.no_stdout(); - } - - #[cfg(feature = "echo")] - #[test] - #[should_panic] - fn test_cmd_result_stdout_str_check_when_false_then_panics() { - let result = TestScenario::new("echo").ucmd().arg("Hello world").run(); - result.stdout_str_check(str::is_empty); - } - - #[cfg(feature = "echo")] - #[test] - #[should_panic] - fn test_cmd_result_stdout_check_when_false_then_panics() { - let result = TestScenario::new("echo").ucmd().arg("Hello world").run(); - result.stdout_check(<[u8]>::is_empty); - } - - #[cfg(feature = "echo")] - #[test] - #[should_panic] - fn test_cmd_result_stderr_str_check_when_false_then_panics() { - let result = TestScenario::new("echo").ucmd().arg("Hello world").run(); - result.stderr_str_check(|s| !s.is_empty()); - } - - #[cfg(feature = "echo")] - #[test] - #[should_panic] - fn test_cmd_result_stderr_check_when_false_then_panics() { - let result = TestScenario::new("echo").ucmd().arg("Hello world").run(); - result.stderr_check(|s| !s.is_empty()); - } - - #[cfg(feature = "echo")] - #[test] - #[should_panic] - fn test_cmd_result_stdout_check_when_predicate_panics_then_panic() { - let result = TestScenario::new("echo").ucmd().run(); - result.stdout_str_check(|_| panic!("Just testing")); - } - - #[cfg(feature = "echo")] - #[cfg(unix)] - #[test] - fn test_cmd_result_signal_when_normal_exit_then_no_signal() { - let result = TestScenario::new("echo").ucmd().run(); - assert!(result.signal().is_none()); - } - - #[cfg(feature = "sleep")] - #[cfg(unix)] - #[test] - #[should_panic = "Program must be run first or has not finished"] - fn test_cmd_result_signal_when_still_running_then_panic() { - let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); - - child - .make_assertion() - .is_alive() - .with_current_output() - .signal(); - } - - #[cfg(feature = "sleep")] - #[cfg(unix)] - #[test] - fn test_cmd_result_signal_when_kill_then_signal() { - let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); - - child.kill(); - child - .make_assertion() - .is_not_alive() - .with_current_output() - .signal_is(9) - .signal_name_is("SIGKILL") - .signal_name_is("KILL") - .signal_name_is("9") - .signal() - .expect("Signal was none"); - - let result = child.wait().unwrap(); - result - .signal_is(9) - .signal_name_is("SIGKILL") - .signal_name_is("KILL") - .signal_name_is("9") - .signal() - .expect("Signal was none"); - } - - #[cfg(feature = "sleep")] - #[cfg(unix)] - #[rstest] - #[case::signal_only_part_of_name("IGKILL")] // spell-checker: disable-line - #[case::signal_just_sig("SIG")] - #[case::signal_value_too_high("100")] - #[case::signal_value_negative("-1")] - #[should_panic = "Invalid signal name or value"] - fn test_cmd_result_signal_when_invalid_signal_name_then_panic(#[case] signal_name: &str) { - let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); - child.kill(); - let result = child.wait().unwrap(); - result.signal_name_is(signal_name); - } - - #[test] - #[cfg(feature = "sleep")] - #[cfg(unix)] - fn test_cmd_result_signal_name_is_accepts_lowercase() { - let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); - child.kill(); - let result = child.wait().unwrap(); - result.signal_name_is("sigkill"); - result.signal_name_is("kill"); - } - #[test] #[cfg(unix)] fn test_parse_coreutil_version() { @@ -3437,7 +3364,6 @@ mod tests { #[test] #[cfg(unix)] - #[cfg(feature = "whoami")] fn test_run_ucmd_as_root() { if is_ci() { println!("TEST SKIPPED (cannot run inside CI)"); @@ -3463,327 +3389,6 @@ mod tests { } } - // This error was first detected when running tail so tail is used here but - // should fail with any command that takes piped input. - // See also https://github.com/uutils/coreutils/issues/3895 - #[cfg(feature = "tail")] - #[test] - #[cfg_attr(not(feature = "expensive_tests"), ignore)] - fn test_when_piped_input_then_no_broken_pipe() { - let ts = TestScenario::new("tail"); - for i in 0..10000 { - dbg!(i); - let test_string = "a\nb\n"; - ts.ucmd() - .args(&["-n", "0"]) - .pipe_in(test_string) - .succeeds() - .no_stdout() - .no_stderr(); - } - } - - #[cfg(feature = "echo")] - #[test] - fn test_uchild_when_run_with_a_non_blocking_util() { - let ts = TestScenario::new("echo"); - ts.ucmd() - .arg("hello world") - .run() - .success() - .stdout_only("hello world\n"); - } - - // Test basically that most of the methods of UChild are working - #[cfg(feature = "echo")] - #[test] - fn test_uchild_when_run_no_wait_with_a_non_blocking_util() { - let ts = TestScenario::new("echo"); - let mut child = ts.ucmd().arg("hello world").run_no_wait(); - - // check `child.is_alive()` and `child.delay()` is working - let mut trials = 10; - while child.is_alive() { - assert!( - trials > 0, - "Assertion failed: child process is still alive." - ); - - child.delay(500); - trials -= 1; - } - - assert!(!child.is_alive()); - - // check `child.is_not_alive()` is working - assert!(child.is_not_alive()); - - // check the current output is correct - std::assert_eq!(child.stdout(), "hello world\n"); - assert!(child.stderr().is_empty()); - - // check the current output of echo is empty. We already called `child.stdout()` and `echo` - // exited so there's no additional output after the first call of `child.stdout()` - assert!(child.stdout().is_empty()); - assert!(child.stderr().is_empty()); - - // check that we're still able to access all output of the child process, even after exit - // and call to `child.stdout()` - std::assert_eq!(child.stdout_all(), "hello world\n"); - assert!(child.stderr_all().is_empty()); - - // we should be able to call kill without panics, even if the process already exited - child.make_assertion().is_not_alive(); - child.kill(); - - // we should be able to call wait without panics and apply some assertions - child.wait().unwrap().code_is(0).no_stdout().no_stderr(); - } - - #[cfg(feature = "cat")] - #[test] - fn test_uchild_when_pipe_in() { - let ts = TestScenario::new("cat"); - let mut child = ts.ucmd().set_stdin(Stdio::piped()).run_no_wait(); - child.pipe_in("content"); - child.wait().unwrap().stdout_only("content").success(); - - ts.ucmd().pipe_in("content").run().stdout_is("content"); - } - - #[cfg(feature = "rm")] - #[test] - fn test_uchild_when_run_no_wait_with_a_blocking_command() { - let ts = TestScenario::new("rm"); - let at = &ts.fixtures; - - at.mkdir("a"); - at.touch("a/empty"); - - #[cfg(target_vendor = "apple")] - let delay: u64 = 2000; - #[cfg(not(target_vendor = "apple"))] - let delay: u64 = 1000; - - let yes = if cfg!(windows) { "y\r\n" } else { "y\n" }; - - let mut child = ts - .ucmd() - .set_stdin(Stdio::piped()) - .stderr_to_stdout() - .args(&["-riv", "a"]) - .run_no_wait(); - child - .make_assertion_with_delay(delay) - .is_alive() - .with_current_output() - .stdout_is("rm: descend into directory 'a'? "); - - #[cfg(windows)] - let expected = "rm: descend into directory 'a'? \ - rm: remove regular empty file 'a\\empty'? "; - #[cfg(unix)] - let expected = "rm: descend into directory 'a'? \ - rm: remove regular empty file 'a/empty'? "; - child.write_in(yes); - child - .make_assertion_with_delay(delay) - .is_alive() - .with_all_output() - .stdout_is(expected); - - #[cfg(windows)] - let expected = "removed 'a\\empty'\nrm: remove directory 'a'? "; - #[cfg(unix)] - let expected = "removed 'a/empty'\nrm: remove directory 'a'? "; - - child - .write_in(yes) - .make_assertion_with_delay(delay) - .is_alive() - .with_exact_output(44, 0) - .stdout_only(expected); - - let expected = "removed directory 'a'\n"; - - child.write_in(yes); - child.wait().unwrap().stdout_only(expected).success(); - } - - #[cfg(feature = "tail")] - #[test] - fn test_uchild_when_run_with_stderr_to_stdout() { - let ts = TestScenario::new("tail"); - let at = &ts.fixtures; - - at.write("data", "file data\n"); - - let expected_stdout = "==> data <==\n\ - file data\n\ - tail: cannot open 'missing' for reading: No such file or directory\n"; - ts.ucmd() - .args(&["data", "missing"]) - .stderr_to_stdout() - .fails() - .stdout_only(expected_stdout); - } - - #[cfg(feature = "cat")] - #[cfg(unix)] - #[test] - fn test_uchild_when_no_capture_reading_from_infinite_source() { - use regex::Regex; - - let ts = TestScenario::new("cat"); - - let expected_stdout = b"\0".repeat(12345); - let mut child = ts - .ucmd() - .set_stdin(Stdio::from(File::open("/dev/zero").unwrap())) - .set_stdout(Stdio::piped()) - .run_no_wait(); - - child - .make_assertion() - .with_exact_output(12345, 0) - .stdout_only_bytes(expected_stdout); - - child - .kill() - .make_assertion() - .with_current_output() - .stdout_matches(&Regex::new("[\0].*").unwrap()) - .no_stderr(); - } - - #[cfg(feature = "sleep")] - #[test] - fn test_uchild_when_wait_and_timeout_is_reached_then_timeout_error() { - let ts = TestScenario::new("sleep"); - let child = ts - .ucmd() - .timeout(Duration::from_secs(1)) - .arg("10.0") - .run_no_wait(); - - match child.wait() { - Err(error) if error.kind() == io::ErrorKind::Other => { - std::assert_eq!(error.to_string(), "wait: Timeout of '1s' reached"); - } - Err(error) => panic!("Assertion failed: Expected error with timeout but was: {error}"), - Ok(_) => panic!("Assertion failed: Expected timeout of `wait`."), - } - } - - #[cfg(feature = "sleep")] - #[rstest] - #[timeout(Duration::from_secs(5))] - fn test_uchild_when_kill_and_timeout_higher_than_kill_time_then_no_panic() { - let ts = TestScenario::new("sleep"); - let mut child = ts - .ucmd() - .timeout(Duration::from_secs(60)) - .arg("20.0") - .run_no_wait(); - - child.kill().make_assertion().is_not_alive(); - } - - #[cfg(feature = "sleep")] - #[test] - fn test_uchild_when_try_kill_and_timeout_is_reached_then_error() { - let ts = TestScenario::new("sleep"); - let mut child = ts.ucmd().timeout(Duration::ZERO).arg("10.0").run_no_wait(); - - match child.try_kill() { - Err(error) if error.kind() == io::ErrorKind::Other => { - std::assert_eq!(error.to_string(), "kill: Timeout of '0s' reached"); - } - Err(error) => panic!("Assertion failed: Expected error with timeout but was: {error}"), - Ok(()) => panic!("Assertion failed: Expected timeout of `try_kill`."), - } - } - - #[cfg(feature = "sleep")] - #[test] - #[should_panic = "kill: Timeout of '0s' reached"] - fn test_uchild_when_kill_with_timeout_and_timeout_is_reached_then_panic() { - let ts = TestScenario::new("sleep"); - let mut child = ts.ucmd().timeout(Duration::ZERO).arg("10.0").run_no_wait(); - - child.kill(); - panic!("Assertion failed: Expected timeout of `kill`."); - } - - #[cfg(feature = "sleep")] - #[test] - #[should_panic(expected = "wait: Timeout of '1.1s' reached")] - fn test_ucommand_when_run_with_timeout_and_timeout_is_reached_then_panic() { - let ts = TestScenario::new("sleep"); - ts.ucmd() - .timeout(Duration::from_millis(1100)) - .arg("10.0") - .run(); - - panic!("Assertion failed: Expected timeout of `run`.") - } - - #[cfg(feature = "sleep")] - #[rstest] - #[timeout(Duration::from_secs(10))] - fn test_ucommand_when_run_with_timeout_higher_then_execution_time_then_no_panic() { - let ts = TestScenario::new("sleep"); - ts.ucmd().timeout(Duration::from_secs(60)).arg("1.0").run(); - } - - #[cfg(feature = "echo")] - #[test] - fn test_ucommand_when_default() { - let shell_cmd = format!("{TESTS_BINARY} echo -n hello"); - - let mut command = UCommand::new(); - command.arg(&shell_cmd).succeeds().stdout_is("hello"); - - #[cfg(target_os = "android")] - let (expected_bin, expected_arg) = (PathBuf::from("/system/bin/sh"), OsString::from("-c")); - #[cfg(all(unix, not(target_os = "android")))] - let (expected_bin, expected_arg) = (PathBuf::from("/bin/sh"), OsString::from("-c")); - #[cfg(windows)] - let (expected_bin, expected_arg) = (PathBuf::from("cmd"), OsString::from("/C")); - - std::assert_eq!(&expected_bin, command.bin_path.as_ref().unwrap()); - assert!(command.util_name.is_none()); - std::assert_eq!(command.args, &[expected_arg, OsString::from(&shell_cmd)]); - assert!(command.tmpd.is_some()); - } - - #[cfg(feature = "echo")] - #[test] - fn test_ucommand_with_util() { - let tmpd = tempfile::tempdir().unwrap(); - let mut command = UCommand::with_util("echo", Rc::new(tmpd)); - - command - .args(&["-n", "hello"]) - .succeeds() - .stdout_only("hello"); - - std::assert_eq!( - &PathBuf::from(TESTS_BINARY), - command.bin_path.as_ref().unwrap() - ); - std::assert_eq!("echo", &command.util_name.unwrap()); - std::assert_eq!( - &[ - OsString::from("echo"), - OsString::from("-n"), - OsString::from("hello") - ], - command.args.make_contiguous() - ); - assert!(command.tmpd.is_some()); - } - #[cfg(all(unix, not(any(target_os = "macos", target_os = "openbsd"))))] #[test] fn test_compare_xattrs() { @@ -3806,217 +3411,6 @@ mod tests { assert!(compare_xattrs(&file_path1, &file_path2)); } - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_false() { - let scene = TestScenario::new("util"); - - let out = scene.ccmd("env").arg("sh").arg("is_a_tty.sh").succeeds(); - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - "stdin is not a tty\nstdout is not a tty\nstderr is not a tty\n" - ); - std::assert_eq!( - String::from_utf8_lossy(out.stderr()), - "This is an error message.\n" - ); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_true() { - let scene = TestScenario::new("util"); - - let out = scene - .ccmd("env") - .arg("sh") - .arg("is_a_tty.sh") - .terminal_simulation(true) - .succeeds(); - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - "stdin is a tty\r\nterminal size: 30 80\r\nstdout is a tty\r\nstderr is a tty\r\n" - ); - std::assert_eq!( - String::from_utf8_lossy(out.stderr()), - "This is an error message.\r\n" - ); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_for_stdin_only() { - let scene = TestScenario::new("util"); - - let out = scene - .ccmd("env") - .arg("sh") - .arg("is_a_tty.sh") - .terminal_sim_stdio(TerminalSimulation { - stdin: true, - stdout: false, - stderr: false, - ..Default::default() - }) - .succeeds(); - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - "stdin is a tty\nterminal size: 30 80\nstdout is not a tty\nstderr is not a tty\n" - ); - std::assert_eq!( - String::from_utf8_lossy(out.stderr()), - "This is an error message.\n" - ); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_for_stdout_only() { - let scene = TestScenario::new("util"); - - let out = scene - .ccmd("env") - .arg("sh") - .arg("is_a_tty.sh") - .terminal_sim_stdio(TerminalSimulation { - stdin: false, - stdout: true, - stderr: false, - ..Default::default() - }) - .succeeds(); - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - "stdin is not a tty\r\nstdout is a tty\r\nstderr is not a tty\r\n" - ); - std::assert_eq!( - String::from_utf8_lossy(out.stderr()), - "This is an error message.\n" - ); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_for_stderr_only() { - let scene = TestScenario::new("util"); - - let out = scene - .ccmd("env") - .arg("sh") - .arg("is_a_tty.sh") - .terminal_sim_stdio(TerminalSimulation { - stdin: false, - stdout: false, - stderr: true, - ..Default::default() - }) - .succeeds(); - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - "stdin is not a tty\nstdout is not a tty\nstderr is a tty\n" - ); - std::assert_eq!( - String::from_utf8_lossy(out.stderr()), - "This is an error message.\r\n" - ); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_size_information() { - let scene = TestScenario::new("util"); - - let out = scene - .ccmd("env") - .arg("sh") - .arg("is_a_tty.sh") - .terminal_sim_stdio(TerminalSimulation { - size: Some(libc::winsize { - ws_col: 40, - ws_row: 10, - ws_xpixel: 40 * 8, - ws_ypixel: 10 * 10, - }), - stdout: true, - stdin: true, - stderr: true, - }) - .succeeds(); - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - "stdin is a tty\r\nterminal size: 10 40\r\nstdout is a tty\r\nstderr is a tty\r\n" - ); - std::assert_eq!( - String::from_utf8_lossy(out.stderr()), - "This is an error message.\r\n" - ); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_pty_sends_eot_automatically() { - let scene = TestScenario::new("util"); - - let mut cmd = scene.ccmd("env"); - cmd.timeout(std::time::Duration::from_secs(10)); - cmd.args(&["cat", "-"]); - cmd.terminal_simulation(true); - let child = cmd.run_no_wait(); - let out = child.wait().unwrap(); // cat would block if there is no eot - - std::assert_eq!(String::from_utf8_lossy(out.stderr()), ""); - std::assert_eq!(String::from_utf8_lossy(out.stdout()), "\r\n"); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_pty_pipes_into_data_and_sends_eot_automatically() { - let scene = TestScenario::new("util"); - - let message = "Hello stdin forwarding!"; - - let mut cmd = scene.ccmd("env"); - cmd.args(&["cat", "-"]); - cmd.terminal_simulation(true); - cmd.pipe_in(message); - let child = cmd.run_no_wait(); - let out = child.wait().unwrap(); - - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - format!("{message}\r\n") - ); - std::assert_eq!(String::from_utf8_lossy(out.stderr()), ""); - } - - #[cfg(unix)] - #[cfg(feature = "env")] - #[test] - fn test_simulation_of_terminal_pty_write_in_data_and_sends_eot_automatically() { - let scene = TestScenario::new("util"); - - let mut cmd = scene.ccmd("env"); - cmd.args(&["cat", "-"]); - cmd.terminal_simulation(true); - let mut child = cmd.run_no_wait(); - child.write_in("Hello stdin forwarding via write_in!"); - let out = child.wait().unwrap(); - - std::assert_eq!( - String::from_utf8_lossy(out.stdout()), - "Hello stdin forwarding via write_in!\r\n" - ); - std::assert_eq!(String::from_utf8_lossy(out.stderr()), ""); - } - #[cfg(unix)] #[test] fn test_application_of_process_resource_limits_unlimited_file_size() { From a0179ea2392424ee07f0ec31ef1f9a17603f53cf Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 28 Mar 2025 09:51:51 +0100 Subject: [PATCH 3/3] uutests: adjust the tests to use them --- tests/by-util/test_arch.rs | 4 +- tests/by-util/test_base32.rs | 4 +- tests/by-util/test_base64.rs | 4 +- tests/by-util/test_basename.rs | 4 +- tests/by-util/test_basenc.rs | 4 +- tests/by-util/test_cat.rs | 49 +++++++- tests/by-util/test_chcon.rs | 5 +- tests/by-util/test_chgrp.rs | 5 +- tests/by-util/test_chmod.rs | 6 +- tests/by-util/test_chown.rs | 5 +- tests/by-util/test_chroot.rs | 7 +- tests/by-util/test_cksum.rs | 7 +- tests/by-util/test_comm.rs | 4 +- tests/by-util/test_cp.rs | 21 ++-- tests/by-util/test_csplit.rs | 5 +- tests/by-util/test_cut.rs | 5 +- tests/by-util/test_date.rs | 5 +- tests/by-util/test_dd.rs | 21 ++-- tests/by-util/test_df.rs | 6 +- tests/by-util/test_dir.rs | 4 +- tests/by-util/test_dircolors.rs | 4 +- tests/by-util/test_dirname.rs | 4 +- tests/by-util/test_du.rs | 9 +- tests/by-util/test_echo.rs | 118 ++++++++++++++++- tests/by-util/test_env.rs | 216 +++++++++++++++++++++++++++++++- tests/by-util/test_expand.rs | 4 +- tests/by-util/test_expr.rs | 8 +- tests/by-util/test_factor.rs | 6 +- tests/by-util/test_false.rs | 7 +- tests/by-util/test_fmt.rs | 4 +- tests/by-util/test_fold.rs | 4 +- tests/by-util/test_groups.rs | 5 +- tests/by-util/test_hashsum.rs | 10 +- tests/by-util/test_head.rs | 5 +- tests/by-util/test_hostid.rs | 4 +- tests/by-util/test_hostname.rs | 4 +- tests/by-util/test_id.rs | 5 +- tests/by-util/test_install.rs | 5 +- tests/by-util/test_join.rs | 4 +- tests/by-util/test_kill.rs | 6 +- tests/by-util/test_link.rs | 5 +- tests/by-util/test_ln.rs | 5 +- tests/by-util/test_logname.rs | 4 +- tests/by-util/test_ls.rs | 11 +- tests/by-util/test_mkdir.rs | 6 +- tests/by-util/test_mkfifo.rs | 4 +- tests/by-util/test_mknod.rs | 4 +- tests/by-util/test_mktemp.rs | 5 +- tests/by-util/test_more.rs | 6 +- tests/by-util/test_mv.rs | 10 +- tests/by-util/test_nice.rs | 4 +- tests/by-util/test_nl.rs | 5 +- tests/by-util/test_nohup.rs | 5 +- tests/by-util/test_nproc.rs | 4 +- tests/by-util/test_numfmt.rs | 4 +- tests/by-util/test_od.rs | 5 +- tests/by-util/test_paste.rs | 5 +- tests/by-util/test_pathchk.rs | 4 +- tests/by-util/test_pinky.rs | 11 +- tests/by-util/test_pr.rs | 4 +- tests/by-util/test_printenv.rs | 3 +- tests/by-util/test_printf.rs | 4 +- tests/by-util/test_ptx.rs | 5 +- tests/by-util/test_pwd.rs | 5 +- tests/by-util/test_readlink.rs | 5 +- tests/by-util/test_realpath.rs | 5 +- tests/by-util/test_rm.rs | 63 +++++++++- tests/by-util/test_rmdir.rs | 5 +- tests/by-util/test_runcon.rs | 4 +- tests/by-util/test_seq.rs | 4 +- tests/by-util/test_shred.rs | 5 +- tests/by-util/test_shuf.rs | 5 +- tests/by-util/test_sleep.rs | 147 +++++++++++++++++++++- tests/by-util/test_sort.rs | 5 +- tests/by-util/test_split.rs | 6 +- tests/by-util/test_stat.rs | 6 +- tests/by-util/test_stdbuf.rs | 4 +- tests/by-util/test_stty.rs | 4 +- tests/by-util/test_sum.rs | 5 +- tests/by-util/test_sync.rs | 4 +- tests/by-util/test_tac.rs | 4 +- tests/by-util/test_tail.rs | 54 +++++++- tests/by-util/test_tee.rs | 9 +- tests/by-util/test_test.rs | 5 +- tests/by-util/test_timeout.rs | 4 +- tests/by-util/test_touch.rs | 5 +- tests/by-util/test_tr.rs | 5 +- tests/by-util/test_true.rs | 6 +- tests/by-util/test_truncate.rs | 5 +- tests/by-util/test_tsort.rs | 5 +- tests/by-util/test_tty.rs | 4 +- tests/by-util/test_uname.rs | 5 +- tests/by-util/test_unexpand.rs | 5 +- tests/by-util/test_uniq.rs | 5 +- tests/by-util/test_unlink.rs | 5 +- tests/by-util/test_uptime.rs | 5 +- tests/by-util/test_users.rs | 4 +- tests/by-util/test_vdir.rs | 4 +- tests/by-util/test_wc.rs | 6 +- tests/by-util/test_who.rs | 6 +- tests/by-util/test_whoami.rs | 8 +- tests/by-util/test_yes.rs | 4 +- 102 files changed, 1018 insertions(+), 157 deletions(-) diff --git a/tests/by-util/test_arch.rs b/tests/by-util/test_arch.rs index 2486f3d4857..99a0cb9e841 100644 --- a/tests/by-util/test_arch.rs +++ b/tests/by-util/test_arch.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_arch() { diff --git a/tests/by-util/test_base32.rs b/tests/by-util/test_base32.rs index eb75a4ddf28..af5df848e21 100644 --- a/tests/by-util/test_base32.rs +++ b/tests/by-util/test_base32.rs @@ -4,7 +4,9 @@ // file that was distributed with this source code. // -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_encode() { diff --git a/tests/by-util/test_base64.rs b/tests/by-util/test_base64.rs index 937e2b073ac..ba0e3adaf40 100644 --- a/tests/by-util/test_base64.rs +++ b/tests/by-util/test_base64.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_encode() { diff --git a/tests/by-util/test_basename.rs b/tests/by-util/test_basename.rs index 701d63eb464..e9c44dbe278 100644 --- a/tests/by-util/test_basename.rs +++ b/tests/by-util/test_basename.rs @@ -4,9 +4,11 @@ // file that was distributed with this source code. // spell-checker:ignore (words) reallylongexecutable nbaz -use crate::common::util::TestScenario; #[cfg(any(unix, target_os = "redox"))] use std::ffi::OsStr; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_help() { diff --git a/tests/by-util/test_basenc.rs b/tests/by-util/test_basenc.rs index c0f40cd1d25..438fea6ccfc 100644 --- a/tests/by-util/test_basenc.rs +++ b/tests/by-util/test_basenc.rs @@ -5,7 +5,9 @@ // spell-checker: ignore (encodings) lsbf msbf -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_z85_not_padded_decode() { diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index 4f28d0527c4..be405dfc65a 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -4,16 +4,18 @@ // file that was distributed with this source code. // spell-checker:ignore NOFILE nonewline cmdline -use crate::common::util::TestScenario; -#[cfg(not(windows))] -use crate::common::util::vec_of_size; #[cfg(any(target_os = "linux", target_os = "android"))] use rlimit::Resource; -#[cfg(target_os = "linux")] +#[cfg(unix)] use std::fs::File; use std::fs::OpenOptions; -#[cfg(not(windows))] use std::process::Stdio; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +#[cfg(not(windows))] +use uutests::util::vec_of_size; +use uutests::util_name; #[test] fn test_output_simple() { @@ -668,3 +670,40 @@ fn test_appending_same_input_output() { .no_stdout() .stderr_contains("input file is output file"); } + +#[cfg(unix)] +#[test] +fn test_uchild_when_no_capture_reading_from_infinite_source() { + use regex::Regex; + + let ts = TestScenario::new("cat"); + + let expected_stdout = b"\0".repeat(12345); + let mut child = ts + .ucmd() + .set_stdin(Stdio::from(File::open("/dev/zero").unwrap())) + .set_stdout(Stdio::piped()) + .run_no_wait(); + + child + .make_assertion() + .with_exact_output(12345, 0) + .stdout_only_bytes(expected_stdout); + + child + .kill() + .make_assertion() + .with_current_output() + .stdout_matches(&Regex::new("[\0].*").unwrap()) + .no_stderr(); +} + +#[test] +fn test_child_when_pipe_in() { + let ts = TestScenario::new("cat"); + let mut child = ts.ucmd().set_stdin(Stdio::piped()).run_no_wait(); + child.pipe_in("content"); + child.wait().unwrap().stdout_only("content").success(); + + ts.ucmd().pipe_in("content").run().stdout_is("content"); +} diff --git a/tests/by-util/test_chcon.rs b/tests/by-util/test_chcon.rs index d05571da0d2..419b595b5b5 100644 --- a/tests/by-util/test_chcon.rs +++ b/tests/by-util/test_chcon.rs @@ -10,7 +10,10 @@ use std::ffi::CString; use std::path::Path; use std::{io, iter, str}; -use crate::common::util::*; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn version() { diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index 2af7620aecf..36250d5071a 100644 --- a/tests/by-util/test_chgrp.rs +++ b/tests/by-util/test_chgrp.rs @@ -4,8 +4,11 @@ // file that was distributed with this source code. // spell-checker:ignore (words) nosuchgroup groupname -use crate::common::util::TestScenario; use uucore::process::getegid; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_option() { diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 48a2d730495..310bdb9d2c9 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -3,9 +3,13 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::{AtPath, TestScenario, UCommand}; use std::fs::{OpenOptions, Permissions, metadata, set_permissions}; use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; +use uutests::at_and_ucmd; +use uutests::util::{AtPath, TestScenario, UCommand}; + +use uutests::new_ucmd; +use uutests::util_name; static TEST_FILE: &str = "file"; static REFERENCE_FILE: &str = "reference"; diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index a13b5dad04b..33bc4b850de 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -4,10 +4,11 @@ // file that was distributed with this source code. // spell-checker:ignore (words) agroupthatdoesntexist auserthatdoesntexist cuuser groupname notexisting passgrp -use crate::common::util::{CmdResult, TestScenario, is_ci, run_ucmd_as_root}; #[cfg(any(target_os = "linux", target_os = "android"))] use uucore::process::geteuid; - +use uutests::new_ucmd; +use uutests::util::{CmdResult, TestScenario, is_ci, run_ucmd_as_root}; +use uutests::util_name; // 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). diff --git a/tests/by-util/test_chroot.rs b/tests/by-util/test_chroot.rs index cd3f4eecc81..38c3727b1cd 100644 --- a/tests/by-util/test_chroot.rs +++ b/tests/by-util/test_chroot.rs @@ -4,9 +4,12 @@ // file that was distributed with this source code. // spell-checker:ignore (words) araba newroot userspec chdir pwd's isroot +use uutests::at_and_ucmd; +use uutests::new_ucmd; #[cfg(not(target_os = "android"))] -use crate::common::util::is_ci; -use crate::common::util::{TestScenario, run_ucmd_as_root}; +use uutests::util::is_ci; +use uutests::util::{TestScenario, run_ucmd_as_root}; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index 97761d5bf32..c6b0f4c3af6 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -4,7 +4,10 @@ // file that was distributed with this source code. // spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb dont -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; const ALGOS: [&str; 11] = [ "sysv", "bsd", "crc", "md5", "sha1", "sha224", "sha256", "sha384", "sha512", "blake2b", "sm3", @@ -1681,7 +1684,7 @@ fn test_check_incorrectly_formatted_checksum_keeps_processing_hex() { /// This module reimplements the cksum-base64.pl GNU test. mod gnu_cksum_base64 { use super::*; - use crate::common::util::log_info; + use uutests::util::log_info; const PAIRS: [(&str, &str); 12] = [ ("sysv", "0 0 f"), diff --git a/tests/by-util/test_comm.rs b/tests/by-util/test_comm.rs index aa2b36962a5..058ab80ed7e 100644 --- a/tests/by-util/test_comm.rs +++ b/tests/by-util/test_comm.rs @@ -4,7 +4,9 @@ // file that was distributed with this source code. // spell-checker:ignore (words) defaultcheck nocheck helpb helpz nwordb nwordwordz wordtotal -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index d7af5faed88..0d6526b47df 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -4,7 +4,12 @@ // file that was distributed with this source code. // spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs neve ROOTDIR USERDIR procfs outfile uufs xattrs // spell-checker:ignore bdfl hlsl IRWXO IRWXG getfattr -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::path_concat; +use uutests::util::TestScenario; +use uutests::util_name; + #[cfg(not(windows))] use std::fs::set_permissions; @@ -34,7 +39,7 @@ use std::time::Duration; #[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(feature = "truncate")] -use crate::common::util::PATH; +use uutests::util::PATH; static TEST_EXISTING_FILE: &str = "existing_file.txt"; static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt"; @@ -60,7 +65,7 @@ static TEST_NONEXISTENT_FILE: &str = "nonexistent_file.txt"; unix, not(any(target_os = "android", target_os = "macos", target_os = "openbsd")) ))] -use crate::common::util::compare_xattrs; +use uutests::util::compare_xattrs; /// Assert that mode, ownership, and permissions of two metadata objects match. #[cfg(all(not(windows), not(target_os = "freebsd")))] @@ -2372,7 +2377,7 @@ fn test_cp_target_file_dev_null() { #[test] #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] fn test_cp_one_file_system() { - use crate::common::util::AtPath; + use uutests::util::AtPath; use walkdir::WalkDir; let mut scene = TestScenario::new(util_name!()); @@ -4669,7 +4674,8 @@ fn test_cp_no_dereference_attributes_only_with_symlink() { /// contains the test for cp when the source and destination points to the same file mod same_file { - use crate::common::util::TestScenario; + use uutests::util::TestScenario; + use uutests::util_name; const FILE_NAME: &str = "foo"; const SYMLINK_NAME: &str = "symlink"; @@ -5594,8 +5600,9 @@ mod same_file { #[cfg(all(unix, not(target_os = "android")))] mod link_deref { - use crate::common::util::{AtPath, TestScenario}; use std::os::unix::fs::MetadataExt; + use uutests::util::{AtPath, TestScenario}; + use uutests::util_name; const FILE: &str = "file"; const FILE_LINK: &str = "file_link"; @@ -6037,8 +6044,8 @@ fn test_cp_no_file() { not(any(target_os = "android", target_os = "macos", target_os = "openbsd")) ))] fn test_cp_preserve_xattr_readonly_source() { - use crate::common::util::compare_xattrs; use std::process::Command; + use uutests::util::compare_xattrs; let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; diff --git a/tests/by-util/test_csplit.rs b/tests/by-util/test_csplit.rs index d571fb5cf5a..1029344d19f 100644 --- a/tests/by-util/test_csplit.rs +++ b/tests/by-util/test_csplit.rs @@ -2,8 +2,11 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use glob::glob; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; /// Returns a string of numbers with the given range, each on a new line. /// The upper bound is not included. diff --git a/tests/by-util/test_cut.rs b/tests/by-util/test_cut.rs index 7c74992aef3..10f9a6c1edc 100644 --- a/tests/by-util/test_cut.rs +++ b/tests/by-util/test_cut.rs @@ -5,7 +5,10 @@ // spell-checker:ignore defg -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; static INPUT: &str = "lists.txt"; diff --git a/tests/by-util/test_date.rs b/tests/by-util/test_date.rs index e15ed2e00fe..40b069c1197 100644 --- a/tests/by-util/test_date.rs +++ b/tests/by-util/test_date.rs @@ -3,10 +3,13 @@ use chrono::{DateTime, Duration, Utc}; // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use regex::Regex; #[cfg(all(unix, not(target_os = "macos")))] use uucore::process::geteuid; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 757f987735d..e46015b557a 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -4,11 +4,14 @@ // file that was distributed with this source code. // spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi nabcde nabcdefg abcdefg fifoname seekable -use crate::common::util::TestScenario; -#[cfg(all(unix, not(feature = "feat_selinux")))] -use crate::common::util::run_ucmd_as_root_with_stdin_stdout; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +#[cfg(unix)] +use uutests::util::run_ucmd_as_root_with_stdin_stdout; #[cfg(all(not(windows), feature = "printf"))] -use crate::common::util::{TESTS_BINARY, UCommand}; +use uutests::util::{UCommand, get_tests_binary}; +use uutests::util_name; use regex::Regex; use uucore::io::OwnedFileDescriptorOrHandle; @@ -1505,9 +1508,9 @@ fn test_skip_input_fifo() { #[test] fn test_multiple_processes_reading_stdin() { // TODO Investigate if this is possible on Windows. - let printf = format!("{TESTS_BINARY} printf 'abcdef\n'"); - let dd_skip = format!("{TESTS_BINARY} dd bs=1 skip=3 count=0"); - let dd = format!("{TESTS_BINARY} dd"); + let printf = format!("{} printf 'abcdef\n'", get_tests_binary()); + let dd_skip = format!("{} dd bs=1 skip=3 count=0", get_tests_binary()); + let dd = format!("{} dd", get_tests_binary()); UCommand::new() .arg(format!("{printf} | ( {dd_skip} && {dd} ) 2> /dev/null")) .succeeds() @@ -1609,7 +1612,7 @@ fn test_reading_partial_blocks_from_fifo() { // Start a `dd` process that reads from the fifo (so it will wait // until the writer process starts). - let mut reader_command = Command::new(TESTS_BINARY); + let mut reader_command = Command::new(get_tests_binary()); let child = reader_command .args(["dd", "ibs=3", "obs=3", &format!("if={fifoname}")]) .stdout(Stdio::piped()) @@ -1653,7 +1656,7 @@ fn test_reading_partial_blocks_from_fifo_unbuffered() { // until the writer process starts). // // `bs=N` takes precedence over `ibs=N` and `obs=N`. - let mut reader_command = Command::new(TESTS_BINARY); + let mut reader_command = Command::new(get_tests_binary()); let child = reader_command .args(["dd", "bs=3", "ibs=1", "obs=1", &format!("if={fifoname}")]) .stdout(Stdio::piped()) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index d3692a7f0dd..95131e55601 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -12,7 +12,11 @@ use std::collections::HashSet; -use crate::common::util::TestScenario; +#[cfg(not(any(target_os = "freebsd", target_os = "windows")))] +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_dir.rs b/tests/by-util/test_dir.rs index 3d16f8a67c6..ef455c6bd8e 100644 --- a/tests/by-util/test_dir.rs +++ b/tests/by-util/test_dir.rs @@ -2,8 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use regex::Regex; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; /* * As dir use the same functions than ls, we don't have to retest them here. diff --git a/tests/by-util/test_dircolors.rs b/tests/by-util/test_dircolors.rs index 6665d3bc780..28722f2e33e 100644 --- a/tests/by-util/test_dircolors.rs +++ b/tests/by-util/test_dircolors.rs @@ -3,7 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore overridable colorterm -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; use dircolors::{OutputFmt, StrUtils, guess_syntax}; diff --git a/tests/by-util/test_dirname.rs b/tests/by-util/test_dirname.rs index 9df287a12be..3b8aee37d7b 100644 --- a/tests/by-util/test_dirname.rs +++ b/tests/by-util/test_dirname.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index c205923ff44..1edbeb63cff 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -7,9 +7,14 @@ #[cfg(not(windows))] use regex::Regex; -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; #[cfg(not(target_os = "windows"))] -use crate::common::util::expected_result; +use uutests::unwrap_or_return; +use uutests::util::TestScenario; +#[cfg(not(target_os = "windows"))] +use uutests::util::expected_result; +use uutests::util_name; #[cfg(not(target_os = "openbsd"))] const SUB_DIR: &str = "subdir/deeper"; diff --git a/tests/by-util/test_echo.rs b/tests/by-util/test_echo.rs index d4430d05655..1045d6a5272 100644 --- a/tests/by-util/test_echo.rs +++ b/tests/by-util/test_echo.rs @@ -2,9 +2,12 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (words) araba merci +// spell-checker:ignore (words) araba merci mright -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util::UCommand; +use uutests::util_name; #[test] fn test_default() { @@ -391,6 +394,64 @@ fn slash_eight_off_by_one() { .stdout_only(r"\8"); } +#[test] +fn test_normalized_newlines_stdout_is() { + let res = new_ucmd!().args(&["-ne", "A\r\nB\nC"]).run(); + + res.normalized_newlines_stdout_is("A\r\nB\nC"); + res.normalized_newlines_stdout_is("A\nB\nC"); + res.normalized_newlines_stdout_is("A\nB\r\nC"); +} + +#[test] +fn test_normalized_newlines_stdout_is_fail() { + new_ucmd!() + .args(&["-ne", "A\r\nB\nC"]) + .run() + .stdout_is("A\r\nB\nC"); +} + +#[test] +fn test_cmd_result_stdout_check_and_stdout_str_check() { + let result = new_ucmd!().arg("Hello world").run(); + + result.stdout_str_check(|stdout| stdout.ends_with("world\n")); + result.stdout_check(|stdout| stdout.get(0..2).unwrap().eq(b"He")); + result.no_stderr(); +} + +#[test] +fn test_cmd_result_stderr_check_and_stderr_str_check() { + let ts = TestScenario::new("echo"); + + let result = UCommand::new() + .arg(format!( + "{} {} Hello world >&2", + ts.bin_path.display(), + ts.util_name + )) + .run(); + + result.stderr_str_check(|stderr| stderr.ends_with("world\n")); + result.stderr_check(|stdout| stdout.get(0..2).unwrap().eq(b"He")); + result.no_stdout(); +} + +#[test] +fn test_cmd_result_stdout_str_check_when_false_then_panics() { + new_ucmd!() + .args(&["-e", "\\f"]) + .succeeds() + .stdout_only("\x0C\n"); +} + +#[cfg(unix)] +#[test] +fn test_cmd_result_signal_when_normal_exit_then_no_signal() { + let result = TestScenario::new("echo").ucmd().run(); + assert!(result.signal().is_none()); +} + mod posixly_correct { use super::*; @@ -442,3 +503,56 @@ mod posixly_correct { .stdout_only("foo"); } } + +#[test] +fn test_child_when_run_with_a_non_blocking_util() { + new_ucmd!() + .arg("hello world") + .run() + .success() + .stdout_only("hello world\n"); +} + +// Test basically that most of the methods of UChild are working +#[test] +fn test_uchild_when_run_no_wait_with_a_non_blocking_util() { + let mut child = new_ucmd!().arg("hello world").run_no_wait(); + + // check `child.is_alive()` and `child.delay()` is working + let mut trials = 10; + while child.is_alive() { + assert!( + trials > 0, + "Assertion failed: child process is still alive." + ); + + child.delay(500); + trials -= 1; + } + + assert!(!child.is_alive()); + + // check `child.is_not_alive()` is working + assert!(child.is_not_alive()); + + // check the current output is correct + std::assert_eq!(child.stdout(), "hello world\n"); + assert!(child.stderr().is_empty()); + + // check the current output of echo is empty. We already called `child.stdout()` and `echo` + // exited so there's no additional output after the first call of `child.stdout()` + assert!(child.stdout().is_empty()); + assert!(child.stderr().is_empty()); + + // check that we're still able to access all output of the child process, even after exit + // and call to `child.stdout()` + std::assert_eq!(child.stdout_all(), "hello world\n"); + assert!(child.stderr_all().is_empty()); + + // we should be able to call kill without panics, even if the process already exited + child.make_assertion().is_not_alive(); + child.kill(); + + // we should be able to call wait without panics and apply some assertions + child.wait().unwrap().code_is(0).no_stdout().no_stderr(); +} diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index c52c540e06c..7a44b18775c 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -2,12 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (words) bamf chdir rlimit prlimit COMSPEC cout cerr FFFD +// spell-checker:ignore (words) bamf chdir rlimit prlimit COMSPEC cout cerr FFFD winsize xpixel ypixel #![allow(clippy::missing_errors_doc)] -use crate::common::util::TestScenario; -#[cfg(unix)] -use crate::common::util::UChild; #[cfg(unix)] use nix::sys::signal::Signal; #[cfg(feature = "echo")] @@ -17,6 +14,13 @@ use std::path::Path; #[cfg(unix)] use std::process::Command; use tempfile::tempdir; +use uutests::new_ucmd; +#[cfg(unix)] +use uutests::util::TerminalSimulation; +use uutests::util::TestScenario; +#[cfg(unix)] +use uutests::util::UChild; +use uutests::util_name; #[cfg(unix)] struct Target { @@ -520,7 +524,7 @@ fn test_split_string_into_args_debug_output_whitespace_handling() { fn test_gnu_e20() { let scene = TestScenario::new(util_name!()); - let env_bin = String::from(crate::common::util::TESTS_BINARY) + " " + util_name!(); + let env_bin = String::from(uutests::util::get_tests_binary()) + " " + util_name!(); let (input, output) = ( [ @@ -1516,3 +1520,205 @@ mod test_raw_string_parser { ); } } + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_false() { + let scene = TestScenario::new("util"); + + let out = scene.ccmd("env").arg("sh").arg("is_a_tty.sh").succeeds(); + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + "stdin is not a tty\nstdout is not a tty\nstderr is not a tty\n" + ); + std::assert_eq!( + String::from_utf8_lossy(out.stderr()), + "This is an error message.\n" + ); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_true() { + let scene = TestScenario::new("util"); + + let out = scene + .ccmd("env") + .arg("sh") + .arg("is_a_tty.sh") + .terminal_simulation(true) + .succeeds(); + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + "stdin is a tty\r\nterminal size: 30 80\r\nstdout is a tty\r\nstderr is a tty\r\n" + ); + std::assert_eq!( + String::from_utf8_lossy(out.stderr()), + "This is an error message.\r\n" + ); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_for_stdin_only() { + let scene = TestScenario::new("util"); + + let out = scene + .ccmd("env") + .arg("sh") + .arg("is_a_tty.sh") + .terminal_sim_stdio(TerminalSimulation { + stdin: true, + stdout: false, + stderr: false, + ..Default::default() + }) + .succeeds(); + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + "stdin is a tty\nterminal size: 30 80\nstdout is not a tty\nstderr is not a tty\n" + ); + std::assert_eq!( + String::from_utf8_lossy(out.stderr()), + "This is an error message.\n" + ); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_for_stdout_only() { + let scene = TestScenario::new("util"); + + let out = scene + .ccmd("env") + .arg("sh") + .arg("is_a_tty.sh") + .terminal_sim_stdio(TerminalSimulation { + stdin: false, + stdout: true, + stderr: false, + ..Default::default() + }) + .succeeds(); + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + "stdin is not a tty\r\nstdout is a tty\r\nstderr is not a tty\r\n" + ); + std::assert_eq!( + String::from_utf8_lossy(out.stderr()), + "This is an error message.\n" + ); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_for_stderr_only() { + let scene = TestScenario::new("util"); + + let out = scene + .ccmd("env") + .arg("sh") + .arg("is_a_tty.sh") + .terminal_sim_stdio(TerminalSimulation { + stdin: false, + stdout: false, + stderr: true, + ..Default::default() + }) + .succeeds(); + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + "stdin is not a tty\nstdout is not a tty\nstderr is a tty\n" + ); + std::assert_eq!( + String::from_utf8_lossy(out.stderr()), + "This is an error message.\r\n" + ); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_size_information() { + let scene = TestScenario::new("util"); + + let out = scene + .ccmd("env") + .arg("sh") + .arg("is_a_tty.sh") + .terminal_sim_stdio(TerminalSimulation { + size: Some(libc::winsize { + ws_col: 40, + ws_row: 10, + ws_xpixel: 40 * 8, + ws_ypixel: 10 * 10, + }), + stdout: true, + stdin: true, + stderr: true, + }) + .succeeds(); + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + "stdin is a tty\r\nterminal size: 10 40\r\nstdout is a tty\r\nstderr is a tty\r\n" + ); + std::assert_eq!( + String::from_utf8_lossy(out.stderr()), + "This is an error message.\r\n" + ); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_pty_sends_eot_automatically() { + let scene = TestScenario::new("util"); + + let mut cmd = scene.ccmd("env"); + cmd.timeout(std::time::Duration::from_secs(10)); + cmd.args(&["cat", "-"]); + cmd.terminal_simulation(true); + let child = cmd.run_no_wait(); + let out = child.wait().unwrap(); // cat would block if there is no eot + + std::assert_eq!(String::from_utf8_lossy(out.stderr()), ""); + std::assert_eq!(String::from_utf8_lossy(out.stdout()), "\r\n"); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_pty_pipes_into_data_and_sends_eot_automatically() { + let scene = TestScenario::new("util"); + + let message = "Hello stdin forwarding!"; + + let mut cmd = scene.ccmd("env"); + cmd.args(&["cat", "-"]); + cmd.terminal_simulation(true); + cmd.pipe_in(message); + let child = cmd.run_no_wait(); + let out = child.wait().unwrap(); + + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + format!("{message}\r\n") + ); + std::assert_eq!(String::from_utf8_lossy(out.stderr()), ""); +} + +#[cfg(unix)] +#[test] +fn test_simulation_of_terminal_pty_write_in_data_and_sends_eot_automatically() { + let scene = TestScenario::new("util"); + + let mut cmd = scene.ccmd("env"); + cmd.args(&["cat", "-"]); + cmd.terminal_simulation(true); + let mut child = cmd.run_no_wait(); + child.write_in("Hello stdin forwarding via write_in!"); + let out = child.wait().unwrap(); + + std::assert_eq!( + String::from_utf8_lossy(out.stdout()), + "Hello stdin forwarding via write_in!\r\n" + ); + std::assert_eq!(String::from_utf8_lossy(out.stderr()), ""); +} diff --git a/tests/by-util/test_expand.rs b/tests/by-util/test_expand.rs index 1d5608ef15a..8e4de344e3d 100644 --- a/tests/by-util/test_expand.rs +++ b/tests/by-util/test_expand.rs @@ -2,8 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use uucore::display::Quotable; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; // spell-checker:ignore (ToDO) taaaa tbbbb tcccc #[test] diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index c391565e41c..df2c27c1ce8 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -7,7 +7,9 @@ // spell-checker:ignore abbccd abcac acabc andand bigcmp bignum emptysub // spell-checker:ignore orempty oror -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_no_arguments() { @@ -400,7 +402,9 @@ fn test_long_input() { /// Regroup the testcases of the GNU test expr.pl mod gnu_expr { - use crate::common::util::TestScenario; + use uutests::new_ucmd; + use uutests::util::TestScenario; + use uutests::util_name; #[test] fn test_a() { diff --git a/tests/by-util/test_factor.rs b/tests/by-util/test_factor.rs index 4e784b701c8..2e86c82a7e4 100644 --- a/tests/by-util/test_factor.rs +++ b/tests/by-util/test_factor.rs @@ -10,7 +10,9 @@ clippy::cast_sign_loss )] -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; use std::time::{Duration, SystemTime}; @@ -44,11 +46,11 @@ fn test_repeated_exponents() { #[cfg(feature = "sort")] #[cfg(not(target_os = "android"))] fn test_parallel() { - use crate::common::util::AtPath; use hex_literal::hex; use sha1::{Digest, Sha1}; use std::{fs::OpenOptions, time::Duration}; use tempfile::TempDir; + use uutests::util::AtPath; // factor should only flush the buffer at line breaks let n_integers = 100_000; let mut input_string = String::new(); diff --git a/tests/by-util/test_false.rs b/tests/by-util/test_false.rs index 23b3e914b0c..fafd9e6a2c2 100644 --- a/tests/by-util/test_false.rs +++ b/tests/by-util/test_false.rs @@ -2,13 +2,12 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. - -use crate::common::util::TestScenario; use regex::Regex; - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] use std::fs::OpenOptions; - +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_no_args() { new_ucmd!().fails().no_output(); diff --git a/tests/by-util/test_fmt.rs b/tests/by-util/test_fmt.rs index c97e795f84e..8d851d5ce44 100644 --- a/tests/by-util/test_fmt.rs +++ b/tests/by-util/test_fmt.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_fold.rs b/tests/by-util/test_fold.rs index 2ef182db1b0..d916a9c77ce 100644 --- a/tests/by-util/test_fold.rs +++ b/tests/by-util/test_fold.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_groups.rs b/tests/by-util/test_groups.rs index 848b816216a..984caef39a5 100644 --- a/tests/by-util/test_groups.rs +++ b/tests/by-util/test_groups.rs @@ -5,7 +5,10 @@ //spell-checker: ignore coreutil -use crate::common::util::{TestScenario, check_coreutil_version, expected_result, whoami}; +use uutests::new_ucmd; +use uutests::unwrap_or_return; +use uutests::util::{TestScenario, check_coreutil_version, expected_result, whoami}; +use uutests::util_name; const VERSION_MIN_MULTIPLE_USERS: &str = "8.31"; // this feature was introduced in GNU's coreutils 8.31 diff --git a/tests/by-util/test_hashsum.rs b/tests/by-util/test_hashsum.rs index f4c320ef94c..12b18b83d0e 100644 --- a/tests/by-util/test_hashsum.rs +++ b/tests/by-util/test_hashsum.rs @@ -2,7 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; + +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; // spell-checker:ignore checkfile, nonames, testf, ntestf macro_rules! get_hash( ($str:expr) => ( @@ -14,7 +17,7 @@ macro_rules! test_digest { ($($id:ident $t:ident $size:expr)*) => ($( mod $id { - use crate::common::util::*; + use uutests::util::*; static DIGEST_ARG: &'static str = concat!("--", stringify!($t)); static BITS_ARG: &'static str = concat!("--bits=", stringify!($size)); static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected"); @@ -72,6 +75,9 @@ macro_rules! test_digest { #[cfg(windows)] #[test] fn test_text_mode() { + use uutests::new_ucmd; + use uutests::util_name; + // TODO Replace this with hard-coded files that store the // expected output of text mode on an input file that has // "\r\n" line endings. diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index 04b68b9c722..efba388dc09 100644 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -6,7 +6,6 @@ // spell-checker:ignore (words) bogusfile emptyfile abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstu // spell-checker:ignore (words) seekable -use crate::common::util::TestScenario; #[cfg(all( not(target_os = "windows"), not(target_os = "macos"), @@ -15,7 +14,9 @@ use crate::common::util::TestScenario; not(target_os = "openbsd") ))] use std::io::Read; - +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; static INPUT: &str = "lorem_ipsum.txt"; #[test] diff --git a/tests/by-util/test_hostid.rs b/tests/by-util/test_hostid.rs index e18deb8939c..198061b1999 100644 --- a/tests/by-util/test_hostid.rs +++ b/tests/by-util/test_hostid.rs @@ -2,8 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use regex::Regex; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_normal() { diff --git a/tests/by-util/test_hostname.rs b/tests/by-util/test_hostname.rs index dc522a4d449..1611a590a2a 100644 --- a/tests/by-util/test_hostname.rs +++ b/tests/by-util/test_hostname.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_hostname() { diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index 71707804fcb..e2e1826ff85 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -5,7 +5,10 @@ // spell-checker:ignore (ToDO) coreutil -use crate::common::util::{TestScenario, check_coreutil_version, expected_result, is_ci, whoami}; +use uutests::new_ucmd; +use uutests::unwrap_or_return; +use uutests::util::{TestScenario, check_coreutil_version, expected_result, is_ci, whoami}; +use uutests::util_name; const VERSION_MIN_MULTIPLE_USERS: &str = "8.31"; // this feature was introduced in GNU's coreutils 8.31 diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index 6d8ba774b93..145ac61f599 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -4,7 +4,6 @@ // file that was distributed with this source code. // spell-checker:ignore (words) helloworld nodir objdump n'source -use crate::common::util::{TestScenario, is_ci, run_ucmd_as_root}; #[cfg(not(target_os = "openbsd"))] use filetime::FileTime; use std::fs; @@ -14,6 +13,10 @@ use std::process::Command; #[cfg(any(target_os = "linux", target_os = "android"))] use std::thread::sleep; use uucore::process::{getegid, geteuid}; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::{TestScenario, is_ci, run_ucmd_as_root}; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_join.rs b/tests/by-util/test_join.rs index 7337064e0f1..e9924eea9ae 100644 --- a/tests/by-util/test_join.rs +++ b/tests/by-util/test_join.rs @@ -4,13 +4,15 @@ // file that was distributed with this source code. // spell-checker:ignore (words) autoformat nocheck -use crate::common::util::TestScenario; #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] use std::fs::OpenOptions; #[cfg(unix)] use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; #[cfg(windows)] use std::{ffi::OsString, os::windows::ffi::OsStringExt}; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_kill.rs b/tests/by-util/test_kill.rs index a4d6971fe05..c163d47b836 100644 --- a/tests/by-util/test_kill.rs +++ b/tests/by-util/test_kill.rs @@ -2,13 +2,13 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. - // spell-checker:ignore IAMNOTASIGNAL - -use crate::common::util::TestScenario; use regex::Regex; use std::os::unix::process::ExitStatusExt; use std::process::{Child, Command}; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; // A child process the tests will try to kill. struct Target { diff --git a/tests/by-util/test_link.rs b/tests/by-util/test_link.rs index 9cc059666a3..d95ada98699 100644 --- a/tests/by-util/test_link.rs +++ b/tests/by-util/test_link.rs @@ -2,7 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_ln.rs b/tests/by-util/test_ln.rs index 57e793dd2ef..b8089f401ec 100644 --- a/tests/by-util/test_ln.rs +++ b/tests/by-util/test_ln.rs @@ -4,8 +4,11 @@ // file that was distributed with this source code. #![allow(clippy::similar_names)] -use crate::common::util::TestScenario; use std::path::PathBuf; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_logname.rs b/tests/by-util/test_logname.rs index 178b470480a..c0f763bb628 100644 --- a/tests/by-util/test_logname.rs +++ b/tests/by-util/test_logname.rs @@ -2,8 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::{TestScenario, is_ci}; use std::env; +use uutests::new_ucmd; +use uutests::util::{TestScenario, is_ci}; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 444d5b8d810..3767b38ec69 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -10,9 +10,6 @@ clippy::cast_possible_truncation )] -use crate::common::util::TestScenario; -#[cfg(any(unix, feature = "feat_selinux"))] -use crate::common::util::expected_result; #[cfg(all(unix, feature = "chmod"))] use nix::unistd::{close, dup}; use regex::Regex; @@ -29,6 +26,13 @@ use std::path::Path; use std::path::PathBuf; use std::thread::sleep; use std::time::Duration; +use uutests::new_ucmd; +#[cfg(unix)] +use uutests::unwrap_or_return; +use uutests::util::TestScenario; +#[cfg(any(unix, feature = "feat_selinux"))] +use uutests::util::expected_result; +use uutests::{at_and_ucmd, util_name}; const LONG_ARGS: &[&str] = &[ "-l", @@ -2232,6 +2236,7 @@ fn test_ls_recursive_1() { #[cfg(unix)] mod quoting { use super::TestScenario; + use uutests::util_name; /// Create a directory with "dirname", then for each check, assert that the /// output is correct. diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index 45c1bcf02a3..e544e342322 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -7,11 +7,15 @@ #![allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] -use crate::common::util::TestScenario; #[cfg(not(windows))] use libc::mode_t; #[cfg(not(windows))] use std::os::unix::fs::PermissionsExt; +#[cfg(not(windows))] +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_mkfifo.rs b/tests/by-util/test_mkfifo.rs index b4c3c7f2b3a..79eed4d6202 100644 --- a/tests/by-util/test_mkfifo.rs +++ b/tests/by-util/test_mkfifo.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_mknod.rs b/tests/by-util/test_mknod.rs index e0a091778b7..644306ffff3 100644 --- a/tests/by-util/test_mknod.rs +++ b/tests/by-util/test_mknod.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] #[cfg(not(windows))] diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index c35ddf31bea..65d0db8cee9 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -4,7 +4,10 @@ // file that was distributed with this source code. // spell-checker:ignore (words) gpghome -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; use uucore::display::Quotable; diff --git a/tests/by-util/test_more.rs b/tests/by-util/test_more.rs index 3c40c8fd9f9..e71e8711412 100644 --- a/tests/by-util/test_more.rs +++ b/tests/by-util/test_more.rs @@ -2,8 +2,12 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use std::io::IsTerminal; +#[cfg(target_family = "unix")] +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_more_no_arg() { diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index e9bed394943..7db0588b3f0 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -4,10 +4,12 @@ // file that was distributed with this source code. // // spell-checker:ignore mydir -use crate::common::util::TestScenario; use filetime::FileTime; use rstest::rstest; use std::io::Write; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::{at_and_ucmd, util_name}; #[test] fn test_mv_invalid_arg() { @@ -1670,7 +1672,7 @@ fn test_mv_dir_into_path_slash() { fn test_acl() { use std::process::Command; - use crate::common::util::compare_xattrs; + use uutests::util::compare_xattrs; let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; @@ -1766,10 +1768,11 @@ fn test_move_should_not_fallback_to_copy() { #[cfg(target_os = "linux")] mod inter_partition_copying { - use crate::common::util::TestScenario; use std::fs::{read_to_string, set_permissions, write}; use std::os::unix::fs::{PermissionsExt, symlink}; use tempfile::TempDir; + use uutests::util::TestScenario; + use uutests::util_name; // Ensure that the copying code used in an inter-partition move unlinks the destination symlink. #[test] @@ -1823,6 +1826,7 @@ mod inter_partition_copying { // that it would output the proper error message. #[test] pub(crate) fn test_mv_unlinks_dest_symlink_error_message() { + use uutests::util::TestScenario; let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; diff --git a/tests/by-util/test_nice.rs b/tests/by-util/test_nice.rs index 802b25344a3..b53a4118b08 100644 --- a/tests/by-util/test_nice.rs +++ b/tests/by-util/test_nice.rs @@ -3,7 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore libc's setpriority -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] #[cfg(not(target_os = "android"))] diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index 4e8dbe5cbf5..7e9fb7c14a2 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -4,7 +4,10 @@ // file that was distributed with this source code. // // spell-checker:ignore binvalid finvalid hinvalid iinvalid linvalid nabcabc nabcabcabc ninvalid vinvalid winvalid dabc näää -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_nohup.rs b/tests/by-util/test_nohup.rs index 1879501005e..d58a7e24de9 100644 --- a/tests/by-util/test_nohup.rs +++ b/tests/by-util/test_nohup.rs @@ -3,9 +3,12 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore winsize Openpty openpty xpixel ypixel ptyprocess -use crate::common::util::TestScenario; #[cfg(not(target_os = "openbsd"))] use std::thread::sleep; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; // General observation: nohup.out will not be created in tests run by cargo test // because stdin/stdout is not attached to a TTY. diff --git a/tests/by-util/test_nproc.rs b/tests/by-util/test_nproc.rs index 2dd32a79b06..c06eed8f0c4 100644 --- a/tests/by-util/test_nproc.rs +++ b/tests/by-util/test_nproc.rs @@ -3,7 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore incorrectnumber -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index 57af46598ef..21b327043d5 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -4,7 +4,9 @@ // file that was distributed with this source code. // spell-checker:ignore (paths) gnutest ronna quetta -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_od.rs b/tests/by-util/test_od.rs index bb95ccf7234..5b1bb2f76ea 100644 --- a/tests/by-util/test_od.rs +++ b/tests/by-util/test_od.rs @@ -5,8 +5,11 @@ // spell-checker:ignore abcdefghijklmnopqrstuvwxyz Anone -use crate::common::util::TestScenario; use unindent::unindent; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; // octal dump of 'abcdefghijklmnopqrstuvwxyz\n' static ALPHA_OUT: &str = " diff --git a/tests/by-util/test_paste.rs b/tests/by-util/test_paste.rs index 53f2dead672..c4c1097f8f9 100644 --- a/tests/by-util/test_paste.rs +++ b/tests/by-util/test_paste.rs @@ -5,7 +5,10 @@ // spell-checker:ignore bsdutils toybox -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; struct TestData<'b> { name: &'b str, diff --git a/tests/by-util/test_pathchk.rs b/tests/by-util/test_pathchk.rs index 599a2308425..6e6b5dd85f3 100644 --- a/tests/by-util/test_pathchk.rs +++ b/tests/by-util/test_pathchk.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_no_args() { diff --git a/tests/by-util/test_pinky.rs b/tests/by-util/test_pinky.rs index 6192a7bb53f..6418906ae55 100644 --- a/tests/by-util/test_pinky.rs +++ b/tests/by-util/test_pinky.rs @@ -3,13 +3,16 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#[cfg(target_os = "openbsd")] -use crate::common::util::TestScenario; -#[cfg(not(target_os = "openbsd"))] -use crate::common::util::{TestScenario, expected_result}; use pinky::Capitalize; #[cfg(not(target_os = "openbsd"))] use uucore::entries::{Locate, Passwd}; +use uutests::new_ucmd; +use uutests::unwrap_or_return; +#[cfg(target_os = "openbsd")] +use uutests::util::TestScenario; +#[cfg(not(target_os = "openbsd"))] +use uutests::util::{TestScenario, expected_result}; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_pr.rs b/tests/by-util/test_pr.rs index f99495edcbb..1dcb162c079 100644 --- a/tests/by-util/test_pr.rs +++ b/tests/by-util/test_pr.rs @@ -4,9 +4,11 @@ // file that was distributed with this source code. // spell-checker:ignore (ToDO) Sdivide -use crate::common::util::{TestScenario, UCommand}; use chrono::{DateTime, Duration, Utc}; use std::fs::metadata; +use uutests::new_ucmd; +use uutests::util::{TestScenario, UCommand}; +use uutests::util_name; const DATE_TIME_FORMAT: &str = "%b %d %H:%M %Y"; diff --git a/tests/by-util/test_printenv.rs b/tests/by-util/test_printenv.rs index c9eb3c60eed..aa8910ba51e 100644 --- a/tests/by-util/test_printenv.rs +++ b/tests/by-util/test_printenv.rs @@ -2,7 +2,8 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_get_all() { diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index be9826d920c..df1fafd60a5 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn basic_literal() { diff --git a/tests/by-util/test_ptx.rs b/tests/by-util/test_ptx.rs index 20d4a328020..6f7f34d17f4 100644 --- a/tests/by-util/test_ptx.rs +++ b/tests/by-util/test_ptx.rs @@ -3,7 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore roff -use crate::common::util::TestScenario; + +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_pwd.rs b/tests/by-util/test_pwd.rs index c5cb7f32ef6..77826b8788a 100644 --- a/tests/by-util/test_pwd.rs +++ b/tests/by-util/test_pwd.rs @@ -6,7 +6,10 @@ use std::path::PathBuf; -use crate::common::util::{TestScenario, UCommand}; +use uutests::new_ucmd; +use uutests::util::{TestScenario, UCommand}; +//use uutests::at_and_ucmd; +use uutests::{at_and_ucmd, util_name}; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_readlink.rs b/tests/by-util/test_readlink.rs index eef42eeebe2..33840c9a183 100644 --- a/tests/by-util/test_readlink.rs +++ b/tests/by-util/test_readlink.rs @@ -3,7 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore regfile -use crate::common::util::{TestScenario, get_root_path}; +use uutests::new_ucmd; +use uutests::path_concat; +use uutests::util::{TestScenario, get_root_path}; +use uutests::{at_and_ucmd, util_name}; static GIBBERISH: &str = "supercalifragilisticexpialidocious"; diff --git a/tests/by-util/test_realpath.rs b/tests/by-util/test_realpath.rs index 14c2aa88127..93c0ebb19e1 100644 --- a/tests/by-util/test_realpath.rs +++ b/tests/by-util/test_realpath.rs @@ -3,7 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore nusr -use crate::common::util::{TestScenario, get_root_path}; +use uutests::new_ucmd; +use uutests::path_concat; +use uutests::util::{TestScenario, get_root_path}; +use uutests::{at_and_ucmd, util_name}; #[cfg(windows)] use regex::Regex; diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 8610ef5ced1..d022d754e3b 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -6,7 +6,10 @@ use std::process::Stdio; -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { @@ -777,6 +780,64 @@ fn test_non_utf8() { assert!(!at.file_exists(file)); } +#[test] +fn test_uchild_when_run_no_wait_with_a_blocking_command() { + let ts = TestScenario::new("rm"); + let at = &ts.fixtures; + + at.mkdir("a"); + at.touch("a/empty"); + + #[cfg(target_vendor = "apple")] + let delay: u64 = 2000; + #[cfg(not(target_vendor = "apple"))] + let delay: u64 = 1000; + + let yes = if cfg!(windows) { "y\r\n" } else { "y\n" }; + + let mut child = ts + .ucmd() + .set_stdin(Stdio::piped()) + .stderr_to_stdout() + .args(&["-riv", "a"]) + .run_no_wait(); + child + .make_assertion_with_delay(delay) + .is_alive() + .with_current_output() + .stdout_is("rm: descend into directory 'a'? "); + + #[cfg(windows)] + let expected = "rm: descend into directory 'a'? \ + rm: remove regular empty file 'a\\empty'? "; + #[cfg(unix)] + let expected = "rm: descend into directory 'a'? \ + rm: remove regular empty file 'a/empty'? "; + child.write_in(yes); + child + .make_assertion_with_delay(delay) + .is_alive() + .with_all_output() + .stdout_is(expected); + + #[cfg(windows)] + let expected = "removed 'a\\empty'\nrm: remove directory 'a'? "; + #[cfg(unix)] + let expected = "removed 'a/empty'\nrm: remove directory 'a'? "; + + child + .write_in(yes) + .make_assertion_with_delay(delay) + .is_alive() + .with_exact_output(44, 0) + .stdout_only(expected); + + let expected = "removed directory 'a'\n"; + + child.write_in(yes); + child.wait().unwrap().stdout_only(expected).success(); +} + #[test] fn test_recursive_interactive() { let (at, mut ucmd) = at_and_ucmd!(); diff --git a/tests/by-util/test_rmdir.rs b/tests/by-util/test_rmdir.rs index cfd9b5c6c56..09a711eafbf 100644 --- a/tests/by-util/test_rmdir.rs +++ b/tests/by-util/test_rmdir.rs @@ -2,7 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; const DIR: &str = "dir"; const DIR_FILE: &str = "dir/file"; diff --git a/tests/by-util/test_runcon.rs b/tests/by-util/test_runcon.rs index ec1f4f8b3a1..c024f571d0b 100644 --- a/tests/by-util/test_runcon.rs +++ b/tests/by-util/test_runcon.rs @@ -6,7 +6,9 @@ #![cfg(feature = "feat_selinux")] -use crate::common::util::*; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; // TODO: Check the implementation of `--compute` somehow. diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 95caa0ccbcd..83bdb7a82c5 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -3,7 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore lmnop xlmnop -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_shred.rs b/tests/by-util/test_shred.rs index f05aed72c61..8e1f4c736c0 100644 --- a/tests/by-util/test_shred.rs +++ b/tests/by-util/test_shred.rs @@ -5,7 +5,10 @@ // spell-checker:ignore wipesync -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_shuf.rs b/tests/by-util/test_shuf.rs index d42cada0109..ad64c52ca51 100644 --- a/tests/by-util/test_shuf.rs +++ b/tests/by-util/test_shuf.rs @@ -4,7 +4,10 @@ // file that was distributed with this source code. // spell-checker:ignore (ToDO) unwritable -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_sleep.rs b/tests/by-util/test_sleep.rs index 2708b01c169..25672d91a5b 100644 --- a/tests/by-util/test_sleep.rs +++ b/tests/by-util/test_sleep.rs @@ -5,10 +5,13 @@ use rstest::rstest; // spell-checker:ignore dont SIGBUS SIGSEGV sigsegv sigbus -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[cfg(unix)] use nix::sys::signal::Signal::{SIGBUS, SIGSEGV}; +use std::io::ErrorKind; use std::time::{Duration, Instant}; #[test] @@ -252,8 +255,8 @@ fn test_sleep_when_input_has_only_whitespace_then_error(#[case] input: &str) { #[test] fn test_sleep_when_multiple_input_some_with_error_then_shows_all_errors() { let expected = "invalid time interval 'abc': Invalid input: abc\n\ - sleep: invalid time interval '1years': Invalid time unit: 'years' at position 2\n\ - sleep: invalid time interval ' ': Found only whitespace in input"; + sleep: invalid time interval '1years': Invalid time unit: 'years' at position 2\n\ + sleep: invalid time interval ' ': Found only whitespace in input"; // Even if one of the arguments is valid, but the rest isn't, we should still fail and exit early. // So, the timeout of 10 seconds ensures we haven't executed `thread::sleep` with the only valid @@ -272,3 +275,141 @@ fn test_negative_interval() { .fails() .usage_error("invalid time interval '-1': Number was negative"); } + +#[cfg(unix)] +#[test] +#[should_panic = "Program must be run first or has not finished"] +fn test_cmd_result_signal_when_still_running_then_panic() { + let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); + + child + .make_assertion() + .is_alive() + .with_current_output() + .signal(); +} + +#[cfg(unix)] +#[test] +fn test_cmd_result_signal_when_kill_then_signal() { + let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); + + child.kill(); + child + .make_assertion() + .is_not_alive() + .with_current_output() + .signal_is(9) + .signal_name_is("SIGKILL") + .signal_name_is("KILL") + .signal_name_is("9") + .signal() + .expect("Signal was none"); + + let result = child.wait().unwrap(); + result + .signal_is(9) + .signal_name_is("SIGKILL") + .signal_name_is("KILL") + .signal_name_is("9") + .signal() + .expect("Signal was none"); +} + +#[cfg(unix)] +#[rstest] +#[case::signal_only_part_of_name("IGKILL")] // spell-checker: disable-line +#[case::signal_just_sig("SIG")] +#[case::signal_value_too_high("100")] +#[case::signal_value_negative("-1")] +#[should_panic = "Invalid signal name or value"] +fn test_cmd_result_signal_when_invalid_signal_name_then_panic(#[case] signal_name: &str) { + let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); + child.kill(); + let result = child.wait().unwrap(); + result.signal_name_is(signal_name); +} + +#[test] +#[cfg(unix)] +fn test_cmd_result_signal_name_is_accepts_lowercase() { + let mut child = TestScenario::new("sleep").ucmd().arg("60").run_no_wait(); + child.kill(); + let result = child.wait().unwrap(); + result.signal_name_is("sigkill"); + result.signal_name_is("kill"); +} + +#[test] +fn test_uchild_when_wait_and_timeout_is_reached_then_timeout_error() { + let ts = TestScenario::new("sleep"); + let child = ts + .ucmd() + .timeout(Duration::from_secs(1)) + .arg("10.0") + .run_no_wait(); + + match child.wait() { + Err(error) if error.kind() == ErrorKind::Other => { + std::assert_eq!(error.to_string(), "wait: Timeout of '1s' reached"); + } + Err(error) => panic!("Assertion failed: Expected error with timeout but was: {error}"), + Ok(_) => panic!("Assertion failed: Expected timeout of `wait`."), + } +} + +#[rstest] +#[timeout(Duration::from_secs(5))] +fn test_uchild_when_kill_and_timeout_higher_than_kill_time_then_no_panic() { + let ts = TestScenario::new("sleep"); + let mut child = ts + .ucmd() + .timeout(Duration::from_secs(60)) + .arg("20.0") + .run_no_wait(); + + child.kill().make_assertion().is_not_alive(); +} + +#[test] +fn test_uchild_when_try_kill_and_timeout_is_reached_then_error() { + let ts = TestScenario::new("sleep"); + let mut child = ts.ucmd().timeout(Duration::ZERO).arg("10.0").run_no_wait(); + + match child.try_kill() { + Err(error) if error.kind() == ErrorKind::Other => { + std::assert_eq!(error.to_string(), "kill: Timeout of '0s' reached"); + } + Err(error) => panic!("Assertion failed: Expected error with timeout but was: {error}"), + Ok(()) => panic!("Assertion failed: Expected timeout of `try_kill`."), + } +} + +#[test] +#[should_panic = "kill: Timeout of '0s' reached"] +fn test_uchild_when_kill_with_timeout_and_timeout_is_reached_then_panic() { + let ts = TestScenario::new("sleep"); + let mut child = ts.ucmd().timeout(Duration::ZERO).arg("10.0").run_no_wait(); + + child.kill(); + panic!("Assertion failed: Expected timeout of `kill`."); +} + +#[test] +#[should_panic(expected = "wait: Timeout of '1.1s' reached")] +fn test_ucommand_when_run_with_timeout_and_timeout_is_reached_then_panic() { + let ts = TestScenario::new("sleep"); + ts.ucmd() + .timeout(Duration::from_millis(1100)) + .arg("10.0") + .run(); + + panic!("Assertion failed: Expected timeout of `run`.") +} + +#[rstest] +#[timeout(Duration::from_secs(10))] +fn test_ucommand_when_run_with_timeout_higher_then_execution_time_then_no_panic() { + let ts = TestScenario::new("sleep"); + ts.ucmd().timeout(Duration::from_secs(60)).arg("1.0").run(); +} diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 5e13369cab6..1f3b2a8b196 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -8,7 +8,10 @@ use std::time::Duration; -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; fn test_helper(file_name: &str, possible_args: &[&str]) { for args in possible_args { diff --git a/tests/by-util/test_split.rs b/tests/by-util/test_split.rs index a12f8d5808f..042b2c2511e 100644 --- a/tests/by-util/test_split.rs +++ b/tests/by-util/test_split.rs @@ -4,7 +4,6 @@ // file that was distributed with this source code. // spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase ghijkl mnopq rstuv wxyz fivelines twohundredfortyonebytes onehundredlines nbbbb dxen ncccc rlimit NOFILE -use crate::common::util::{AtPath, TestScenario}; use rand::{Rng, SeedableRng, rng}; use regex::Regex; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -16,6 +15,11 @@ use std::{ fs::{File, read_dir}, io::{BufWriter, Read, Write}, }; +use uutests::util::{AtPath, TestScenario}; + +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util_name; fn random_chars(n: usize) -> String { rng() diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index a0835c8f24b..ffe99bcb67a 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -3,7 +3,11 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::{TestScenario, expected_result}; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::unwrap_or_return; +use uutests::util::{TestScenario, expected_result}; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_stdbuf.rs b/tests/by-util/test_stdbuf.rs index 379af607a50..c4294c6af41 100644 --- a/tests/by-util/test_stdbuf.rs +++ b/tests/by-util/test_stdbuf.rs @@ -2,8 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use uutests::new_ucmd; #[cfg(not(target_os = "windows"))] -use crate::common::util::TestScenario; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn invalid_input() { diff --git a/tests/by-util/test_stty.rs b/tests/by-util/test_stty.rs index 5cc6d39d0b9..7ccc56e5dee 100644 --- a/tests/by-util/test_stty.rs +++ b/tests/by-util/test_stty.rs @@ -4,7 +4,9 @@ // file that was distributed with this source code. // spell-checker:ignore parenb parmrk ixany iuclc onlcr ofdel icanon noflsh -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_sum.rs b/tests/by-util/test_sum.rs index 163f691b698..a87084cb460 100644 --- a/tests/by-util/test_sum.rs +++ b/tests/by-util/test_sum.rs @@ -2,7 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_sync.rs b/tests/by-util/test_sync.rs index 9eb2c33df1f..757dc65c12c 100644 --- a/tests/by-util/test_sync.rs +++ b/tests/by-util/test_sync.rs @@ -2,9 +2,11 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use std::fs; use tempfile::tempdir; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_tac.rs b/tests/by-util/test_tac.rs index b5931ce5390..f725615b391 100644 --- a/tests/by-util/test_tac.rs +++ b/tests/by-util/test_tac.rs @@ -3,7 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore axxbxx bxxaxx axxx axxxx xxaxx xxax xxxxa axyz zyax zyxa -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index c4da1de6def..76a93b7c661 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -13,12 +13,6 @@ clippy::cast_possible_truncation )] -use crate::common::random::{AlphanumericNewline, RandomizedString}; -use crate::common::util::TestScenario; -#[cfg(unix)] -use crate::common::util::expected_result; -#[cfg(not(windows))] -use crate::common::util::is_ci; use pretty_assertions::assert_eq; use rand::distr::Alphanumeric; use rstest::rstest; @@ -45,6 +39,18 @@ use tail::chunks::BUFFER_SIZE as CHUNK_BUFFER_SIZE; not(target_os = "openbsd") ))] use tail::text; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::random::{AlphanumericNewline, RandomizedString}; +#[cfg(unix)] +use uutests::unwrap_or_return; +use uutests::util::TestScenario; +#[cfg(unix)] +use uutests::util::expected_result; +#[cfg(unix)] +#[cfg(not(windows))] +use uutests::util::is_ci; +use uutests::util_name; const FOOBAR_TXT: &str = "foobar.txt"; const FOOBAR_2_TXT: &str = "foobar2.txt"; @@ -4805,3 +4811,39 @@ fn test_following_with_pid() { child.kill(); } + +// This error was first detected when running tail so tail is used here but +// should fail with any command that takes piped input. +// See also https://github.com/uutils/coreutils/issues/3895 +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_when_piped_input_then_no_broken_pipe() { + let ts = TestScenario::new("tail"); + for i in 0..10000 { + dbg!(i); + let test_string = "a\nb\n"; + ts.ucmd() + .args(&["-n", "0"]) + .pipe_in(test_string) + .succeeds() + .no_stdout() + .no_stderr(); + } +} + +#[test] +fn test_child_when_run_with_stderr_to_stdout() { + let ts = TestScenario::new("tail"); + let at = &ts.fixtures; + + at.write("data", "file data\n"); + + let expected_stdout = "==> data <==\n\ + file data\n\ + tail: cannot open 'missing' for reading: No such file or directory\n"; + ts.ucmd() + .args(&["data", "missing"]) + .stderr_to_stdout() + .fails() + .stdout_only(expected_stdout); +} diff --git a/tests/by-util/test_tee.rs b/tests/by-util/test_tee.rs index e4e24acb48f..12f4f04e8be 100644 --- a/tests/by-util/test_tee.rs +++ b/tests/by-util/test_tee.rs @@ -4,7 +4,9 @@ // file that was distributed with this source code. #![allow(clippy::borrow_as_ptr)] -use crate::common::util::TestScenario; +use uutests::util::TestScenario; +use uutests::{at_and_ucmd, new_ucmd, util_name}; + use regex::Regex; #[cfg(target_os = "linux")] use std::fmt::Write; @@ -160,12 +162,15 @@ fn test_tee_no_more_writeable_2() { #[cfg(target_os = "linux")] mod linux_only { - use crate::common::util::{AtPath, CmdResult, TestScenario, UCommand}; + use uutests::util::{AtPath, CmdResult, TestScenario, UCommand}; use std::fmt::Write; use std::fs::File; use std::process::Stdio; use std::time::Duration; + use uutests::at_and_ucmd; + use uutests::new_ucmd; + use uutests::util_name; fn make_broken_pipe() -> File { use libc::c_int; diff --git a/tests/by-util/test_test.rs b/tests/by-util/test_test.rs index 41d83c5201e..1dba782f540 100644 --- a/tests/by-util/test_test.rs +++ b/tests/by-util/test_test.rs @@ -5,7 +5,10 @@ // spell-checker:ignore (words) egid euid pseudofloat -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_empty_test_equivalent_to_false() { diff --git a/tests/by-util/test_timeout.rs b/tests/by-util/test_timeout.rs index 423d7f041ef..20d3e8fef05 100644 --- a/tests/by-util/test_timeout.rs +++ b/tests/by-util/test_timeout.rs @@ -3,7 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore dont -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 12015967259..c3b5f1ae1e2 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -4,12 +4,15 @@ // file that was distributed with this source code. // spell-checker:ignore (formats) cymdhm cymdhms mdhm mdhms ymdhm ymdhms datetime mktime -use crate::common::util::{AtPath, TestScenario}; use filetime::FileTime; #[cfg(not(target_os = "freebsd"))] use filetime::set_symlink_file_times; use std::fs::remove_file; use std::path::PathBuf; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::{AtPath, TestScenario}; +use uutests::util_name; fn get_file_times(at: &AtPath, path: &str) -> (FileTime, FileTime) { let m = at.metadata(path); diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index e58872da99e..964fb5df2b0 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -3,7 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore aabbaa aabbcc aabc abbb abbbcddd abcc abcdefabcdef abcdefghijk abcdefghijklmn abcdefghijklmnop ABCDEFGHIJKLMNOPQRS abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFZZ abcxyz ABCXYZ abcxyzabcxyz ABCXYZABCXYZ acbdef alnum amzamz AMZXAMZ bbbd cclass cefgm cntrl compl dabcdef dncase Gzabcdefg PQRST upcase wxyzz xdigit XXXYYY xycde xyyye xyyz xyzzzzxyzzzz ZABCDEF Zamz Cdefghijkl Cdefghijklmn asdfqqwweerr qwerr asdfqwer qwer aassddffqwer asdfqwer -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[cfg(unix)] use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; diff --git a/tests/by-util/test_true.rs b/tests/by-util/test_true.rs index 7711d9b7206..34f82c60284 100644 --- a/tests/by-util/test_true.rs +++ b/tests/by-util/test_true.rs @@ -2,12 +2,12 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. - -use crate::common::util::TestScenario; use regex::Regex; - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] use std::fs::OpenOptions; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_no_args() { diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index f8e4dbe1a46..32e1b152094 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -5,8 +5,11 @@ // spell-checker:ignore (words) RFILE -use crate::common::util::TestScenario; use std::io::{Seek, SeekFrom, Write}; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; static FILE1: &str = "truncate_test_1"; static FILE2: &str = "truncate_test_2"; diff --git a/tests/by-util/test_tsort.rs b/tests/by-util/test_tsort.rs index 8c51883b4f9..c957a59a1cc 100644 --- a/tests/by-util/test_tsort.rs +++ b/tests/by-util/test_tsort.rs @@ -4,7 +4,10 @@ // file that was distributed with this source code. #![allow(clippy::cast_possible_wrap)] -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_tty.rs b/tests/by-util/test_tty.rs index c1a6dc50137..c0124328c46 100644 --- a/tests/by-util/test_tty.rs +++ b/tests/by-util/test_tty.rs @@ -4,7 +4,9 @@ // file that was distributed with this source code. use std::fs::File; -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] #[cfg(not(windows))] diff --git a/tests/by-util/test_uname.rs b/tests/by-util/test_uname.rs index d41bd3cd6c3..986312f68e7 100644 --- a/tests/by-util/test_uname.rs +++ b/tests/by-util/test_uname.rs @@ -2,7 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_unexpand.rs b/tests/by-util/test_unexpand.rs index 89f76c072cd..8b447ecdb5f 100644 --- a/tests/by-util/test_unexpand.rs +++ b/tests/by-util/test_unexpand.rs @@ -3,7 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore contenta -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_uniq.rs b/tests/by-util/test_uniq.rs index 0aa01e46c9a..b59b5e49519 100644 --- a/tests/by-util/test_uniq.rs +++ b/tests/by-util/test_uniq.rs @@ -4,8 +4,11 @@ // file that was distributed with this source code. // spell-checker:ignore nabcd badoption schar -use crate::common::util::TestScenario; use uucore::posix::OBSOLETE; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; static INPUT: &str = "sorted.txt"; static OUTPUT: &str = "sorted-output.txt"; diff --git a/tests/by-util/test_unlink.rs b/tests/by-util/test_unlink.rs index 187ac922e0c..36d1630d300 100644 --- a/tests/by-util/test_unlink.rs +++ b/tests/by-util/test_unlink.rs @@ -2,7 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_uptime.rs b/tests/by-util/test_uptime.rs index 1a2afd638f6..ff5c01df5f8 100644 --- a/tests/by-util/test_uptime.rs +++ b/tests/by-util/test_uptime.rs @@ -6,7 +6,10 @@ // spell-checker:ignore bincode serde utmp runlevel testusr testx #![allow(clippy::cast_possible_wrap, clippy::unreadable_literal)] -use crate::common::util::TestScenario; +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[cfg(not(any(target_os = "macos", target_os = "openbsd")))] use bincode::serialize; diff --git a/tests/by-util/test_users.rs b/tests/by-util/test_users.rs index d000552d358..ec77ffff5e0 100644 --- a/tests/by-util/test_users.rs +++ b/tests/by-util/test_users.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_vdir.rs b/tests/by-util/test_vdir.rs index 97d5b847fb8..cf389f45e1b 100644 --- a/tests/by-util/test_vdir.rs +++ b/tests/by-util/test_vdir.rs @@ -2,8 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::TestScenario; use regex::Regex; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; /* * As vdir use the same functions than ls, we don't have to retest them here. diff --git a/tests/by-util/test_wc.rs b/tests/by-util/test_wc.rs index a48b0558180..b97d6c471be 100644 --- a/tests/by-util/test_wc.rs +++ b/tests/by-util/test_wc.rs @@ -3,7 +3,11 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use crate::common::util::{TestScenario, vec_of_size}; +#[cfg(target_os = "linux")] +use uutests::at_and_ucmd; +use uutests::new_ucmd; +use uutests::util::{TestScenario, vec_of_size}; +use uutests::util_name; // spell-checker:ignore (flags) lwmcL clmwL ; (path) bogusfile emptyfile manyemptylines moby notrailingnewline onelongemptyline onelongword weirdchars #[test] diff --git a/tests/by-util/test_who.rs b/tests/by-util/test_who.rs index 252c26ec1ea..74475895cb0 100644 --- a/tests/by-util/test_who.rs +++ b/tests/by-util/test_who.rs @@ -5,8 +5,10 @@ // spell-checker:ignore (flags) runlevel mesg -use crate::common::util::{TestScenario, expected_result}; - +use uutests::new_ucmd; +use uutests::unwrap_or_return; +use uutests::util::{TestScenario, expected_result}; +use uutests::util_name; #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails_with_code(1); diff --git a/tests/by-util/test_whoami.rs b/tests/by-util/test_whoami.rs index bc0a9908c6f..32fdf719aa7 100644 --- a/tests/by-util/test_whoami.rs +++ b/tests/by-util/test_whoami.rs @@ -3,9 +3,13 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use uutests::new_ucmd; #[cfg(unix)] -use crate::common::util::expected_result; -use crate::common::util::{TestScenario, is_ci, whoami}; +use uutests::unwrap_or_return; +#[cfg(unix)] +use uutests::util::expected_result; +use uutests::util::{TestScenario, is_ci, whoami}; +use uutests::util_name; #[test] fn test_invalid_arg() { diff --git a/tests/by-util/test_yes.rs b/tests/by-util/test_yes.rs index 9f5f84ed85d..b2706de75eb 100644 --- a/tests/by-util/test_yes.rs +++ b/tests/by-util/test_yes.rs @@ -8,7 +8,9 @@ use std::process::ExitStatus; #[cfg(unix)] use std::os::unix::process::ExitStatusExt; -use crate::common::util::TestScenario; +use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[cfg(unix)] fn check_termination(result: ExitStatus) {