]> git.proxmox.com Git - rustc.git/blobdiff - vendor/tester/src/lib.rs
Merge tag 'debian/1.52.1+dfsg1-1_exp2' into proxmox/buster
[rustc.git] / vendor / tester / src / lib.rs
index c10c3c16b09ec09d496d40e3ba33b1379ebdff0a..b0aefbb6d3ec5c5b4a1bb0110ebb2bfccc8f871d 100644 (file)
 // running tests while providing a base that other test frameworks may
 // build off of.
 
-// N.B., this is also specified in this crate's Cargo.toml, but libsyntax contains logic specific to
+// N.B., this is also specified in this crate's Cargo.toml, but librustc_ast contains logic specific to
 // this crate, which relies on this attribute (rather than the value of `--crate-name` passed by
 // cargo) to detect this crate.
-
-#![cfg_attr(feature = "asm_black_box", feature(asm))]
-#![cfg_attr(feature = "capture", feature(set_stdio))]
+#![cfg_attr(feature = "asm_black_box", feature(test))]
+#![cfg_attr(feature = "capture", feature(internal_output_capture))]
 
 // Public reexports
-pub use self::ColorConfig::*;
-pub use self::types::*;
-pub use self::types::TestName::*;
-pub use self::options::{ColorConfig, Options, OutputFormat, RunIgnored, ShouldPanic};
-pub use self::bench::Bencher;
+pub use self::bench::{black_box, Bencher};
 pub use self::console::run_tests_console;
+pub use self::options::{ColorConfig, Options, OutputFormat, RunIgnored, ShouldPanic};
+pub use self::types::TestName::*;
+pub use self::types::*;
+pub use self::ColorConfig::*;
 pub use cli::TestOpts;
 
 // Module to be used by rustc to compile tests in libtest
 pub mod test {
     pub use crate::{
+        assert_test_result,
         bench::Bencher,
         cli::{parse_opts, TestOpts},
+        filter_tests,
         helpers::metrics::{Metric, MetricMap},
-        options::{ShouldPanic, Options, RunIgnored, RunStrategy},
+        options::{Options, RunIgnored, RunStrategy, ShouldPanic},
+        run_test, test_main, test_main_static,
         test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk},
-        time::{TestTimeOptions, TestExecTime},
+        time::{TestExecTime, TestTimeOptions},
         types::{
-            DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName,
-            TestDesc, TestDescAndFn, TestName, TestType,
+            DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc,
+            TestDescAndFn, TestName, TestType,
         },
-        assert_test_result, filter_tests, run_test, test_main, test_main_static,
     };
 }
 
 use std::{
-    env,
-    io,
+    env, io,
     io::prelude::Write,
     panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
-    process,
-    process::Command,
+    process::{self, Command},
     sync::mpsc::{channel, Sender},
     sync::{Arc, Mutex},
     thread,
     time::{Duration, Instant},
 };
 
-pub mod stats;
 pub mod bench;
-mod formatters;
 mod cli;
 mod console;
 mod event;
+mod formatters;
 mod helpers;
-mod time;
-mod types;
 mod options;
+pub mod stats;
 mod test_result;
+mod time;
+mod types;
 
 #[cfg(test)]
 mod tests;
 
-use test_result::*;
-use time::TestExecTime;
-use options::{RunStrategy, Concurrent};
 use event::{CompletedTest, TestEvent};
-#[cfg(feature = "capture")]
-use helpers::sink::Sink;
 use helpers::concurrency::get_concurrency;
 use helpers::exit_code::get_exit_code;
+use options::{Concurrent, RunStrategy};
+use test_result::*;
+use time::TestExecTime;
 
 // Process exit code to be used to indicate test failures.
 const ERROR_EXIT_CODE: i32 = 101;
 
-const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE";
+const SECONDARY_TEST_INVOKER_VAR: &str = "__RUST_TEST_INVOKE";
 
 // The default console test runner. It accepts the command line
 // arguments and a vector of test_descs.
@@ -142,12 +139,13 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
     // If we're being run in SpawnedSecondary mode, run the test here. run_test
     // will then exit the process.
     if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) {
+        env::remove_var(SECONDARY_TEST_INVOKER_VAR);
         let test = tests
             .iter()
             .filter(|test| test.desc.name.as_slice() == name)
             .map(make_owned_test)
             .next()
-            .expect("couldn't find a test with the provided name");
+            .unwrap_or_else(|| panic!("couldn't find a test with the provided name '{}'", name));
         let TestDescAndFn { desc, testfn } = test;
         let testfn = match testfn {
             StaticTestFn(f) => f,
@@ -167,14 +165,8 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
 /// This will panic when fed any dynamic tests, because they cannot be cloned.
 fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn {
     match test.testfn {
-        StaticTestFn(f) => TestDescAndFn {
-            testfn: StaticTestFn(f),
-            desc: test.desc.clone(),
-        },
-        StaticBenchFn(f) => TestDescAndFn {
-            testfn: StaticBenchFn(f),
-            desc: test.desc.clone(),
-        },
+        StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: test.desc.clone() },
+        StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: test.desc.clone() },
         _ => panic!("non-static tests passed to test::test_main_static"),
     }
 }
