libtest: run all tests in their own thread, if supported by the host
This commit is contained in:
parent
cdd7afeaad
commit
3af058ec94
@ -40,7 +40,7 @@ pub mod test {
|
|||||||
cli::{parse_opts, TestOpts},
|
cli::{parse_opts, TestOpts},
|
||||||
filter_tests,
|
filter_tests,
|
||||||
helpers::metrics::{Metric, MetricMap},
|
helpers::metrics::{Metric, MetricMap},
|
||||||
options::{Concurrent, Options, RunIgnored, RunStrategy, ShouldPanic},
|
options::{Options, RunIgnored, RunStrategy, ShouldPanic},
|
||||||
run_test, test_main, test_main_static,
|
run_test, test_main, test_main_static,
|
||||||
test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk},
|
test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk},
|
||||||
time::{TestExecTime, TestTimeOptions},
|
time::{TestExecTime, TestTimeOptions},
|
||||||
@ -85,7 +85,7 @@ use event::{CompletedTest, TestEvent};
|
|||||||
use helpers::concurrency::get_concurrency;
|
use helpers::concurrency::get_concurrency;
|
||||||
use helpers::exit_code::get_exit_code;
|
use helpers::exit_code::get_exit_code;
|
||||||
use helpers::shuffle::{get_shuffle_seed, shuffle_tests};
|
use helpers::shuffle::{get_shuffle_seed, shuffle_tests};
|
||||||
use options::{Concurrent, RunStrategy};
|
use options::RunStrategy;
|
||||||
use test_result::*;
|
use test_result::*;
|
||||||
use time::TestExecTime;
|
use time::TestExecTime;
|
||||||
|
|
||||||
@ -235,6 +235,19 @@ where
|
|||||||
join_handle: Option<thread::JoinHandle<()>>,
|
join_handle: Option<thread::JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RunningTest {
|
||||||
|
fn join(self, completed_test: &mut CompletedTest) {
|
||||||
|
if let Some(join_handle) = self.join_handle {
|
||||||
|
if let Err(_) = join_handle.join() {
|
||||||
|
if let TrOk = completed_test.result {
|
||||||
|
completed_test.result =
|
||||||
|
TrFailedMsg("panicked after reporting success".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Use a deterministic hasher
|
// Use a deterministic hasher
|
||||||
type TestMap =
|
type TestMap =
|
||||||
HashMap<TestId, RunningTest, BuildHasherDefault<collections::hash_map::DefaultHasher>>;
|
HashMap<TestId, RunningTest, BuildHasherDefault<collections::hash_map::DefaultHasher>>;
|
||||||
@ -328,10 +341,10 @@ where
|
|||||||
let (id, test) = remaining.pop_front().unwrap();
|
let (id, test) = remaining.pop_front().unwrap();
|
||||||
let event = TestEvent::TeWait(test.desc.clone());
|
let event = TestEvent::TeWait(test.desc.clone());
|
||||||
notify_about_test_event(event)?;
|
notify_about_test_event(event)?;
|
||||||
let join_handle =
|
let join_handle = run_test(opts, !opts.run_tests, id, test, run_strategy, tx.clone());
|
||||||
run_test(opts, !opts.run_tests, id, test, run_strategy, tx.clone(), Concurrent::No);
|
// Wait for the test to complete.
|
||||||
assert!(join_handle.is_none());
|
let mut completed_test = rx.recv().unwrap();
|
||||||
let completed_test = rx.recv().unwrap();
|
RunningTest { join_handle }.join(&mut completed_test);
|
||||||
|
|
||||||
let event = TestEvent::TeResult(completed_test);
|
let event = TestEvent::TeResult(completed_test);
|
||||||
notify_about_test_event(event)?;
|
notify_about_test_event(event)?;
|
||||||
@ -345,15 +358,8 @@ where
|
|||||||
|
|
||||||
let event = TestEvent::TeWait(desc.clone());
|
let event = TestEvent::TeWait(desc.clone());
|
||||||
notify_about_test_event(event)?; //here no pad
|
notify_about_test_event(event)?; //here no pad
|
||||||
let join_handle = run_test(
|
let join_handle =
|
||||||
opts,
|
run_test(opts, !opts.run_tests, id, test, run_strategy, tx.clone());
|
||||||
!opts.run_tests,
|
|
||||||
id,
|
|
||||||
test,
|
|
||||||
run_strategy,
|
|
||||||
tx.clone(),
|
|
||||||
Concurrent::Yes,
|
|
||||||
);
|
|
||||||
running_tests.insert(id, RunningTest { join_handle });
|
running_tests.insert(id, RunningTest { join_handle });
|
||||||
timeout_queue.push_back(TimeoutEntry { id, desc, timeout });
|
timeout_queue.push_back(TimeoutEntry { id, desc, timeout });
|
||||||
pending += 1;
|
pending += 1;
|
||||||
@ -385,14 +391,7 @@ where
|
|||||||
|
|
||||||
let mut completed_test = res.unwrap();
|
let mut completed_test = res.unwrap();
|
||||||
let running_test = running_tests.remove(&completed_test.id).unwrap();
|
let running_test = running_tests.remove(&completed_test.id).unwrap();
|
||||||
if let Some(join_handle) = running_test.join_handle {
|
running_test.join(&mut completed_test);
|
||||||
if let Err(_) = join_handle.join() {
|
|
||||||
if let TrOk = completed_test.result {
|
|
||||||
completed_test.result =
|
|
||||||
TrFailedMsg("panicked after reporting success".to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = TestEvent::TeResult(completed_test);
|
let event = TestEvent::TeResult(completed_test);
|
||||||
notify_about_test_event(event)?;
|
notify_about_test_event(event)?;
|
||||||
@ -405,8 +404,10 @@ where
|
|||||||
for (id, b) in filtered_benchs {
|
for (id, b) in filtered_benchs {
|
||||||
let event = TestEvent::TeWait(b.desc.clone());
|
let event = TestEvent::TeWait(b.desc.clone());
|
||||||
notify_about_test_event(event)?;
|
notify_about_test_event(event)?;
|
||||||
run_test(opts, false, id, b, run_strategy, tx.clone(), Concurrent::No);
|
let join_handle = run_test(opts, false, id, b, run_strategy, tx.clone());
|
||||||
let completed_test = rx.recv().unwrap();
|
// Wait for the test to complete.
|
||||||
|
let mut completed_test = rx.recv().unwrap();
|
||||||
|
RunningTest { join_handle }.join(&mut completed_test);
|
||||||
|
|
||||||
let event = TestEvent::TeResult(completed_test);
|
let event = TestEvent::TeResult(completed_test);
|
||||||
notify_about_test_event(event)?;
|
notify_about_test_event(event)?;
|
||||||
@ -480,7 +481,6 @@ pub fn run_test(
|
|||||||
test: TestDescAndFn,
|
test: TestDescAndFn,
|
||||||
strategy: RunStrategy,
|
strategy: RunStrategy,
|
||||||
monitor_ch: Sender<CompletedTest>,
|
monitor_ch: Sender<CompletedTest>,
|
||||||
concurrency: Concurrent,
|
|
||||||
) -> Option<thread::JoinHandle<()>> {
|
) -> Option<thread::JoinHandle<()>> {
|
||||||
let TestDescAndFn { desc, testfn } = test;
|
let TestDescAndFn { desc, testfn } = test;
|
||||||
|
|
||||||
@ -498,7 +498,6 @@ pub fn run_test(
|
|||||||
struct TestRunOpts {
|
struct TestRunOpts {
|
||||||
pub strategy: RunStrategy,
|
pub strategy: RunStrategy,
|
||||||
pub nocapture: bool,
|
pub nocapture: bool,
|
||||||
pub concurrency: Concurrent,
|
|
||||||
pub time: Option<time::TestTimeOptions>,
|
pub time: Option<time::TestTimeOptions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +508,6 @@ pub fn run_test(
|
|||||||
testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
|
testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
|
||||||
opts: TestRunOpts,
|
opts: TestRunOpts,
|
||||||
) -> Option<thread::JoinHandle<()>> {
|
) -> Option<thread::JoinHandle<()>> {
|
||||||
let concurrency = opts.concurrency;
|
|
||||||
let name = desc.name.clone();
|
let name = desc.name.clone();
|
||||||
|
|
||||||
let runtest = move || match opts.strategy {
|
let runtest = move || match opts.strategy {
|
||||||
@ -536,7 +534,7 @@ pub fn run_test(
|
|||||||
// the test synchronously, regardless of the concurrency
|
// the test synchronously, regardless of the concurrency
|
||||||
// level.
|
// level.
|
||||||
let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_family = "wasm");
|
let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_family = "wasm");
|
||||||
if concurrency == Concurrent::Yes && supports_threads {
|
if supports_threads {
|
||||||
let cfg = thread::Builder::new().name(name.as_slice().to_owned());
|
let cfg = thread::Builder::new().name(name.as_slice().to_owned());
|
||||||
let mut runtest = Arc::new(Mutex::new(Some(runtest)));
|
let mut runtest = Arc::new(Mutex::new(Some(runtest)));
|
||||||
let runtest2 = runtest.clone();
|
let runtest2 = runtest.clone();
|
||||||
@ -557,7 +555,7 @@ pub fn run_test(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let test_run_opts =
|
let test_run_opts =
|
||||||
TestRunOpts { strategy, nocapture: opts.nocapture, concurrency, time: opts.time_options };
|
TestRunOpts { strategy, nocapture: opts.nocapture, time: opts.time_options };
|
||||||
|
|
||||||
match testfn {
|
match testfn {
|
||||||
DynBenchFn(benchfn) => {
|
DynBenchFn(benchfn) => {
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
//! Enums denoting options for test execution.
|
//! Enums denoting options for test execution.
|
||||||
|
|
||||||
/// Whether to execute tests concurrently or not
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum Concurrent {
|
|
||||||
Yes,
|
|
||||||
No,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Number of times to run a benchmarked function
|
/// Number of times to run a benchmarked function
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub enum BenchMode {
|
pub enum BenchMode {
|
||||||
|
@ -102,7 +102,7 @@ pub fn do_not_run_ignored_tests() {
|
|||||||
testfn: DynTestFn(Box::new(f)),
|
testfn: DynTestFn(Box::new(f)),
|
||||||
};
|
};
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
assert_ne!(result, TrOk);
|
assert_ne!(result, TrOk);
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ pub fn ignored_tests_result_in_ignored() {
|
|||||||
testfn: DynTestFn(Box::new(f)),
|
testfn: DynTestFn(Box::new(f)),
|
||||||
};
|
};
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
assert_eq!(result, TrIgnored);
|
assert_eq!(result, TrIgnored);
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ fn test_should_panic() {
|
|||||||
testfn: DynTestFn(Box::new(f)),
|
testfn: DynTestFn(Box::new(f)),
|
||||||
};
|
};
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
assert_eq!(result, TrOk);
|
assert_eq!(result, TrOk);
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ fn test_should_panic_good_message() {
|
|||||||
testfn: DynTestFn(Box::new(f)),
|
testfn: DynTestFn(Box::new(f)),
|
||||||
};
|
};
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
assert_eq!(result, TrOk);
|
assert_eq!(result, TrOk);
|
||||||
}
|
}
|
||||||
@ -205,7 +205,7 @@ fn test_should_panic_bad_message() {
|
|||||||
testfn: DynTestFn(Box::new(f)),
|
testfn: DynTestFn(Box::new(f)),
|
||||||
};
|
};
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
assert_eq!(result, TrFailedMsg(failed_msg.to_string()));
|
assert_eq!(result, TrFailedMsg(failed_msg.to_string()));
|
||||||
}
|
}
|
||||||
@ -239,7 +239,7 @@ fn test_should_panic_non_string_message_type() {
|
|||||||
testfn: DynTestFn(Box::new(f)),
|
testfn: DynTestFn(Box::new(f)),
|
||||||
};
|
};
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
assert_eq!(result, TrFailedMsg(failed_msg));
|
assert_eq!(result, TrFailedMsg(failed_msg));
|
||||||
}
|
}
|
||||||
@ -267,15 +267,7 @@ fn test_should_panic_but_succeeds() {
|
|||||||
testfn: DynTestFn(Box::new(f)),
|
testfn: DynTestFn(Box::new(f)),
|
||||||
};
|
};
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(
|
run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
&TestOpts::new(),
|
|
||||||
false,
|
|
||||||
TestId(0),
|
|
||||||
desc,
|
|
||||||
RunStrategy::InProcess,
|
|
||||||
tx,
|
|
||||||
Concurrent::No,
|
|
||||||
);
|
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
@ -306,7 +298,7 @@ fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
|
|||||||
|
|
||||||
let test_opts = TestOpts { time_options, ..TestOpts::new() };
|
let test_opts = TestOpts { time_options, ..TestOpts::new() };
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let exec_time = rx.recv().unwrap().exec_time;
|
let exec_time = rx.recv().unwrap().exec_time;
|
||||||
exec_time
|
exec_time
|
||||||
}
|
}
|
||||||
@ -345,7 +337,7 @@ fn time_test_failure_template(test_type: TestType) -> TestResult {
|
|||||||
|
|
||||||
let test_opts = TestOpts { time_options: Some(time_options), ..TestOpts::new() };
|
let test_opts = TestOpts { time_options: Some(time_options), ..TestOpts::new() };
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No);
|
run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx);
|
||||||
let result = rx.recv().unwrap().result;
|
let result = rx.recv().unwrap().result;
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ "type": "test", "event": "started", "name": "a" }
|
{ "type": "test", "event": "started", "name": "a" }
|
||||||
{ "type": "test", "name": "a", "event": "ok" }
|
{ "type": "test", "name": "a", "event": "ok" }
|
||||||
{ "type": "test", "event": "started", "name": "b" }
|
{ "type": "test", "event": "started", "name": "b" }
|
||||||
{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:9:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n" }
|
{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'b' panicked at 'assertion failed: false', f.rs:9:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n" }
|
||||||
{ "type": "test", "event": "started", "name": "c" }
|
{ "type": "test", "event": "started", "name": "c" }
|
||||||
{ "type": "test", "name": "c", "event": "ok" }
|
{ "type": "test", "name": "c", "event": "ok" }
|
||||||
{ "type": "test", "event": "started", "name": "d" }
|
{ "type": "test", "event": "started", "name": "d" }
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
{ "type": "test", "event": "started", "name": "a" }
|
{ "type": "test", "event": "started", "name": "a" }
|
||||||
{ "type": "test", "name": "a", "event": "ok", "stdout": "print from successful test\n" }
|
{ "type": "test", "name": "a", "event": "ok", "stdout": "print from successful test\n" }
|
||||||
{ "type": "test", "event": "started", "name": "b" }
|
{ "type": "test", "event": "started", "name": "b" }
|
||||||
{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:9:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n" }
|
{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'b' panicked at 'assertion failed: false', f.rs:9:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n" }
|
||||||
{ "type": "test", "event": "started", "name": "c" }
|
{ "type": "test", "event": "started", "name": "c" }
|
||||||
{ "type": "test", "name": "c", "event": "ok", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:15:5\n" }
|
{ "type": "test", "name": "c", "event": "ok", "stdout": "thread 'c' panicked at 'assertion failed: false', f.rs:15:5\n" }
|
||||||
{ "type": "test", "event": "started", "name": "d" }
|
{ "type": "test", "event": "started", "name": "d" }
|
||||||
{ "type": "test", "name": "d", "event": "ignored", "message": "msg" }
|
{ "type": "test", "name": "d", "event": "ignored", "message": "msg" }
|
||||||
{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
|
{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
|
||||||
|
@ -10,7 +10,7 @@ fee
|
|||||||
fie
|
fie
|
||||||
foe
|
foe
|
||||||
fum
|
fum
|
||||||
thread 'main' panicked at 'explicit panic', $DIR/test-thread-capture.rs:32:5
|
thread 'thready_fail' panicked at 'explicit panic', $DIR/test-thread-capture.rs:32:5
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
thread 'main' panicked at 'explicit panic', $DIR/test-thread-nocapture.rs:32:5
|
thread 'thready_fail' panicked at 'explicit panic', $DIR/test-thread-nocapture.rs:32:5
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
|
Loading…
x
Reference in New Issue
Block a user