Skip to content

Commit ddd390a

Browse files
committed
ptest: Allow all test runs to be logged
Check the environment variable `PTEST_LOG_ALL`, and if it is set, write the output of all test runs to a log file instead of just the failures. Failed test are still written to failure-nnnn.log, but successful runs are written to success-nnnn.log. If the environment variable is not set, ptest behaves as before, only writing to log files when there is a failure. Signed-off-by: David Brown <[email protected]>
1 parent ed90fbf commit ddd390a

File tree

1 file changed

+61
-38
lines changed

1 file changed

+61
-38
lines changed

ptest/src/main.rs

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ use chrono::Local;
1414
use log::{debug, error, warn};
1515
use std::{
1616
collections::HashSet,
17+
env,
1718
fs::{self, OpenOptions},
1819
io::{ErrorKind, stdout, Write},
19-
process::{Command, Output},
20+
process::Command,
2021
result,
2122
sync::{
2223
Arc,
@@ -82,6 +83,15 @@ struct State {
8283
total: usize,
8384
}
8485

86+
/// Result of a test run.
87+
struct TestResult {
88+
/// Was this run successful.
89+
success: bool,
90+
91+
/// The captured output.
92+
output: Vec<u8>,
93+
}
94+
8595
impl State {
8696
fn new(total: usize) -> Arc<Mutex<State>> {
8797
Arc::new(Mutex::new(State {
@@ -101,46 +111,41 @@ impl State {
101111
self.status();
102112
}
103113

104-
fn done(&mut self, fs: &FeatureSet, output: Result<Option<Output>>) {
114+
fn done(&mut self, fs: &FeatureSet, output: Result<TestResult>) {
105115
let key = fs.textual();
106116
self.running.remove(&key);
107117
self.done.insert(key.clone());
108118
match output {
109-
Ok(None) => {
110-
// println!("Success {} ({} running)", key, self.running.len());
111-
}
112-
Ok(Some(output)) => {
113-
// Write the output into a file.
114-
let mut count = 1;
115-
let (mut fd, logname) = loop {
116-
let name = format!("./failure-{:04}.log", count);
117-
count += 1;
118-
match OpenOptions::new()
119-
.create_new(true)
120-
.write(true)
121-
.open(&name)
122-
{
123-
Ok(file) => break (file, name),
124-
Err(ref err) if err.kind() == ErrorKind::AlreadyExists => continue,
125-
Err(err) => {
126-
error!("Unable to write log file to current directory: {:?}", err);
127-
return;
119+
Ok(output) => {
120+
if !output.success || log_all() {
121+
// Write the output into a file.
122+
let mut count = 1;
123+
let (mut fd, logname) = loop {
124+
let base = if output.success { "success" } else { "failure" };
125+
let name = format!("./{}-{:04}.log", base, count);
126+
count += 1;
127+
match OpenOptions::new()
128+
.create_new(true)
129+
.write(true)
130+
.open(&name)
131+
{
132+
Ok(file) => break (file, name),
133+
Err(ref err) if err.kind() == ErrorKind::AlreadyExists => continue,
134+
Err(err) => {
135+
error!("Unable to write log file to current directory: {:?}", err);
136+
return;
137+
}
128138
}
139+
};
140+
fd.write_all(&output.output).unwrap();
141+
if !output.success {
142+
error!("Failure {} log:{:?} ({} running)", key, logname,
143+
self.running.len());
129144
}
130-
};
131-
writeln!(&mut fd, "Test failure {}", key).unwrap();
132-
writeln!(&mut fd, "time: {}", Local::now().to_rfc3339()).unwrap();
133-
writeln!(&mut fd, "----------------------------------------").unwrap();
134-
writeln!(&mut fd, "stdout:").unwrap();
135-
fd.write_all(&output.stdout).unwrap();
136-
writeln!(&mut fd, "----------------------------------------").unwrap();
137-
writeln!(&mut fd, "\nstderr:").unwrap();
138-
fd.write_all(&output.stderr).unwrap();
139-
error!("Failure {} log:{:?} ({} running)", key, logname,
140-
self.running.len());
145+
}
141146
}
142147
Err(err) => {
143-
error!("Unable to run test {:?} ({:?})", key, err);
148+
error!("Unable to run test {:?} ({:?}", key, err);
144149
}
145150
}
146151
self.status();
@@ -239,18 +244,31 @@ impl FeatureSet {
239244
/// Run a test for this given feature set. Output is captured and will be returned if there is
240245
/// an error. Each will be run successively, and the first failure will be returned.
241246
/// Otherwise, it returns None, which means everything worked.
242-
fn run(&self) -> Result<Option<Output>> {
247+
fn run(&self) -> Result<TestResult> {
248+
let mut output = vec![];
249+
let mut success = true;
243250
for v in &self.values {
244-
let output = Command::new("bash")
251+
let cmdout = Command::new("bash")
245252
.arg("./ci/sim_run.sh")
246253
.current_dir("..")
247254
.env(&self.env, v)
248255
.output()?;
249-
if !output.status.success() {
250-
return Ok(Some(output));
256+
// Grab the output for logging, etc.
257+
writeln!(&mut output, "Test {} {}",
258+
if cmdout.status.success() { "success" } else { "FAILURE" },
259+
self.textual())?;
260+
writeln!(&mut output, "time: {}", Local::now().to_rfc3339())?;
261+
writeln!(&mut output, "----------------------------------------")?;
262+
writeln!(&mut output, "stdout:")?;
263+
output.extend(&cmdout.stdout);
264+
writeln!(&mut output, "----------------------------------------")?;
265+
writeln!(&mut output, "stderr:")?;
266+
output.extend(&cmdout.stderr);
267+
if !cmdout.status.success() {
268+
success = false;
251269
}
252270
}
253-
return Ok(None);
271+
Ok(TestResult { success, output })
254272
}
255273

256274
/// Convert this feature set into a textual representation
@@ -282,3 +300,8 @@ fn lookup_matrix(y: &Yaml) -> Option<&Vec<Yaml>> {
282300
.as_hash()?.get(&features)?
283301
.as_vec()
284302
}
303+
304+
/// Query if we should be logging all tests and not only failures.
305+
fn log_all() -> bool {
306+
env::var("PTEST_LOG_ALL").is_ok()
307+
}

0 commit comments

Comments
 (0)