@@ -17,7 +17,7 @@ use std::io::prelude::*;
17
17
use std:: io;
18
18
use std:: panic:: { self , AssertUnwindSafe } ;
19
19
use std:: path:: PathBuf ;
20
- use std:: process:: Command ;
20
+ use std:: process:: { self , Command } ;
21
21
use std:: str;
22
22
use std:: sync:: { Arc , Mutex } ;
23
23
use syntax:: symbol:: sym;
@@ -160,13 +160,45 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
160
160
opts
161
161
}
162
162
163
- fn run_test ( test : & str , cratename : & str , filename : & FileName , line : usize ,
164
- cfgs : Vec < String > , libs : Vec < SearchPath > ,
165
- cg : CodegenOptions , externs : Externs ,
166
- should_panic : bool , no_run : bool , as_test_harness : bool ,
167
- compile_fail : bool , mut error_codes : Vec < String > , opts : & TestOptions ,
168
- maybe_sysroot : Option < PathBuf > , linker : Option < PathBuf > , edition : Edition ,
169
- persist_doctests : Option < PathBuf > ) {
163
+ /// Documentation test failure modes.
164
+ enum TestFailure {
165
+ /// The test failed to compile.
166
+ CompileError ,
167
+ /// The test is marked `compile_fail` but compiled successfully.
168
+ UnexpectedCompilePass ,
169
+ /// The test failed to compile (as expected) but the compiler output did not contain all
170
+ /// expected error codes.
171
+ MissingErrorCodes ( Vec < String > ) ,
172
+ /// The test binary was unable to be executed.
173
+ ExecutionError ( io:: Error ) ,
174
+ /// The test binary exited with a non-zero exit code.
175
+ ///
176
+ /// This typically means an assertion in the test failed or another form of panic occurred.
177
+ ExecutionFailure ( process:: Output ) ,
178
+ /// The test is marked `should_panic` but the test binary executed successfully.
179
+ UnexpectedRunPass ,
180
+ }
181
+
182
+ fn run_test (
183
+ test : & str ,
184
+ cratename : & str ,
185
+ filename : & FileName ,
186
+ line : usize ,
187
+ cfgs : Vec < String > ,
188
+ libs : Vec < SearchPath > ,
189
+ cg : CodegenOptions ,
190
+ externs : Externs ,
191
+ should_panic : bool ,
192
+ no_run : bool ,
193
+ as_test_harness : bool ,
194
+ compile_fail : bool ,
195
+ mut error_codes : Vec < String > ,
196
+ opts : & TestOptions ,
197
+ maybe_sysroot : Option < PathBuf > ,
198
+ linker : Option < PathBuf > ,
199
+ edition : Edition ,
200
+ persist_doctests : Option < PathBuf > ,
201
+ ) -> Result < ( ) , TestFailure > {
170
202
let ( test, line_offset) = match panic:: catch_unwind ( || {
171
203
make_test ( test, Some ( cratename) , as_test_harness, opts, edition)
172
204
} ) {
@@ -307,44 +339,43 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
307
339
308
340
match ( compile_result, compile_fail) {
309
341
( Ok ( ( ) ) , true ) => {
310
- panic ! ( "test compiled while it wasn't supposed to" )
342
+ return Err ( TestFailure :: UnexpectedCompilePass ) ;
311
343
}
312
344
( Ok ( ( ) ) , false ) => { }
313
345
( Err ( _) , true ) => {
314
- if error_codes. len ( ) > 0 {
346
+ if ! error_codes. is_empty ( ) {
315
347
let out = String :: from_utf8 ( data. lock ( ) . unwrap ( ) . to_vec ( ) ) . unwrap ( ) ;
316
348
error_codes. retain ( |err| !out. contains ( err) ) ;
349
+
350
+ if !error_codes. is_empty ( ) {
351
+ return Err ( TestFailure :: MissingErrorCodes ( error_codes) ) ;
352
+ }
317
353
}
318
354
}
319
355
( Err ( _) , false ) => {
320
- panic ! ( "couldn't compile the test" )
356
+ return Err ( TestFailure :: CompileError ) ;
321
357
}
322
358
}
323
359
324
- if error_codes . len ( ) > 0 {
325
- panic ! ( "Some expected error codes were not found: {:?}" , error_codes ) ;
360
+ if no_run {
361
+ return Ok ( ( ) ) ;
326
362
}
327
363
328
- if no_run { return }
329
-
330
364
// Run the code!
331
365
let mut cmd = Command :: new ( output_file) ;
332
366
333
367
match cmd. output ( ) {
334
- Err ( e) => panic ! ( "couldn't run the test: {}{}" , e,
335
- if e. kind( ) == io:: ErrorKind :: PermissionDenied {
336
- " - maybe your tempdir is mounted with noexec?"
337
- } else { "" } ) ,
368
+ Err ( e) => return Err ( TestFailure :: ExecutionError ( e) ) ,
338
369
Ok ( out) => {
339
370
if should_panic && out. status . success ( ) {
340
- panic ! ( "test executable succeeded when it should have failed" ) ;
371
+ return Err ( TestFailure :: UnexpectedRunPass ) ;
341
372
} else if !should_panic && !out. status . success ( ) {
342
- panic ! ( "test executable failed:\n {}\n {}\n " ,
343
- str :: from_utf8( & out. stdout) . unwrap_or( "" ) ,
344
- str :: from_utf8( & out. stderr) . unwrap_or( "" ) ) ;
373
+ return Err ( TestFailure :: ExecutionFailure ( out) ) ;
345
374
}
346
375
}
347
376
}
377
+
378
+ Ok ( ( ) )
348
379
}
349
380
350
381
/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
@@ -711,7 +742,7 @@ impl Tester for Collector {
711
742
allow_fail : config. allow_fail ,
712
743
} ,
713
744
testfn : testing:: DynTestFn ( box move || {
714
- run_test (
745
+ let res = run_test (
715
746
& test,
716
747
& cratename,
717
748
& filename,
@@ -730,7 +761,65 @@ impl Tester for Collector {
730
761
linker,
731
762
edition,
732
763
persist_doctests
733
- )
764
+ ) ;
765
+
766
+ if let Err ( err) = res {
767
+ match err {
768
+ TestFailure :: CompileError => {
769
+ eprint ! ( "Couldn't compile the test." ) ;
770
+ }
771
+ TestFailure :: UnexpectedCompilePass => {
772
+ eprint ! ( "Test compiled successfully, but it's marked `compile_fail`." ) ;
773
+ }
774
+ TestFailure :: UnexpectedRunPass => {
775
+ eprint ! ( "Test executable succeeded, but it's marked `should_panic`." ) ;
776
+ }
777
+ TestFailure :: MissingErrorCodes ( codes) => {
778
+ eprint ! ( "Some expected error codes were not found: {:?}" , codes) ;
779
+ }
780
+ TestFailure :: ExecutionError ( err) => {
781
+ eprint ! ( "Couldn't run the test: {}" , err) ;
782
+ if err. kind ( ) == io:: ErrorKind :: PermissionDenied {
783
+ eprint ! ( " - maybe your tempdir is mounted with noexec?" ) ;
784
+ }
785
+ }
786
+ TestFailure :: ExecutionFailure ( out) => {
787
+ let reason = if let Some ( code) = out. status . code ( ) {
788
+ format ! ( "exit code {}" , code)
789
+ } else {
790
+ String :: from ( "terminated by signal" )
791
+ } ;
792
+
793
+ eprintln ! ( "Test executable failed ({})." , reason) ;
794
+
795
+ // FIXME(#12309): An unfortunate side-effect of capturing the test
796
+ // executable's output is that the relative ordering between the test's
797
+ // stdout and stderr is lost. However, this is better than the
798
+ // alternative: if the test executable inherited the parent's I/O
799
+ // handles the output wouldn't be captured at all, even on success.
800
+ //
801
+ // The ordering could be preserved if the test process' stderr was
802
+ // redirected to stdout, but that functionality does not exist in the
803
+ // standard library, so it may not be portable enough.
804
+ let stdout = str:: from_utf8 ( & out. stdout ) . unwrap_or_default ( ) ;
805
+ let stderr = str:: from_utf8 ( & out. stderr ) . unwrap_or_default ( ) ;
806
+
807
+ if !stdout. is_empty ( ) || !stderr. is_empty ( ) {
808
+ eprintln ! ( ) ;
809
+
810
+ if !stdout. is_empty ( ) {
811
+ eprintln ! ( "stdout:\n {}" , stdout) ;
812
+ }
813
+
814
+ if !stderr. is_empty ( ) {
815
+ eprintln ! ( "stderr:\n {}" , stderr) ;
816
+ }
817
+ }
818
+ }
819
+ }
820
+
821
+ panic:: resume_unwind ( box ( ) ) ;
822
+ }
734
823
} ) ,
735
824
} ) ;
736
825
}
0 commit comments