@@ -192,6 +184,9 @@ impl Termination for () {
     }
 }
 
+/// 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) {
     let code = result.report();
     assert_eq!(
@@ -205,7 +200,7 @@ pub fn assert_test_result<T: Termination>(result: T) {
 pub fn run_tests<F>(
     opts: &TestOpts,
     tests: Vec<TestDescAndFn>,
-    mut notify_about_test_event: F
+    mut notify_about_test_event: F,
 ) -> io::Result<()>
 where
     F: FnMut(TestEvent) -> io::Result<()>,
@@ -242,11 +237,9 @@ where
     let event = TestEvent::TeFiltered(filtered_descs);
     notify_about_test_event(event)?;
 
-    let (filtered_tests, filtered_benchs): (Vec<_>, _) =
-        filtered_tests.into_iter().partition(|e| match e.testfn {
-            StaticTestFn(_) | DynTestFn(_) => true,
-            _ => false,
-        });
+    let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests
+        .into_iter()
+        .partition(|e| matches!(e.testfn, StaticTestFn(_) | DynTestFn(_)));
 
     let concurrency = opts.test_threads.unwrap_or_else(get_concurrency);
 
@@ -267,30 +260,20 @@ where
         let now = Instant::now();
         let timed_out = running_tests
             .iter()
-            .filter_map(|(desc, timeout)| {
-                if &now >= timeout {
-                    Some(desc.clone())
-                } else {
-                    None
-                }
-            })
+            .filter_map(|(desc, timeout)| if &now >= timeout { Some(desc.clone()) } else { None })
             .collect();
         for test in &timed_out {
             running_tests.remove(test);
         }
         timed_out
-    };
+    }
 
     fn calc_timeout(running_tests: &TestMap) -> Option<Duration> {
         running_tests.values().min().map(|next_timeout| {
             let now = Instant::now();
-            if *next_timeout >= now {
-                *next_timeout - now
-            } else {
-                Duration::new(0, 0)
-            }
+            if *next_timeout >= now { *next_timeout - now } else { Duration::new(0, 0) }
         })
-    };
+    }
 
     if concurrency == 1 {
         while !remaining.is_empty() {
@@ -376,8 +359,8 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
     };
 
     // Remove tests that don't match the test filter
-    if let Some(ref filter) = opts.filter {
-        filtered.retain(|test| matches_filter(test, filter));
+    if !opts.filters.is_empty() {
+        filtered.retain(|test| opts.filters.iter().any(|filter| matches_filter(test, filter)));
     }
 
     // Skip tests that match any of the skip filters
@@ -391,15 +374,11 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
     // maybe unignore tests
     match opts.run_ignored {
         RunIgnored::Yes => {
-            filtered
-                .iter_mut()
-                .for_each(|test| test.desc.ignore = false);
+            filtered.iter_mut().for_each(|test| test.desc.ignore = false);
         }
         RunIgnored::Only => {
             filtered.retain(|test| test.desc.ignore);
-            filtered
-                .iter_mut()
-                .for_each(|test| test.desc.ignore = false);
+            filtered.iter_mut().for_each(|test| test.desc.ignore = false);
         }
         RunIgnored::No => {}
     }
@@ -424,10 +403,7 @@ pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAnd
                 })),
                 f => f,
             };
-            TestDescAndFn {
-                desc: x.desc,
-                testfn,
-            }
+            TestDescAndFn { desc: x.desc, testfn }
         })
         .collect()
 }
