Make merged doctests run in their own process
This commit is contained in:
parent
dcc77b4cbc
commit
0f0681e941
@ -629,9 +629,13 @@ fn drop(&mut self) {
|
|||||||
let tool = make_maybe_absolute_path(tool.into());
|
let tool = make_maybe_absolute_path(tool.into());
|
||||||
cmd = Command::new(tool);
|
cmd = Command::new(tool);
|
||||||
cmd.args(&rustdoc_options.runtool_args);
|
cmd.args(&rustdoc_options.runtool_args);
|
||||||
cmd.arg(output_file);
|
cmd.arg(&output_file);
|
||||||
} else {
|
} else {
|
||||||
cmd = Command::new(output_file);
|
cmd = Command::new(&output_file);
|
||||||
|
if is_multiple_tests {
|
||||||
|
cmd.arg("*doctest-bin-path");
|
||||||
|
cmd.arg(&output_file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(run_directory) = &rustdoc_options.test_run_directory {
|
if let Some(run_directory) = &rustdoc_options.test_run_directory {
|
||||||
cmd.current_dir(run_directory);
|
cmd.current_dir(run_directory);
|
||||||
|
@ -75,8 +75,9 @@ pub(crate) fn run_merged_tests(
|
|||||||
#![allow(internal_features)]
|
#![allow(internal_features)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#![feature(rustc_attrs)]
|
#![feature(rustc_attrs)]
|
||||||
#![feature(coverage_attribute)]\n"
|
#![feature(coverage_attribute)]
|
||||||
.to_string();
|
"
|
||||||
|
.to_string();
|
||||||
|
|
||||||
for crate_attr in &self.crate_attrs {
|
for crate_attr in &self.crate_attrs {
|
||||||
code.push_str(crate_attr);
|
code.push_str(crate_attr);
|
||||||
@ -104,15 +105,67 @@ pub(crate) fn run_merged_tests(
|
|||||||
code,
|
code,
|
||||||
"\
|
"\
|
||||||
{output}
|
{output}
|
||||||
|
|
||||||
|
mod __doctest_mod {{
|
||||||
|
pub static mut BINARY_PATH: Option<std::path::PathBuf> = None;
|
||||||
|
pub const RUN_OPTION: &str = \"*doctest-inner-test\";
|
||||||
|
pub const BIN_OPTION: &str = \"*doctest-bin-path\";
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn get_doctest_path() -> Option<&'static std::path::Path> {{
|
||||||
|
unsafe {{ self::BINARY_PATH.as_deref() }}
|
||||||
|
}}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> Result<(), String> {{
|
||||||
|
let out = std::process::Command::new(bin)
|
||||||
|
.arg(self::RUN_OPTION)
|
||||||
|
.arg(test_nb.to_string())
|
||||||
|
.output()
|
||||||
|
.expect(\"failed to run command\");
|
||||||
|
if !out.status.success() {{
|
||||||
|
Err(String::from_utf8_lossy(&out.stderr).to_string())
|
||||||
|
}} else {{
|
||||||
|
Ok(())
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
#[rustc_main]
|
#[rustc_main]
|
||||||
#[coverage(off)]
|
#[coverage(off)]
|
||||||
fn main() {{
|
fn main() -> std::process::ExitCode {{
|
||||||
const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}];
|
const TESTS: [test::TestDescAndFn; {nb_tests}] = [{ids}];
|
||||||
test::test_main(
|
let bin_marker = std::ffi::OsStr::new(__doctest_mod::BIN_OPTION);
|
||||||
&[{test_args}],
|
let test_marker = std::ffi::OsStr::new(__doctest_mod::RUN_OPTION);
|
||||||
Vec::from(TESTS),
|
|
||||||
None,
|
let mut args = std::env::args_os().skip(1);
|
||||||
);
|
while let Some(arg) = args.next() {{
|
||||||
|
if arg == bin_marker {{
|
||||||
|
let Some(binary) = args.next() else {{
|
||||||
|
panic!(\"missing argument after `{{}}`\", __doctest_mod::BIN_OPTION);
|
||||||
|
}};
|
||||||
|
unsafe {{ crate::__doctest_mod::BINARY_PATH = Some(binary.into()); }}
|
||||||
|
return std::process::Termination::report(test::test_main(
|
||||||
|
&[{test_args}],
|
||||||
|
Vec::from(TESTS),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}} else if arg == test_marker {{
|
||||||
|
let Some(nb_test) = args.next() else {{
|
||||||
|
panic!(\"missing argument after `{{}}`\", __doctest_mod::RUN_OPTION);
|
||||||
|
}};
|
||||||
|
if let Some(nb_test) = nb_test.to_str().and_then(|nb| nb.parse::<usize>().ok()) {{
|
||||||
|
if let Some(test) = TESTS.get(nb_test) {{
|
||||||
|
if let test::StaticTestFn(f) = test.testfn {{
|
||||||
|
return std::process::Termination::report(f());
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
panic!(\"Unexpected value after `{{}}`\", __doctest_mod::RUN_OPTION);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
panic!(\"missing argument for merged doctest binary\");
|
||||||
}}",
|
}}",
|
||||||
nb_tests = self.nb_tests,
|
nb_tests = self.nb_tests,
|
||||||
output = self.output,
|
output = self.output,
|
||||||
@ -156,6 +209,10 @@ fn generate_mergeable_doctest(
|
|||||||
} else {
|
} else {
|
||||||
writeln!(output, "mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs)
|
writeln!(output, "mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
if scraped_test.langstr.no_run {
|
||||||
|
// To prevent having warnings about unused items since they're not called.
|
||||||
|
writeln!(output, "#![allow(unused)]").unwrap();
|
||||||
|
}
|
||||||
if doctest.has_main_fn {
|
if doctest.has_main_fn {
|
||||||
output.push_str(&doctest.everything_else);
|
output.push_str(&doctest.everything_else);
|
||||||
} else {
|
} else {
|
||||||
@ -167,14 +224,15 @@ fn generate_mergeable_doctest(
|
|||||||
write!(
|
write!(
|
||||||
output,
|
output,
|
||||||
"\
|
"\
|
||||||
fn main() {returns_result} {{
|
fn main() {returns_result} {{
|
||||||
{}
|
{}
|
||||||
}}",
|
}}",
|
||||||
doctest.everything_else
|
doctest.everything_else
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let not_running = ignore || scraped_test.langstr.no_run;
|
||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
"
|
"
|
||||||
@ -196,7 +254,7 @@ fn main() {returns_result} {{
|
|||||||
}},
|
}},
|
||||||
testfn: test::StaticTestFn(
|
testfn: test::StaticTestFn(
|
||||||
#[coverage(off)]
|
#[coverage(off)]
|
||||||
|| test::assert_test_result({runner}),
|
|| {{{runner}}},
|
||||||
)
|
)
|
||||||
}};
|
}};
|
||||||
}}",
|
}}",
|
||||||
@ -211,10 +269,18 @@ fn main() {returns_result} {{
|
|||||||
},
|
},
|
||||||
// Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply
|
// Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply
|
||||||
// don't give it the function to run.
|
// don't give it the function to run.
|
||||||
runner = if ignore || scraped_test.langstr.no_run {
|
runner = if not_running {
|
||||||
"Ok::<(), String>(())"
|
"test::assert_test_result(Ok::<(), String>(()))".to_string()
|
||||||
} else {
|
} else {
|
||||||
"self::main()"
|
format!(
|
||||||
|
"
|
||||||
|
if let Some(bin_path) = crate::__doctest_mod::get_doctest_path() {{
|
||||||
|
test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}))
|
||||||
|
}} else {{
|
||||||
|
test::assert_test_result(self::main())
|
||||||
|
}}
|
||||||
|
",
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user