Skip to content

Commit 9006913

Browse files
author
Felix Rath
committed
add warning timeout for tests that run >1min
this makes it easier to identify hanging tests
1 parent ecdd51b commit 9006913

File tree

1 file changed

+47
-1
lines changed

1 file changed

+47
-1
lines changed

src/libtest/lib.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#![feature(staged_api)]
4343
#![feature(question_mark)]
4444
#![feature(panic_unwind)]
45+
#![feature(mpsc_recv_timeout)]
4546

4647
extern crate getopts;
4748
extern crate term;
@@ -73,6 +74,8 @@ use std::sync::{Arc, Mutex};
7374
use std::thread;
7475
use std::time::{Instant, Duration};
7576

77+
const TEST_WARN_TIMEOUT_S: u64 = 60;
78+
7679
// to be used by rustc to compile tests in libtest
7780
pub mod test {
7881
pub use {Bencher, TestName, TestResult, TestDesc, TestDescAndFn, TestOpts, TrFailed,
@@ -592,6 +595,10 @@ impl<T: Write> ConsoleTestState<T> {
592595
}
593596
}
594597

598+
pub fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
599+
self.write_plain(&format!("test {} has been running for over {} seconds\n", desc.name, TEST_WARN_TIMEOUT_S))
600+
}
601+
595602
pub fn write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> {
596603
match self.log_out {
597604
None => Ok(()),
@@ -709,6 +716,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
709716
match (*event).clone() {
710717
TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
711718
TeWait(ref test, padding) => st.write_test_start(test, padding),
719+
TeTimeout(ref test) => st.write_timeout(test),
712720
TeResult(test, result, stdout) => {
713721
st.write_log(&test, &result)?;
714722
st.write_result(&result)?;
@@ -830,6 +838,7 @@ enum TestEvent {
830838
TeFiltered(Vec<TestDesc>),
831839
TeWait(TestDesc, NamePadding),
832840
TeResult(TestDesc, TestResult, Vec<u8>),
841+
TeTimeout(TestDesc),
833842
}
834843

835844
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
@@ -838,6 +847,9 @@ pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
838847
fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
839848
where F: FnMut(TestEvent) -> io::Result<()>
840849
{
850+
use std::collections::HashMap;
851+
use std::sync::mpsc::RecvTimeoutError;
852+
841853
let mut filtered_tests = filter_tests(opts, tests);
842854
if !opts.bench_benchmarks {
843855
filtered_tests = convert_benchmarks_to_tests(filtered_tests);
@@ -867,6 +879,8 @@ fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) ->
867879

868880
let (tx, rx) = channel::<MonitorMsg>();
869881

882+
let mut running_tests: HashMap<TestDesc, Duration> = HashMap::new();
883+
870884
while pending > 0 || !remaining.is_empty() {
871885
while pending < concurrency && !remaining.is_empty() {
872886
let test = remaining.pop().unwrap();
@@ -876,11 +890,43 @@ fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) ->
876890
// that hang forever.
877891
callback(TeWait(test.desc.clone(), test.testfn.padding()))?;
878892
}
893+
running_tests.insert(test.desc.clone(), Duration::from_secs(TEST_WARN_TIMEOUT_S));
879894
run_test(opts, !opts.run_tests, test, tx.clone());
880895
pending += 1;
881896
}
882897

883-
let (desc, result, stdout) = rx.recv().unwrap();
898+
let mut res;
899+
if let Some(min_timeout) = running_tests.values().min().cloned() {
900+
loop {
901+
let before = Instant::now();
902+
res = rx.recv_timeout(min_timeout);
903+
let elapsed = Instant::now() - before;
904+
905+
let mut to_remove = Vec::new();
906+
for (desc, time_left) in &mut running_tests {
907+
if *time_left >= elapsed {
908+
*time_left -= elapsed;
909+
} else {
910+
to_remove.push(desc.clone());
911+
callback(TeTimeout(desc.clone()))?;
912+
}
913+
}
914+
915+
for rem in to_remove {
916+
running_tests.remove(&rem);
917+
}
918+
919+
if res != Err(RecvTimeoutError::Timeout) {
920+
break;
921+
}
922+
}
923+
} else {
924+
res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected);
925+
}
926+
927+
let (desc, result, stdout) = res.unwrap();
928+
running_tests.remove(&desc);
929+
884930
if concurrency != 1 {
885931
callback(TeWait(desc.clone(), PadNone))?;
886932
}

0 commit comments

Comments
 (0)