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,12 @@ 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 " ,
600+ desc. name,
601+ TEST_WARN_TIMEOUT_S ) )
602+ }
603+
595604 pub fn write_log ( & mut self , test : & TestDesc , result : & TestResult ) -> io:: Result < ( ) > {
596605 match self . log_out {
597606 None => Ok ( ( ) ) ,
@@ -709,6 +718,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
709718 match ( * event) . clone ( ) {
710719 TeFiltered ( ref filtered_tests) => st. write_run_start ( filtered_tests. len ( ) ) ,
711720 TeWait ( ref test, padding) => st. write_test_start ( test, padding) ,
721+ TeTimeout ( ref test) => st. write_timeout ( test) ,
712722 TeResult ( test, result, stdout) => {
713723 st. write_log ( & test, & result) ?;
714724 st. write_result ( & result) ?;
@@ -830,6 +840,7 @@ enum TestEvent {
830840 TeFiltered ( Vec < TestDesc > ) ,
831841 TeWait ( TestDesc , NamePadding ) ,
832842 TeResult ( TestDesc , TestResult , Vec < u8 > ) ,
843+ TeTimeout ( TestDesc ) ,
833844}
834845
835846pub type MonitorMsg = ( TestDesc , TestResult , Vec < u8 > ) ;
@@ -838,6 +849,9 @@ pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
838849fn run_tests < F > ( opts : & TestOpts , tests : Vec < TestDescAndFn > , mut callback : F ) -> io:: Result < ( ) >
839850 where F : FnMut ( TestEvent ) -> io:: Result < ( ) >
840851{
852+ use std:: collections:: HashMap ;
853+ use std:: sync:: mpsc:: RecvTimeoutError ;
854+
841855 let mut filtered_tests = filter_tests ( opts, tests) ;
842856 if !opts. bench_benchmarks {
843857 filtered_tests = convert_benchmarks_to_tests ( filtered_tests) ;
@@ -867,6 +881,29 @@ fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) ->
867881
868882 let ( tx, rx) = channel :: < MonitorMsg > ( ) ;
869883
884+ let mut running_tests: HashMap < TestDesc , Instant > = HashMap :: new ( ) ;
885+
886+ fn get_timed_out_tests ( running_tests : & mut HashMap < TestDesc , Instant > ) -> Vec < TestDesc > {
887+ let now = Instant :: now ( ) ;
888+ let timed_out = running_tests. iter ( )
889+ . filter_map ( |( desc, timeout) | if & now >= timeout { Some ( desc. clone ( ) ) } else { None } )
890+ . collect ( ) ;
891+ for test in & timed_out {
892+ running_tests. remove ( test) ;
893+ }
894+ timed_out
895+ } ;
896+
897+ fn calc_timeout ( running_tests : & HashMap < TestDesc , Instant > ) -> Option < Duration > {
898+ running_tests. values ( ) . min ( ) . map ( |next_timeout| {
899+ let now = Instant :: now ( ) ;
900+ if * next_timeout >= now {
901+ * next_timeout - now
902+ } else {
903+ Duration :: new ( 0 , 0 )
904+ } } )
905+ } ;
906+
870907 while pending > 0 || !remaining. is_empty ( ) {
871908 while pending < concurrency && !remaining. is_empty ( ) {
872909 let test = remaining. pop ( ) . unwrap ( ) ;
@@ -876,11 +913,31 @@ fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) ->
876913 // that hang forever.
877914 callback ( TeWait ( test. desc . clone ( ) , test. testfn . padding ( ) ) ) ?;
878915 }
916+ let timeout = Instant :: now ( ) + Duration :: from_secs ( TEST_WARN_TIMEOUT_S ) ;
917+ running_tests. insert ( test. desc . clone ( ) , timeout) ;
879918 run_test ( opts, !opts. run_tests , test, tx. clone ( ) ) ;
880919 pending += 1 ;
881920 }
882921
883- let ( desc, result, stdout) = rx. recv ( ) . unwrap ( ) ;
922+ let mut res;
923+ loop {
924+ if let Some ( timeout) = calc_timeout ( & running_tests) {
925+ res = rx. recv_timeout ( timeout) ;
926+ for test in get_timed_out_tests ( & mut running_tests) {
927+ callback ( TeTimeout ( test) ) ?;
928+ }
929+ if res != Err ( RecvTimeoutError :: Timeout ) {
930+ break ;
931+ }
932+ } else {
933+ res = rx. recv ( ) . map_err ( |_| RecvTimeoutError :: Disconnected ) ;
934+ break ;
935+ }
936+ }
937+
938+ let ( desc, result, stdout) = res. unwrap ( ) ;
939+ running_tests. remove ( & desc) ;
940+
884941 if concurrency != 1 {
885942 callback ( TeWait ( desc. clone ( ) , PadNone ) ) ?;
886943 }
0 commit comments