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