Skip to content

Commit 3499575

Browse files
committed
Auto merge of rust-lang#56243 - RalfJung:test-deterministic, r=alexcrichton
libtest: Use deterministic HashMap, avoid spawning thread if there is no concurrency It seems desirable to make a test and bench runner deterministic, which this achieves by using a deterministic hasher. Also, we we only have 1 thread, we don't bother spawning one and just use the main thread. The motivation for this is to be able to run the test harness in miri, where we can neither access the OS RNG, nor spawn threads.
2 parents 3a31213 + c28c287 commit 3499575

File tree

2 files changed

+28
-16
lines changed

2 files changed

+28
-16
lines changed

src/libtest/lib.rs

+27-15
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ mod formatters;
9999

100100
use formatters::{JsonFormatter, OutputFormatter, PrettyFormatter, TerseFormatter};
101101

102+
/// Whether to execute tests concurrently or not
103+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
104+
pub enum Concurrent { Yes, No }
105+
102106
// The name of a test. By convention this follows the rules for rust
103107
// paths; i.e., it should be a series of identifiers separated by double
104108
// colons. This way if some test runner wants to arrange the tests
@@ -1073,8 +1077,12 @@ pub fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F)
10731077
where
10741078
F: FnMut(TestEvent) -> io::Result<()>,
10751079
{
1076-
use std::collections::HashMap;
1080+
use std::collections::{self, HashMap};
1081+
use std::hash::BuildHasherDefault;
10771082
use std::sync::mpsc::RecvTimeoutError;
1083+
// Use a deterministic hasher
1084+
type TestMap =
1085+
HashMap<TestDesc, Instant, BuildHasherDefault<collections::hash_map::DefaultHasher>>;
10781086

10791087
let tests_len = tests.len();
10801088

@@ -1113,9 +1121,9 @@ where
11131121

11141122
let (tx, rx) = channel::<MonitorMsg>();
11151123

1116-
let mut running_tests: HashMap<TestDesc, Instant> = HashMap::new();
1124+
let mut running_tests: TestMap = HashMap::default();
11171125

1118-
fn get_timed_out_tests(running_tests: &mut HashMap<TestDesc, Instant>) -> Vec<TestDesc> {
1126+
fn get_timed_out_tests(running_tests: &mut TestMap) -> Vec<TestDesc> {
11191127
let now = Instant::now();
11201128
let timed_out = running_tests
11211129
.iter()
@@ -1133,7 +1141,7 @@ where
11331141
timed_out
11341142
};
11351143

1136-
fn calc_timeout(running_tests: &HashMap<TestDesc, Instant>) -> Option<Duration> {
1144+
fn calc_timeout(running_tests: &TestMap) -> Option<Duration> {
11371145
running_tests.values().min().map(|next_timeout| {
11381146
let now = Instant::now();
11391147
if *next_timeout >= now {
@@ -1148,7 +1156,7 @@ where
11481156
while !remaining.is_empty() {
11491157
let test = remaining.pop().unwrap();
11501158
callback(TeWait(test.desc.clone()))?;
1151-
run_test(opts, !opts.run_tests, test, tx.clone());
1159+
run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::No);
11521160
let (test, result, stdout) = rx.recv().unwrap();
11531161
callback(TeResult(test, result, stdout))?;
11541162
}
@@ -1159,7 +1167,7 @@ where
11591167
let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S);
11601168
running_tests.insert(test.desc.clone(), timeout);
11611169
callback(TeWait(test.desc.clone()))?; //here no pad
1162-
run_test(opts, !opts.run_tests, test, tx.clone());
1170+
run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::Yes);
11631171
pending += 1;
11641172
}
11651173

@@ -1191,7 +1199,7 @@ where
11911199
// All benchmarks run at the end, in serial.
11921200
for b in filtered_benchs {
11931201
callback(TeWait(b.desc.clone()))?;
1194-
run_test(opts, false, b, tx.clone());
1202+
run_test(opts, false, b, tx.clone(), Concurrent::No);
11951203
let (test, result, stdout) = rx.recv().unwrap();
11961204
callback(TeResult(test, result, stdout))?;
11971205
}
@@ -1393,6 +1401,7 @@ pub fn run_test(
13931401
force_ignore: bool,
13941402
test: TestDescAndFn,
13951403
monitor_ch: Sender<MonitorMsg>,
1404+
concurrency: Concurrent,
13961405
) {
13971406
let TestDescAndFn { desc, testfn } = test;
13981407

@@ -1409,6 +1418,7 @@ pub fn run_test(
14091418
monitor_ch: Sender<MonitorMsg>,
14101419
nocapture: bool,
14111420
testfn: Box<dyn FnBox() + Send>,
1421+
concurrency: Concurrent,
14121422
) {
14131423
// Buffer for capturing standard I/O
14141424
let data = Arc::new(Mutex::new(Vec::new()));
@@ -1443,7 +1453,7 @@ pub fn run_test(
14431453
// the test synchronously, regardless of the concurrency
14441454
// level.
14451455
let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_arch = "wasm32");
1446-
if supports_threads {
1456+
if concurrency == Concurrent::Yes && supports_threads {
14471457
let cfg = thread::Builder::new().name(name.as_slice().to_owned());
14481458
cfg.spawn(runtest).unwrap();
14491459
} else {
@@ -1464,13 +1474,14 @@ pub fn run_test(
14641474
}
14651475
DynTestFn(f) => {
14661476
let cb = move || __rust_begin_short_backtrace(f);
1467-
run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(cb))
1477+
run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(cb), concurrency)
14681478
}
14691479
StaticTestFn(f) => run_test_inner(
14701480
desc,
14711481
monitor_ch,
14721482
opts.nocapture,
14731483
Box::new(move || __rust_begin_short_backtrace(f)),
1484+
concurrency,
14741485
),
14751486
}
14761487
}
@@ -1753,6 +1764,7 @@ mod tests {
17531764
use std::sync::mpsc::channel;
17541765
use bench;
17551766
use Bencher;
1767+
use Concurrent;
17561768

17571769

17581770
fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
@@ -1793,7 +1805,7 @@ mod tests {
17931805
testfn: DynTestFn(Box::new(f)),
17941806
};
17951807
let (tx, rx) = channel();
1796-
run_test(&TestOpts::new(), false, desc, tx);
1808+
run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
17971809
let (_, res, _) = rx.recv().unwrap();
17981810
assert!(res != TrOk);
17991811
}
@@ -1811,7 +1823,7 @@ mod tests {
18111823
testfn: DynTestFn(Box::new(f)),
18121824
};
18131825
let (tx, rx) = channel();
1814-
run_test(&TestOpts::new(), false, desc, tx);
1826+
run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
18151827
let (_, res, _) = rx.recv().unwrap();
18161828
assert!(res == TrIgnored);
18171829
}
@@ -1831,7 +1843,7 @@ mod tests {
18311843
testfn: DynTestFn(Box::new(f)),
18321844
};
18331845
let (tx, rx) = channel();
1834-
run_test(&TestOpts::new(), false, desc, tx);
1846+
run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
18351847
let (_, res, _) = rx.recv().unwrap();
18361848
assert!(res == TrOk);
18371849
}
@@ -1851,7 +1863,7 @@ mod tests {
18511863
testfn: DynTestFn(Box::new(f)),
18521864
};
18531865
let (tx, rx) = channel();
1854-
run_test(&TestOpts::new(), false, desc, tx);
1866+
run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
18551867
let (_, res, _) = rx.recv().unwrap();
18561868
assert!(res == TrOk);
18571869
}
@@ -1873,7 +1885,7 @@ mod tests {
18731885
testfn: DynTestFn(Box::new(f)),
18741886
};
18751887
let (tx, rx) = channel();
1876-
run_test(&TestOpts::new(), false, desc, tx);
1888+
run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
18771889
let (_, res, _) = rx.recv().unwrap();
18781890
assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected)));
18791891
}
@@ -1891,7 +1903,7 @@ mod tests {
18911903
testfn: DynTestFn(Box::new(f)),
18921904
};
18931905
let (tx, rx) = channel();
1894-
run_test(&TestOpts::new(), false, desc, tx);
1906+
run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
18951907
let (_, res, _) = rx.recv().unwrap();
18961908
assert!(res == TrFailed);
18971909
}

src/test/run-make-fulldeps/libtest-json/output.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{ "type": "test", "event": "started", "name": "a" }
33
{ "type": "test", "name": "a", "event": "ok" }
44
{ "type": "test", "event": "started", "name": "b" }
5-
{ "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" }
5+
{ "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" }
66
{ "type": "test", "event": "started", "name": "c" }
77
{ "type": "test", "name": "c", "event": "ok" }
88
{ "type": "test", "event": "started", "name": "d" }

0 commit comments

Comments
 (0)