@@ -444,7 +420,8 @@ pub fn run_test(
 
     // Emscripten can catch panics but other wasm targets cannot
     let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No
-        && cfg!(target_arch = "wasm32") && !cfg!(target_os = "emscripten");
+        && cfg!(target_arch = "wasm32")
+        && !cfg!(target_os = "emscripten");
 
     if force_ignore || desc.ignore || ignore_because_no_process_support {
         let message = CompletedTest::new(desc, TrIgnored, None, Vec::new());
@@ -468,20 +445,22 @@ pub fn run_test(
         let concurrency = opts.concurrency;
         let name = desc.name.clone();
 
-        let runtest = move || {
-            match opts.strategy {
-                RunStrategy::InProcess =>
-                    run_test_in_process(
-                        desc,
-                        opts.nocapture,
-                        opts.time.is_some(),
-                        testfn,
-                        monitor_ch,
-                        opts.time
-                    ),
-                RunStrategy::SpawnPrimary =>
-                    spawn_test_subprocess(desc, opts.time.is_some(), monitor_ch, opts.time),
-            }
+        let runtest = move || match opts.strategy {
+            RunStrategy::InProcess => run_test_in_process(
+                desc,
+                opts.nocapture,
+                opts.time.is_some(),
+                testfn,
+                monitor_ch,
+                opts.time,
+            ),
+            RunStrategy::SpawnPrimary => spawn_test_subprocess(
+                desc,
+                opts.nocapture,
+                opts.time.is_some(),
+                monitor_ch,
+                opts.time,
+            ),
         };
 
         // If the platform is single-threaded we're just going to run
@@ -496,12 +475,8 @@ pub fn run_test(
         }
     }
 
-    let test_run_opts = TestRunOpts {
-        strategy,
-        nocapture: opts.nocapture,
-        concurrency,
-        time: opts.time_options
-    };
+    let test_run_opts =
+        TestRunOpts { strategy, nocapture: opts.nocapture, concurrency, time: opts.time_options };
 
     match testfn {
         DynBenchFn(bencher) => {
@@ -512,9 +487,7 @@ pub fn run_test(
         }
         StaticBenchFn(benchfn) => {
             // Benchmarks aren't expected to panic, so we run them all in-process.
-            crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| {
-                (benchfn.clone())(harness)
-            });
+            crate::bench::benchmark(desc, monitor_ch, opts.nocapture, benchfn);
         }
         DynTestFn(f) => {
             match strategy {
@@ -540,7 +513,10 @@ 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()
+    f();
+
+    // prevent this frame from being tail-call optimised away
+    black_box(());
 }
 
 fn run_test_in_process(
@@ -554,27 +530,12 @@ fn run_test_in_process(
     // Buffer for capturing standard I/O
     let data = Arc::new(Mutex::new(Vec::new()));
 
-    let _oldio = if !nocapture {
-        Some((
-            #[cfg(feature = "capture")]
-            io::set_print(Some(Sink::new_boxed(&data))),
-            #[cfg(not(feature = "capture"))]
-            (),
-
-            #[cfg(feature = "capture")]
-            io::set_panic(Some(Sink::new_boxed(&data))),
-            #[cfg(not(feature = "capture"))]
-            (),
-        ))
-    } else {
-        None
-    };
+    if !nocapture {
+        #[cfg(feature = "capture")]
+        io::set_output_capture(Some(data.clone()));
+    }
 
-    let start = if report_time {
-        Some(Instant::now())
-    } else {
-        None
-    };
+    let start = if report_time { Some(Instant::now()) } else { None };
     let result = catch_unwind(AssertUnwindSafe(testfn));
     let exec_time = start.map(|start| {
         let duration = start.elapsed();
@@ -582,24 +543,20 @@ fn run_test_in_process(
     });
 
     #[cfg(feature = "capture")]
-    {
-        if let Some((printio, panicio)) = _oldio {
-            io::set_print(printio);
-            io::set_panic(panicio);
-        }
-    }
+    io::set_output_capture(None);
 
     let test_result = match result {
         Ok(()) => calc_result(&desc, Ok(()), &time_opts, &exec_time),
         Err(e) => calc_result(&desc, Err(e.as_ref()), &time_opts, &exec_time),
     };
-    let stdout = data.lock().unwrap().to_vec();
-    let message = CompletedTest::new(desc.clone(), test_result, exec_time, stdout);
+    let stdout = data.lock().unwrap_or_else(|e| e.into_inner()).to_vec();
+    let message = CompletedTest::new(desc, test_result, exec_time, stdout);
     monitor_ch.send(message).unwrap();
 }
 
 fn spawn_test_subprocess(
     desc: TestDesc,
+    nocapture: bool,
     report_time: bool,
     monitor_ch: Sender<CompletedTest>,
     time_opts: Option<time::TestTimeOptions>,
@@ -608,20 +565,21 @@ fn spawn_test_subprocess(
         let args = env::args().collect::<Vec<_>>();
         let current_exe = &args[0];
 
-        let start = if report_time {
-            Some(Instant::now())
-        } else {
-            None
+        let mut command = Command::new(current_exe);
+        command.env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice());
+        if nocapture {
+            command.stdout(process::Stdio::inherit());
+            command.stderr(process::Stdio::inherit());
+        }
+
+        let start = if report_time { Some(Instant::now()) } else { None };
+        let output = match command.output() {
+            Ok(out) => out,
+            Err(e) => {
+                let err = format!("Failed to spawn {} as child for test: {:?}", args[0], e);
+                return (TrFailed, err.into_bytes(), None);
+            }
         };
-        let output = match Command::new(current_exe)
-            .env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice())
-            .output() {
-                Ok(out) => out,
-                Err(e) => {
-                    let err = format!("Failed to spawn {} as child for test: {:?}", args[0], e);
-                    return (TrFailed, err.into_bytes(), None);
-                }
-            };
         let exec_time = start.map(|start| {
             let duration = start.elapsed();
             TestExecTime(duration)
@@ -646,14 +604,11 @@ fn spawn_test_subprocess(
         (result, test_output, exec_time)
     })();
 
-    let message = CompletedTest::new(desc.clone(), result, exec_time, test_output);
+    let message = CompletedTest::new(desc, result, exec_time, test_output);
     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() + Send>) -> ! {
     let builtin_panic_hook = panic::take_hook();
     let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| {
         let test_result = match panic_info {