4242#![ feature( staged_api) ]
4343#![ feature( question_mark) ]
4444#![ feature( panic_unwind) ]
45+ #![ feature( mpsc_recv_timeout) ]
4546
4647extern crate getopts;
4748extern crate term;
@@ -73,6 +74,8 @@ use std::sync::{Arc, Mutex};
7374use std:: thread;
7475use 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
7780pub 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
835844pub type MonitorMsg = ( TestDesc , TestResult , Vec < u8 > ) ;
@@ -838,6 +847,9 @@ pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
838847fn 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