Skip to content

Commit 63e4bee

Browse files
committed
rustdoc: Extract actual doctest running logic into function
1 parent 46b1685 commit 63e4bee

File tree

2 files changed

+126
-75
lines changed

2 files changed

+126
-75
lines changed

src/librustdoc/doctest.rs

+117-72
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,
@@ -990,7 +990,7 @@ pub(crate) trait DoctestVisitor {
990990
fn visit_header(&mut self, _name: &str, _level: u32) {}
991991
}
992992

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

996996
rustdoc_options: RustdocOptions,
@@ -1002,14 +1002,14 @@ pub(crate) struct Collector {
10021002
arg_file: PathBuf,
10031003
}
10041004

1005-
impl Collector {
1005+
impl CreateRunnableDoctests {
10061006
pub(crate) fn new(
10071007
crate_name: String,
10081008
rustdoc_options: RustdocOptions,
10091009
opts: GlobalTestOptions,
10101010
arg_file: PathBuf,
1011-
) -> Collector {
1012-
Collector {
1011+
) -> CreateRunnableDoctests {
1012+
CreateRunnableDoctests {
10131013
tests: Vec::new(),
10141014
rustdoc_options,
10151015
crate_name,
@@ -1106,77 +1106,122 @@ impl Collector {
11061106
test_type: test::TestType::DocTest,
11071107
},
11081108
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}");
11721213
}
11731214

1174-
panic::resume_unwind(Box::new(()));
1215+
if !stderr.is_empty() {
1216+
eprintln!("stderr:\n{stderr}");
1217+
}
11751218
}
1176-
Ok(())
1177-
})),
1178-
});
1219+
}
1220+
}
1221+
1222+
panic::resume_unwind(Box::new(()));
11791223
}
1224+
Ok(())
11801225
}
11811226

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

src/librustdoc/doctest/markdown.rs

+9-3
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

@@ -120,8 +122,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
120122
false,
121123
);
122124

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

0 commit comments

Comments
 (0)