@@ -179,7 +179,7 @@ pub(crate) fn run(
179
179
180
180
let opts = scrape_test_config ( crate_attrs) ;
181
181
let enable_per_target_ignores = options. enable_per_target_ignores ;
182
- let mut collector = Collector :: new (
182
+ let mut collector = CreateRunnableDoctests :: new (
183
183
tcx. crate_name ( LOCAL_CRATE ) . to_string ( ) ,
184
184
options,
185
185
opts,
@@ -990,7 +990,7 @@ pub(crate) trait DoctestVisitor {
990
990
fn visit_header ( & mut self , _name : & str , _level : u32 ) { }
991
991
}
992
992
993
- pub ( crate ) struct Collector {
993
+ pub ( crate ) struct CreateRunnableDoctests {
994
994
pub ( crate ) tests : Vec < test:: TestDescAndFn > ,
995
995
996
996
rustdoc_options : RustdocOptions ,
@@ -1002,14 +1002,14 @@ pub(crate) struct Collector {
1002
1002
arg_file : PathBuf ,
1003
1003
}
1004
1004
1005
- impl Collector {
1005
+ impl CreateRunnableDoctests {
1006
1006
pub ( crate ) fn new (
1007
1007
crate_name : String ,
1008
1008
rustdoc_options : RustdocOptions ,
1009
1009
opts : GlobalTestOptions ,
1010
1010
arg_file : PathBuf ,
1011
- ) -> Collector {
1012
- Collector {
1011
+ ) -> CreateRunnableDoctests {
1012
+ CreateRunnableDoctests {
1013
1013
tests : Vec :: new ( ) ,
1014
1014
rustdoc_options,
1015
1015
crate_name,
@@ -1106,77 +1106,122 @@ impl Collector {
1106
1106
test_type : test:: TestType :: DocTest ,
1107
1107
} ,
1108
1108
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}" ) ;
1172
1213
}
1173
1214
1174
- panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
1215
+ if !stderr. is_empty ( ) {
1216
+ eprintln ! ( "stderr:\n {stderr}" ) ;
1217
+ }
1175
1218
}
1176
- Ok ( ( ) )
1177
- } ) ) ,
1178
- } ) ;
1219
+ }
1220
+ }
1221
+
1222
+ panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
1179
1223
}
1224
+ Ok ( ( ) )
1180
1225
}
1181
1226
1182
1227
#[ cfg( test) ] // used in tests
0 commit comments