diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 8be32183fe780..524658bce139d 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -26,6 +26,10 @@ pub struct TestOpts { pub test_threads: Option, pub skip: Vec, pub time_options: Option, + /// Stop at first failing test. + /// May run a few more tests due to threading, but will + /// abort as soon as possible. + pub fail_fast: bool, pub options: Options, } @@ -296,6 +300,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { skip, time_options, options, + fail_fast: false, }; Ok(test_opts) diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 8cb88016b23ad..a3c39f71f08b8 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -293,7 +293,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; st.exec_time = start_time.map(|t| TestSuiteExecTime(t.elapsed())); - assert!(st.current_test_count() == st.total); + assert!(opts.fail_fast || st.current_test_count() == st.total); out.write_run_finish(&st) } diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 27320e8dbc5ad..256c9e8d141e0 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -384,8 +384,17 @@ where let mut completed_test = rx.recv().unwrap(); RunningTest { join_handle }.join(&mut completed_test); + let fail_fast = match completed_test.result { + TrIgnored | TrOk | TrBench(_) => false, + TrFailed | TrFailedMsg(_) | TrTimedFail => opts.fail_fast, + }; + let event = TestEvent::TeResult(completed_test); notify_about_test_event(event)?; + + if fail_fast { + return Ok(()); + } } } else { while pending > 0 || !remaining.is_empty() { @@ -431,9 +440,20 @@ where let running_test = running_tests.remove(&completed_test.id).unwrap(); running_test.join(&mut completed_test); + let fail_fast = match completed_test.result { + TrIgnored | TrOk | TrBench(_) => false, + TrFailed | TrFailedMsg(_) | TrTimedFail => opts.fail_fast, + }; + let event = TestEvent::TeResult(completed_test); notify_about_test_event(event)?; pending -= 1; + + if fail_fast { + // Prevent remaining test threads from panicking + std::mem::forget(rx); + return Ok(()); + } } } diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 7b2e6707f9d11..3a0260f86cf5d 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -51,6 +51,7 @@ impl TestOpts { skip: vec![], time_options: None, options: Options::new(), + fail_fast: false, } } } diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 519da685f940a..91c701a5ddd2e 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -514,6 +514,7 @@ pub fn test_opts(config: &Config) -> test::TestOpts { options: test::Options::new(), time_options: None, force_run_in_process: false, + fail_fast: std::env::var_os("RUSTC_TEST_FAIL_FAST").is_some(), } }