diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 7c26d042a7ccf..49cb3e55b098b 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -99,6 +99,10 @@ mod formatters; use formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter}; +/// Whether to execute tests concurrently or not +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Concurrent { Yes, No } + // 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 // colons. This way if some test runner wants to arrange the tests @@ -1071,8 +1075,12 @@ pub fn run_tests(opts: &TestOpts, tests: Vec, mut callback: F) where F: FnMut(TestEvent) -> io::Result<()>, { - use std::collections::HashMap; + use std::collections::{self, HashMap}; + use std::hash::BuildHasherDefault; use std::sync::mpsc::RecvTimeoutError; + // Use a deterministic hasher + type TestMap = + HashMap>; let tests_len = tests.len(); @@ -1111,9 +1119,9 @@ where let (tx, rx) = channel::(); - let mut running_tests: HashMap = HashMap::new(); + let mut running_tests: TestMap = HashMap::default(); - fn get_timed_out_tests(running_tests: &mut HashMap) -> Vec { + fn get_timed_out_tests(running_tests: &mut TestMap) -> Vec { let now = Instant::now(); let timed_out = running_tests .iter() @@ -1131,7 +1139,7 @@ where timed_out }; - fn calc_timeout(running_tests: &HashMap) -> Option { + fn calc_timeout(running_tests: &TestMap) -> Option { running_tests.values().min().map(|next_timeout| { let now = Instant::now(); if *next_timeout >= now { @@ -1146,7 +1154,7 @@ where while !remaining.is_empty() { let test = remaining.pop().unwrap(); callback(TeWait(test.desc.clone()))?; - run_test(opts, !opts.run_tests, test, tx.clone()); + run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::No); let (test, result, stdout) = rx.recv().unwrap(); callback(TeResult(test, result, stdout))?; } @@ -1157,7 +1165,7 @@ where let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S); running_tests.insert(test.desc.clone(), timeout); callback(TeWait(test.desc.clone()))?; //here no pad - run_test(opts, !opts.run_tests, test, tx.clone()); + run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::Yes); pending += 1; } @@ -1189,7 +1197,7 @@ where // All benchmarks run at the end, in serial. for b in filtered_benchs { callback(TeWait(b.desc.clone()))?; - run_test(opts, false, b, tx.clone()); + run_test(opts, false, b, tx.clone(), Concurrent::No); let (test, result, stdout) = rx.recv().unwrap(); callback(TeResult(test, result, stdout))?; } @@ -1391,6 +1399,7 @@ pub fn run_test( force_ignore: bool, test: TestDescAndFn, monitor_ch: Sender, + concurrency: Concurrent, ) { let TestDescAndFn { desc, testfn } = test; @@ -1407,6 +1416,7 @@ pub fn run_test( monitor_ch: Sender, nocapture: bool, testfn: Box, + concurrency: Concurrent, ) { // Buffer for capturing standard I/O let data = Arc::new(Mutex::new(Vec::new())); @@ -1441,7 +1451,7 @@ pub fn run_test( // the test synchronously, regardless of the concurrency // level. let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_arch = "wasm32"); - if supports_threads { + if concurrency == Concurrent::Yes && supports_threads { let cfg = thread::Builder::new().name(name.as_slice().to_owned()); cfg.spawn(runtest).unwrap(); } else { @@ -1462,13 +1472,14 @@ pub fn run_test( } DynTestFn(f) => { let cb = move || __rust_begin_short_backtrace(f); - run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(cb)) + run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(cb), concurrency) } StaticTestFn(f) => run_test_inner( desc, monitor_ch, opts.nocapture, Box::new(move || __rust_begin_short_backtrace(f)), + concurrency, ), } } @@ -1751,6 +1762,7 @@ mod tests { use std::sync::mpsc::channel; use bench; use Bencher; + use Concurrent; fn one_ignored_one_unignored_test() -> Vec { @@ -1791,7 +1803,7 @@ mod tests { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, tx); + run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); let (_, res, _) = rx.recv().unwrap(); assert!(res != TrOk); } @@ -1809,7 +1821,7 @@ mod tests { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, tx); + run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); let (_, res, _) = rx.recv().unwrap(); assert!(res == TrIgnored); } @@ -1829,7 +1841,7 @@ mod tests { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, tx); + run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); let (_, res, _) = rx.recv().unwrap(); assert!(res == TrOk); } @@ -1849,7 +1861,7 @@ mod tests { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, tx); + run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); let (_, res, _) = rx.recv().unwrap(); assert!(res == TrOk); } @@ -1871,7 +1883,7 @@ mod tests { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, tx); + run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); let (_, res, _) = rx.recv().unwrap(); assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected))); } @@ -1889,7 +1901,7 @@ mod tests { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, tx); + run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); let (_, res, _) = rx.recv().unwrap(); assert!(res == TrFailed); } diff --git a/src/test/run-make-fulldeps/libtest-json/output.json b/src/test/run-make-fulldeps/libtest-json/output.json index d8169ece89b38..80e75c89bfc16 100644 --- a/src/test/run-make-fulldeps/libtest-json/output.json +++ b/src/test/run-make-fulldeps/libtest-json/output.json @@ -2,7 +2,7 @@ { "type": "test", "event": "started", "name": "a" } { "type": "test", "name": "a", "event": "ok" } { "type": "test", "event": "started", "name": "b" } -{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'b' panicked at 'assertion failed: false', f.rs:18:5\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n" } +{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:18:5\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n" } { "type": "test", "event": "started", "name": "c" } { "type": "test", "name": "c", "event": "ok" } { "type": "test", "event": "started", "name": "d" }