1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::{io, path::Path, process, time::Duration};
use thiserror::Error;
use wait_timeout::ChildExt;
use process::{ExitStatus, Command};
pub fn binary_kind(binary_path: &Path) -> BinaryKind {
let exe_parent = binary_path.parent();
let parent_dir_name = exe_parent.and_then(|p| p.file_name()).and_then(|name| name.to_str());
match parent_dir_name {
Some("deps") => BinaryKind::Test,
Some(name) if name.starts_with("rustdoctest") => BinaryKind::DocTest,
_other => BinaryKind::Other,
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum BinaryKind {
Test,
DocTest,
Other,
}
impl BinaryKind {
pub fn is_test(&self) -> bool {
match self {
BinaryKind::Test | BinaryKind::DocTest => true,
BinaryKind::Other => false,
}
}
}
pub fn run_with_timeout(
command: &mut Command,
timeout: Duration,
) -> Result<ExitStatus, RunError> {
let mut child = command.spawn().map_err(|error| RunError::Io {
context: IoErrorContext::Command {
command: format!("{:?}", command),
},
error,
})?;
match child
.wait_timeout(timeout)
.map_err(context(IoErrorContext::WaitWithTimeout))?
{
None => {
child.kill().map_err(context(IoErrorContext::KillProcess))?;
child.wait().map_err(context(IoErrorContext::WaitForProcess))?;
Err(RunError::TimedOut)
}
Some(exit_status) => Ok(exit_status),
}
}
#[derive(Debug, Error)]
pub enum RunError {
#[error("Command timed out")]
TimedOut,
#[error("I/O error: {context}")]
Io {
context: IoErrorContext,
#[source]
error: io::Error,
},
}
#[derive(Debug, Error)]
pub enum IoErrorContext {
#[error("Failed to execute command `{command}`")]
Command {
command: String,
},
#[error("Failed to wait with timeout")]
WaitWithTimeout,
#[error("Failed to kill process after timeout")]
KillProcess,
#[error("Failed to wait for process after killing it after timeout")]
WaitForProcess,
}
fn context(context: IoErrorContext) -> impl FnOnce(io::Error) -> RunError {
|error| RunError::Io { context, error }
}