mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
more ergonomic testing
This commit is contained in:
parent
aafd20d565
commit
5e764ecf6e
5 changed files with 97 additions and 34 deletions
|
@ -27,6 +27,7 @@ static FIXTURES_DIR: &'static str = "tests/fixtures";
|
||||||
static ALREADY_RUN: &'static str = " you have already run this UCommand, if you want to run \
|
static ALREADY_RUN: &'static str = " you have already run this UCommand, if you want to run \
|
||||||
another command in the same test, use TestSet::new instead of \
|
another command in the same test, use TestSet::new instead of \
|
||||||
testing();";
|
testing();";
|
||||||
|
static MULTIPLE_STDIN_MEANINGLESS: &'static str = "Ucommand is designed around a typical use case of: provide args and input stream -> spawn process -> block until completion -> return output streams. For verifying that a particular section of the input stream is what causes a particular behavior, use the Command type directly.";
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! assert_empty_stderr(
|
macro_rules! assert_empty_stderr(
|
||||||
|
@ -53,6 +54,44 @@ pub struct CmdResult {
|
||||||
pub stderr: String,
|
pub stderr: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CmdResult {
|
||||||
|
pub fn success(&self) -> Box<&CmdResult> {
|
||||||
|
assert!(self.success);
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn failure(&self) -> Box<&CmdResult> {
|
||||||
|
assert!(!self.success);
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn no_stderr(&self) -> Box<&CmdResult> {
|
||||||
|
assert!(self.stderr.len() == 0);
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn no_stdout(&self) -> Box<&CmdResult> {
|
||||||
|
assert!(self.stdout.len() == 0);
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn stdout_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
|
||||||
|
assert!(self.stdout.trim_right() == String::from(msg.as_ref()).trim_right());
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn stderr_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
|
||||||
|
assert!(self.stderr.trim_right() == String::from(msg.as_ref()).trim_right());
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn stdout_only<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
|
||||||
|
self.stdout_is(msg).no_stderr()
|
||||||
|
}
|
||||||
|
pub fn stderr_only<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
|
||||||
|
self.stderr_is(msg).no_stdout()
|
||||||
|
}
|
||||||
|
pub fn fails_silently(&self) -> Box<&CmdResult> {
|
||||||
|
assert!(!self.success);
|
||||||
|
assert!(self.stderr.len() == 0);
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn log_info<T: AsRef<str>, U: AsRef<str>>(msg: T, par: U) {
|
pub fn log_info<T: AsRef<str>, U: AsRef<str>>(msg: T, par: U) {
|
||||||
println!("{}: {}", msg.as_ref(), par.as_ref());
|
println!("{}: {}", msg.as_ref(), par.as_ref());
|
||||||
}
|
}
|
||||||
|
@ -272,6 +311,7 @@ pub struct UCommand {
|
||||||
comm_string: String,
|
comm_string: String,
|
||||||
tmpd: Option<Rc<TempDir>>,
|
tmpd: Option<Rc<TempDir>>,
|
||||||
has_run: bool,
|
has_run: bool,
|
||||||
|
stdin: Option<Vec<u8>>
|
||||||
}
|
}
|
||||||
impl UCommand {
|
impl UCommand {
|
||||||
pub fn new<T: AsRef<OsStr>, U: AsRef<OsStr>>(arg: T, curdir: U, env_clear: bool) -> UCommand {
|
pub fn new<T: AsRef<OsStr>, U: AsRef<OsStr>>(arg: T, curdir: U, env_clear: bool) -> UCommand {
|
||||||
|
@ -299,6 +339,7 @@ impl UCommand {
|
||||||
cmd
|
cmd
|
||||||
},
|
},
|
||||||
comm_string: String::from(arg.as_ref().to_str().unwrap()),
|
comm_string: String::from(arg.as_ref().to_str().unwrap()),
|
||||||
|
stdin: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_from_tmp<T: AsRef<OsStr>>(arg: T, tmpd: Rc<TempDir>, env_clear: bool) -> UCommand {
|
pub fn new_from_tmp<T: AsRef<OsStr>>(arg: T, tmpd: Rc<TempDir>, env_clear: bool) -> UCommand {
|
||||||
|
@ -319,7 +360,7 @@ impl UCommand {
|
||||||
|
|
||||||
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> Box<&mut UCommand> {
|
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> Box<&mut UCommand> {
|
||||||
if self.has_run {
|
if self.has_run {
|
||||||
panic!(ALREADY_RUN);
|
panic!(MULTIPLE_STDIN_MEANINGLESS);
|
||||||
}
|
}
|
||||||
for s in args {
|
for s in args {
|
||||||
self.comm_string.push_str(" ");
|
self.comm_string.push_str(" ");
|
||||||
|
@ -339,37 +380,59 @@ impl UCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> CmdResult {
|
pub fn run(&mut self) -> CmdResult {
|
||||||
|
if self.has_run {
|
||||||
|
panic!(ALREADY_RUN);
|
||||||
|
}
|
||||||
self.has_run = true;
|
self.has_run = true;
|
||||||
log_info("run", &self.comm_string);
|
log_info("run", &self.comm_string);
|
||||||
let prog = self.raw.output().unwrap();
|
let prog = match self.stdin {
|
||||||
|
Some(ref input) => {
|
||||||
|
let mut result = self.raw
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
result.stdin
|
||||||
|
.take()
|
||||||
|
.unwrap_or_else(
|
||||||
|
|| panic!(
|
||||||
|
"Could not take child process stdin"))
|
||||||
|
.write_all(&input)
|
||||||
|
.unwrap_or_else(|e| panic!("{}", e));
|
||||||
|
|
||||||
|
result.wait_with_output().unwrap()
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.raw.output().unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
CmdResult {
|
CmdResult {
|
||||||
success: prog.status.success(),
|
success: prog.status.success(),
|
||||||
stdout: from_utf8(&prog.stdout).unwrap().to_string(),
|
stdout: from_utf8(&prog.stdout).unwrap().to_string(),
|
||||||
stderr: from_utf8(&prog.stderr).unwrap().to_string(),
|
stderr: from_utf8(&prog.stderr).unwrap().to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn run_piped_stdin<T: AsRef<[u8]>>(&mut self, input: T) -> CmdResult {
|
pub fn pipe_in<T: Into<Vec<u8>>>(&mut self, input: T) -> Box<&mut UCommand> {
|
||||||
self.has_run = true;
|
if self.stdin.is_some() {
|
||||||
log_info("run_piped_stdin", &self.comm_string);
|
panic!(MULTIPLE_STDIN_MEANINGLESS);
|
||||||
let mut result = self.raw
|
|
||||||
.stdin(Stdio::piped())
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
result.stdin
|
|
||||||
.take()
|
|
||||||
.unwrap_or_else(|| panic!("Could not take child process stdin"))
|
|
||||||
.write_all(input.as_ref())
|
|
||||||
.unwrap_or_else(|e| panic!("{}", e));
|
|
||||||
|
|
||||||
let prog = result.wait_with_output().unwrap();
|
|
||||||
CmdResult {
|
|
||||||
success: prog.status.success(),
|
|
||||||
stdout: from_utf8(&prog.stdout).unwrap().to_string(),
|
|
||||||
stderr: from_utf8(&prog.stderr).unwrap().to_string(),
|
|
||||||
}
|
}
|
||||||
|
self.stdin = Some(input.into());
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
pub fn run_piped_stdin<T: Into<Vec<u8>>>(&mut self, input: T) -> CmdResult {
|
||||||
|
self.pipe_in(input).run()
|
||||||
|
}
|
||||||
|
pub fn succeeds(&mut self) -> CmdResult {
|
||||||
|
let cmd_result = self.run();
|
||||||
|
cmd_result.success();
|
||||||
|
cmd_result
|
||||||
|
}
|
||||||
|
pub fn fails(&mut self) -> CmdResult {
|
||||||
|
let cmd_result = self.run();
|
||||||
|
cmd_result.failure();
|
||||||
|
cmd_result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ fn test_symlink_interactive() {
|
||||||
|
|
||||||
let result1 = ts.util_cmd()
|
let result1 = ts.util_cmd()
|
||||||
.args(&["-i", "-s", file, link])
|
.args(&["-i", "-s", file, link])
|
||||||
.run_piped_stdin(b"n");
|
.run_piped_stdin("n");
|
||||||
|
|
||||||
assert_empty_stderr!(result1);
|
assert_empty_stderr!(result1);
|
||||||
assert!(result1.success);
|
assert!(result1.success);
|
||||||
|
@ -136,7 +136,7 @@ fn test_symlink_interactive() {
|
||||||
|
|
||||||
let result2 = ts.util_cmd()
|
let result2 = ts.util_cmd()
|
||||||
.args(&["-i", "-s", file, link])
|
.args(&["-i", "-s", file, link])
|
||||||
.run_piped_stdin(b"Yesh");
|
.run_piped_stdin("Yesh");
|
||||||
|
|
||||||
assert_empty_stderr!(result2);
|
assert_empty_stderr!(result2);
|
||||||
assert!(result2.success);
|
assert!(result2.success);
|
||||||
|
|
|
@ -131,7 +131,7 @@ fn test_mv_interactive() {
|
||||||
at.touch(file_b);
|
at.touch(file_b);
|
||||||
|
|
||||||
|
|
||||||
let result1 = ts.util_cmd().arg("-i").arg(file_a).arg(file_b).run_piped_stdin(b"n");
|
let result1 = ts.util_cmd().arg("-i").arg(file_a).arg(file_b).run_piped_stdin("n");
|
||||||
|
|
||||||
assert_empty_stderr!(result1);
|
assert_empty_stderr!(result1);
|
||||||
assert!(result1.success);
|
assert!(result1.success);
|
||||||
|
@ -140,7 +140,7 @@ fn test_mv_interactive() {
|
||||||
assert!(at.file_exists(file_b));
|
assert!(at.file_exists(file_b));
|
||||||
|
|
||||||
|
|
||||||
let result2 = ts.util_cmd().arg("-i").arg(file_a).arg(file_b).run_piped_stdin(b"Yesh");
|
let result2 = ts.util_cmd().arg("-i").arg(file_a).arg(file_b).run_piped_stdin("Yesh");
|
||||||
|
|
||||||
assert_empty_stderr!(result2);
|
assert_empty_stderr!(result2);
|
||||||
assert!(result2.success);
|
assert!(result2.success);
|
||||||
|
|
|
@ -51,7 +51,7 @@ fn test_rm_interactive() {
|
||||||
.arg("-i")
|
.arg("-i")
|
||||||
.arg(file_a)
|
.arg(file_a)
|
||||||
.arg(file_b)
|
.arg(file_b)
|
||||||
.run_piped_stdin(b"n");
|
.run_piped_stdin("n");
|
||||||
|
|
||||||
assert!(result1.success);
|
assert!(result1.success);
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ fn test_rm_interactive() {
|
||||||
.arg("-i")
|
.arg("-i")
|
||||||
.arg(file_a)
|
.arg(file_a)
|
||||||
.arg(file_b)
|
.arg(file_b)
|
||||||
.run_piped_stdin(b"Yesh");
|
.run_piped_stdin("Yesh");
|
||||||
|
|
||||||
assert!(result2.success);
|
assert!(result2.success);
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ static UTIL_NAME: &'static str = "tr";
|
||||||
#[test]
|
#[test]
|
||||||
fn test_toupper() {
|
fn test_toupper() {
|
||||||
let (_, mut ucmd) = testing(UTIL_NAME);
|
let (_, mut ucmd) = testing(UTIL_NAME);
|
||||||
let result = ucmd.args(&["a-z", "A-Z"]).run_piped_stdin(b"!abcd!");
|
let result = ucmd.args(&["a-z", "A-Z"]).run_piped_stdin("!abcd!");
|
||||||
assert_eq!(result.stdout, "!ABCD!");
|
assert_eq!(result.stdout, "!ABCD!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_small_set2() {
|
fn test_small_set2() {
|
||||||
let (_, mut ucmd) = testing(UTIL_NAME);
|
let (_, mut ucmd) = testing(UTIL_NAME);
|
||||||
let result = ucmd.args(&["0-9", "X"]).run_piped_stdin(b"@0123456789");
|
let result = ucmd.args(&["0-9", "X"]).run_piped_stdin("@0123456789");
|
||||||
assert_eq!(result.stdout, "@XXXXXXXXXX");
|
assert_eq!(result.stdout, "@XXXXXXXXXX");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,13 +32,13 @@ fn test_unicode() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete() {
|
fn test_delete() {
|
||||||
let (_, mut ucmd) = testing(UTIL_NAME);
|
let (_, mut ucmd) = testing(UTIL_NAME);
|
||||||
let result = ucmd.args(&["-d", "a-z"]).run_piped_stdin(b"aBcD");
|
let result = ucmd.args(&["-d", "a-z"]).run_piped_stdin("aBcD");
|
||||||
assert_eq!(result.stdout, "BD");
|
assert_eq!(result.stdout, "BD");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delete_complement() {
|
fn test_delete_complement() {
|
||||||
let (_, mut ucmd) = testing(UTIL_NAME);
|
let (_, mut ucmd) = testing(UTIL_NAME);
|
||||||
let result = ucmd.args(&["-d", "-c", "a-z"]).run_piped_stdin(b"aBcD");
|
let result = ucmd.args(&["-d", "-c", "a-z"]).run_piped_stdin("aBcD");
|
||||||
assert_eq!(result.stdout, "ac");
|
assert_eq!(result.stdout, "ac");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue