@@ -17,7 +17,7 @@ use std::io::prelude::*;
1717use std:: io;
1818use std:: panic:: { self , AssertUnwindSafe } ;
1919use std:: path:: PathBuf ;
20- use std:: process:: Command ;
20+ use std:: process:: { self , Command } ;
2121use std:: str;
2222use std:: sync:: { Arc , Mutex } ;
2323use syntax:: symbol:: sym;
@@ -160,13 +160,45 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
160160 opts
161161}
162162
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 > {
170202 let ( test, line_offset) = match panic:: catch_unwind ( || {
171203 make_test ( test, Some ( cratename) , as_test_harness, opts, edition)
172204 } ) {
@@ -307,44 +339,43 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
307339
308340 match ( compile_result, compile_fail) {
309341 ( Ok ( ( ) ) , true ) => {
310- panic ! ( "test compiled while it wasn't supposed to" )
342+ return Err ( TestFailure :: UnexpectedCompilePass ) ;
311343 }
312344 ( Ok ( ( ) ) , false ) => { }
313345 ( Err ( _) , true ) => {
314- if error_codes. len ( ) > 0 {
346+ if ! error_codes. is_empty ( ) {
315347 let out = String :: from_utf8 ( data. lock ( ) . unwrap ( ) . to_vec ( ) ) . unwrap ( ) ;
316348 error_codes. retain ( |err| !out. contains ( err) ) ;
349+
350+ if !error_codes. is_empty ( ) {
351+ return Err ( TestFailure :: MissingErrorCodes ( error_codes) ) ;
352+ }
317353 }
318354 }
319355 ( Err ( _) , false ) => {
320- panic ! ( "couldn't compile the test" )
356+ return Err ( TestFailure :: CompileError ) ;
321357 }
322358 }
323359
324- if error_codes . len ( ) > 0 {
325- panic ! ( "Some expected error codes were not found: {:?}" , error_codes ) ;
360+ if no_run {
361+ return Ok ( ( ) ) ;
326362 }
327363
328- if no_run { return }
329-
330364 // Run the code!
331365 let mut cmd = Command :: new ( output_file) ;
332366
333367 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) ) ,
338369 Ok ( out) => {
339370 if should_panic && out. status . success ( ) {
340- panic ! ( "test executable succeeded when it should have failed" ) ;
371+ return Err ( TestFailure :: UnexpectedRunPass ) ;
341372 } 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) ) ;
345374 }
346375 }
347376 }
377+
378+ Ok ( ( ) )
348379}
349380
350381/// 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 {
711742 allow_fail : config. allow_fail ,
712743 } ,
713744 testfn : testing:: DynTestFn ( box move || {
714- run_test (
745+ let res = run_test (
715746 & test,
716747 & cratename,
717748 & filename,
@@ -730,7 +761,65 @@ impl Tester for Collector {
730761 linker,
731762 edition,
732763 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+ }
734823 } ) ,
735824 } ) ;
736825 }
0 commit comments