11//! Run all tests in a project, similar to `cargo test`, but using the mir interpreter.
22
3+ use std:: time:: { Duration , Instant } ;
34use std:: { cell:: RefCell , fs:: read_to_string, panic:: AssertUnwindSafe , path:: PathBuf } ;
45
56use hir:: { Change , Crate } ;
67use ide:: { AnalysisHost , DiagnosticCode , DiagnosticsConfig } ;
8+ use itertools:: Either ;
79use profile:: StopWatch ;
810use project_model:: target_data_layout:: RustcDataLayoutConfig ;
911use project_model:: { target_data_layout, CargoConfig , ProjectWorkspace , RustLibSource , Sysroot } ;
@@ -132,15 +134,39 @@ impl Tester {
132134 self . host . apply_change ( change) ;
133135 let diagnostic_config = DiagnosticsConfig :: test_sample ( ) ;
134136
137+ let res = std:: thread:: scope ( |s| {
138+ let worker = s. spawn ( {
139+ let diagnostic_config = & diagnostic_config;
140+ let main = std:: thread:: current ( ) ;
141+ let analysis = self . host . analysis ( ) ;
142+ let root_file = self . root_file ;
143+ move || {
144+ let res = analysis. diagnostics (
145+ diagnostic_config,
146+ ide:: AssistResolveStrategy :: None ,
147+ root_file,
148+ ) ;
149+ main. unpark ( ) ;
150+ res
151+ }
152+ } ) ;
153+
154+ let timeout = Duration :: from_secs ( 30 ) ;
155+ let now = Instant :: now ( ) ;
156+ while now. elapsed ( ) <= timeout {
157+ std:: thread:: park_timeout ( timeout - now. elapsed ( ) ) ;
158+ }
159+
160+ if !worker. is_finished ( ) {
161+ // attempt to cancel the worker, won't work for chalk hangs unfortunately
162+ self . host . request_cancellation ( ) ;
163+ }
164+ worker. join ( )
165+ } ) ;
135166 let mut actual = FxHashMap :: default ( ) ;
136- let panicked = match std:: panic:: catch_unwind ( || {
137- self . host
138- . analysis ( )
139- . diagnostics ( & diagnostic_config, ide:: AssistResolveStrategy :: None , self . root_file )
140- . unwrap ( )
141- } ) {
142- Err ( e) => Some ( e) ,
143- Ok ( diags) => {
167+ let panicked = match res {
168+ Err ( e) => Some ( Either :: Left ( e) ) ,
169+ Ok ( Ok ( diags) ) => {
144170 for diag in diags {
145171 if !matches ! ( diag. code, DiagnosticCode :: RustcHardError ( _) ) {
146172 continue ;
@@ -152,21 +178,27 @@ impl Tester {
152178 }
153179 None
154180 }
181+ Ok ( Err ( e) ) => Some ( Either :: Right ( e) ) ,
155182 } ;
156183 // Ignore tests with diagnostics that we don't emit.
157184 ignore_test |= expected. keys ( ) . any ( |k| !SUPPORTED_DIAGNOSTICS . contains ( k) ) ;
158185 if ignore_test {
159186 println ! ( "{p:?} IGNORE" ) ;
160187 self . ignore_count += 1 ;
161188 } else if let Some ( panic) = panicked {
162- if let Some ( msg) = panic
163- . downcast_ref :: < String > ( )
164- . map ( String :: as_str)
165- . or_else ( || panic. downcast_ref :: < & str > ( ) . copied ( ) )
166- {
167- println ! ( "{msg:?} " )
189+ match panic {
190+ Either :: Left ( panic) => {
191+ if let Some ( msg) = panic
192+ . downcast_ref :: < String > ( )
193+ . map ( String :: as_str)
194+ . or_else ( || panic. downcast_ref :: < & str > ( ) . copied ( ) )
195+ {
196+ println ! ( "{msg:?} " )
197+ }
198+ println ! ( "{p:?} PANIC" ) ;
199+ }
200+ Either :: Right ( _) => println ! ( "{p:?} CANCELLED" ) ,
168201 }
169- println ! ( "PANIC" ) ;
170202 self . fail_count += 1 ;
171203 } else if actual == expected {
172204 println ! ( "{p:?} PASS" ) ;
0 commit comments