Feature Request: wait
and/or then
methods after writing to stdin #106
Description
I came across this crate recently, but I cannot remember where...maybe from the CLI-WG? Anyways, it provides a lot of the functionality I wrote in the past for testing some internal projects, so I am excited to start using it in the future, and add tests for some of my Rust CLI applications. Great Work!
I am currently working on a project where I am writing messages into stdin and reading from stdout. I have some tests that deliberately result in relatively long execution times (on the order of 10's of seconds). The general test follows: (1) write message to stdin, (2) wait some amount of time depending on expected processing time, (3) read stdout and check for correctness. For these tests, where a long execution time occurs, I have not found a way to use this crate. I believe the problem is that the stdin handle from the internal Command
is being dropped after writing to stdin but before the command has had time to write to stdout, which leaves the contents of stdout from the wait_with_output
method empty. Any correctness assertions on the output then fail.
Instead of using this crate, here is a non-working example of the test I implemented:
#[test]
fn slow_reply_works() {
const RESPONSE_TIME: u64 = 10; // seconds
let mut child = Command::new("some_cmd") // <-- Not actual command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Child command");
{
let stdin = child.stdin.as_mut().expect("Stdin");
stdin.write_all("slowReplyMessage".as_bytes()).expect("Write to stdin");
// Without this, the `stdin` handle is dropped. My CLI Under Test (CUT) exits when
// stdin reaches EOF or a Broken Pipe occurs. So, there is no time for
// the cmd to complete the long/slow computation and write anything to stdout
// when the output is later retrieved with the `wait_with_output` method. The
// output is empty and any assertion for correctness fails.
thread::sleep(Duration::from_secs(RESPONSE_TIME));
}
let output = child.wait_with_output().expect("Wait on child");
// Correctness assertions for the output follow here, but without the
// `thread::sleep`, the output is empty. Omitted for clarity.
// ...
}
I would like to see a wait
, hold
, or delay
method added to the Assert
struct as I believe the Assert
struct is essentially doing the same time as my non-working example test, but does not have the thread::sleep
line. If such a method existed, the above test could be rewritten more succinctly using this crate as follows:
#[test]
fn slow_reply_works() {
const RESPONSE_TIME: u64 = 10; // seconds
Assert::command(&["some_cmd"])
.stdin("slowReplyMessage").wait(RESPONSE_TIME)
.succeeds()
.and()
.stdout().contains("expected message after delay")
.unwrap();
}
I believe this is cleaner and easier to read, but I understand wait
might be confusing since there is the wait
and wait_with_output
methods for the Command
struct in std::process
. So, maybe hold
or delay
would be better, with a slight personal preference at the moment towards "hold"?
However, this got me thinking that maybe a wait/hold/delay
method is too restrictive? There might be other tests in the future that need to do more than just wait for some time after writing to stdin. So, maybe a then
method with a predicate would be better? The above example with the wait/hold/delay
method could be re-implemented as follows:
#[test]
fn slow_reply_works() {
const RESPONSE_TIME: u64 = 10; // seconds
Assert::command(&["some_cmd"])
// Not sure if anything needs to be passed to the closure, handle to stdin?
.stdin("slowReplyMessage").then(|| {
// Waits for the slow reply to complete, just like the previous example,
// but other actions can be executed.
thread::sleep(Duration::from_secs(RESPONSE_TIME);
})
.succeeds()
.and()
.stdout().contains("expected message after delay")
.unwrap();
}
Of course, both a wait/hold/delay
and then
methods could be added because the then
implementation of wait/hold/delay is a little more verbose/noisy. I am open to alternatives for the then
method name as well.
Any thoughts or comments about this proposed feature/enhancement would be greatly appreciated.
Thanks again for the great crate and work!