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