Skip to content

Commit df24055

Browse files
committed
Factor inner test runners out, simplify RunStrategy
1 parent c6b4fb7 commit df24055

File tree

1 file changed

+131
-140
lines changed

1 file changed

+131
-140
lines changed

src/libtest/lib.rs

Lines changed: 131 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,12 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
333333
.map(make_owned_test)
334334
.next()
335335
.expect("couldn't find a test with the provided name");
336-
let opts = parse_opts(&[]).unwrap().unwrap();
337-
run_test(&opts, false, test, RunStrategy::SpawnedSecondary, Concurrent::No);
338-
unreachable!();
336+
let TestDescAndFn { desc, testfn } = test;
337+
let testfn = match testfn {
338+
StaticTestFn(f) => f,
339+
_ => panic!("only static tests are supported"),
340+
};
341+
run_test_in_spawned_subprocess(desc, Box::new(testfn));
339342
}
340343

341344
let args = env::args().collect::<Vec<_>>();
@@ -1088,31 +1091,16 @@ impl Write for Sink {
10881091
}
10891092
}
10901093

1091-
#[derive(Clone)]
1094+
#[derive(Clone, Copy)]
10921095
pub enum RunStrategy {
10931096
/// Runs the test in the current process, and sends the result back over the
10941097
/// supplied channel.
1095-
InProcess(Sender<MonitorMsg>),
1098+
InProcess,
10961099

10971100
/// Spawns a subprocess to run the test, and sends the result back over the
10981101
/// supplied channel. Requires argv[0] to exist and point to the binary
10991102
/// that's currently running.
1100-
SpawnPrimary(Sender<MonitorMsg>),
1101-
1102-
/// Runs the test in the current process, then exits the process.
1103-
/// Prints to stdout the custom result format that the parent process
1104-
/// expects in SpawnPrimary mode.
1105-
SpawnedSecondary,
1106-
}
1107-
1108-
impl RunStrategy {
1109-
fn monitor_ch(self) -> Option<Sender<MonitorMsg>> {
1110-
match self {
1111-
RunStrategy::InProcess(ch) => Some(ch),
1112-
RunStrategy::SpawnPrimary(ch) => Some(ch),
1113-
RunStrategy::SpawnedSecondary => None,
1114-
}
1115-
}
1103+
SpawnPrimary,
11161104
}
11171105

11181106
pub fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
@@ -1163,9 +1151,9 @@ where
11631151

11641152
let (tx, rx) = channel::<MonitorMsg>();
11651153
let run_strategy = if opts.options.panic_abort {
1166-
RunStrategy::SpawnPrimary(tx)
1154+
RunStrategy::SpawnPrimary
11671155
} else {
1168-
RunStrategy::InProcess(tx)
1156+
RunStrategy::InProcess
11691157
};
11701158

11711159
let mut running_tests: TestMap = HashMap::default();
@@ -1203,7 +1191,7 @@ where
12031191
while !remaining.is_empty() {
12041192
let test = remaining.pop().unwrap();
12051193
callback(TeWait(test.desc.clone()))?;
1206-
run_test(opts, !opts.run_tests, test, run_strategy.clone(), Concurrent::No);
1194+
run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No);
12071195
let (test, result, stdout) = rx.recv().unwrap();
12081196
callback(TeResult(test, result, stdout))?;
12091197
}
@@ -1214,7 +1202,7 @@ where
12141202
let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S);
12151203
running_tests.insert(test.desc.clone(), timeout);
12161204
callback(TeWait(test.desc.clone()))?; //here no pad
1217-
run_test(opts, !opts.run_tests, test, run_strategy.clone(), Concurrent::Yes);
1205+
run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes);
12181206
pending += 1;
12191207
}
12201208

