about summary refs log tree commit diff
diff options
context:
space:
mode:
authorIgor Aleksanov <popzxc@yandex.ru>2019-10-17 17:38:16 +0300
committerIgor Aleksanov <popzxc@yandex.ru>2019-10-17 17:47:03 +0300
commit12397e9dd5a97460d76c884d449ca1c2d26da8ed (patch)
treeb7d194c34c3ba67579303f34e5d5a1879f5755d5
parentc951882c7364c672fe127f61d25b458e810572ff (diff)
downloadrust-12397e9dd5a97460d76c884d449ca1c2d26da8ed.tar.gz
rust-12397e9dd5a97460d76c884d449ca1c2d26da8ed.zip
Make enum usage explicit and fix tests
-rw-r--r--src/libtest/bench.rs16
-rw-r--r--src/libtest/console.rs18
-rw-r--r--src/libtest/event.rs25
-rw-r--r--src/libtest/formatters/json.rs31
-rw-r--r--src/libtest/formatters/mod.rs13
-rw-r--r--src/libtest/formatters/pretty.rs27
-rw-r--r--src/libtest/formatters/terse.rs31
-rw-r--r--src/libtest/helpers/exit_code.rs20
-rw-r--r--src/libtest/helpers/mod.rs2
-rw-r--r--src/libtest/helpers/sink.rs24
-rw-r--r--src/libtest/lib.rs184
-rw-r--r--src/libtest/stats/tests.rs2
-rw-r--r--src/libtest/tests.rs55
-rw-r--r--src/libtest/time.rs2
14 files changed, 289 insertions, 161 deletions
diff --git a/src/libtest/bench.rs b/src/libtest/bench.rs
index 055a74f691c..bb5b0d1da53 100644
--- a/src/libtest/bench.rs
+++ b/src/libtest/bench.rs
@@ -1,6 +1,11 @@
 //! Benchmarking module.
 use super::{
-    BenchMode, MonitorMsg, Sender, Sink, TestDesc, TestResult
+    event::CompletedTest,
+    helpers::sink::Sink,
+    options::BenchMode,
+    types::TestDesc,
+    test_result::TestResult,
+    Sender,
 };
 
 use crate::stats;
@@ -182,7 +187,7 @@ where
     }
 }
 
-pub fn benchmark<F>(desc: TestDesc, monitor_ch: Sender<MonitorMsg>, nocapture: bool, f: F)
+pub fn benchmark<F>(desc: TestDesc, monitor_ch: Sender<CompletedTest>, nocapture: bool, f: F)
 where
     F: FnMut(&mut Bencher),
 {
@@ -195,8 +200,8 @@ where
     let data = Arc::new(Mutex::new(Vec::new()));
     let oldio = if !nocapture {
         Some((
-            io::set_print(Some(Box::new(Sink(data.clone())))),
-            io::set_panic(Some(Box::new(Sink(data.clone())))),
+            io::set_print(Some(Sink::new_boxed(&data))),
+            io::set_panic(Some(Sink::new_boxed(&data))),
         ))
     } else {
         None
@@ -235,7 +240,8 @@ where
     };
 
     let stdout = data.lock().unwrap().to_vec();
-    monitor_ch.send((desc, test_result, None, stdout)).unwrap();
+    let message = CompletedTest::new(desc, test_result, None, stdout);
+    monitor_ch.send(message).unwrap();
 }
 
 pub fn run_once<F>(f: F)
diff --git a/src/libtest/console.rs b/src/libtest/console.rs
index 851c0389ff3..2c14e9a1591 100644
--- a/src/libtest/console.rs
+++ b/src/libtest/console.rs
@@ -1,7 +1,7 @@
 //! Module providing interface for running tests in the console.
 
 use std::fs::File;
-use std::io::prelude::*;
+use std::io::prelude::Write;
 use std::io;
 
 use term;
