1
1
//! Run all tests in a project, similar to `cargo test`, but using the mir interpreter.
2
2
3
+ use std:: convert:: identity;
4
+ use std:: thread:: Builder ;
5
+ use std:: time:: { Duration , Instant } ;
3
6
use std:: { cell:: RefCell , fs:: read_to_string, panic:: AssertUnwindSafe , path:: PathBuf } ;
4
7
5
8
use hir:: { Change , Crate } ;
6
9
use ide:: { AnalysisHost , DiagnosticCode , DiagnosticsConfig } ;
10
+ use itertools:: Either ;
7
11
use profile:: StopWatch ;
8
12
use project_model:: target_data_layout:: RustcDataLayoutConfig ;
9
13
use project_model:: { target_data_layout, CargoConfig , ProjectWorkspace , RustLibSource , Sysroot } ;
@@ -100,6 +104,7 @@ impl Tester {
100
104
}
101
105
102
106
fn test ( & mut self , p : PathBuf ) {
107
+ println ! ( "{}" , p. display( ) ) ;
103
108
if p. parent ( ) . unwrap ( ) . file_name ( ) . unwrap ( ) == "auxiliary" {
104
109
// These are not tests
105
110
return ;
@@ -132,15 +137,44 @@ impl Tester {
132
137
self . host . apply_change ( change) ;
133
138
let diagnostic_config = DiagnosticsConfig :: test_sample ( ) ;
134
139
140
+ let res = std:: thread:: scope ( |s| {
141
+ let worker = Builder :: new ( )
142
+ . stack_size ( 40 * 1024 * 1024 )
143
+ . spawn_scoped ( s, {
144
+ let diagnostic_config = & diagnostic_config;
145
+ let main = std:: thread:: current ( ) ;
146
+ let analysis = self . host . analysis ( ) ;
147
+ let root_file = self . root_file ;
148
+ move || {
149
+ let res = std:: panic:: catch_unwind ( move || {
150
+ analysis. diagnostics (
151
+ diagnostic_config,
152
+ ide:: AssistResolveStrategy :: None ,
153
+ root_file,
154
+ )
155
+ } ) ;
156
+ main. unpark ( ) ;
157
+ res
158
+ }
159
+ } )
160
+ . unwrap ( ) ;
161
+
162
+ let timeout = Duration :: from_secs ( 5 ) ;
163
+ let now = Instant :: now ( ) ;
164
+ while now. elapsed ( ) <= timeout && !worker. is_finished ( ) {
165
+ std:: thread:: park_timeout ( timeout - now. elapsed ( ) ) ;
166
+ }
167
+
168
+ if !worker. is_finished ( ) {
169
+ // attempt to cancel the worker, won't work for chalk hangs unfortunately
170
+ self . host . request_cancellation ( ) ;
171
+ }
172
+ worker. join ( ) . and_then ( identity)
173
+ } ) ;
135
174
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) => {
175
+ let panicked = match res {
176
+ Err ( e) => Some ( Either :: Left ( e) ) ,
177
+ Ok ( Ok ( diags) ) => {
144
178
for diag in diags {
145
179
if !matches ! ( diag. code, DiagnosticCode :: RustcHardError ( _) ) {
146
180
continue ;
@@ -152,21 +186,27 @@ impl Tester {
152
186
}
153
187
None
154
188
}
189
+ Ok ( Err ( e) ) => Some ( Either :: Right ( e) ) ,
155
190
} ;
156
191
// Ignore tests with diagnostics that we don't emit.
157
192
ignore_test |= expected. keys ( ) . any ( |k| !SUPPORTED_DIAGNOSTICS . contains ( k) ) ;
158
193
if ignore_test {
159
194
println ! ( "{p:?} IGNORE" ) ;
160
195
self . ignore_count += 1 ;
161
196
} 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:?} " )
197
+ match panic {
198
+ Either :: Left ( panic) => {
199
+ if let Some ( msg) = panic
200
+ . downcast_ref :: < String > ( )
201
+ . map ( String :: as_str)
202
+ . or_else ( || panic. downcast_ref :: < & str > ( ) . copied ( ) )
203
+ {
204
+ println ! ( "{msg:?} " )
205
+ }
206
+ println ! ( "{p:?} PANIC" ) ;
207
+ }
208
+ Either :: Right ( _) => println ! ( "{p:?} CANCELLED" ) ,
168
209
}
169
- println ! ( "PANIC" ) ;
170
210
self . fail_count += 1 ;
171
211
} else if actual == expected {
172
212
println ! ( "{p:?} PASS" ) ;
0 commit comments