@@ -1246,7 +1234,7 @@ where
12461234
// All benchmarks run at the end, in serial.
12471235
for b in filtered_benchs {
12481236
callback(TeWait(b.desc.clone()))?;
1249-
run_test(opts, false, b, run_strategy.clone(), Concurrent::No);
1237+
run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No);
12501238
let (test, result, stdout) = rx.recv().unwrap();
12511239
callback(TeResult(test, result, stdout))?;
12521240
}
@@ -1474,129 +1462,34 @@ pub fn run_test(
14741462
force_ignore: bool,
14751463
test: TestDescAndFn,
14761464
strategy: RunStrategy,
1465+
monitor_ch: Sender<MonitorMsg>,
14771466
concurrency: Concurrent,
14781467
) {
14791468
let TestDescAndFn { desc, testfn } = test;
14801469

1481-
if force_ignore || desc.ignore {
1482-
match strategy {
1483-
RunStrategy::InProcess(tx) | RunStrategy::SpawnPrimary(tx) => {
1484-
tx.send((desc, TrIgnored, Vec::new())).unwrap();
1485-
}
1486-
RunStrategy::SpawnedSecondary => panic!(),
1487-
}
1470+
let ignore_because_no_process_support = cfg!(target_arch = "wasm32")
1471+
&& !cfg!(target_os = "emscripten")
1472+
&& desc.should_panic != ShouldPanic::No;
1473+
1474+
if force_ignore || desc.ignore || ignore_because_no_process_support {
1475+
monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
14881476
return;
14891477
}
14901478

14911479
fn run_test_inner(
14921480
desc: TestDesc,
14931481
nocapture: bool,
14941482
strategy: RunStrategy,
1483+
monitor_ch: Sender<MonitorMsg>,
14951484
testfn: Box<dyn FnOnce() + Send>,
14961485
concurrency: Concurrent,
14971486
) {
1498-
// Buffer for capturing standard I/O
1499-
let data = Arc::new(Mutex::new(Vec::new()));
1500-
15011487
let name = desc.name.clone();
1488+
15021489
let runtest = move || {
1503-
// TODO split into functions
15041490
match strategy {
1505-
RunStrategy::InProcess(monitor_ch) => {
1506-
let oldio = if !nocapture {
1507-
Some((
1508-
io::set_print(Some(Box::new(Sink(data.clone())))),
1509-
io::set_panic(Some(Box::new(Sink(data.clone())))),
1510-
))
1511-
} else {
1512-
None
1513-
};
1514-
1515-
let result = catch_unwind(AssertUnwindSafe(testfn));
1516-
1517-
if let Some((printio, panicio)) = oldio {
1518-
io::set_print(printio);
1519-
io::set_panic(panicio);
1520-
}
1521-
1522-
let test_result = match result {
1523-
Ok(()) => calc_result(&desc, Ok(())),
1524-
Err(e) => calc_result(&desc, Err(e.as_ref())),
1525-
};
1526-
let stdout = data.lock().unwrap().to_vec();
1527-
monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
1528-
}
1529-
1530-
RunStrategy::SpawnPrimary(monitor_ch) => {
1531-
let (result, test_output) = (|| {
1532-
let args = env::args().collect::<Vec<_>>();
1533-
let current_exe = &args[0];
1534-
let output = match Command::new(current_exe)
1535-
.env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice())
1536-
.output() {
1537-
Ok(out) => out,
1538-
Err(e) => {
1539-
let err = format!("Failed to spawn {} as child for test: {:?}",
1540-
args[0], e);
1541-
return (TrFailed, err.into_bytes());
1542-
}
1543-
};
1544-
1545-
let std::process::Output { stdout, stderr, status } = output;
1546-
let mut test_output = stdout;
1547-
test_output.extend_from_slice(&stderr);
1548-
1549-
let result = match (move || {
1550-
let exit_code = status.code().ok_or("child process was terminated")?;
1551-
TestResult::try_from(exit_code).map_err(|_| {
1552-
format!("child process returned unexpected exit code {}", exit_code)
1553-
})
1554-
})() {
1555-
Ok(r) => r,
1556-
Err(e) => {
1557-
write!(&mut test_output, "Unexpected error: {}", e).unwrap();
1558-
TrFailed
1559-
}
1560-
};
1561-
1562-
(result, test_output)
1563-
})();
1564-
1565-
monitor_ch.send((desc.clone(), result, test_output)).unwrap();
1566-
}
1567-
1568-
RunStrategy::SpawnedSecondary => {
1569-
let record_lock = Mutex::new(());
1570-
let builtin_panic_hook = panic::take_hook();
1571-
let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| {
1572-
let _lock = record_lock.lock().unwrap();
1573-
1574-
let test_result = match panic_info {
1575-
Some(info) => calc_result(&desc, Err(info.payload())),
1576-
None => calc_result(&desc, Ok(())),
1577-
};
1578-
1579-
// We don't support serializing TrFailedMsg, so just
1580-
// print the message out to stderr.
1581-
let test_result = match test_result {
1582-
TrFailedMsg(msg) => {
1583-
eprintln!("{}", msg);
1584-
TrFailed
1585-
}
1586-
_ => test_result,
1587-
};
1588-
1589-
if let Some(info) = panic_info {
1590-
builtin_panic_hook(info);
1591-
}
1592-
process::exit(test_result.try_into().unwrap());
1593-
});
1594-
let record_result2 = record_result.clone();
1595-
panic::set_hook(Box::new(move |info| record_result2(Some(&info))));
1596-
testfn();
1597-
record_result(None);
1598-
unreachable!("panic=abort callback should have exited the process");
1599-
}
1491+
RunStrategy::InProcess => run_test_in_process(desc, nocapture, testfn, monitor_ch),
1492+
RunStrategy::SpawnPrimary => spawn_test_subprocess(desc, monitor_ch),
16001493
}
16011494
};
16021495

@@ -1615,27 +1508,27 @@ pub fn run_test(
16151508
match testfn {
16161509
DynBenchFn(bencher) => {
16171510
// Benchmarks aren't expected to panic, so we run them all in-process.
1618-
crate::bench::benchmark(opts, desc, strategy.monitor_ch().unwrap(), |harness| {
1511+
crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| {
16191512
bencher.run(harness)
16201513
});
16211514
}
16221515
StaticBenchFn(benchfn) => {
16231516
// Benchmarks aren't expected to panic, so we run them all in-process.
1624-
crate::bench::benchmark(opts, desc, strategy.monitor_ch().unwrap(), |harness| {
1517+
crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| {
16251518
(benchfn.clone())(harness)
16261519
});
16271520
}
16281521
DynTestFn(f) => {
16291522
match strategy {
1630-
RunStrategy::InProcess(_) => (),
1523+
RunStrategy::InProcess => (),
16311524
_ => panic!("Cannot run dynamic test fn out-of-process"),
16321525
};
16331526
let cb = move || __rust_begin_short_backtrace(f);
1634-
run_test_inner(desc, opts.nocapture, strategy, Box::new(cb), concurrency);
1527+
run_test_inner(desc, opts.nocapture, strategy, monitor_ch, Box::new(cb), concurrency);
16351528
}
16361529
StaticTestFn(f) => {
16371530
let cb = move || __rust_begin_short_backtrace(f);
1638-
run_test_inner(desc, opts.nocapture, strategy, Box::new(cb), concurrency);
1531+
run_test_inner(desc, opts.nocapture, strategy, monitor_ch, Box::new(cb), concurrency);
16391532
}
16401533
}
16411534
}
@@ -1673,6 +1566,104 @@ fn calc_result<'a>(desc: &TestDesc,
16731566
}
16741567
}
16751568

1569+
fn run_test_in_process(desc: TestDesc,
1570+
nocapture: bool,
1571+
testfn: Box<dyn FnOnce() + Send>,
1572+
monitor_ch: Sender<MonitorMsg>) {
1573+
// Buffer for capturing standard I/O
1574+
let data = Arc::new(Mutex::new(Vec::new()));
1575+
1576+
let oldio = if !nocapture {
1577+
Some((
1578+
io::set_print(Some(Box::new(Sink(data.clone())))),
1579+
io::set_panic(Some(Box::new(Sink(data.clone())))),
1580+
))
1581+
} else {
1582+
None
1583+
};
1584+
1585+
let result = catch_unwind(AssertUnwindSafe(testfn));
1586+
1587+
if let Some((printio, panicio)) = oldio {
1588+
io::set_print(printio);
1589+
io::set_panic(panicio);
1590+
}
1591+
1592+
let test_result = match result {
1593+
Ok(()) => calc_result(&desc, Ok(())),
1594+
Err(e) => calc_result(&desc, Err(e.as_ref())),
1595+
};
1596+
let stdout = data.lock().unwrap().to_vec();
1597+
monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
1598+
}
1599+
1600+
fn spawn_test_subprocess(desc: TestDesc, monitor_ch: Sender<MonitorMsg>) {
1601+
let (result, test_output) = (|| {
1602+
let args = env::args().collect::<Vec<_>>();
1603+
let current_exe = &args[0];
1604+
let output = match Command::new(current_exe)
1605+
.env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice())
1606+
.output() {
1607+
Ok(out) => out,
1608+
Err(e) => {
1609+
let err = format!("Failed to spawn {} as child for test: {:?}", args[0], e);
1610+
return (TrFailed, err.into_bytes());
1611+
}
1612+
};
1613+
1614+
let std::process::Output { stdout, stderr, status } = output;
1615+
let mut test_output = stdout;
1616+
test_output.extend_from_slice(&stderr);
1617+
1618+
let result = match (move || {
1619+
let exit_code = status.code().ok_or("child process was terminated")?;
1620+
TestResult::try_from(exit_code).map_err(|_| {
1621+
format!("child process returned unexpected exit code {}", exit_code)
1622+
})
1623+
})() {
1624+
Ok(r) => r,
1625+
Err(e) => {
1626+
write!(&mut test_output, "Unexpected error: {}", e).unwrap();
1627+
TrFailed
1628+
}
1629+
};
1630+
1631+
(result, test_output)
1632+
})();
1633+
1634+
monitor_ch.send((desc.clone(), result, test_output)).unwrap();
1635+
}
1636+
1637+
fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box<dyn FnOnce() + Send>) -> ! {
1638+
let builtin_panic_hook = panic::take_hook();
1639+
let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| {
1640+
let test_result = match panic_info {
1641+
Some(info) => calc_result(&desc, Err(info.payload())),
1642+
None => calc_result(&desc, Ok(())),
1643+
};
1644+
1645+
// We don't support serializing TrFailedMsg, so just
1646+
// print the message out to stderr.
1647+
let test_result = match test_result {
1648+
TrFailedMsg(msg) => {
1649+
eprintln!("{}", msg);
1650+
TrFailed
1651+
}
1652+
_ => test_result,
1653+
};
1654+
1655+
if let Some(info) = panic_info {
1656+
builtin_panic_hook(info);
1657+
}
1658+
process::exit(test_result.try_into().unwrap());
1659+
});
1660+
let record_result2 = record_result.clone();
1661+
panic::set_hook(Box::new(move |info| record_result2(Some(&info))));
1662+
testfn();
1663+
record_result(None);
1664+
unreachable!("panic=abort callback should have exited the process")
1665+
}
1666+
16761667
#[derive(Clone, PartialEq)]
16771668
pub struct MetricMap(BTreeMap<String, Metric>);
16781669