@@ -192,7 +192,8 @@ 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(
+    // A callback handling events that occure during test execution.
+    fn on_test_event(
         event: &TestEvent,
         st: &mut ConsoleTestState,
         out: &mut dyn OutputFormatter,
@@ -205,9 +206,14 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
             TestEvent::TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out),
             TestEvent::TeWait(ref test) => out.write_test_start(test),
             TestEvent::TeTimeout(ref test) => out.write_timeout(test),
-            TestEvent::TeResult(test, result, exec_time, stdout) => {
-                st.write_log_result(&test, &result, exec_time.as_ref())?;
-                out.write_result(&test, &result, exec_time.as_ref(), &*stdout, &st)?;
+            TestEvent::TeResult(completed_test) => {
+                let test = completed_test.desc;
+                let result = &completed_test.result;
+                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)?;
                 match result {
                     TestResult::TrOk => {
                         st.passed += 1;
@@ -280,7 +286,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
         }
     }
 
-    run_tests(opts, tests, |x| callback(&x, &mut st, &mut *out))?;
+    run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?;
 
     assert!(st.current_test_count() == st.total);
 
diff --git a/src/libtest/event.rs b/src/libtest/event.rs
index b84551826c6..e1b606149c5 100644
--- a/src/libtest/event.rs
+++ b/src/libtest/event.rs
@@ -5,11 +5,32 @@ use super::types::TestDesc;
 use super::test_result::TestResult;
 use super::time::TestExecTime;
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
+pub struct CompletedTest {
+    pub desc: TestDesc,
+    pub result: TestResult,
+    pub exec_time: Option<TestExecTime>,
+    pub stdout: Vec<u8>,
+}
+
+impl CompletedTest {
+    pub fn new(desc: TestDesc, result: TestResult, exec_time: Option<TestExecTime>, stdout: Vec<u8>) -> Self {
+        Self {
+            desc,
+            result,
+            exec_time,
+            stdout,
+        }
+    }
+}
+
+unsafe impl Send for CompletedTest {}
+
+#[derive(Debug, Clone)]
 pub enum TestEvent {
     TeFiltered(Vec<TestDesc>),
     TeWait(TestDesc),
-    TeResult(TestDesc, TestResult, Option<TestExecTime>, Vec<u8>),
+    TeResult(CompletedTest),
     TeTimeout(TestDesc),
     TeFilteredOut(usize),
 }
diff --git a/src/libtest/formatters/json.rs b/src/libtest/formatters/json.rs
index 41a293195cc..fc677036dab 100644
--- a/src/libtest/formatters/json.rs
+++ b/src/libtest/formatters/json.rs
@@ -1,5 +1,16 @@
-use super::*;
-use super::console::{ConsoleTestState, OutputLocation};
+use std::{
+    io,
+    io::prelude::Write,
+    borrow::Cow,
+};
+
+use crate::{
+    types::TestDesc,
+    time,
+    test_result::TestResult,
+    console::{ConsoleTestState, OutputLocation},
+};
+use super::OutputFormatter;
 
 pub(crate) struct JsonFormatter<T> {
     out: OutputLocation<T>,
@@ -81,21 +92,21 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
         stdout: &[u8],
         state: &ConsoleTestState,
     ) -> io::Result<()> {
-        let stdout = if (state.options.display_output || *result != TrOk) && stdout.len() > 0 {
+        let stdout = if (state.options.display_output || *result != TestResult::TrOk) && stdout.len() > 0 {
             Some(String::from_utf8_lossy(stdout))
         } else {
             None
         };
         match *result {
-            TrOk => {
+            TestResult::TrOk => {
                 self.write_event("test", desc.name.as_slice(), "ok", exec_time, stdout, None)
             }
 
-            TrFailed => {
+            TestResult::TrFailed => {
                 self.write_event("test", desc.name.as_slice(), "failed", exec_time, stdout, None)
             }
 
-            TrTimedFail => self.write_event(
+            TestResult::TrTimedFail => self.write_event(
                 "test",
                 desc.name.as_slice(),
                 "failed",
@@ -104,7 +115,7 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
                 Some(r#""reason": "time limit exceeded""#),
             ),
 
-            TrFailedMsg(ref m) => self.write_event(
+            TestResult::TrFailedMsg(ref m) => self.write_event(
                 "test",
                 desc.name.as_slice(),
                 "failed",
@@ -113,11 +124,11 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
                 Some(&*format!(r#""message": "{}""#, EscapedString(m))),
             ),
 
-            TrIgnored => {
+            TestResult::TrIgnored => {
                 self.write_event("test", desc.name.as_slice(), "ignored", exec_time, stdout, None)
             }
 
-            TrAllowedFail => self.write_event(
+            TestResult::TrAllowedFail => self.write_event(
                 "test",
                 desc.name.as_slice(),
                 "allowed_failure",
@@ -126,7 +137,7 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
                 None,
             ),
 
-            TrBench(ref bs) => {
+            TestResult::TrBench(ref bs) => {
                 let median = bs.ns_iter_summ.median as usize;
                 let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
 
diff --git a/src/libtest/formatters/mod.rs b/src/libtest/formatters/mod.rs
index 4f3ffdafe3f..b6649a3effc 100644
--- a/src/libtest/formatters/mod.rs
+++ b/src/libtest/formatters/mod.rs
@@ -1,5 +1,14 @@
-use super::*;
-use super::console::ConsoleTestState;
+use std::{
+    io,
+    io::prelude::Write,
+};
+
+use crate::{
+    types::{TestDesc, TestName},
+    time,
+    test_result::TestResult,
+    console::{ConsoleTestState},
+};
 
 mod pretty;
 mod json;
diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs
index 6f2c56bdf45..2fdbc63d513 100644
--- a/src/libtest/formatters/pretty.rs
+++ b/src/libtest/formatters/pretty.rs
@@ -1,5 +1,16 @@
-use super::*;
-use super::console::{ConsoleTestState, OutputLocation};
+use std::{
+    io,
+    io::prelude::Write,
+};
+
+use crate::{
+    types::TestDesc,
+    time,
+    test_result::TestResult,
+    console::{ConsoleTestState, OutputLocation},
+    bench::fmt_bench_samples,
+};
+use super::OutputFormatter;
 
 pub(crate) struct PrettyFormatter<T> {
     out: OutputLocation<T>,
@@ -204,15 +215,15 @@ impl<T: Write> OutputFormatter for PrettyFormatter<T> {
         }
 
         match *result {
-            TrOk => self.write_ok()?,
-            TrFailed | TrFailedMsg(_) => self.write_failed()?,
-            TrIgnored => self.write_ignored()?,
-            TrAllowedFail => self.write_allowed_fail()?,
-            TrBench(ref bs) => {
+            TestResult::TrOk => self.write_ok()?,
+            TestResult::TrFailed | TestResult::TrFailedMsg(_) => self.write_failed()?,
+            TestResult::TrIgnored => self.write_ignored()?,
+            TestResult::TrAllowedFail => self.write_allowed_fail()?,
+            TestResult::TrBench(ref bs) => {
                 self.write_bench()?;
                 self.write_plain(&format!(": {}", fmt_bench_samples(bs)))?;
             }
-            TrTimedFail => self.write_time_failed()?,
+            TestResult::TrTimedFail => self.write_time_failed()?,
         }
 
         self.write_time(desc, exec_time)?;
diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs
index 96203d5ea42..90eb62251fb 100644
--- a/src/libtest/formatters/terse.rs
+++ b/src/libtest/formatters/terse.rs
@@ -1,5 +1,20 @@
-use super::*;
-use super::console::{ConsoleTestState, OutputLocation};
+use std::{
+    io,
+    io::prelude::Write,
+};
+
+use crate::{
+    types::TestDesc,
+    time,
+    test_result::TestResult,
+    types::NamePadding,
+    console::{ConsoleTestState, OutputLocation},
+    bench::fmt_bench_samples,
+};
+use super::OutputFormatter;
+
+// insert a '\n' after 100 tests in quiet mode
+const QUIET_MODE_MAX_COLUMN: usize = 100;
 
 pub(crate) struct TerseFormatter<T> {
     out: OutputLocation<T>,
@@ -164,7 +179,7 @@ impl<T: Write> OutputFormatter for TerseFormatter<T> {
         // in order to indicate benchmarks.
         // When running benchmarks, terse-mode should still print their name as if
         // it is the Pretty formatter.
-        if !self.is_multithreaded && desc.name.padding() == PadOnRight {
+        if !self.is_multithreaded && desc.name.padding() == NamePadding::PadOnRight {
             self.write_test_name(desc)?;
         }
 
@@ -180,11 +195,11 @@ impl<T: Write> OutputFormatter for TerseFormatter<T> {
         _: &ConsoleTestState,
     ) -> io::Result<()> {
         match *result {
-            TrOk => self.write_ok(),
-            TrFailed | TrFailedMsg(_) | TrTimedFail => self.write_failed(),
-            TrIgnored => self.write_ignored(),
-            TrAllowedFail => self.write_allowed_fail(),
-            TrBench(ref bs) => {
+            TestResult::TrOk => self.write_ok(),
+            TestResult::TrFailed | TestResult::TrFailedMsg(_) | TestResult::TrTimedFail => self.write_failed(),
+            TestResult::TrIgnored => self.write_ignored(),
+            TestResult::TrAllowedFail => self.write_allowed_fail(),
+            TestResult::TrBench(ref bs) => {
                 if self.is_multithreaded {
                     self.write_test_name(desc)?;
                 }
diff --git a/src/libtest/helpers/exit_code.rs b/src/libtest/helpers/exit_code.rs
new file mode 100644
index 00000000000..831bef3b118
--- /dev/null
+++ b/src/libtest/helpers/exit_code.rs
@@ -0,0 +1,20 @@
+//! Helper module to detect subprocess exit code.
+
+use std::process::ExitStatus;
+
+#[cfg(not(unix))]
+pub fn get_exit_code(status: ExitStatus) -> Result<i32, String> {
+    status.code().ok_or("received no exit code from child process".into())
+}
+
+#[cfg(unix)]
+pub fn get_exit_code(status: ExitStatus) -> Result<i32, String> {
+    use std::os::unix::process::ExitStatusExt;
+    match status.code() {
+        Some(code) => Ok(code),
+        None => match status.signal() {
+            Some(signal) => Err(format!("child process exited with signal {}", signal)),
+            None => Err("child process exited with unknown signal".into()),
+        }
+    }
+}
diff --git a/src/libtest/helpers/mod.rs b/src/libtest/helpers/mod.rs
index 0bbe77b1c50..6a2ef6086cb 100644
--- a/src/libtest/helpers/mod.rs
+++ b/src/libtest/helpers/mod.rs
@@ -4,3 +4,5 @@
 pub mod concurrency;
 pub mod isatty;
 pub mod metrics;
+pub mod sink;
+pub mod exit_code;
diff --git a/src/libtest/helpers/sink.rs b/src/libtest/helpers/sink.rs
new file mode 100644
index 00000000000..aa7fe248773
--- /dev/null
+++ b/src/libtest/helpers/sink.rs
@@ -0,0 +1,24 @@
+//! Module providing a helper structure to capture output in subprocesses.
+
+use std::{
+    io,
+    io::prelude::Write,
+    sync::{Arc, Mutex},
+};
+
+pub struct Sink(Arc<Mutex<Vec<u8>>>);
+
+impl Sink {
+    pub fn new_boxed(data: &Arc<Mutex<Vec<u8>>>) -> Box<Self> {
+        Box::new(Self(data.clone()))
+    }
+}
+
+impl Write for Sink {
+    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+        Write::write(&mut *self.0.lock().unwrap(), data)
+    }
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs
index 6221140f606..31da97b736a 100644
--- a/src/libtest/lib.rs
+++ b/src/libtest/lib.rs
@@ -30,30 +30,13 @@
 #![feature(termination_trait_lib)]
 #![feature(test)]
 
+// Public reexports
 pub use self::ColorConfig::*;
-use self::event::TestEvent::*;
+pub use self::types::*;
 pub use self::types::TestName::*;
+pub use self::options::{Options, ShouldPanic};
 
-use std::borrow::Cow;
-use std::env;
-use std::io;
-use std::io::prelude::*;
-use std::panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo};
-use std::process;
-use std::process::{ExitStatus, Command, Termination};
-use std::sync::mpsc::{channel, Sender};
-use std::sync::{Arc, Mutex};
-use std::thread;
-use std::time::{Duration, Instant};
-
-#[cfg(test)]
-mod tests;
-
-const QUIET_MODE_MAX_COLUMN: usize = 100; // insert a '\n' after 100 tests in quiet mode
-
-const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE";
-
-// to be used by rustc to compile tests in libtest
+// Module to be used by rustc to compile tests in libtest
 pub mod test {
     pub use crate::{
         bench::Bencher,
@@ -61,7 +44,7 @@ pub mod test {
         helpers::metrics::{Metric, MetricMap},
         options::{ShouldPanic, Options, RunIgnored, RunStrategy},
         test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk},
-        time::TestTimeOptions,
+        time::{TestTimeOptions, TestExecTime},
         types::{
             DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn,
             TestName, TestType,
@@ -70,18 +53,21 @@ pub mod test {
     };
 }
 
-use bench::*;
-use test_result::*;
-use types::*;
-use options::*;
-use cli::*;
-use event::*;
-
-use helpers::concurrency::get_concurrency;
+use std::{
+    env,
+    io,
+    io::prelude::Write,
+    panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
+    process,
+    process::{Command, Termination},
+    sync::mpsc::{channel, Sender},
+    sync::{Arc, Mutex},
+    thread,
+    time::{Duration, Instant},
+};
 
-mod formatters;
 pub mod stats;
-
+mod formatters;
 mod cli;
 mod console;
 mod event;
@@ -92,14 +78,31 @@ mod options;
 mod bench;
 mod test_result;
 
+#[cfg(test)]
+mod tests;
+
+use test_result::*;
+use time::TestExecTime;
+use options::{RunStrategy, Concurrent, RunIgnored, ColorConfig};
+use event::{CompletedTest, TestEvent};
+use cli::TestOpts;
+use helpers::sink::Sink;
+use helpers::concurrency::get_concurrency;
+use helpers::exit_code::get_exit_code;
+
+// Process exit code to be used to indicate test failures.
+const ERROR_EXIT_CODE: i32 = 101;
+
+const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE";
+
 // The default console test runner. It accepts the command line
 // arguments and a vector of test_descs.
 pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Options>) {
-    let mut opts = match parse_opts(args) {
+    let mut opts = match cli::parse_opts(args) {
         Some(Ok(o)) => o,
         Some(Err(msg)) => {
             eprintln!("error: {}", msg);
-            process::exit(101);
+            process::exit(ERROR_EXIT_CODE);
         }
         None => return,
     };
@@ -109,15 +112,15 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Opt
     if opts.list {
         if let Err(e) = console::list_tests_console(&opts, tests) {
             eprintln!("error: io error when listing tests: {:?}", e);
-            process::exit(101);
+            process::exit(ERROR_EXIT_CODE);
         }
     } else {
         match console::run_tests_console(&opts, tests) {
             Ok(true) => {}
-            Ok(false) => process::exit(101),
+            Ok(false) => process::exit(ERROR_EXIT_CODE),
             Err(e) => {
                 eprintln!("error: io error when listing tests: {:?}", e);
-                process::exit(101);
+                process::exit(ERROR_EXIT_CODE);
             }
         }
     }
@@ -196,19 +199,7 @@ pub fn assert_test_result<T: Termination>(result: T) {
     );
 }
 
-pub type MonitorMsg = (TestDesc, TestResult, Option<time::TestExecTime>, Vec<u8>);
-
-struct Sink(Arc<Mutex<Vec<u8>>>);
-impl Write for Sink {
-    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
-        Write::write(&mut *self.0.lock().unwrap(), data)
-    }
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-}
-
-pub fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
+pub fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut notify_about_test_event: F) -> io::Result<()>
 where
     F: FnMut(TestEvent) -> io::Result<()>,
 {
@@ -236,11 +227,13 @@ where
     };
 
     let filtered_out = tests_len - filtered_tests.len();
-    callback(TeFilteredOut(filtered_out))?;
+    let event = TestEvent::TeFilteredOut(filtered_out);
+    notify_about_test_event(event)?;
 
     let filtered_descs = filtered_tests.iter().map(|t| t.desc.clone()).collect();
 
-    callback(TeFiltered(filtered_descs))?;
+    let event = TestEvent::TeFiltered(filtered_descs);
+    notify_about_test_event(event)?;
 
     let (filtered_tests, filtered_benchs): (Vec<_>, _) =
         filtered_tests.into_iter().partition(|e| match e.testfn {
@@ -254,7 +247,7 @@ where
     remaining.reverse();
     let mut pending = 0;
 
-    let (tx, rx) = channel::<MonitorMsg>();
+    let (tx, rx) = channel::<CompletedTest>();
     let run_strategy = if opts.options.panic_abort {
         RunStrategy::SpawnPrimary
     } else {
@@ -295,10 +288,13 @@ where
     if concurrency == 1 {
         while !remaining.is_empty() {
             let test = remaining.pop().unwrap();
-            callback(TeWait(test.desc.clone()))?;
+            let event = TestEvent::TeWait(test.desc.clone());
+            notify_about_test_event(event)?;
             run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No);
-            let (test, result, exec_time, stdout) = rx.recv().unwrap();
-            callback(TeResult(test, result, exec_time, stdout))?;
+            let completed_test = rx.recv().unwrap();
+
+            let event = TestEvent::TeResult(completed_test);
+            notify_about_test_event(event)?;
         }
     } else {
         while pending > 0 || !remaining.is_empty() {
@@ -306,7 +302,9 @@ where
                 let test = remaining.pop().unwrap();
                 let timeout = time::get_default_test_timeout();
                 running_tests.insert(test.desc.clone(), timeout);
-                callback(TeWait(test.desc.clone()))?; //here no pad
+
+                let event = TestEvent::TeWait(test.desc.clone());
+                notify_about_test_event(event)?; //here no pad
                 run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes);
                 pending += 1;
             }
@@ -316,10 +314,18 @@ where
                 if let Some(timeout) = calc_timeout(&running_tests) {
                     res = rx.recv_timeout(timeout);
                     for test in get_timed_out_tests(&mut running_tests) {
-                        callback(TeTimeout(test))?;
+                        let event = TestEvent::TeTimeout(test);
+                        notify_about_test_event(event)?;
                     }
-                    if res != Err(RecvTimeoutError::Timeout) {
-                        break;
+
+                    match res {
+                        Err(RecvTimeoutError::Timeout) => {
+                            // Result is not yet ready, continue waiting.
+                        }
+                        _ => {
+                            // We've got a result, stop the loop.
+                            break;
+                        }            
                     }
                 } else {
                     res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected);
@@ -327,10 +333,11 @@ where
                 }
             }
 
-            let (desc, result, exec_time, stdout) = res.unwrap();
-            running_tests.remove(&desc);
+            let completed_test = res.unwrap();
+            running_tests.remove(&completed_test.desc);
 
-            callback(TeResult(desc, result, exec_time, stdout))?;
+            let event = TestEvent::TeResult(completed_test);
+            notify_about_test_event(event)?;
             pending -= 1;
         }
     }
@@ -338,10 +345,13 @@ where
     if opts.bench_benchmarks {
         // All benchmarks run at the end, in serial.
         for b in filtered_benchs {
-            callback(TeWait(b.desc.clone()))?;
+            let event = TestEvent::TeWait(b.desc.clone());
+            notify_about_test_event(event)?;
             run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No);
-            let (test, result, exec_time, stdout) = rx.recv().unwrap();
-            callback(TeResult(test, result, exec_time, stdout))?;
+            let completed_test = rx.recv().unwrap();
+
+            let event = TestEvent::TeResult(completed_test);
+            notify_about_test_event(event)?;
         }
     }
     Ok(())
@@ -420,7 +430,7 @@ pub fn run_test(
     force_ignore: bool,
     test: TestDescAndFn,
     strategy: RunStrategy,
-    monitor_ch: Sender<MonitorMsg>,
+    monitor_ch: Sender<CompletedTest>,
     concurrency: Concurrent,
 ) {
     let TestDescAndFn { desc, testfn } = test;
@@ -430,7 +440,8 @@ pub fn run_test(
         && (cfg!(target_arch = "wasm32") || cfg!(target_os = "emscripten"));
 
     if force_ignore || desc.ignore || ignore_because_no_process_support {
-        monitor_ch.send((desc, TrIgnored, None, Vec::new())).unwrap();
+        let message = CompletedTest::new(desc, TrIgnored, None, Vec::new());
+        monitor_ch.send(message).unwrap();
         return;
     }
 
@@ -443,7 +454,7 @@ pub fn run_test(
 
     fn run_test_inner(
         desc: TestDesc,
-        monitor_ch: Sender<MonitorMsg>,
+        monitor_ch: Sender<CompletedTest>,
         testfn: Box<dyn FnOnce() + Send>,
         opts: TestRunOpts,
     ) {
@@ -530,7 +541,7 @@ fn run_test_in_process(
     nocapture: bool,
     report_time: bool,
     testfn: Box<dyn FnOnce() + Send>,
-    monitor_ch: Sender<MonitorMsg>,
+    monitor_ch: Sender<CompletedTest>,
     time_opts: Option<time::TestTimeOptions>,
 ) {
     // Buffer for capturing standard I/O
@@ -538,8 +549,8 @@ fn run_test_in_process(
 
     let oldio = if !nocapture {
         Some((
-            io::set_print(Some(Box::new(Sink(data.clone())))),
-            io::set_panic(Some(Box::new(Sink(data.clone())))),
+            io::set_print(Some(Sink::new_boxed(&data))),
+            io::set_panic(Some(Sink::new_boxed(&data))),
         ))
     } else {
         None
@@ -553,7 +564,7 @@ fn run_test_in_process(
     let result = catch_unwind(AssertUnwindSafe(testfn));
     let exec_time = start.map(|start| {
         let duration = start.elapsed();
-        time::TestExecTime(duration)
+        TestExecTime(duration)
     });
 
     if let Some((printio, panicio)) = oldio {
@@ -566,13 +577,14 @@ fn run_test_in_process(
         Err(e) => calc_result(&desc, Err(e.as_ref()), &time_opts, &exec_time),
     };
     let stdout = data.lock().unwrap().to_vec();
-    monitor_ch.send((desc.clone(), test_result, exec_time, stdout)).unwrap();
+    let message = CompletedTest::new(desc.clone(), test_result, exec_time, stdout);
+    monitor_ch.send(message).unwrap();
 }
 
 fn spawn_test_subprocess(
     desc: TestDesc,
     report_time: bool,
-    monitor_ch: Sender<MonitorMsg>,
+    monitor_ch: Sender<CompletedTest>,
     time_opts: Option<time::TestTimeOptions>,
 ) {
     let (result, test_output, exec_time) = (|| {
@@ -595,7 +607,7 @@ fn spawn_test_subprocess(
             };
         let exec_time = start.map(|start| {
             let duration = start.elapsed();
-            time::TestExecTime(duration)
+            TestExecTime(duration)
         });
 
         let std::process::Output { stdout, stderr, status } = output;
@@ -617,7 +629,8 @@ fn spawn_test_subprocess(
         (result, test_output, exec_time)
     })();
 
-    monitor_ch.send((desc.clone(), result, exec_time, test_output)).unwrap();
+    let message = CompletedTest::new(desc.clone(), result, exec_time, test_output);
+    monitor_ch.send(message).unwrap();
 }
 
 fn run_test_in_spawned_subprocess(
@@ -653,20 +666,3 @@ fn run_test_in_spawned_subprocess(
     record_result(None);
     unreachable!("panic=abort callback should have exited the process")
 }
-
-#[cfg(not(unix))]
-fn get_exit_code(status: ExitStatus) -> Result<i32, String> {
-    status.code().ok_or("received no exit code from child process".into())
-}
-
-#[cfg(unix)]
-fn get_exit_code(status: ExitStatus) -> Result<i32, String> {
-    use std::os::unix::process::ExitStatusExt;
-    match status.code() {
-        Some(code) => Ok(code),
-        None => match status.signal() {
-            Some(signal) => Err(format!("child process exited with signal {}", signal)),
-            None => Err("child process exited with unknown signal".into()),
-        }
-    }
-}
diff --git a/src/libtest/stats/tests.rs b/src/libtest/stats/tests.rs
index 7d1d635186f..eaf41bc9e22 100644
--- a/src/libtest/stats/tests.rs
+++ b/src/libtest/stats/tests.rs
@@ -4,7 +4,7 @@ extern crate test;
 use std::f64;
 use std::io::prelude::*;
 use std::io;
-use self::test::Bencher;
+use self::test::test::Bencher;
 
 // Test vectors generated from R, using the script src/etc/stat-test-vectors.r.
 
diff --git a/src/libtest/tests.rs b/src/libtest/tests.rs
index 5f7150a8eeb..f6470b40a39 100644
--- a/src/libtest/tests.rs
+++ b/src/libtest/tests.rs
@@ -1,11 +1,18 @@
 use super::*;
 
-use crate::test::{
-    filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored, RunStrategy,
-    // ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TestTimeOptions,
-    // TestType, TrFailedMsg, TrIgnored, TrOk,
-    ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts,
-    TrIgnored, TrOk,
+use crate::{
+    bench::Bencher,
+    console::OutputLocation,
+    options::OutputFormat,
+    time::{TimeThreshold, TestTimeOptions},
+    formatters::PrettyFormatter,
+    test::{
+        filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored, RunStrategy,
+        // ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TestTimeOptions,
+        // TestType, TrFailedMsg, TrIgnored, TrOk,
+        ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts,
+        TrIgnored, TrOk,
+    },
 };
 use std::sync::mpsc::channel;
 use std::time::Duration;
@@ -74,8 +81,8 @@ pub fn do_not_run_ignored_tests() {
     };
     let (tx, rx) = channel();
     run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, res, _, _) = rx.recv().unwrap();
-    assert!(res != TrOk);
+    let result = rx.recv().unwrap().result;
+    assert!(result != TrOk);
 }
 
 #[test]
@@ -93,8 +100,8 @@ pub fn ignored_tests_result_in_ignored() {
     };
     let (tx, rx) = channel();
     run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, res, _, _) = rx.recv().unwrap();
-    assert!(res == TrIgnored);
+    let result = rx.recv().unwrap().result;
+    assert!(result == TrIgnored);
 }
 
 // FIXME: Re-enable emscripten once it can catch panics again
@@ -116,8 +123,8 @@ fn test_should_panic() {
     };
     let (tx, rx) = channel();
     run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, res, _, _) = rx.recv().unwrap();
-    assert!(res == TrOk);
+    let result = rx.recv().unwrap().result;
+    assert!(result == TrOk);
 }
 
 // FIXME: Re-enable emscripten once it can catch panics again
@@ -139,8 +146,8 @@ fn test_should_panic_good_message() {
     };
     let (tx, rx) = channel();
     run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, res, _, _) = rx.recv().unwrap();
-    assert!(res == TrOk);
+    let result = rx.recv().unwrap().result;
+    assert!(result == TrOk);
 }
 
 // FIXME: Re-enable emscripten once it can catch panics again
@@ -165,8 +172,8 @@ fn test_should_panic_bad_message() {
     };
     let (tx, rx) = channel();
     run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, res, _, _) = rx.recv().unwrap();
-    assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected)));
+    let result = rx.recv().unwrap().result;
+    assert!(result == TrFailedMsg(format!("{} '{}'", failed_msg, expected)));
 }
 
 // FIXME: Re-enable emscripten once it can catch panics again
@@ -186,8 +193,8 @@ fn test_should_panic_but_succeeds() {
     };
     let (tx, rx) = channel();
     run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, res, _, _) = rx.recv().unwrap();
-    assert!(res == TrFailedMsg("test did not panic as expected".to_string()));
+    let result = rx.recv().unwrap().result;
+    assert!(result == TrFailedMsg("test did not panic as expected".to_string()));
 }
 
 fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
@@ -214,7 +221,7 @@ fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
     };
     let (tx, rx) = channel();
     run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, _, exec_time, _) = rx.recv().unwrap();
+    let exec_time = rx.recv().unwrap().exec_time;
     exec_time
 }
 
@@ -252,7 +259,7 @@ fn time_test_failure_template(test_type: TestType) -> TestResult {
     };
     let (tx, rx) = channel();
     run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let (_, result, _, _) = rx.recv().unwrap();
+    let result = rx.recv().unwrap().result;
 
     result
 }
@@ -658,9 +665,9 @@ fn should_sort_failures_before_printing_them() {
         test_type: TestType::Unknown,
     };
 
-    let mut out = PrettyFormatter::new(Raw(Vec::new()), false, 10, false, None);
+    let mut out = PrettyFormatter::new(OutputLocation::Raw(Vec::new()), false, 10, false, None);
 
-    let st = ConsoleTestState {
+    let st = console::ConsoleTestState {
         log_out: None,
         total: 0,
         passed: 0,
@@ -678,8 +685,8 @@ fn should_sort_failures_before_printing_them() {
 
     out.write_failures(&st).unwrap();
     let s = match out.output_location() {
-        &Raw(ref m) => String::from_utf8_lossy(&m[..]),
-        &Pretty(_) => unreachable!(),
+        &OutputLocation::Raw(ref m) => String::from_utf8_lossy(&m[..]),
+        &OutputLocation::Pretty(_) => unreachable!(),
     };
 
     let apos = s.find("a").unwrap();
diff --git a/src/libtest/time.rs b/src/libtest/time.rs
index b7ce764505b..83a545470ef 100644
--- a/src/libtest/time.rs
+++ b/src/libtest/time.rs
@@ -61,7 +61,7 @@ pub fn get_default_test_timeout() -> Instant {
 }
 
 /// The meassured execution time of a unit test.
-#[derive(Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct TestExecTime(pub Duration);
 
 impl fmt::Display for TestExecTime {