summaryrefslogtreecommitdiffstats
path: root/library/test
diff options
context:
space:
mode:
Diffstat (limited to 'library/test')
-rw-r--r--library/test/src/bench.rs21
-rw-r--r--library/test/src/cli.rs4
-rw-r--r--library/test/src/console.rs2
-rw-r--r--library/test/src/helpers/isatty.rs32
-rw-r--r--library/test/src/helpers/mod.rs1
-rw-r--r--library/test/src/lib.rs105
-rw-r--r--library/test/src/term.rs2
-rw-r--r--library/test/src/term/win.rs7
-rw-r--r--library/test/src/tests.rs117
-rw-r--r--library/test/src/types.rs15
10 files changed, 169 insertions, 137 deletions
diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs
index 7869ba2c0..23925e6ea 100644
--- a/library/test/src/bench.rs
+++ b/library/test/src/bench.rs
@@ -49,12 +49,12 @@ impl Bencher {
self.summary = Some(iter(&mut inner));
}
- pub fn bench<F>(&mut self, mut f: F) -> Option<stats::Summary>
+ pub fn bench<F>(&mut self, mut f: F) -> Result<Option<stats::Summary>, String>
where
- F: FnMut(&mut Bencher),
+ F: FnMut(&mut Bencher) -> Result<(), String>,
{
- f(self);
- self.summary
+ let result = f(self);
+ result.map(|_| self.summary)
}
}
@@ -195,7 +195,7 @@ pub fn benchmark<F>(
nocapture: bool,
f: F,
) where
- F: FnMut(&mut Bencher),
+ F: FnMut(&mut Bencher) -> Result<(), String>,
{
let mut bs = Bencher { mode: BenchMode::Auto, summary: None, bytes: 0 };
@@ -211,14 +211,14 @@ pub fn benchmark<F>(
let test_result = match result {
//bs.bench(f) {
- Ok(Some(ns_iter_summ)) => {
+ Ok(Ok(Some(ns_iter_summ))) => {
let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
let mb_s = bs.bytes * 1000 / ns_iter;
let bs = BenchSamples { ns_iter_summ, mb_s: mb_s as usize };
TestResult::TrBench(bs)
}
- Ok(None) => {
+ Ok(Ok(None)) => {
// iter not called, so no data.
// FIXME: error in this case?
let samples: &mut [f64] = &mut [0.0_f64; 1];
@@ -226,6 +226,7 @@ pub fn benchmark<F>(
TestResult::TrBench(bs)
}
Err(_) => TestResult::TrFailed,
+ Ok(Err(_)) => TestResult::TrFailed,
};
let stdout = data.lock().unwrap().to_vec();
@@ -233,10 +234,10 @@ pub fn benchmark<F>(
monitor_ch.send(message).unwrap();
}
-pub fn run_once<F>(f: F)
+pub fn run_once<F>(f: F) -> Result<(), String>
where
- F: FnMut(&mut Bencher),
+ F: FnMut(&mut Bencher) -> Result<(), String>,
{
let mut bs = Bencher { mode: BenchMode::Single, summary: None, bytes: 0 };
- bs.bench(f);
+ bs.bench(f).map(|_| ())
}
diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs
index f981b9c49..8be32183f 100644
--- a/library/test/src/cli.rs
+++ b/library/test/src/cli.rs
@@ -3,9 +3,9 @@
use std::env;
use std::path::PathBuf;
-use super::helpers::isatty;
use super::options::{ColorConfig, Options, OutputFormat, RunIgnored};
use super::time::TestTimeOptions;
+use std::io::{self, IsTerminal};
#[derive(Debug)]
pub struct TestOpts {
@@ -32,7 +32,7 @@ pub struct TestOpts {
impl TestOpts {
pub fn use_color(&self) -> bool {
match self.color {
- ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
+ ColorConfig::AutoColor => !self.nocapture && io::stdout().is_terminal(),
ColorConfig::AlwaysColor => true,
ColorConfig::NeverColor => false,
}
diff --git a/library/test/src/console.rs b/library/test/src/console.rs
index e9dda9896..b1270c272 100644
--- a/library/test/src/console.rs
+++ b/library/test/src/console.rs
@@ -147,7 +147,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Res
let mut ntest = 0;
let mut nbench = 0;
- for test in filter_tests(&opts, tests) {
+ for test in filter_tests(&opts, tests).into_iter() {
use crate::TestFn::*;
let TestDescAndFn { desc: TestDesc { name, .. }, testfn } = test;
diff --git a/library/test/src/helpers/isatty.rs b/library/test/src/helpers/isatty.rs
deleted file mode 100644
index 874ecc376..000000000
--- a/library/test/src/helpers/isatty.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Helper module which provides a function to test
-//! if stdout is a tty.
-
-cfg_if::cfg_if! {
- if #[cfg(unix)] {
- pub fn stdout_isatty() -> bool {
- unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
- }
- } else if #[cfg(windows)] {
- pub fn stdout_isatty() -> bool {
- type DWORD = u32;
- type BOOL = i32;
- type HANDLE = *mut u8;
- type LPDWORD = *mut u32;
- const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
- extern "system" {
- fn GetStdHandle(which: DWORD) -> HANDLE;
- fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
- }
- unsafe {
- let handle = GetStdHandle(STD_OUTPUT_HANDLE);
- let mut out = 0;
- GetConsoleMode(handle, &mut out) != 0
- }
- }
- } else {
- // FIXME: Implement isatty on SGX
- pub fn stdout_isatty() -> bool {
- false
- }
- }
-}
diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs
index 049cadf86..6f366a911 100644
--- a/library/test/src/helpers/mod.rs
+++ b/library/test/src/helpers/mod.rs
@@ -3,6 +3,5 @@
pub mod concurrency;
pub mod exit_code;
-pub mod isatty;
pub mod metrics;
pub mod shuffle;
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 3b7193adc..141f16d17 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -6,7 +6,8 @@
//! benchmarks themselves) should be done via the `#[test]` and
//! `#[bench]` attributes.
//!
-//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more details.
+//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more
+//! details.
// Currently, not much of this is meant for users. It is intended to
// support the simplest interface possible for representing and
@@ -15,10 +16,11 @@
#![unstable(feature = "test", issue = "50297")]
#![doc(test(attr(deny(warnings))))]
-#![feature(bench_black_box)]
#![feature(internal_output_capture)]
+#![feature(is_terminal)]
#![feature(staged_api)]
#![feature(process_exitcode_internals)]
+#![feature(panic_can_unwind)]
#![feature(test)]
// Public reexports
@@ -53,6 +55,7 @@ use std::{
collections::VecDeque,
env, io,
io::prelude::Write,
+ mem::ManuallyDrop,
panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
process::{self, Command, Termination},
sync::mpsc::{channel, Sender},
@@ -77,6 +80,7 @@ mod types;
#[cfg(test)]
mod tests;
+use core::any::Any;
use event::{CompletedTest, TestEvent};
use helpers::concurrency::get_concurrency;
use helpers::exit_code::get_exit_code;
@@ -110,6 +114,29 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Opt
process::exit(ERROR_EXIT_CODE);
}
} else {
+ if !opts.nocapture {
+ // If we encounter a non-unwinding panic, flush any captured output from the current test,
+ // and stop capturing output to ensure that the non-unwinding panic message is visible.
+ // We also acquire the locks for both output streams to prevent output from other threads
+ // from interleaving with the panic message or appearing after it.
+ let builtin_panic_hook = panic::take_hook();
+ let hook = Box::new({
+ move |info: &'_ PanicInfo<'_>| {
+ if !info.can_unwind() {
+ std::mem::forget(std::io::stderr().lock());
+ let mut stdout = ManuallyDrop::new(std::io::stdout().lock());
+ if let Some(captured) = io::set_output_capture(None) {
+ if let Ok(data) = captured.lock() {
+ let _ = stdout.write_all(&data);
+ let _ = stdout.flush();
+ }
+ }
+ }
+ builtin_panic_hook(info);
+ }
+ });
+ panic::set_hook(hook);
+ }
match console::run_tests_console(&opts, tests) {
Ok(true) => {}
Ok(false) => process::exit(ERROR_EXIT_CODE),
@@ -176,17 +203,20 @@ fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn {
}
}
-/// Invoked when unit tests terminate. Should panic if the unit
-/// Tests is considered a failure. By default, invokes `report()`
-/// and checks for a `0` result.
-pub fn assert_test_result<T: Termination>(result: T) {
+/// Invoked when unit tests terminate. Returns `Result::Err` if the test is
+/// considered a failure. By default, invokes `report() and checks for a `0`
+/// result.
+pub fn assert_test_result<T: Termination>(result: T) -> Result<(), String> {
let code = result.report().to_i32();
- assert_eq!(
- code, 0,
- "the test returned a termination value with a non-zero status code ({}) \
- which indicates a failure",
- code
- );
+ if code == 0 {
+ Ok(())
+ } else {
+ Err(format!(
+ "the test returned a termination value with a non-zero status code \
+ ({}) which indicates a failure",
+ code
+ ))
+ }
}
pub fn run_tests<F>(
@@ -242,7 +272,7 @@ where
let event = TestEvent::TeFiltered(filtered_descs, shuffle_seed);
notify_about_test_event(event)?;
- let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests
+ let (mut filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests
.into_iter()
.enumerate()
.map(|(i, e)| (TestId(i), e))
@@ -250,12 +280,12 @@ where
let concurrency = opts.test_threads.unwrap_or_else(get_concurrency);
- let mut remaining = filtered_tests;
if let Some(shuffle_seed) = shuffle_seed {
- shuffle_tests(shuffle_seed, &mut remaining);
- } else {
- remaining.reverse();
+ shuffle_tests(shuffle_seed, &mut filtered_tests);
}
+ // Store the tests in a VecDeque so we can efficiently remove the first element to run the
+ // tests in the order they were passed (unless shuffled).
+ let mut remaining = VecDeque::from(filtered_tests);
let mut pending = 0;
let (tx, rx) = channel::<CompletedTest>();
@@ -295,7 +325,7 @@ where
if concurrency == 1 {
while !remaining.is_empty() {
- let (id, test) = remaining.pop().unwrap();
+ let (id, test) = remaining.pop_front().unwrap();
let event = TestEvent::TeWait(test.desc.clone());
notify_about_test_event(event)?;
let join_handle =
@@ -309,7 +339,7 @@ where
} else {
while pending > 0 || !remaining.is_empty() {
while pending < concurrency && !remaining.is_empty() {
- let (id, test) = remaining.pop().unwrap();
+ let (id, test) = remaining.pop_front().unwrap();
let timeout = time::get_default_test_timeout();
let desc = test.desc.clone();
@@ -421,9 +451,6 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
RunIgnored::No => {}
}
- // Sort the tests alphabetically
- filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice()));
-
filtered
}
@@ -479,7 +506,7 @@ pub fn run_test(
id: TestId,
desc: TestDesc,
monitor_ch: Sender<CompletedTest>,
- testfn: Box<dyn FnOnce() + Send>,
+ testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
opts: TestRunOpts,
) -> Option<thread::JoinHandle<()>> {
let concurrency = opts.concurrency;
@@ -568,11 +595,11 @@ pub fn run_test(
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
#[inline(never)]
-fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) {
- f();
+fn __rust_begin_short_backtrace<T, F: FnOnce() -> T>(f: F) -> T {
+ let result = f();
// prevent this frame from being tail-call optimised away
- black_box(());
+ black_box(result)
}
fn run_test_in_process(
@@ -580,7 +607,7 @@ fn run_test_in_process(
desc: TestDesc,
nocapture: bool,
report_time: bool,
- testfn: Box<dyn FnOnce() + Send>,
+ testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
monitor_ch: Sender<CompletedTest>,
time_opts: Option<time::TestTimeOptions>,
) {
@@ -592,7 +619,7 @@ fn run_test_in_process(
}
let start = report_time.then(Instant::now);
- let result = catch_unwind(AssertUnwindSafe(testfn));
+ let result = fold_err(catch_unwind(AssertUnwindSafe(testfn)));
let exec_time = start.map(|start| {
let duration = start.elapsed();
TestExecTime(duration)
@@ -609,6 +636,19 @@ fn run_test_in_process(
monitor_ch.send(message).unwrap();
}
+fn fold_err<T, E>(
+ result: Result<Result<T, E>, Box<dyn Any + Send>>,
+) -> Result<T, Box<dyn Any + Send>>
+where
+ E: Send + 'static,
+{
+ match result {
+ Ok(Err(e)) => Err(Box::new(e)),
+ Ok(Ok(v)) => Ok(v),
+ Err(e) => Err(e),
+ }
+}
+
fn spawn_test_subprocess(
id: TestId,
desc: TestDesc,
@@ -664,7 +704,10 @@ fn spawn_test_subprocess(
monitor_ch.send(message).unwrap();
}
-fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box<dyn FnOnce() + Send>) -> ! {
+fn run_test_in_spawned_subprocess(
+ desc: TestDesc,
+ testfn: Box<dyn FnOnce() -> Result<(), String> + Send>,
+) -> ! {
let builtin_panic_hook = panic::take_hook();
let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| {
let test_result = match panic_info {
@@ -690,7 +733,9 @@ fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box<dyn FnOnce() + Sen
});
let record_result2 = record_result.clone();
panic::set_hook(Box::new(move |info| record_result2(Some(&info))));
- testfn();
+ if let Err(message) = testfn() {
+ panic!("{}", message);
+ }
record_result(None);
unreachable!("panic=abort callback should have exited the process")
}
diff --git a/library/test/src/term.rs b/library/test/src/term.rs
index b256ab7b8..a14b0d4f5 100644
--- a/library/test/src/term.rs
+++ b/library/test/src/term.rs
@@ -39,7 +39,7 @@ pub(crate) fn stdout() -> Option<Box<StdoutTerminal>> {
pub(crate) fn stdout() -> Option<Box<StdoutTerminal>> {
TerminfoTerminal::new(io::stdout())
.map(|t| Box::new(t) as Box<StdoutTerminal>)
- .or_else(|| WinConsole::new(io::stdout()).ok().map(|t| Box::new(t) as Box<StdoutTerminal>))
+ .or_else(|| Some(Box::new(WinConsole::new(io::stdout())) as Box<StdoutTerminal>))
}
/// Terminal color definitions
diff --git a/library/test/src/term/win.rs b/library/test/src/term/win.rs
index 4bdbd6ee7..55020141a 100644
--- a/library/test/src/term/win.rs
+++ b/library/test/src/term/win.rs
@@ -113,8 +113,7 @@ impl<T: Write + Send + 'static> WinConsole<T> {
}
}
- /// Returns `None` whenever the terminal cannot be created for some reason.
- pub(crate) fn new(out: T) -> io::Result<WinConsole<T>> {
+ pub(crate) fn new(out: T) -> WinConsole<T> {
use std::mem::MaybeUninit;
let fg;
@@ -132,13 +131,13 @@ impl<T: Write + Send + 'static> WinConsole<T> {
bg = color::BLACK;
}
}
- Ok(WinConsole {
+ WinConsole {
buf: out,
def_foreground: fg,
def_background: bg,
foreground: fg,
background: bg,
- })
+ }
}
}
diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs
index 0b81aff59..b54be64ef 100644
--- a/library/test/src/tests.rs
+++ b/library/test/src/tests.rs
@@ -67,7 +67,7 @@ fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
no_run: false,
test_type: TestType::Unknown,
},
- testfn: DynTestFn(Box::new(move || {})),
+ testfn: DynTestFn(Box::new(move || Ok(()))),
},
TestDescAndFn {
desc: TestDesc {
@@ -79,14 +79,14 @@ fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
no_run: false,
test_type: TestType::Unknown,
},
- testfn: DynTestFn(Box::new(move || {})),
+ testfn: DynTestFn(Box::new(move || Ok(()))),
},
]
}
#[test]
pub fn do_not_run_ignored_tests() {
- fn f() {
+ fn f() -> Result<(), String> {
panic!();
}
let desc = TestDescAndFn {
@@ -109,7 +109,9 @@ pub fn do_not_run_ignored_tests() {
#[test]
pub fn ignored_tests_result_in_ignored() {
- fn f() {}
+ fn f() -> Result<(), String> {
+ Ok(())
+ }
let desc = TestDescAndFn {
desc: TestDesc {
name: StaticTestName("whatever"),
@@ -132,7 +134,7 @@ pub fn ignored_tests_result_in_ignored() {
#[test]
#[cfg(not(target_os = "emscripten"))]
fn test_should_panic() {
- fn f() {
+ fn f() -> Result<(), String> {
panic!();
}
let desc = TestDescAndFn {
@@ -157,7 +159,7 @@ fn test_should_panic() {
#[test]
#[cfg(not(target_os = "emscripten"))]
fn test_should_panic_good_message() {
- fn f() {
+ fn f() -> Result<(), String> {
panic!("an error message");
}
let desc = TestDescAndFn {
@@ -183,7 +185,7 @@ fn test_should_panic_good_message() {
#[cfg(not(target_os = "emscripten"))]
fn test_should_panic_bad_message() {
use crate::tests::TrFailedMsg;
- fn f() {
+ fn f() -> Result<(), String> {
panic!("an error message");
}
let expected = "foobar";
@@ -214,7 +216,7 @@ fn test_should_panic_bad_message() {
fn test_should_panic_non_string_message_type() {
use crate::tests::TrFailedMsg;
use std::any::TypeId;
- fn f() {
+ fn f() -> Result<(), String> {
std::panic::panic_any(1i32);
}
let expected = "foobar";
@@ -249,7 +251,9 @@ fn test_should_panic_but_succeeds() {
let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")];
for &should_panic in should_panic_variants.iter() {
- fn f() {}
+ fn f() -> Result<(), String> {
+ Ok(())
+ }
let desc = TestDescAndFn {
desc: TestDesc {
name: StaticTestName("whatever"),
@@ -283,7 +287,9 @@ fn test_should_panic_but_succeeds() {
}
fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
- fn f() {}
+ fn f() -> Result<(), String> {
+ Ok(())
+ }
let desc = TestDescAndFn {
desc: TestDesc {
name: StaticTestName("whatever"),
@@ -318,7 +324,9 @@ fn test_should_report_time() {
}
fn time_test_failure_template(test_type: TestType) -> TestResult {
- fn f() {}
+ fn f() -> Result<(), String> {
+ Ok(())
+ }
let desc = TestDescAndFn {
desc: TestDesc {
name: StaticTestName("whatever"),
@@ -480,7 +488,7 @@ pub fn exclude_should_panic_option() {
no_run: false,
test_type: TestType::Unknown,
},
- testfn: DynTestFn(Box::new(move || {})),
+ testfn: DynTestFn(Box::new(move || Ok(()))),
});
let filtered = filter_tests(&opts, tests);
@@ -504,7 +512,7 @@ pub fn exact_filter_match() {
no_run: false,
test_type: TestType::Unknown,
},
- testfn: DynTestFn(Box::new(move || {})),
+ testfn: DynTestFn(Box::new(move || Ok(()))),
})
.collect()
}
@@ -580,7 +588,9 @@ fn sample_tests() -> Vec<TestDescAndFn> {
"test::run_include_ignored_option".to_string(),
"test::sort_tests".to_string(),
];
- fn testfn() {}
+ fn testfn() -> Result<(), String> {
+ Ok(())
+ }
let mut tests = Vec::new();
for name in &names {
let test = TestDescAndFn {
@@ -601,33 +611,6 @@ fn sample_tests() -> Vec<TestDescAndFn> {
}
#[test]
-pub fn sort_tests() {
- let mut opts = TestOpts::new();
- opts.run_tests = true;
-
- let tests = sample_tests();
- let filtered = filter_tests(&opts, tests);
-
- let expected = vec![
- "isize::test_pow".to_string(),
- "isize::test_to_str".to_string(),
- "sha1::test".to_string(),
- "test::do_not_run_ignored_tests".to_string(),
- "test::filter_for_ignored_option".to_string(),
- "test::first_free_arg_should_be_a_filter".to_string(),
- "test::ignored_tests_result_in_ignored".to_string(),
- "test::parse_ignored_flag".to_string(),
- "test::parse_include_ignored_flag".to_string(),
- "test::run_include_ignored_option".to_string(),
- "test::sort_tests".to_string(),
- ];
-
- for (a, b) in expected.iter().zip(filtered) {
- assert_eq!(*a, b.desc.name.to_string());
- }
-}
-
-#[test]
pub fn shuffle_tests() {
let mut opts = TestOpts::new();
opts.shuffle = true;
@@ -717,21 +700,26 @@ pub fn test_metricmap_compare() {
#[test]
pub fn test_bench_once_no_iter() {
- fn f(_: &mut Bencher) {}
- bench::run_once(f);
+ fn f(_: &mut Bencher) -> Result<(), String> {
+ Ok(())
+ }
+ bench::run_once(f).unwrap();
}
#[test]
pub fn test_bench_once_iter() {
- fn f(b: &mut Bencher) {
- b.iter(|| {})
+ fn f(b: &mut Bencher) -> Result<(), String> {
+ b.iter(|| {});
+ Ok(())
}
- bench::run_once(f);
+ bench::run_once(f).unwrap();
}
#[test]
pub fn test_bench_no_iter() {
- fn f(_: &mut Bencher) {}
+ fn f(_: &mut Bencher) -> Result<(), String> {
+ Ok(())
+ }
let (tx, rx) = channel();
@@ -751,8 +739,9 @@ pub fn test_bench_no_iter() {
#[test]
pub fn test_bench_iter() {
- fn f(b: &mut Bencher) {
- b.iter(|| {})
+ fn f(b: &mut Bencher) -> Result<(), String> {
+ b.iter(|| {});
+ Ok(())
}
let (tx, rx) = channel();
@@ -821,3 +810,33 @@ fn should_sort_failures_before_printing_them() {
let bpos = s.find("b").unwrap();
assert!(apos < bpos);
}
+
+#[test]
+#[cfg(not(target_os = "emscripten"))]
+fn test_dyn_bench_returning_err_fails_when_run_as_test() {
+ fn f(_: &mut Bencher) -> Result<(), String> {
+ Result::Err("An error".into())
+ }
+ let desc = TestDescAndFn {
+ desc: TestDesc {
+ name: StaticTestName("whatever"),
+ ignore: false,
+ ignore_message: None,
+ should_panic: ShouldPanic::No,
+ compile_fail: false,
+ no_run: false,
+ test_type: TestType::Unknown,
+ },
+ testfn: DynBenchFn(Box::new(f)),
+ };
+ let (tx, rx) = channel();
+ let notify = move |event: TestEvent| {
+ if let TestEvent::TeResult(result) = event {
+ tx.send(result).unwrap();
+ }
+ Ok(())
+ };
+ run_tests(&TestOpts { run_tests: true, ..TestOpts::new() }, vec![desc], notify).unwrap();
+ let result = rx.recv().unwrap().result;
+ assert_eq!(result, TrFailed);
+}
diff --git a/library/test/src/types.rs b/library/test/src/types.rs
index ffb1efe18..888afff79 100644
--- a/library/test/src/types.rs
+++ b/library/test/src/types.rs
@@ -75,14 +75,15 @@ impl fmt::Display for TestName {
}
// A function that runs a test. If the function returns successfully,
-// the test succeeds; if the function panics then the test fails. We
-// may need to come up with a more clever definition of test in order
-// to support isolation of tests into threads.
+// the test succeeds; if the function panics or returns Result::Err
+// then the test fails. We may need to come up with a more clever
+// definition of test in order to support isolation of tests into
+// threads.
pub enum TestFn {
- StaticTestFn(fn()),
- StaticBenchFn(fn(&mut Bencher)),
- DynTestFn(Box<dyn FnOnce() + Send>),
- DynBenchFn(Box<dyn Fn(&mut Bencher) + Send>),
+ StaticTestFn(fn() -> Result<(), String>),
+ StaticBenchFn(fn(&mut Bencher) -> Result<(), String>),
+ DynTestFn(Box<dyn FnOnce() -> Result<(), String> + Send>),
+ DynBenchFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
}
impl TestFn {