You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As food for thoughts, here's how I'm using assert_cmd.
The guiding idea is that I build a struct describing programs to run/delay/kill, a set of input files, and a set of post-run checks that operate on created files (including the program's stdout/stderr). I can run() on that struct once it is set up, and it takes care of setting up the TempDir, scheduling program start, and running the checks.
/// Main struct described above. It's got builder-pattern methods,/// and a `run` method that will do all the work and `assert` upon failure.pubstructExec{cmds:Vec<Cmd>,timeout:Duration,files:Vec<FileIn>,checks:Vec<FilePredicate>,}pubstructCmd{/// Program to run.bin:Command,/// Command name for `after()`, std/err filname prefix, and logs.name:String,/// Expected exit status. Some(0)=success, Some(n)=failure, None=timeout.exit:Option<i32>,/// Fail if the cmd exits too early.mintime:Duration,/// Fail if the cmd run for too long.maxtime:Duration,/// Current state.state:CmdState,/// List of signals to send to the process after startup.signals:Vec<(CmdCond,c_int)>,}enumCmdState{Wait(CmdCond),Started(Child,Instant),Done,}pubenumCmdCond{/// Immediately trueNone,/// Duration elapsedDelay(Duration),/// Other Cmd exitedCmd(String),/// File-based predicatePredicate(FilePredicate),}pubstructFilePredicate{/// Desciption for assert-logging purpose.desc:String,/// Which file to operate on.file:String,/// Closure that tests the content of the file.pred:Box<dynFn(&str) -> bool>,}pubenumFileIn{FromFs(&'static str,&'static str),Bin(&'static str,Vec<u8>),}
With that in place, I have a pretty powerful way to write a unittest, using my crate's main binary and a few ancillary processes:
// As basic as it gets.exec().cmd(Cmd::any("echo","echo","-n a")).check("echo a","echo.out", |s| s == "a").run();// File from hardcoded data.exec().inbytes("a","a").cmd(Cmd::any("cat","cat","a")).check("cat a","cat.out", |s| s == "a").run();// File from file in test/ directory.exec().infile("j","input.basic.json").cmd(Cmd::any("cat","cat","j")).check("cat j","cat.out", |s| s.starts_with("{")).run();// run sequentiallylet start = Instant::now();exec().cmd(Cmd::any("s1","sleep","0.3")).cmd(Cmd::any("s2","sleep","0.3").after("s1")).cmd(Cmd::any("s3","sleep","0.3").after("s2")).cmd(Cmd::any("cat","cat","s1.out s2.out s3.out").after("s3")).run();assert!(Instant::now() - start > Duration::from_millis(900));// delayed startexec().cmd(Cmd::any("0","cat","1.out 2.out").after(20)).cmd(Cmd::any("1","echo","a")).cmd(Cmd::any("2","echo","b")).check("cat","0.out", |s| s == "a\nb\n").run();// timeoutexec().cmd(Cmd::any("s","sleep","1").maxtime(100).exit(None)).run();exec().cmd(Cmd::any("s","sleep","0.05").mintime(50).maxtime(70)).run();// Multiple signals, ordered by Instant.exec().cmd(Cmd::any("s","sleep","1").signal(SIGINT,70)// Added first but triggers last.signal(SIGCONT,10)// Triggers first but doesn't terminate.signal(SIGTERM,30)// Actual terminator.exit(SIGTERM)).run();// Signal after another cmd exit.exec().cmd(Cmd::any("s1","sleep","1").signal(SIGINT,"s2").maxtime(50).exit(SIGINT)).cmd(Cmd::any("s2","sleep","0.01")).run();// Signal after file content matchesexec().cmd(Cmd::any("s1","sleep","1").signal(SIGINT,("s2.out","4")).maxtime(100).exit(SIGINT)).cmd(Cmd::bash("s2","for i in $(seq 10);do echo $i;sleep 0.01;done")).timeout(1000).run();// Main binaryexec().cmd(Cmd::main("m","-V")).check("progname","0.out", |s| s.starts_with("mybin"));// Set/unset envexec().cmd(Cmd::any("env","env","").env("foo","bar").env("PATH",&format!("/ut:{}", env::var("PATH").unwrap())).env_remove("HOME")).check("added_foo","env.out", |s| s.lines().any(|l| l == "foo=bar")).check("changed_path","env.out", |s| s.lines().any(|l| l.starts_with("PATH=/ut:"))).check("removed_home","env.out", |s| !s.lines().any(|l| l.starts_with("HOME="))).run();
If there's enough interest, I could try to get this cleared for upstreaming. Currently there are a few extensions and API decisions that are specific to my project, and the code is a bit ad-hoc in places.
In the tree view, we already show the original and the current value, we
shouldnt show an entire Diff that is only parseable by color.
In changing this, we removed the more cosmetic atom selector. We also
removed the edit distance, since there isn't a known case for it. Let
us know if you needed this!
Fixesassert-rs#94Fixesassert-rs#105
As food for thoughts, here's how I'm using
assert_cmd
.The guiding idea is that I build a struct describing programs to run/delay/kill, a set of input files, and a set of post-run checks that operate on created files (including the program's stdout/stderr). I can
run()
on that struct once it is set up, and it takes care of setting up the TempDir, scheduling program start, and running the checks.With that in place, I have a pretty powerful way to write a unittest, using my crate's
main
binary and a few ancillary processes:If there's enough interest, I could try to get this cleared for upstreaming. Currently there are a few extensions and API decisions that are specific to my project, and the code is a bit ad-hoc in places.
Originally posted by @vincentdephily in #74 (comment)
The text was updated successfully, but these errors were encountered: