@@ -39,6 +39,7 @@ impl DocTestRunner {
39
39
doctest : & DocTestBuilder ,
40
40
scraped_test : & ScrapedDocTest ,
41
41
target_str : & str ,
42
+ opts : & RustdocOptions ,
42
43
) {
43
44
let ignore = match scraped_test. langstr . ignore {
44
45
Ignore :: All => true ,
@@ -62,6 +63,7 @@ impl DocTestRunner {
62
63
self . nb_tests,
63
64
& mut self . output,
64
65
& mut self . output_merged_tests,
66
+ opts,
65
67
) ,
66
68
) ) ;
67
69
self . supports_color &= doctest. supports_color ;
@@ -121,26 +123,81 @@ impl DocTestRunner {
121
123
{output}
122
124
123
125
mod __doctest_mod {{
124
- use std::sync::OnceLock;
126
+ #[cfg(unix)]
127
+ use std::os::unix::process::ExitStatusExt;
125
128
use std::path::PathBuf;
126
129
use std::process::ExitCode;
130
+ use std::sync::OnceLock;
127
131
128
132
pub static BINARY_PATH: OnceLock<PathBuf> = OnceLock::new();
129
133
pub const RUN_OPTION: &str = \" RUSTDOC_DOCTEST_RUN_NB_TEST\" ;
134
+ pub const SHOULD_PANIC_DISABLED: bool = (
135
+ cfg!(target_family = \" wasm\" ) || cfg!(target_os = \" zkvm\" )
136
+ ) && !cfg!(target_os = \" emscripten\" );
130
137
131
138
#[allow(unused)]
132
139
pub fn doctest_path() -> Option<&'static PathBuf> {{
133
140
self::BINARY_PATH.get()
134
141
}}
135
142
136
143
#[allow(unused)]
137
- pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> ExitCode {{
144
+ pub fn doctest_runner(bin: &std::path::Path, test_nb: usize, should_panic: bool ) -> ExitCode {{
138
145
let out = std::process::Command::new(bin)
139
146
.env(self::RUN_OPTION, test_nb.to_string())
140
147
.args(std::env::args().skip(1).collect::<Vec<_>>())
141
148
.output()
142
149
.expect(\" failed to run command\" );
143
- if !out.status.success() {{
150
+ if should_panic {{
151
+ // FIXME: Make `test::get_result_from_exit_code` public and use this code instead of this.
152
+ //
153
+ // On Zircon (the Fuchsia kernel), an abort from userspace calls the
154
+ // LLVM implementation of __builtin_trap(), e.g., ud2 on x86, which
155
+ // raises a kernel exception. If a userspace process does not
156
+ // otherwise arrange exception handling, the kernel kills the process
157
+ // with this return code.
158
+ #[cfg(target_os = \" fuchsia\" )]
159
+ const ZX_TASK_RETCODE_EXCEPTION_KILL: i32 = -1028;
160
+ // On Windows we use __fastfail to abort, which is documented to use this
161
+ // exception code.
162
+ #[cfg(windows)]
163
+ const STATUS_FAIL_FAST_EXCEPTION: i32 = 0xC0000409u32 as i32;
164
+ #[cfg(unix)]
165
+ const SIGABRT: std::ffi::c_int = 6;
166
+
167
+ match out.status.code() {{
168
+ Some(test::ERROR_EXIT_CODE) => ExitCode::SUCCESS,
169
+ #[cfg(windows)]
170
+ Some(STATUS_FAIL_FAST_EXCEPTION) => ExitCode::SUCCESS,
171
+ #[cfg(unix)]
172
+ None => match out.status.signal() {{
173
+ Some(SIGABRT) => ExitCode::SUCCESS,
174
+ Some(signal) => {{
175
+ eprintln!(\" Test didn't panic, but it's marked `should_panic` (exit signal: {{signal}}).\" );
176
+ ExitCode::FAILURE
177
+ }}
178
+ None => {{
179
+ eprintln!(\" Test didn't panic, but it's marked `should_panic` and exited with no error code and no signal.\" );
180
+ ExitCode::FAILURE
181
+ }}
182
+ }},
183
+ #[cfg(not(unix))]
184
+ None => {{
185
+ eprintln!(\" Test didn't panic, but it's marked `should_panic`.\" );
186
+ ExitCode::FAILURE
187
+ }}
188
+ // Upon an abort, Fuchsia returns the status code ZX_TASK_RETCODE_EXCEPTION_KILL.
189
+ #[cfg(target_os = \" fuchsia\" )]
190
+ Some(ZX_TASK_RETCODE_EXCEPTION_KILL) => ExitCode::SUCCESS,
191
+ Some(exit_code) => {{
192
+ if !out.status.success() {{
193
+ eprintln!(\" Test didn't panic, but it's marked `should_panic` (exit status: {{exit_code}}).\" );
194
+ }} else {{
195
+ eprintln!(\" Test didn't panic, but it's marked `should_panic`.\" );
196
+ }}
197
+ ExitCode::FAILURE
198
+ }}
199
+ }}
200
+ }} else if !out.status.success() {{
144
201
if let Some(code) = out.status.code() {{
145
202
eprintln!(\" Test executable failed (exit status: {{code}}).\" );
146
203
}} else {{
@@ -223,6 +280,7 @@ fn generate_mergeable_doctest(
223
280
id : usize ,
224
281
output : & mut String ,
225
282
output_merged_tests : & mut String ,
283
+ opts : & RustdocOptions ,
226
284
) -> String {
227
285
let test_id = format ! ( "__doctest_{id}" ) ;
228
286
@@ -256,31 +314,33 @@ fn main() {returns_result} {{
256
314
)
257
315
. unwrap ( ) ;
258
316
}
259
- let not_running = ignore || scraped_test. langstr . no_run ;
317
+ let should_panic = scraped_test. langstr . should_panic ;
318
+ let not_running = ignore || scraped_test. no_run ( opts) ;
260
319
writeln ! (
261
320
output_merged_tests,
262
321
"
263
322
mod {test_id} {{
264
323
pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest(
265
- {test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic} ,
324
+ {test_name:?}, {ignore} || ({should_panic} && crate::__doctest_mod::SHOULD_PANIC_DISABLED) , {file:?}, {line}, {no_run}, false ,
266
325
test::StaticTestFn(
267
326
|| {{{runner}}},
268
327
));
269
328
}}" ,
270
329
test_name = scraped_test. name,
271
330
file = scraped_test. path( ) ,
272
331
line = scraped_test. line,
273
- no_run = scraped_test. langstr. no_run,
274
- should_panic = !scraped_test. langstr. no_run && scraped_test. langstr. should_panic,
332
+ no_run = scraped_test. no_run( opts) ,
275
333
// Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply
276
334
// don't give it the function to run.
277
335
runner = if not_running {
278
336
"test::assert_test_result(Ok::<(), String>(()))" . to_string( )
279
337
} else {
280
338
format!(
281
339
"
282
- if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{
283
- test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}))
340
+ if {should_panic} && crate::__doctest_mod::SHOULD_PANIC_DISABLED {{
341
+ test::assert_test_result(Ok::<(), String>(()))
342
+ }} else if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{
343
+ test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}, {should_panic}))
284
344
}} else {{
285
345
test::assert_test_result(doctest_bundle::{test_id}::__main_fn())
286
346
}}
0 commit comments