@@ -179,7 +179,7 @@ pub(crate) fn run(
179179
180180 let opts = scrape_test_config ( crate_attrs) ;
181181 let enable_per_target_ignores = options. enable_per_target_ignores ;
182- let mut collector = Collector :: new (
182+ let mut collector = CreateRunnableDoctests :: new (
183183 tcx. crate_name ( LOCAL_CRATE ) . to_string ( ) ,
184184 options,
185185 opts,
@@ -989,7 +989,7 @@ pub(crate) trait DoctestVisitor {
989989 fn visit_header ( & mut self , _name : & str , _level : u32 ) { }
990990}
991991
992- pub ( crate ) struct Collector {
992+ pub ( crate ) struct CreateRunnableDoctests {
993993 pub ( crate ) tests : Vec < test:: TestDescAndFn > ,
994994
995995 rustdoc_options : RustdocOptions ,
@@ -1001,14 +1001,14 @@ pub(crate) struct Collector {
10011001 arg_file : PathBuf ,
10021002}
10031003
1004- impl Collector {
1004+ impl CreateRunnableDoctests {
10051005 pub ( crate ) fn new (
10061006 crate_name : String ,
10071007 rustdoc_options : RustdocOptions ,
10081008 opts : GlobalTestOptions ,
10091009 arg_file : PathBuf ,
1010- ) -> Collector {
1011- Collector {
1010+ ) -> CreateRunnableDoctests {
1011+ CreateRunnableDoctests {
10121012 tests : Vec :: new ( ) ,
10131013 rustdoc_options,
10141014 crate_name,
@@ -1105,77 +1105,122 @@ impl Collector {
11051105 test_type : test:: TestType :: DocTest ,
11061106 } ,
11071107 testfn : test:: DynTestFn ( Box :: new ( move || {
1108- let report_unused_externs = |uext| {
1109- unused_externs. lock ( ) . unwrap ( ) . push ( uext) ;
1110- } ;
1111- let res = run_test (
1112- & text,
1113- & crate_name,
1114- line,
1115- rustdoc_test_options,
1116- langstr,
1117- no_run,
1118- & opts,
1119- edition,
1120- path,
1121- report_unused_externs,
1122- ) ;
1123-
1124- if let Err ( err) = res {
1125- match err {
1126- TestFailure :: CompileError => {
1127- eprint ! ( "Couldn't compile the test." ) ;
1128- }
1129- TestFailure :: UnexpectedCompilePass => {
1130- eprint ! ( "Test compiled successfully, but it's marked `compile_fail`." ) ;
1131- }
1132- TestFailure :: UnexpectedRunPass => {
1133- eprint ! ( "Test executable succeeded, but it's marked `should_panic`." ) ;
1134- }
1135- TestFailure :: MissingErrorCodes ( codes) => {
1136- eprint ! ( "Some expected error codes were not found: {codes:?}" ) ;
1137- }
1138- TestFailure :: ExecutionError ( err) => {
1139- eprint ! ( "Couldn't run the test: {err}" ) ;
1140- if err. kind ( ) == io:: ErrorKind :: PermissionDenied {
1141- eprint ! ( " - maybe your tempdir is mounted with noexec?" ) ;
1142- }
1143- }
1144- TestFailure :: ExecutionFailure ( out) => {
1145- eprintln ! ( "Test executable failed ({reason})." , reason = out. status) ;
1146-
1147- // FIXME(#12309): An unfortunate side-effect of capturing the test
1148- // executable's output is that the relative ordering between the test's
1149- // stdout and stderr is lost. However, this is better than the
1150- // alternative: if the test executable inherited the parent's I/O
1151- // handles the output wouldn't be captured at all, even on success.
1152- //
1153- // The ordering could be preserved if the test process' stderr was
1154- // redirected to stdout, but that functionality does not exist in the
1155- // standard library, so it may not be portable enough.
1156- let stdout = str:: from_utf8 ( & out. stdout ) . unwrap_or_default ( ) ;
1157- let stderr = str:: from_utf8 ( & out. stderr ) . unwrap_or_default ( ) ;
1158-
1159- if !stdout. is_empty ( ) || !stderr. is_empty ( ) {
1160- eprintln ! ( ) ;
1161-
1162- if !stdout. is_empty ( ) {
1163- eprintln ! ( "stdout:\n {stdout}" ) ;
1164- }
1165-
1166- if !stderr. is_empty ( ) {
1167- eprintln ! ( "stderr:\n {stderr}" ) ;
1168- }
1169- }
1170- }
1108+ doctest_run_fn (
1109+ RunnableDoctest {
1110+ crate_name,
1111+ line,
1112+ rustdoc_test_options,
1113+ langstr,
1114+ no_run,
1115+ opts,
1116+ edition,
1117+ path,
1118+ text,
1119+ } ,
1120+ unused_externs,
1121+ )
1122+ } ) ) ,
1123+ } ) ;
1124+ }
1125+ }
1126+
1127+ /// A doctest that is ready to run.
1128+ struct RunnableDoctest {
1129+ crate_name : String ,
1130+ line : usize ,
1131+ rustdoc_test_options : IndividualTestOptions ,
1132+ langstr : LangString ,
1133+ no_run : bool ,
1134+ opts : GlobalTestOptions ,
1135+ edition : Edition ,
1136+ path : PathBuf ,
1137+ text : String ,
1138+ }
1139+
1140+ fn doctest_run_fn (
1141+ test : RunnableDoctest ,
1142+ unused_externs : Arc < Mutex < Vec < UnusedExterns > > > ,
1143+ ) -> Result < ( ) , String > {
1144+ let RunnableDoctest {
1145+ crate_name,
1146+ line,
1147+ rustdoc_test_options,
1148+ langstr,
1149+ no_run,
1150+ opts,
1151+ edition,
1152+ path,
1153+ text,
1154+ } = test;
1155+
1156+ let report_unused_externs = |uext| {
1157+ unused_externs. lock ( ) . unwrap ( ) . push ( uext) ;
1158+ } ;
1159+ let res = run_test (
1160+ & text,
1161+ & crate_name,
1162+ line,
1163+ rustdoc_test_options,
1164+ langstr,
1165+ no_run,
1166+ & opts,
1167+ edition,
1168+ path,
1169+ report_unused_externs,
1170+ ) ;
1171+
1172+ if let Err ( err) = res {
1173+ match err {
1174+ TestFailure :: CompileError => {
1175+ eprint ! ( "Couldn't compile the test." ) ;
1176+ }
1177+ TestFailure :: UnexpectedCompilePass => {
1178+ eprint ! ( "Test compiled successfully, but it's marked `compile_fail`." ) ;
1179+ }
1180+ TestFailure :: UnexpectedRunPass => {
1181+ eprint ! ( "Test executable succeeded, but it's marked `should_panic`." ) ;
1182+ }
1183+ TestFailure :: MissingErrorCodes ( codes) => {
1184+ eprint ! ( "Some expected error codes were not found: {codes:?}" ) ;
1185+ }
1186+ TestFailure :: ExecutionError ( err) => {
1187+ eprint ! ( "Couldn't run the test: {err}" ) ;
1188+ if err. kind ( ) == io:: ErrorKind :: PermissionDenied {
1189+ eprint ! ( " - maybe your tempdir is mounted with noexec?" ) ;
1190+ }
1191+ }
1192+ TestFailure :: ExecutionFailure ( out) => {
1193+ eprintln ! ( "Test executable failed ({reason})." , reason = out. status) ;
1194+
1195+ // FIXME(#12309): An unfortunate side-effect of capturing the test
1196+ // executable's output is that the relative ordering between the test's
1197+ // stdout and stderr is lost. However, this is better than the
1198+ // alternative: if the test executable inherited the parent's I/O
1199+ // handles the output wouldn't be captured at all, even on success.
1200+ //
1201+ // The ordering could be preserved if the test process' stderr was
1202+ // redirected to stdout, but that functionality does not exist in the
1203+ // standard library, so it may not be portable enough.
1204+ let stdout = str:: from_utf8 ( & out. stdout ) . unwrap_or_default ( ) ;
1205+ let stderr = str:: from_utf8 ( & out. stderr ) . unwrap_or_default ( ) ;
1206+
1207+ if !stdout. is_empty ( ) || !stderr. is_empty ( ) {
1208+ eprintln ! ( ) ;
1209+
1210+ if !stdout. is_empty ( ) {
1211+ eprintln ! ( "stdout:\n {stdout}" ) ;
11711212 }
11721213
1173- panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
1214+ if !stderr. is_empty ( ) {
1215+ eprintln ! ( "stderr:\n {stderr}" ) ;
1216+ }
11741217 }
1175- Ok ( ( ) )
1176- } ) ) ,
1177- } ) ;
1218+ }
1219+ }
1220+
1221+ panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
11781222 }
1223+ Ok ( ( ) )
11791224}
11801225
11811226#[ cfg( test) ] // used in tests
0 commit comments