11use  std:: any:: Any ; 
22#[ cfg( unix) ]  
33use  std:: os:: unix:: process:: ExitStatusExt ; 
4- use  std:: process:: ExitStatus ; 
4+ use  std:: process:: { ExitStatus ,  Output } ; 
5+ use  std:: { fmt,  io} ; 
56
67pub  use  self :: TestResult :: * ; 
78use  super :: bench:: BenchSamples ; 
@@ -103,15 +104,14 @@ pub(crate) fn calc_result(
103104    result
104105} 
105106
106- /// Creates a `TestResult` depending on the exit code of test subprocess. 
107- pub ( crate )  fn  get_result_from_exit_code ( 
108-     desc :  & TestDesc , 
107+ /// Creates a `TestResult` depending on the exit code of test subprocess 
108+ pub ( crate )  fn  get_result_from_exit_code_inner ( 
109109    status :  ExitStatus , 
110-     time_opts :  Option < & time:: TestTimeOptions > , 
111-     exec_time :  Option < & time:: TestExecTime > , 
110+     success_error_code :  i32 , 
112111)  -> TestResult  { 
113-     let  result = match  status. code ( )  { 
114-         Some ( TR_OK )  => TestResult :: TrOk , 
112+     match  status. code ( )  { 
113+         Some ( error_code)  if  error_code == success_error_code => TestResult :: TrOk , 
114+         Some ( crate :: ERROR_EXIT_CODE )  => TestResult :: TrFailed , 
115115        #[ cfg( windows) ]  
116116        Some ( STATUS_FAIL_FAST_EXCEPTION )  => TestResult :: TrFailed , 
117117        #[ cfg( unix) ]  
@@ -131,7 +131,17 @@ pub(crate) fn get_result_from_exit_code(
131131        Some ( code)  => TestResult :: TrFailedMsg ( format ! ( "got unexpected return code {code}" ) ) , 
132132        #[ cfg( not( any( windows,  unix) ) ) ]  
133133        Some ( _)  => TestResult :: TrFailed , 
134-     } ; 
134+     } 
135+ } 
136+ 
137+ /// Creates a `TestResult` depending on the exit code of test subprocess and on its runtime. 
138+ pub ( crate )  fn  get_result_from_exit_code ( 
139+     desc :  & TestDesc , 
140+     status :  ExitStatus , 
141+     time_opts :  Option < & time:: TestTimeOptions > , 
142+     exec_time :  Option < & time:: TestExecTime > , 
143+ )  -> TestResult  { 
144+     let  result = get_result_from_exit_code_inner ( status,  TR_OK ) ; 
135145
136146    // If test is already failed (or allowed to fail), do not change the result. 
137147    if  result != TestResult :: TrOk  { 
@@ -147,3 +157,93 @@ pub(crate) fn get_result_from_exit_code(
147157
148158    result
149159} 
160+ 
161+ #[ derive( Debug ) ]  
162+ pub  enum  RustdocResult  { 
163+     /// The test failed to compile. 
164+ CompileError , 
165+     /// The test is marked `compile_fail` but compiled successfully. 
166+ UnexpectedCompilePass , 
167+     /// The test failed to compile (as expected) but the compiler output did not contain all 
168+ /// expected error codes. 
169+ MissingErrorCodes ( Vec < String > ) , 
170+     /// The test binary was unable to be executed. 
171+ ExecutionError ( io:: Error ) , 
172+     /// The test binary exited with a non-zero exit code. 
173+ /// 
174+ /// This typically means an assertion in the test failed or another form of panic occurred. 
175+ ExecutionFailure ( Output ) , 
176+     /// The test is marked `should_panic` but the test binary executed successfully. 
177+ NoPanic ( Option < String > ) , 
178+ } 
179+ 
180+ impl  fmt:: Display  for  RustdocResult  { 
181+     fn  fmt ( & self ,  f :  & mut  fmt:: Formatter < ' _ > )  -> fmt:: Result  { 
182+         match  self  { 
183+             Self :: CompileError  => { 
184+                 write ! ( f,  "Couldn't compile the test." ) 
185+             } 
186+             Self :: UnexpectedCompilePass  => { 
187+                 write ! ( f,  "Test compiled successfully, but it's marked `compile_fail`." ) 
188+             } 
189+             Self :: NoPanic ( msg)  => { 
190+                 write ! ( f,  "Test didn't panic, but it's marked `should_panic`" ) ?; 
191+                 if  let  Some ( msg)  = msg { 
192+                     write ! ( f,  " ({msg})" ) ?; 
193+                 } 
194+                 f. write_str ( "." ) 
195+             } 
196+             Self :: MissingErrorCodes ( codes)  => { 
197+                 write ! ( f,  "Some expected error codes were not found: {codes:?}" ) 
198+             } 
199+             Self :: ExecutionError ( err)  => { 
200+                 write ! ( f,  "Couldn't run the test: {err}" ) ?; 
201+                 if  err. kind ( )  == io:: ErrorKind :: PermissionDenied  { 
202+                     f. write_str ( " - maybe your tempdir is mounted with noexec?" ) ?; 
203+                 } 
204+                 Ok ( ( ) ) 
205+             } 
206+             Self :: ExecutionFailure ( out)  => { 
207+                 writeln ! ( f,  "Test executable failed ({reason})." ,  reason = out. status) ?; 
208+ 
209+                 // FIXME(#12309): An unfortunate side-effect of capturing the test 
210+                 // executable's output is that the relative ordering between the test's 
211+                 // stdout and stderr is lost. However, this is better than the 
212+                 // alternative: if the test executable inherited the parent's I/O 
213+                 // handles the output wouldn't be captured at all, even on success. 
214+                 // 
215+                 // The ordering could be preserved if the test process' stderr was 
216+                 // redirected to stdout, but that functionality does not exist in the 
217+                 // standard library, so it may not be portable enough. 
218+                 let  stdout = str:: from_utf8 ( & out. stdout ) . unwrap_or_default ( ) ; 
219+                 let  stderr = str:: from_utf8 ( & out. stderr ) . unwrap_or_default ( ) ; 
220+ 
221+                 if  !stdout. is_empty ( )  || !stderr. is_empty ( )  { 
222+                     writeln ! ( f) ?; 
223+ 
224+                     if  !stdout. is_empty ( )  { 
225+                         writeln ! ( f,  "stdout:\n {stdout}" ) ?; 
226+                     } 
227+ 
228+                     if  !stderr. is_empty ( )  { 
229+                         writeln ! ( f,  "stderr:\n {stderr}" ) ?; 
230+                     } 
231+                 } 
232+                 Ok ( ( ) ) 
233+             } 
234+         } 
235+     } 
236+ } 
237+ 
238+ pub  fn  get_rustdoc_result ( output :  Output ,  should_panic :  bool )  -> Result < ( ) ,  RustdocResult >  { 
239+     let  result = get_result_from_exit_code_inner ( output. status ,  0 ) ; 
240+     match  ( result,  should_panic)  { 
241+         ( TestResult :: TrFailed ,  true )  | ( TestResult :: TrOk ,  false )  => Ok ( ( ) ) , 
242+         ( TestResult :: TrOk ,  true )  => Err ( RustdocResult :: NoPanic ( None ) ) , 
243+         ( TestResult :: TrFailedMsg ( msg) ,  true )  => Err ( RustdocResult :: NoPanic ( Some ( msg) ) ) , 
244+         ( TestResult :: TrFailedMsg ( _)  | TestResult :: TrFailed ,  false )  => { 
245+             Err ( RustdocResult :: ExecutionFailure ( output) ) 
246+         } 
247+         _ => unreachable ! ( "unexpected status for rustdoc test output" ) , 
248+     } 
249+ } 
0 commit comments