mirror of
https://github.com/uutils/coreutils
synced 2024-12-12 22:32:53 +00:00
tests/util: Use ExitStatus instead of code, success in CmdResult. Rewrite tests for CmdResult
This commit is contained in:
parent
37e06edadc
commit
19db042022
1 changed files with 185 additions and 217 deletions
|
@ -25,7 +25,7 @@ use std::os::windows::fs::{symlink_dir, symlink_file};
|
|||
#[cfg(windows)]
|
||||
use std::path::MAIN_SEPARATOR;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, Output, Stdio};
|
||||
use std::process::{Child, Command, ExitStatus, Output, Stdio};
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::{self, RecvTimeoutError};
|
||||
use std::thread::{sleep, JoinHandle};
|
||||
|
@ -73,10 +73,7 @@ pub struct CmdResult {
|
|||
//tmpd is used for convenience functions for asserts against fixtures
|
||||
tmpd: Option<Rc<TempDir>>,
|
||||
/// exit status for command (if there is one)
|
||||
code: Option<i32>,
|
||||
/// zero-exit from running the Command?
|
||||
/// see [`success`]
|
||||
success: bool,
|
||||
exit_status: Option<ExitStatus>,
|
||||
/// captured standard output after running the Command
|
||||
stdout: Vec<u8>,
|
||||
/// captured standard error after running the Command
|
||||
|
@ -88,8 +85,7 @@ impl CmdResult {
|
|||
bin_path: String,
|
||||
util_name: Option<String>,
|
||||
tmpd: Option<Rc<TempDir>>,
|
||||
code: Option<i32>,
|
||||
success: bool,
|
||||
exit_status: Option<ExitStatus>,
|
||||
stdout: T,
|
||||
stderr: U,
|
||||
) -> Self
|
||||
|
@ -101,8 +97,7 @@ impl CmdResult {
|
|||
bin_path,
|
||||
util_name,
|
||||
tmpd,
|
||||
code,
|
||||
success,
|
||||
exit_status,
|
||||
stdout: stdout.into(),
|
||||
stderr: stderr.into(),
|
||||
}
|
||||
|
@ -117,8 +112,7 @@ impl CmdResult {
|
|||
self.bin_path.clone(),
|
||||
self.util_name.clone(),
|
||||
self.tmpd.clone(),
|
||||
self.code,
|
||||
self.success,
|
||||
self.exit_status,
|
||||
function(&self.stdout),
|
||||
self.stderr.as_slice(),
|
||||
)
|
||||
|
@ -133,8 +127,7 @@ impl CmdResult {
|
|||
self.bin_path.clone(),
|
||||
self.util_name.clone(),
|
||||
self.tmpd.clone(),
|
||||
self.code,
|
||||
self.success,
|
||||
self.exit_status,
|
||||
function(self.stdout_str()),
|
||||
self.stderr.as_slice(),
|
||||
)
|
||||
|
@ -149,8 +142,7 @@ impl CmdResult {
|
|||
self.bin_path.clone(),
|
||||
self.util_name.clone(),
|
||||
self.tmpd.clone(),
|
||||
self.code,
|
||||
self.success,
|
||||
self.exit_status,
|
||||
self.stdout.as_slice(),
|
||||
function(&self.stderr),
|
||||
)
|
||||
|
@ -165,8 +157,7 @@ impl CmdResult {
|
|||
self.bin_path.clone(),
|
||||
self.util_name.clone(),
|
||||
self.tmpd.clone(),
|
||||
self.code,
|
||||
self.success,
|
||||
self.exit_status,
|
||||
self.stdout.as_slice(),
|
||||
function(self.stderr_str()),
|
||||
)
|
||||
|
@ -219,8 +210,10 @@ impl CmdResult {
|
|||
/// Returns the program's exit code
|
||||
/// Panics if not run or has not finished yet for example when run with `run_no_wait()`
|
||||
pub fn code(&self) -> i32 {
|
||||
self.code
|
||||
self.exit_status
|
||||
.expect("Program must be run first or has not finished, yet")
|
||||
.code()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
@ -240,14 +233,14 @@ impl CmdResult {
|
|||
|
||||
/// Returns whether the program succeeded
|
||||
pub fn succeeded(&self) -> bool {
|
||||
self.success
|
||||
self.exit_status.map_or(true, |e| e.success())
|
||||
}
|
||||
|
||||
/// asserts that the command resulted in a success (zero) status code
|
||||
#[track_caller]
|
||||
pub fn success(&self) -> &Self {
|
||||
assert!(
|
||||
self.success,
|
||||
self.succeeded(),
|
||||
"Command was expected to succeed.\nstdout = {}\n stderr = {}",
|
||||
self.stdout_str(),
|
||||
self.stderr_str()
|
||||
|
@ -259,7 +252,7 @@ impl CmdResult {
|
|||
#[track_caller]
|
||||
pub fn failure(&self) -> &Self {
|
||||
assert!(
|
||||
!self.success,
|
||||
!self.succeeded(),
|
||||
"Command was expected to fail.\nstdout = {}\n stderr = {}",
|
||||
self.stdout_str(),
|
||||
self.stderr_str()
|
||||
|
@ -476,7 +469,7 @@ impl CmdResult {
|
|||
|
||||
#[track_caller]
|
||||
pub fn fails_silently(&self) -> &Self {
|
||||
assert!(!self.success);
|
||||
assert!(!self.succeeded());
|
||||
assert!(self.stderr.is_empty());
|
||||
self
|
||||
}
|
||||
|
@ -1447,12 +1440,10 @@ impl<'a> UChildAssertion<'a> {
|
|||
}
|
||||
|
||||
fn with_output(&mut self, mode: AssertionMode) -> CmdResult {
|
||||
let (code, success) = match self.uchild.is_alive() {
|
||||
true => (None, true),
|
||||
false => {
|
||||
let status = self.uchild.raw.wait().unwrap();
|
||||
(status.code(), status.success())
|
||||
}
|
||||
let exit_status = if self.uchild.is_alive() {
|
||||
None
|
||||
} else {
|
||||
Some(self.uchild.raw.wait().unwrap())
|
||||
};
|
||||
let (stdout, stderr) = match mode {
|
||||
AssertionMode::All => (
|
||||
|
@ -1469,8 +1460,7 @@ impl<'a> UChildAssertion<'a> {
|
|||
bin_path: self.uchild.bin_path.clone(),
|
||||
util_name: self.uchild.util_name.clone(),
|
||||
tmpd: self.uchild.tmpd.clone(),
|
||||
code,
|
||||
success,
|
||||
exit_status,
|
||||
stdout,
|
||||
stderr,
|
||||
}
|
||||
|
@ -1699,8 +1689,7 @@ impl UChild {
|
|||
bin_path,
|
||||
util_name,
|
||||
tmpd,
|
||||
code: output.status.code(),
|
||||
success: output.status.success(),
|
||||
exit_status: Some(output.status),
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr,
|
||||
})
|
||||
|
@ -2233,8 +2222,7 @@ pub fn expected_result(ts: &TestScenario, args: &[&str]) -> std::result::Result<
|
|||
ts.bin_path.as_os_str().to_str().unwrap().to_string(),
|
||||
Some(ts.util_name.clone()),
|
||||
Some(result.tmpd()),
|
||||
Some(result.code()),
|
||||
result.succeeded(),
|
||||
result.exit_status,
|
||||
stdout.as_bytes(),
|
||||
stderr.as_bytes(),
|
||||
))
|
||||
|
@ -2309,152 +2297,156 @@ pub fn run_ucmd_as_root(
|
|||
mod tests {
|
||||
// spell-checker:ignore (tests) asdfsadfa
|
||||
use super::*;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[cfg(windows)]
|
||||
fn run_cmd(cmd: &str) -> CmdResult {
|
||||
UCommand::new_from_tmp::<&str, String>("cmd", &None, Rc::new(tempdir().unwrap()), true)
|
||||
.arg("/C")
|
||||
.arg(cmd)
|
||||
.run()
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
fn run_cmd(cmd: &str) -> CmdResult {
|
||||
return UCommand::new_from_tmp::<&str, String>(
|
||||
"sh",
|
||||
&None,
|
||||
Rc::new(tempdir().unwrap()),
|
||||
true,
|
||||
)
|
||||
.arg("-c")
|
||||
.arg(cmd)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_is() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: Some(32),
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.code_is(32);
|
||||
fn test_command_result_when_no_output_with_exit_32() {
|
||||
let result = run_cmd("exit 32");
|
||||
|
||||
if cfg!(windows) {
|
||||
std::assert!(result.bin_path.ends_with("cmd"));
|
||||
} else {
|
||||
std::assert!(result.bin_path.ends_with("sh"));
|
||||
}
|
||||
|
||||
std::assert!(result.util_name.is_none());
|
||||
std::assert!(result.tmpd.is_some());
|
||||
|
||||
assert!(result.exit_status.is_some());
|
||||
std::assert_eq!(result.code(), 32);
|
||||
result.code_is(32);
|
||||
assert!(!result.succeeded());
|
||||
result.failure();
|
||||
result.fails_silently();
|
||||
assert!(result.stderr.is_empty());
|
||||
assert!(result.stdout.is_empty());
|
||||
result.no_output();
|
||||
result.no_stderr();
|
||||
result.no_stdout();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_code_is_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: Some(32),
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.code_is(1);
|
||||
fn test_command_result_when_exit_32_then_success_panic() {
|
||||
run_cmd("exit 32").success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_failure() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.failure();
|
||||
fn test_command_result_when_no_output_with_exit_0() {
|
||||
let result = run_cmd("exit 0");
|
||||
|
||||
assert!(result.exit_status.is_some());
|
||||
std::assert_eq!(result.code(), 0);
|
||||
result.code_is(0);
|
||||
assert!(result.succeeded());
|
||||
result.success();
|
||||
assert!(result.stderr.is_empty());
|
||||
assert!(result.stdout.is_empty());
|
||||
result.no_output();
|
||||
result.no_stderr();
|
||||
result.no_stdout();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_failure_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_success() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.success();
|
||||
fn test_command_result_when_exit_0_then_failure_panics() {
|
||||
run_cmd("exit 0").failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_success_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.success();
|
||||
fn test_command_result_when_exit_0_then_silent_failure_panics() {
|
||||
run_cmd("exit 0").fails_silently();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_stderr_output() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.no_stderr();
|
||||
res.no_stdout();
|
||||
fn test_command_result_when_stdout_with_exit_0() {
|
||||
#[cfg(windows)]
|
||||
let (result, vector, string) = (
|
||||
run_cmd("echo hello& exit 0"),
|
||||
vec![b'h', b'e', b'l', b'l', b'o', b'\r', b'\n'],
|
||||
"hello\r\n",
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
let (result, vector, string) = (
|
||||
run_cmd("echo hello; exit 0"),
|
||||
vec![b'h', b'e', b'l', b'l', b'o', b'\n'],
|
||||
"hello\n",
|
||||
);
|
||||
|
||||
assert!(result.exit_status.is_some());
|
||||
std::assert_eq!(result.code(), 0);
|
||||
result.code_is(0);
|
||||
assert!(result.succeeded());
|
||||
result.success();
|
||||
assert!(result.stderr.is_empty());
|
||||
std::assert_eq!(result.stdout, vector);
|
||||
result.no_stderr();
|
||||
result.stdout_is(string);
|
||||
result.stdout_is_bytes(&vector);
|
||||
result.stdout_only(string);
|
||||
result.stdout_only_bytes(&vector);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_no_stderr_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "asdfsadfa".into(),
|
||||
};
|
||||
fn test_command_result_when_stderr_with_exit_0() {
|
||||
#[cfg(windows)]
|
||||
let (result, vector, string) = (
|
||||
run_cmd("echo hello>&2& exit 0"),
|
||||
vec![b'h', b'e', b'l', b'l', b'o', b'\r', b'\n'],
|
||||
"hello\r\n",
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
let (result, vector, string) = (
|
||||
run_cmd("echo hello >&2; exit 0"),
|
||||
vec![b'h', b'e', b'l', b'l', b'o', b'\n'],
|
||||
"hello\n",
|
||||
);
|
||||
|
||||
res.no_stderr();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_no_stdout_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "asdfsadfa".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
|
||||
res.no_stdout();
|
||||
assert!(result.exit_status.is_some());
|
||||
std::assert_eq!(result.code(), 0);
|
||||
result.code_is(0);
|
||||
assert!(result.succeeded());
|
||||
result.success();
|
||||
assert!(result.stdout.is_empty());
|
||||
result.no_stdout();
|
||||
std::assert_eq!(result.stderr, vector);
|
||||
result.stderr_is(string);
|
||||
result.stderr_is_bytes(&vector);
|
||||
result.stderr_only(string);
|
||||
result.stderr_only_bytes(&vector);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_std_does_not_contain() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let res = run_cmd(
|
||||
"(echo This is a likely error message& echo This is a likely error message>&2) & exit 0",
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
let res = run_cmd(
|
||||
"echo This is a likely error message; echo This is a likely error message >&2; exit 0",
|
||||
);
|
||||
res.stdout_does_not_contain("unlikely");
|
||||
res.stderr_does_not_contain("unlikely");
|
||||
}
|
||||
|
@ -2462,15 +2454,10 @@ mod tests {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stdout_does_not_contain_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let res = run_cmd("echo This is a likely error message& exit 0");
|
||||
#[cfg(not(windows))]
|
||||
let res = run_cmd("echo This is a likely error message; exit 0");
|
||||
|
||||
res.stdout_does_not_contain("likely");
|
||||
}
|
||||
|
@ -2478,30 +2465,25 @@ mod tests {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stderr_does_not_contain_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let res = run_cmd("echo This is a likely error message>&2 & exit 0");
|
||||
#[cfg(not(windows))]
|
||||
let res = run_cmd("echo This is a likely error message >&2; exit 0");
|
||||
|
||||
res.stderr_does_not_contain("likely");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdout_matches() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let res = run_cmd(
|
||||
"(echo This is a likely error message& echo This is a likely error message>&2 ) & exit 0",
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
let res = run_cmd(
|
||||
"echo This is a likely error message; echo This is a likely error message >&2; exit 0",
|
||||
);
|
||||
|
||||
let positive = regex::Regex::new(".*likely.*").unwrap();
|
||||
let negative = regex::Regex::new(".*unlikely.*").unwrap();
|
||||
res.stdout_matches(&positive);
|
||||
|
@ -2511,66 +2493,52 @@ mod tests {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stdout_matches_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
let negative = regex::Regex::new(".*unlikely.*").unwrap();
|
||||
#[cfg(windows)]
|
||||
let res = run_cmd(
|
||||
"(echo This is a likely error message& echo This is a likely error message>&2) & exit 0",
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
let res = run_cmd(
|
||||
"echo This is a likely error message; echo This is a likely error message >&2; exit 0",
|
||||
);
|
||||
|
||||
let negative = regex::Regex::new(".*unlikely.*").unwrap();
|
||||
res.stdout_matches(&negative);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stdout_not_matches_fail() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
let positive = regex::Regex::new(".*likely.*").unwrap();
|
||||
#[cfg(windows)]
|
||||
let res = run_cmd(
|
||||
"(echo This is a likely error message& echo This is a likely error message>&2) & exit 0",
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
let res = run_cmd(
|
||||
"echo This is a likely error message; echo This is a likely error message >&2; exit 0",
|
||||
);
|
||||
|
||||
let positive = regex::Regex::new(".*likely.*").unwrap();
|
||||
res.stdout_does_not_match(&positive);
|
||||
}
|
||||
|
||||
#[cfg(feature = "echo")]
|
||||
#[test]
|
||||
fn test_normalized_newlines_stdout_is() {
|
||||
let res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "A\r\nB\nC".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
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 res = CmdResult {
|
||||
bin_path: String::new(),
|
||||
util_name: None,
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "A\r\nB\nC".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
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");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue