about summary refs log tree commit diff
path: root/src/libtest
diff options
context:
space:
mode:
authorFelix Rath <felixr@archlinux.info>2016-08-06 00:53:10 +0200
committerFelix Rath <felixr@archlinux.info>2016-08-06 11:52:10 +0200
commit9006913bf5c1ecfebbffd5c3417bd26c3d6c4aeb (patch)
treeb3bc60306af5bdac979e86f143f93ef1a346c8a6 /src/libtest
parentecdd51b7bb7fd993acd2ff5dbd72209244b1e4aa (diff)
downloadrust-9006913bf5c1ecfebbffd5c3417bd26c3d6c4aeb.tar.gz
rust-9006913bf5c1ecfebbffd5c3417bd26c3d6c4aeb.zip
add warning timeout for tests that run >1min
this makes it easier to identify hanging tests
Diffstat (limited to 'src/libtest')
-rw-r--r--src/libtest/lib.rs48
1 files changed, 47 insertions, 1 deletions
diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs
index 248f6f98650..106c14850ca 100644
--- a/src/libtest/lib.rs
+++ b/src/libtest/lib.rs
@@ -42,6 +42,7 @@
 #![feature(staged_api)]
 #![feature(question_mark)]
 #![feature(panic_unwind)]
+#![feature(mpsc_recv_timeout)]
 
 extern crate getopts;
 extern crate term;
@@ -73,6 +74,8 @@ use std::sync::{Arc, Mutex};
 use std::thread;
 use std::time::{Instant, Duration};
 
+const TEST_WARN_TIMEOUT_S: u64 = 60;
+
 // to be used by rustc to compile tests in libtest
 pub mod test {
     pub use {Bencher, TestName, TestResult, TestDesc, TestDescAndFn, TestOpts, TrFailed,
@@ -592,6 +595,10 @@ impl<T: Write> ConsoleTestState<T> {
         }
     }
 
+    pub fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
+        self.write_plain(&format!("test {} has been running for over {} seconds\n", desc.name, TEST_WARN_TIMEOUT_S))
+    }
+
     pub fn write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> {
         match self.log_out {
             None => Ok(()),
@@ -709,6 +716,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
         match (*event).clone() {
             TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
             TeWait(ref test, padding) => st.write_test_start(test, padding),
+            TeTimeout(ref test) => st.write_timeout(test),
             TeResult(test, result, stdout) => {
                 st.write_log(&test, &result)?;
                 st.write_result(&result)?;
@@ -830,6 +838,7 @@ enum TestEvent {
     TeFiltered(Vec<TestDesc>),
     TeWait(TestDesc, NamePadding),
     TeResult(TestDesc, TestResult, Vec<u8>),
+    TeTimeout(TestDesc),
 }
 
 pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
@@ -838,6 +847,9 @@ pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
 fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
     where F: FnMut(TestEvent) -> io::Result<()>
 {
+    use std::collections::HashMap;
+    use std::sync::mpsc::RecvTimeoutError;
+
     let mut filtered_tests = filter_tests(opts, tests);
     if !opts.bench_benchmarks {
         filtered_tests = convert_benchmarks_to_tests(filtered_tests);
@@ -867,6 +879,8 @@ fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) ->
 
     let (tx, rx) = channel::<MonitorMsg>();
 
+    let mut running_tests: HashMap<TestDesc, Duration> = HashMap::new();
+
     while pending > 0 || !remaining.is_empty() {
         while pending < concurrency && !remaining.is_empty() {
             let test = remaining.pop().unwrap();
@@ -876,11 +890,43 @@ fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) ->
                 // that hang forever.
                 callback(TeWait(test.desc.clone(), test.testfn.padding()))?;
             }
+            running_tests.insert(test.desc.clone(), Duration::from_secs(TEST_WARN_TIMEOUT_S));
             run_test(opts, !opts.run_tests, test, tx.clone());
             pending += 1;
         }
 
-        let (desc, result, stdout) = rx.recv().unwrap();
+        let mut res;
+        if let Some(min_timeout) = running_tests.values().min().cloned() {
+            loop {
+                let before = Instant::now();
+                res = rx.recv_timeout(min_timeout);
+                let elapsed = Instant::now() - before;
+
+                let mut to_remove = Vec::new();
+                for (desc, time_left) in &mut running_tests {
+                    if *time_left >= elapsed {
+                        *time_left -= elapsed;
+                    } else {
+                        to_remove.push(desc.clone());
+                        callback(TeTimeout(desc.clone()))?;
+                    }
+                }
+
+                for rem in to_remove {
+                    running_tests.remove(&rem);
+                }
+
+                if res != Err(RecvTimeoutError::Timeout) {
+                    break;
+                }
+            }
+        } else {
+            res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected);
+        }
+
+        let (desc, result, stdout) = res.unwrap();
+        running_tests.remove(&desc);
+
         if concurrency != 1 {
             callback(TeWait(desc.clone(), PadNone))?;
         }