about summary refs log tree commit diff
path: root/src
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 /src
parenta97cd17f5d71fb4ec362f4fbd79373a6e7ed7b82 (diff)
downloadrust-d24f9af31c4e96e5158677408b513d3e33be7357.tar.gz
rust-d24f9af31c4e96e5158677408b513d3e33be7357.zip
Refactoring needed in order to have test json output.
Diffstat (limited to 'src')
-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();