@@ -23,12 +23,17 @@ use crate::output::OutputResult;
23
23
pub struct Command {
24
24
cmd : process:: Command ,
25
25
stdin : Option < Vec < u8 > > ,
26
+ timeout : Option < std:: time:: Duration > ,
26
27
}
27
28
28
29
impl Command {
29
30
/// Constructs a new `Command` from a `std` `Command`.
30
31
pub fn from_std ( cmd : process:: Command ) -> Self {
31
- Self { cmd, stdin : None }
32
+ Self {
33
+ cmd,
34
+ stdin : None ,
35
+ timeout : None ,
36
+ }
32
37
}
33
38
34
39
/// Create a `Command` to run a specific binary of the current crate.
@@ -82,6 +87,23 @@ impl Command {
82
87
self
83
88
}
84
89
90
+ /// Error out if a timeout is reached
91
+ ///
92
+ /// ```rust,no_run
93
+ /// use assert_cmd::Command;
94
+ ///
95
+ /// let assert = Command::cargo_bin("bin_fixture")
96
+ /// .unwrap()
97
+ /// .timeout(std::time::Duration::from_secs(1))
98
+ /// .env("sleep", "100")
99
+ /// .assert();
100
+ /// assert.failure();
101
+ /// ```
102
+ pub fn timeout ( & mut self , timeout : std:: time:: Duration ) -> & mut Self {
103
+ self . timeout = Some ( timeout) ;
104
+ self
105
+ }
106
+
85
107
/// Write `path`s content to `stdin` when the `Command` is run.
86
108
///
87
109
/// Paths are relative to the [`env::current_dir`][env_current_dir] and not
@@ -417,7 +439,7 @@ impl Command {
417
439
/// ```
418
440
pub fn output ( & mut self ) -> io:: Result < process:: Output > {
419
441
let spawn = self . spawn ( ) ?;
420
- Self :: wait_with_input_output ( spawn, self . stdin . clone ( ) )
442
+ Self :: wait_with_input_output ( spawn, self . stdin . clone ( ) , self . timeout )
421
443
}
422
444
423
445
/// If `input`, write it to `child`'s stdin while also reading `child`'s
@@ -428,6 +450,7 @@ impl Command {
428
450
fn wait_with_input_output (
429
451
mut child : process:: Child ,
430
452
input : Option < Vec < u8 > > ,
453
+ timeout : Option < std:: time:: Duration > ,
431
454
) -> io:: Result < process:: Output > {
432
455
let stdin = input. and_then ( |i| {
433
456
child
@@ -449,14 +472,28 @@ impl Command {
449
472
450
473
// Finish writing stdin before waiting, because waiting drops stdin.
451
474
stdin. and_then ( |t| t. join ( ) . unwrap ( ) . ok ( ) ) ;
452
- let status = child. wait ( ) ?;
453
- let stdout = stdout. and_then ( |t| t. join ( ) . unwrap ( ) . ok ( ) ) ;
454
- let stderr = stderr. and_then ( |t| t. join ( ) . unwrap ( ) . ok ( ) ) ;
475
+ let status = if let Some ( timeout) = timeout {
476
+ wait_timeout:: ChildExt :: wait_timeout ( & mut child, timeout)
477
+ . transpose ( )
478
+ . unwrap_or_else ( || {
479
+ let _ = child. kill ( ) ;
480
+ child. wait ( )
481
+ } )
482
+ } else {
483
+ child. wait ( )
484
+ } ?;
485
+
486
+ let stdout = stdout
487
+ . and_then ( |t| t. join ( ) . unwrap ( ) . ok ( ) )
488
+ . unwrap_or_default ( ) ;
489
+ let stderr = stderr
490
+ . and_then ( |t| t. join ( ) . unwrap ( ) . ok ( ) )
491
+ . unwrap_or_default ( ) ;
455
492
456
493
Ok ( process:: Output {
457
- status : status ,
458
- stdout : stdout . unwrap_or_default ( ) ,
459
- stderr : stderr . unwrap_or_default ( ) ,
494
+ status,
495
+ stdout,
496
+ stderr,
460
497
} )
461
498
}
462
499
0 commit comments