@@ -1822,15 +1813,15 @@ where
18221813

18231814
pub mod bench {
18241815
use super::{
1825-
BenchMode, BenchSamples, Bencher, MonitorMsg, Sender, Sink, TestDesc, TestOpts, TestResult
1816+
BenchMode, BenchSamples, Bencher, MonitorMsg, Sender, Sink, TestDesc, TestResult
18261817
};
18271818
use crate::stats;
18281819
use std::cmp;
18291820
use std::io;
18301821
use std::panic::{catch_unwind, AssertUnwindSafe};
18311822
use std::sync::{Arc, Mutex};
18321823

1833-
pub fn benchmark<F>(opts: &TestOpts, desc: TestDesc, monitor_ch: Sender<MonitorMsg>, f: F)
1824+
pub fn benchmark<F>(desc: TestDesc, monitor_ch: Sender<MonitorMsg>, nocapture: bool, f: F)
18341825
where
18351826
F: FnMut(&mut Bencher),
18361827
{
@@ -1841,7 +1832,7 @@ pub mod bench {
18411832
};
18421833

18431834
let data = Arc::new(Mutex::new(Vec::new()));
1844-
let oldio = if !opts.nocapture {
1835+
let oldio = if !nocapture {
18451836
Some((
18461837
io::set_print(Some(Box::new(Sink(data.clone())))),
18471838
io::set_panic(Some(Box::new(Sink(data.clone())))),

0 commit comments

Comments
 (0)