Skip to content

Commit b7dd401

Browse files
camelidGuillaumeGomez
authored andcommitted
rustdoc: Extract actual doctest running logic into function
1 parent 85499eb commit b7dd401

File tree

2 files changed

+126
-75
lines changed

2 files changed

+126
-75
lines changed

src/librustdoc/doctest.rs

Lines changed: 117 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -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,
@@ -989,7 +989,7 @@ pub(crate) trait DoctestVisitor {
989989
fn visit_header(&mut self, _name: &str, _level: u32) {}
990990
}
991991

992-
pub(crate) struct Collector {
992+
pub(crate) struct CreateRunnableDoctests {
993993
pub(crate) tests: Vec<test::TestDescAndFn>,
994994

995995
rustdoc_options: RustdocOptions,
@@ -1001,14 +1001,14 @@ pub(crate) struct Collector {
10011001
arg_file: PathBuf,
10021002
}
10031003

1004-
impl Collector {
1004+
impl CreateRunnableDoctests {
10051005
pub(crate) fn new(
10061006
crate_name: String,
10071007
rustdoc_options: RustdocOptions,
10081008
opts: GlobalTestOptions,
10091009
arg_file: PathBuf,
1010-
) -> Collector {
1011-
Collector {
1010+
) -> CreateRunnableDoctests {
1011+
CreateRunnableDoctests {
10121012
tests: Vec::new(),
10131013
rustdoc_options,
10141014
crate_name,
@@ -1105,77 +1105,122 @@ impl Collector {
11051105
test_type: test::TestType::DocTest,
11061106
},
11071107
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}");
11711212
}
11721213

1173-
panic::resume_unwind(Box::new(()));
1214+
if !stderr.is_empty() {
1215+
eprintln!("stderr:\n{stderr}");
1216+
}
11741217
}
1175-
Ok(())
1176-
})),
1177-
});
1218+
}
1219+
}
1220+
1221+
panic::resume_unwind(Box::new(()));
11781222
}
1223+
Ok(())
11791224
}
11801225

11811226
#[cfg(test)] // used in tests

src/librustdoc/doctest/markdown.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use std::fs::read_to_string;
55
use rustc_span::FileName;
66
use tempfile::tempdir;
77

8-
use super::{generate_args_file, Collector, DoctestVisitor, GlobalTestOptions, ScrapedDoctest};
8+
use super::{
9+
generate_args_file, CreateRunnableDoctests, DoctestVisitor, GlobalTestOptions, ScrapedDoctest,
10+
};
911
use crate::config::Options;
1012
use crate::html::markdown::{find_testable_code, ErrorCodes, LangString};
1113

@@ -119,8 +121,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
119121
None,
120122
);
121123

122-
let mut collector =
123-
Collector::new(options.input.filestem().to_string(), options.clone(), opts, file_path);
124+
let mut collector = CreateRunnableDoctests::new(
125+
options.input.filestem().to_string(),
126+
options.clone(),
127+
opts,
128+
file_path,
129+
);
124130
md_collector.tests.into_iter().for_each(|t| collector.add_test(ScrapedDoctest::Markdown(t)));
125131
crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests);
126132
Ok(())

0 commit comments

Comments
 (0)