summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGilad Naaman <gilad.naaman@gmail.com>2017-11-08 06:06:16 +0200
committerGilad Naaman <gilad.naaman@gmail.com>2018-01-26 19:46:04 +0200
commitd24f9af31c4e96e5158677408b513d3e33be7357 (patch)
treec3ff05111217cd576a8e8e19026fead2e7a54f14
parenta97cd17f5d71fb4ec362f4fbd79373a6e7ed7b82 (diff)
downloadrust-d24f9af31c4e96e5158677408b513d3e33be7357.tar.gz
Refactoring needed in order to have test json output.
-rw-r--r--src/libtest/formatters.rs246
-rw-r--r--src/libtest/lib.rs294
2 files changed, 308 insertions, 232 deletions
diff --git a/src/libtest/formatters.rs b/src/libtest/formatters.rs
new file mode 100644
index 00000000000..4454e7ed115
--- /dev/null
+++ b/src/libtest/formatters.rs
@@ -0,0 +1,246 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::*;
+
+pub(crate) trait OutputFormatter {
+ fn write_run_start(&mut self, len: usize) -> io::Result<()>;
+ fn write_test_start(&mut self,
+ test: &TestDesc,
+ align: NamePadding,
+ max_name_len: usize) -> io::Result<()>;
+ fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()>;
+ fn write_result(&mut self, result: &TestResult) -> io::Result<()>;
+ fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool>;
+}
+
+pub(crate) struct HumanFormatter<T> {
+ out: OutputLocation<T>,
+ terse: bool,
+ use_color: bool,
+ test_count: usize,
+}
+
+impl<T: Write> HumanFormatter<T> {
+ pub fn new(out: OutputLocation<T>, use_color: bool, terse: bool) -> Self {
+ HumanFormatter {
+ out,
+ terse,
+ use_color,
+ test_count: 0,
+ }
+ }
+
+ #[cfg(test)]
+ pub fn output_location(&self) -> &OutputLocation<T> {
+ &self.out
+ }
+
+ pub fn write_ok(&mut self) -> io::Result<()> {
+ self.write_short_result("ok", ".", term::color::GREEN)
+ }
+
+ pub fn write_failed(&mut self) -> io::Result<()> {
+ self.write_short_result("FAILED", "F", term::color::RED)
+ }
+
+ pub fn write_ignored(&mut self) -> io::Result<()> {
+ self.write_short_result("ignored", "i", term::color::YELLOW)
+ }
+
+ pub fn write_allowed_fail(&mut self) -> io::Result<()> {
+ self.write_short_result("FAILED (allowed)", "a", term::color::YELLOW)
+ }
+
+ pub fn write_bench(&mut self) -> io::Result<()> {
+ self.write_pretty("bench", term::color::CYAN)
+ }
+
+ pub fn write_short_result(&mut self, verbose: &str, quiet: &str, color: term::color::Color)
+ -> io::Result<()> {
+ if self.terse {
+ self.write_pretty(quiet, color)?;
+ if self.test_count % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 {
+ // we insert a new line every 100 dots in order to flush the
+ // screen when dealing with line-buffered output (e.g. piping to
+ // `stamp` in the rust CI).
+ self.write_plain("\n")?;
+ }
+
+ self.test_count += 1;
+ Ok(())
+ } else {
+ self.write_pretty(verbose, color)?;
+ self.write_plain("\n")
+ }
+ }
+
+ pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
+ match self.out {
+ Pretty(ref mut term) => {
+ if self.use_color {
+ term.fg(color)?;
+ }
+ term.write_all(word.as_bytes())?;
+ if self.use_color {
+ term.reset()?;
+ }
+ term.flush()
+ }
+ Raw(ref mut stdout) => {
+ stdout.write_all(word.as_bytes())?;
+ stdout.flush()
+ }
+ }
+ }
+
+ pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> {
+ let s = s.as_ref();
+ self.out.write_all(s.as_bytes())?;
+ self.out.flush()
+ }
+
+ pub fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> {
+ self.write_plain("\nsuccesses:\n")?;
+ let mut successes = Vec::new();
+ let mut stdouts = String::new();
+ for &(ref f, ref stdout) in &state.not_failures {
+ successes.push(f.name.to_string());
+ if !stdout.is_empty() {
+ stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name));
+ let output = String::from_utf8_lossy(stdout);
+ stdouts.push_str(&output);
+ stdouts.push_str("\n");
+ }
+ }
+ if !stdouts.is_empty() {
+ self.write_plain("\n")?;
+ self.write_plain(&stdouts)?;
+ }
+
+ self.write_plain("\nsuccesses:\n")?;
+ successes.sort();
+ for name in &successes {
+ self.write_plain(&format!(" {}\n", name))?;
+ }
+ Ok(())
+ }
+
+ pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> {
+ self.write_plain("\nfailures:\n")?;
+ let mut failures = Vec::new();
+ let mut fail_out = String::new();
+ for &(ref f, ref stdout) in &state.failures {
+ failures.push(f.name.to_string());
+ if !stdout.is_empty() {
+ fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
+ let output = String::from_utf8_lossy(stdout);
+ fail_out.push_str(&output);
+ fail_out.push_str("\n");
+ }
+ }
+ if !fail_out.is_empty() {
+ self.write_plain("\n")?;
+ self.write_plain(&fail_out)?;
+ }
+
+ self.write_plain("\nfailures:\n")?;
+ failures.sort();
+ for name in &failures {
+ self.write_plain(&format!(" {}\n", name))?;
+ }
+ Ok(())
+ }
+}
+
+impl<T: Write> OutputFormatter for HumanFormatter<T> {
+ fn write_run_start(&mut self, len: usize) -> io::Result<()> {
+ let noun = if len != 1 {
+ "tests"
+ } else {
+ "test"
+ };
+ self.write_plain(&format!("\nrunning {} {}\n", len, noun))
+ }
+
+ fn write_test_start(&mut self,
+ test: &TestDesc,
+ align: NamePadding,
+ max_name_len: usize) -> io::Result<()> {
+ if self.terse && align != PadOnRight {
+ Ok(())
+ }
+ else {
+ let name = test.padded_name(max_name_len, align);
+ self.write_plain(&format!("test {} ... ", name))
+ }
+ }
+
+ fn write_result(&mut self, result: &TestResult) -> io::Result<()> {
+ match *result {
+ TrOk => self.write_ok(),
+ TrFailed | TrFailedMsg(_) => self.write_failed(),
+ TrIgnored => self.write_ignored(),
+ TrAllowedFail => self.write_allowed_fail(),
+ TrBench(ref bs) => {
+ self.write_bench()?;
+ self.write_plain(&format!(": {}\n", fmt_bench_samples(bs)))
+ }
+ }
+ }
+
+ fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
+ self.write_plain(&format!("test {} has been running for over {} seconds\n",
+ desc.name,
+ TEST_WARN_TIMEOUT_S))
+ }
+
+ fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
+ if state.options.display_output {
+ self.write_outputs(state)?;
+ }
+ let success = state.failed == 0;
+ if !success {
+ self.write_failures(state)?;
+ }
+
+ self.write_plain("\ntest result: ")?;
+
+ if success {
+ // There's no parallelism at this point so it's safe to use color
+ self.write_pretty("ok", term::color::GREEN)?;
+ } else {
+ self.write_pretty("FAILED", term::color::RED)?;
+ }
+
+ let s = if state.allowed_fail > 0 {
+ format!(
+ ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out\n\n",
+ state.passed,
+ state.failed + state.allowed_fail,
+ state.allowed_fail,
+ state.ignored,
+ state.measured,
+ state.filtered_out)
+ } else {
+ format!(
+ ". {} passed; {} failed; {} ignored; {} measured; {} filtered out\n\n",
+ state.passed,
+ state.failed,
+ state.ignored,
+ state.measured,
+ state.filtered_out)
+ };
+
+ self.write_plain(&s)?;
+
+ Ok(success)
+ }
+}
diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs
index f7880d3c4d8..2fb62c832f8 100644
--- a/src/libtest/lib.rs
+++ b/src/libtest/lib.rs
@@ -84,6 +84,9 @@ pub mod test {
}
pub mod stats;
+mod formatters;
+
+use formatters::*;
// The name of a test. By convention this follows the rules for rust
// paths; i.e. it should be a series of identifiers separated by double
@@ -359,7 +362,8 @@ fn optgroups() -> getopts::Options {
in parallel", "n_threads")
.optmulti("", "skip", "Skip tests whose names contain FILTER (this flag can \
be used multiple times)","FILTER")
- .optflag("q", "quiet", "Display one character per test instead of one line")
+ .optflag("q", "quiet", "Display one character per test instead of one line.\
+ Equivalent to --format=terse")
.optflag("", "exact", "Exactly match filters rather than by substring")
.optopt("", "color", "Configure coloring of output:
auto = colorize if stdout is a tty and tests are run on serially (default);
@@ -507,11 +511,24 @@ enum OutputLocation<T> {
Raw(T),
}
-struct ConsoleTestState<T> {
+impl<T: Write> Write for OutputLocation<T> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ match *self {
+ Pretty(ref mut term) => term.write(buf),
+ Raw(ref mut stdout) => stdout.write(buf)
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ match *self {
+ Pretty(ref mut term) => term.flush(),
+ Raw(ref mut stdout) => stdout.flush()
+ }
+ }
+}
+
+struct ConsoleTestState {
log_out: Option<File>,
- out: OutputLocation<T>,
- use_color: bool,
- quiet: bool,
total: usize,
passed: usize,
failed: usize,
@@ -526,22 +543,15 @@ struct ConsoleTestState<T> {
options: Options,
}
-impl<T: Write> ConsoleTestState<T> {
- pub fn new(opts: &TestOpts, _: Option<T>) -> io::Result<ConsoleTestState<io::Stdout>> {
+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,
};
- let out = match term::stdout() {
- None => Raw(io::stdout()),
- Some(t) => Pretty(t),
- };
Ok(ConsoleTestState {
- out,
log_out,
- use_color: use_color(opts),
- quiet: opts.quiet,
total: 0,
passed: 0,
failed: 0,
@@ -557,114 +567,6 @@ impl<T: Write> ConsoleTestState<T> {
})
}
- pub fn write_ok(&mut self) -> io::Result<()> {
- self.write_short_result("ok", ".", term::color::GREEN)
- }
-
- pub fn write_failed(&mut self) -> io::Result<()> {
- self.write_short_result("FAILED", "F", term::color::RED)
- }
-
- pub fn write_ignored(&mut self) -> io::Result<()> {
- self.write_short_result("ignored", "i", term::color::YELLOW)
- }
-
- pub fn write_allowed_fail(&mut self) -> io::Result<()> {
- self.write_short_result("FAILED (allowed)", "a", term::color::YELLOW)
- }
-
- pub fn write_bench(&mut self) -> io::Result<()> {
- self.write_pretty("bench", term::color::CYAN)
- }
-
- pub fn write_short_result(&mut self, verbose: &str, quiet: &str, color: term::color::Color)
- -> io::Result<()> {
- if self.quiet {
- self.write_pretty(quiet, color)?;
- if self.current_test_count() % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 {
- // we insert a new line every 100 dots in order to flush the
- // screen when dealing with line-buffered output (e.g. piping to
- // `stamp` in the rust CI).
- self.write_plain("\n")?;
- }
- Ok(())
- } else {
- self.write_pretty(verbose, color)?;
- self.write_plain("\n")
- }
- }
-
- pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
- match self.out {
- Pretty(ref mut term) => {
- if self.use_color {
- term.fg(color)?;
- }
- term.write_all(word.as_bytes())?;
- if self.use_color {
- term.reset()?;
- }
- term.flush()
- }
- Raw(ref mut stdout) => {
- stdout.write_all(word.as_bytes())?;
- stdout.flush()
- }
- }
- }
-
- pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> {
- let s = s.as_ref();
- match self.out {
- Pretty(ref mut term) => {
- term.write_all(s.as_bytes())?;
- term.flush()
- }
- Raw(ref mut stdout) => {
- stdout.write_all(s.as_bytes())?;
- stdout.flush()
- }
- }
- }
-
- pub fn write_run_start(&mut self, len: usize) -> io::Result<()> {
- self.total = len;
- let noun = if len != 1 {
- "tests"
- } else {
- "test"
- };
- self.write_plain(&format!("\nrunning {} {}\n", len, noun))
- }
-
- pub fn write_test_start(&mut self, test: &TestDesc, align: NamePadding) -> io::Result<()> {
- if self.quiet && align != PadOnRight {
- Ok(())
- } else {
- let name = test.padded_name(self.max_name_len, align);
- self.write_plain(&format!("test {} ... ", name))
- }
- }
-
- pub fn write_result(&mut self, result: &TestResult) -> io::Result<()> {
- match *result {
- TrOk => self.write_ok(),
- TrFailed | TrFailedMsg(_) => self.write_failed(),
- TrIgnored => self.write_ignored(),
- TrAllowedFail => self.write_allowed_fail(),
- TrBench(ref bs) => {
- self.write_bench()?;
- self.write_plain(&format!(": {}\n", fmt_bench_samples(bs)))
- }
- }
- }
-
- pub fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
- self.write_plain(&format!("test {} has been running for over {} seconds\n",
- desc.name,
- TEST_WARN_TIMEOUT_S))
- }
-
pub fn write_log<S: AsRef<str>>(&mut self, msg: S) -> io::Result<()> {
let msg = msg.as_ref();
match self.log_out {
@@ -687,101 +589,9 @@ impl<T: Write> ConsoleTestState<T> {
test.name))
}
- pub fn write_failures(&mut self) -> io::Result<()> {
- self.write_plain("\nfailures:\n")?;
- let mut failures = Vec::new();
- let mut fail_out = String::new();
- for &(ref f, ref stdout) in &self.failures {
- failures.push(f.name.to_string());
- if !stdout.is_empty() {
- fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
- let output = String::from_utf8_lossy(stdout);
- fail_out.push_str(&output);
- fail_out.push_str("\n");
- }
- }
- if !fail_out.is_empty() {
- self.write_plain("\n")?;
- self.write_plain(&fail_out)?;
- }
-
- self.write_plain("\nfailures:\n")?;
- failures.sort();
- for name in &failures {
- self.write_plain(&format!(" {}\n", name))?;
- }
- Ok(())
- }
-
- pub fn write_outputs(&mut self) -> io::Result<()> {
- self.write_plain("\nsuccesses:\n")?;
- let mut successes = Vec::new();
- let mut stdouts = String::new();
- for &(ref f, ref stdout) in &self.not_failures {
- successes.push(f.name.to_string());
- if !stdout.is_empty() {
- stdouts.push_str(&format!("---- {} stdout ----\n\t", f.name));
- let output = String::from_utf8_lossy(stdout);
- stdouts.push_str(&output);
- stdouts.push_str("\n");
- }
- }
- if !stdouts.is_empty() {
- self.write_plain("\n")?;
- self.write_plain(&stdouts)?;
- }
-
- self.write_plain("\nsuccesses:\n")?;
- successes.sort();
- for name in &successes {
- self.write_plain(&format!(" {}\n", name))?;
- }
- Ok(())
- }
-
fn current_test_count(&self) -> usize {
self.passed + self.failed + self.ignored + self.measured + self.allowed_fail
}
-
- pub fn write_run_finish(&mut self) -> io::Result<bool> {
- assert!(self.current_test_count() == self.total);
-
- if self.options.display_output {
- self.write_outputs()?;
- }
- let success = self.failed == 0;
- if !success {
- self.write_failures()?;
- }
-
- self.write_plain("\ntest result: ")?;
- if success {
- // There's no parallelism at this point so it's safe to use color
- self.write_pretty("ok", term::color::GREEN)?;
- } else {
- self.write_pretty("FAILED", term::color::RED)?;
- }
- let s = if self.allowed_fail > 0 {
- format!(
- ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out\n\n",
- self.passed,
- self.failed + self.allowed_fail,
- self.allowed_fail,
- self.ignored,
- self.measured,
- self.filtered_out)
- } else {
- format!(
- ". {} passed; {} failed; {} ignored; {} measured; {} filtered out\n\n",
- self.passed,
- self.failed,
- self.ignored,
- self.measured,
- self.filtered_out)
- };
- self.write_plain(&s)?;
- return Ok(success);
- }
}
// Format a number with thousands separators
@@ -827,7 +637,12 @@ pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
// List the tests to console, and optionally to logfile. Filters are honored.
pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<()> {
- let mut st = ConsoleTestState::new(opts, None::<io::Stdout>)?;
+ let output = match term::stdout() {
+ None => Raw(io::stdout()),
+ Some(t) => Pretty(t),
+ };
+ let mut out = HumanFormatter::new(output, use_color(opts), opts.quiet);
+ let mut st = ConsoleTestState::new(opts)?;
let mut ntest = 0;
let mut nbench = 0;
@@ -842,7 +657,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Res
StaticBenchFn(..) | DynBenchFn(..) => { nbench += 1; "benchmark" },
};
- st.write_plain(format!("{}: {}\n", name, fntype))?;
+ out.write_plain(format!("{}: {}\n", name, fntype))?;
st.write_log(format!("{} {}\n", fntype, name))?;
}
@@ -868,15 +683,21 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Res
// A simple console test runner
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
- fn callback<T: Write>(event: &TestEvent, st: &mut ConsoleTestState<T>) -> io::Result<()> {
+ fn callback(event: &TestEvent,
+ st: &mut ConsoleTestState,
+ out: &mut OutputFormatter) -> io::Result<()> {
+
match (*event).clone() {
- TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
+ TeFiltered(ref filtered_tests) => {
+ st.total = filtered_tests.len();
+ out.write_run_start(filtered_tests.len())
+ },
TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out),
- TeWait(ref test, padding) => st.write_test_start(test, padding),
- TeTimeout(ref test) => st.write_timeout(test),
+ TeWait(ref test, padding) => out.write_test_start(test, padding, st.max_name_len),
+ TeTimeout(ref test) => out.write_timeout(test),
TeResult(test, result, stdout) => {
st.write_log_result(&test, &result)?;
- st.write_result(&result)?;
+ out.write_result(&result)?;
match result {
TrOk => {
st.passed += 1;
@@ -908,7 +729,14 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
}
}
- let mut st = ConsoleTestState::new(opts, None::<io::Stdout>)?;
+ let output = match term::stdout() {
+ None => Raw(io::stdout()),
+ Some(t) => Pretty(t),
+ };
+
+ let mut out = HumanFormatter::new(output, use_color(opts), opts.quiet);
+
+ let mut st = ConsoleTestState::new(opts)?;
fn len_if_padded(t: &TestDescAndFn) -> usize {
match t.testfn.padding() {
PadNone => 0,
@@ -919,8 +747,11 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
let n = t.desc.name.as_slice();
st.max_name_len = n.len();
}
- run_tests(opts, tests, |x| callback(&x, &mut st))?;
- return st.write_run_finish();
+ run_tests(opts, tests, |x| callback(&x, &mut st, &mut out))?;
+
+ assert!(st.current_test_count() == st.total);
+
+ return out.write_run_finish(&st);
}
#[test]
@@ -939,11 +770,10 @@ fn should_sort_failures_before_printing_them() {
allow_fail: false,
};
- let mut st = ConsoleTestState {
+ let mut out = HumanFormatter::new(Raw(Vec::new()), false, false);
+
+ let st = ConsoleTestState {
log_out: None,
- out: Raw(Vec::new()),
- use_color: false,
- quiet: false,
total: 0,
passed: 0,
failed: 0,
@@ -958,10 +788,10 @@ fn should_sort_failures_before_printing_them() {
not_failures: Vec::new(),
};
- st.write_failures().unwrap();
- let s = match st.out {
- Raw(ref m) => String::from_utf8_lossy(&m[..]),
- Pretty(_) => unreachable!(),
+ out.write_failures(&st).unwrap();
+ let s = match out.output_location() {
+ &Raw(ref m) => String::from_utf8_lossy(&m[..]),
+ &Pretty(_) => unreachable!(),
};
let apos = s.find("a").unwrap();