Skip to content

Commit

Permalink
At test runs, write to logfile in the same format as to stdout.
Browse files Browse the repository at this point in the history
Example of affected command:

```
cargo test -- --logfile=/tmp/1.log
```

Old format:
```
ok whatever
```

New format:
```

running 1 test
test whatever ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

```

Rationale for the behavior change:
* The code becomes easier to maintain: we reuse the same code to generate output into stdout and into log file
* Fix user confusion, as they currently see different formats in stdout and in log file.
  • Loading branch information
aspotashev committed Apr 12, 2024
1 parent 1c78c9c commit 346b807
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 69 deletions.
74 changes: 7 additions & 67 deletions library/test/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::time::Instant;
use std::vec;

use super::{
bench::fmt_bench_samples,
cli::TestOpts,
event::{CompletedTest, TestEvent},
filter_tests,
Expand All @@ -17,7 +16,7 @@ use super::{
options::{Options, OutputFormat},
run_tests, term,
test_result::TestResult,
time::{TestExecTime, TestSuiteExecTime},
time::TestSuiteExecTime,
types::{NamePadding, TestDesc, TestDescAndFn},
};

Expand Down Expand Up @@ -132,7 +131,6 @@ impl ConsoleTestDiscoveryState {
}

pub struct ConsoleTestState {
pub log_out: Option<File>,
pub total: usize,
pub passed: usize,
pub failed: usize,
Expand All @@ -150,13 +148,7 @@ pub struct ConsoleTestState {

impl ConsoleTestState {
pub fn new(opts: &TestOpts) -> io::Result<ConsoleTestState> {
let log_out = match opts.logfile {
Some(ref path) => Some(File::create(path)?),
None => None,
};

Ok(ConsoleTestState {
log_out,
total: 0,
passed: 0,
failed: 0,
Expand All @@ -173,54 +165,6 @@ impl ConsoleTestState {
})
}

pub fn write_log<F, S>(&mut self, msg: F) -> io::Result<()>
where
S: AsRef<str>,
F: FnOnce() -> S,
{
match self.log_out {
None => Ok(()),
Some(ref mut o) => {
let msg = msg();
let msg = msg.as_ref();
o.write_all(msg.as_bytes())
}
}
}

pub fn write_log_result(
&mut self,
test: &TestDesc,
result: &TestResult,
exec_time: Option<&TestExecTime>,
) -> io::Result<()> {
self.write_log(|| {
let TestDesc { name, ignore_message, .. } = test;
format!(
"{} {}",
match *result {
TestResult::TrOk => "ok".to_owned(),
TestResult::TrFailed => "failed".to_owned(),
TestResult::TrFailedMsg(ref msg) => format!("failed: {msg}"),
TestResult::TrIgnored => {
if let Some(msg) = ignore_message {
format!("ignored: {msg}")
} else {
"ignored".to_owned()
}
}
TestResult::TrBench(ref bs) => fmt_bench_samples(bs),
TestResult::TrTimedFail => "failed (time limit exceeded)".to_owned(),
},
name,
)
})?;
if let Some(exec_time) = exec_time {
self.write_log(|| format!(" <{exec_time}>"))?;
}
self.write_log(|| "\n")
}

fn current_test_count(&self) -> usize {
self.passed + self.failed + self.ignored + self.measured
}
Expand Down Expand Up @@ -325,7 +269,6 @@ fn on_test_event(
let exec_time = &completed_test.exec_time;
let stdout = &completed_test.stdout;

st.write_log_result(test, result, exec_time.as_ref())?;
out.write_result(test, result, exec_time.as_ref(), stdout, st)?;
handle_test_result(st, completed_test);
}
Expand All @@ -337,11 +280,6 @@ fn on_test_event(
/// A simple console test runner.
/// Runs provided tests reporting process and results to the stdout.
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
let mut output = match term::stdout() {
None => OutputLocation::Raw(io::stdout()),
Some(t) => OutputLocation::Pretty(t),
};

let max_name_len = tests
.iter()
.max_by_key(|t| len_if_padded(t))
Expand All @@ -350,23 +288,25 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu

let is_multithreaded = opts.test_threads.unwrap_or_else(get_concurrency) > 1;

let mut multiplexer = OutputMultiplexer::new(false, &opts.logfile)?;
let mut out: Box<dyn OutputFormatter> = match opts.format {
OutputFormat::Pretty => Box::new(PrettyFormatter::new(
&mut output,
&mut multiplexer,
opts.use_color(),
max_name_len,
is_multithreaded,
opts.time_options,
)),
OutputFormat::Terse => Box::new(TerseFormatter::new(
&mut output,
&mut multiplexer,
opts.use_color(),
max_name_len,
is_multithreaded,
)),
OutputFormat::Json => Box::new(JsonFormatter::new(&mut output)),
OutputFormat::Junit => Box::new(JunitFormatter::new(&mut output)),
OutputFormat::Json => Box::new(JsonFormatter::new(&mut multiplexer)),
OutputFormat::Junit => Box::new(JunitFormatter::new(&mut multiplexer)),
};

let mut st = ConsoleTestState::new(opts)?;

// Prevent the usage of `Instant` in some cases:
Expand Down
13 changes: 11 additions & 2 deletions library/test/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,6 @@ fn should_sort_failures_before_printing_them() {
let mut out = PrettyFormatter::new(&mut raw, false, 10, false, None);

let st = console::ConsoleTestState {
log_out: None,
total: 0,
passed: 0,
failed: 0,
Expand Down Expand Up @@ -1033,5 +1032,15 @@ fn test_logfile_format() {
// Split output at line breaks to make the comparison platform-agnostic regarding newline style.
let contents_lines = contents.as_str().lines().collect::<Vec<&str>>();

assert_eq!(contents_lines, vec!["ok whatever"]);
assert_eq!(
contents_lines,
vec![
"",
"running 1 test",
"test whatever ... ok",
"",
"test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s",
""
]
);
}

0 comments on commit 346b807

Please sign in to comment.