libtest: Fixed pretty-printing of test names in single-threaded code.

This commit is contained in:
Gilad Naaman 2017-12-15 17:25:44 +02:00
parent e570e9e79a
commit 94bd1216bb
2 changed files with 69 additions and 36 deletions

View file

@ -11,8 +11,8 @@
use super::*; use super::*;
pub(crate) trait OutputFormatter { pub(crate) trait OutputFormatter {
fn write_run_start(&mut self, len: usize) -> io::Result<()>; fn write_run_start(&mut self, test_count: usize) -> io::Result<()>;
fn write_test_start(&mut self, test: &TestDesc) -> io::Result<()>; fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()>;
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()>; fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()>;
fn write_result(&mut self, fn write_result(&mut self,
desc: &TestDesc, desc: &TestDesc,
@ -26,17 +26,26 @@ pub(crate) struct HumanFormatter<T> {
terse: bool, terse: bool,
use_color: bool, use_color: bool,
test_count: usize, test_count: usize,
max_name_len: usize, // number of columns to fill when aligning names
/// Number of columns to fill when aligning names
max_name_len: usize,
is_multithreaded: bool,
} }
impl<T: Write> HumanFormatter<T> { impl<T: Write> HumanFormatter<T> {
pub fn new(out: OutputLocation<T>, use_color: bool, terse: bool, max_name_len: usize) -> Self { pub fn new(out: OutputLocation<T>,
use_color: bool,
terse: bool,
max_name_len: usize,
is_multithreaded: bool) -> Self {
HumanFormatter { HumanFormatter {
out, out,
terse, terse,
use_color, use_color,
test_count: 0, test_count: 0,
max_name_len, max_name_len,
is_multithreaded,
} }
} }
@ -160,28 +169,42 @@ impl<T: Write> HumanFormatter<T> {
} }
Ok(()) Ok(())
} }
fn write_test_name(&mut self, desc: &TestDesc) -> io::Result<()> {
if !(self.terse && desc.name.padding() != PadOnRight) {
let name = desc.padded_name(self.max_name_len, desc.name.padding());
self.write_plain(&format!("test {} ... ", name))?;
}
Ok(())
}
} }
impl<T: Write> OutputFormatter for HumanFormatter<T> { impl<T: Write> OutputFormatter for HumanFormatter<T> {
fn write_run_start(&mut self, len: usize) -> io::Result<()> { fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
let noun = if len != 1 { let noun = if test_count != 1 {
"tests" "tests"
} else { } else {
"test" "test"
}; };
self.write_plain(&format!("\nrunning {} {}\n", len, noun)) self.write_plain(&format!("\nrunning {} {}\n", test_count, noun))
} }
fn write_test_start(&mut self, _desc: &TestDesc) -> io::Result<()> { fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
// Do not print header, as priting it at this point will result in // When running tests concurrently, we should not print
// an unreadable output when running tests concurrently. // the test's name as the result will be mis-aligned.
// When running the tests serially, we print the name here so
// that the user can see which test hangs.
if !self.is_multithreaded {
self.write_test_name(desc)?;
}
Ok(()) Ok(())
} }
fn write_result(&mut self, desc: &TestDesc, result: &TestResult, _: &[u8]) -> io::Result<()> { fn write_result(&mut self, desc: &TestDesc, result: &TestResult, _: &[u8]) -> io::Result<()> {
if !(self.terse && desc.name.padding() != PadOnRight) { if self.is_multithreaded {
let name = desc.padded_name(self.max_name_len, desc.name.padding()); self.write_test_name(desc)?;
self.write_plain(&format!("test {} ... ", name))?;
} }
match *result { match *result {
@ -197,6 +220,10 @@ impl<T: Write> OutputFormatter for HumanFormatter<T> {
} }
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> { fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
if self.is_multithreaded {
self.write_test_name(desc)?;
}
self.write_plain(&format!("test {} has been running for over {} seconds\n", self.write_plain(&format!("test {} has been running for over {} seconds\n",
desc.name, desc.name,
TEST_WARN_TIMEOUT_S)) TEST_WARN_TIMEOUT_S))
@ -251,13 +278,14 @@ pub(crate) struct JsonFormatter<T> {
impl<T: Write> JsonFormatter<T> { impl<T: Write> JsonFormatter<T> {
pub fn new(out: OutputLocation<T>) -> Self { pub fn new(out: OutputLocation<T>) -> Self {
Self { Self { out }
out, }
} }
fn write_str<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> { fn write_message(&mut self, s: &str) -> io::Result<()> {
self.out.write_all(s.as_ref().as_ref())?; assert!(!s.contains('\n'));
self.out.write_all("\n".as_ref())
self.out.write_all(s.as_ref())?;
self.out.write_all(b"\n")
} }
fn write_event(&mut self, fn write_event(&mut self,
@ -266,14 +294,14 @@ impl<T: Write> JsonFormatter<T> {
evt: &str, evt: &str,
extra: Option<String>) -> io::Result<()> { extra: Option<String>) -> io::Result<()> {
if let Some(extras) = extra { if let Some(extras) = extra {
self.write_str(&*format!(r#"{{ "type": "{}", "name": "{}", "event": "{}", {} }}"#, self.write_message(&*format!(r#"{{ "type": "{}", "name": "{}", "event": "{}", {} }}"#,
ty, ty,
name, name,
evt, evt,
extras)) extras))
} }
else { else {
self.write_str(&*format!(r#"{{ "type": "{}", "name": "{}", "event": "{}" }}"#, self.write_message(&*format!(r#"{{ "type": "{}", "name": "{}", "event": "{}" }}"#,
ty, ty,
name, name,
evt)) evt))
@ -282,13 +310,14 @@ impl<T: Write> JsonFormatter<T> {
} }
impl<T: Write> OutputFormatter for JsonFormatter<T> { impl<T: Write> OutputFormatter for JsonFormatter<T> {
fn write_run_start(&mut self, len: usize) -> io::Result<()> { fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
self.write_str( self.write_message(
&*format!(r#"{{ "type": "suite", "event": "started", "test_count": "{}" }}"#, len)) &*format!(r#"{{ "type": "suite", "event": "started", "test_count": "{}" }}"#,
test_count))
} }
fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> { fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
self.write_str(&*format!(r#"{{ "type": "test", "event": "started", "name": "{}" }}"#, self.write_message(&*format!(r#"{{ "type": "test", "event": "started", "name": "{}" }}"#,
desc.name)) desc.name))
} }
@ -348,19 +377,19 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
deviation, deviation,
mbps); mbps);
self.write_str(&*line) self.write_message(&*line)
}, },
} }
} }
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> { fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
self.write_str(&*format!(r#"{{ "type": "test", "event": "timeout", "name": "{}" }}"#, self.write_message(&*format!(r#"{{ "type": "test", "event": "timeout", "name": "{}" }}"#,
desc.name)) desc.name))
} }
fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> { fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
self.write_str(&*format!("{{ \"type\": \"suite\", \ self.write_message(&*format!("{{ \"type\": \"suite\", \
\"event\": \"{}\", \ \"event\": \"{}\", \
\"passed\": {}, \ \"passed\": {}, \
\"failed\": {}, \ \"failed\": {}, \

View file

@ -719,7 +719,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Res
}; };
let quiet = opts.format == OutputFormat::Terse; let quiet = opts.format == OutputFormat::Terse;
let mut out = HumanFormatter::new(output, use_color(opts), quiet, 0); let mut out = HumanFormatter::new(output, use_color(opts), quiet, 0, false);
let mut st = ConsoleTestState::new(opts)?; let mut st = ConsoleTestState::new(opts)?;
let mut ntest = 0; let mut ntest = 0;
@ -820,23 +820,27 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
Some(t) => Pretty(t), Some(t) => Pretty(t),
}; };
let max_name_len = if let Some(t) = tests.iter().max_by_key(|t| len_if_padded(*t)) { let max_name_len = tests.iter()
let n = t.desc.name.as_slice(); .max_by_key(|t| len_if_padded(*t))
n.len() .map(|t| t.desc.name.as_slice().len())
} .unwrap_or(0);
else {
0 let is_multithreaded = match opts.test_threads {
Some(n) => n > 1,
None => get_concurrency() > 1,
}; };
let mut out: Box<OutputFormatter> = match opts.format { let mut out: Box<OutputFormatter> = match opts.format {
OutputFormat::Pretty => Box::new(HumanFormatter::new(output, OutputFormat::Pretty => Box::new(HumanFormatter::new(output,
use_color(opts), use_color(opts),
false, false,
max_name_len)), max_name_len,
is_multithreaded)),
OutputFormat::Terse => Box::new(HumanFormatter::new(output, OutputFormat::Terse => Box::new(HumanFormatter::new(output,
use_color(opts), use_color(opts),
true, true,
max_name_len)), max_name_len,
is_multithreaded)),
OutputFormat::Json => Box::new(JsonFormatter::new(output)), OutputFormat::Json => Box::new(JsonFormatter::new(output)),
}; };
let mut st = ConsoleTestState::new(opts)?; let mut st = ConsoleTestState::new(opts)?;