Skip to content

Commit 61760fb

Browse files
Rollup merge of #144308 - GuillaumeGomez:merged-doctest-times, r=lolbinarycat
[rustdoc] Display total time and compilation time of merged doctests Fixes #144270. Does it look good to you `@kpreid?` <img width="908" height="263" alt="image" src="https://github.com/user-attachments/assets/cd5d082d-c4e0-42ed-91dd-bd263b413dcd" />
2 parents b38ece9 + 64e3078 commit 61760fb

30 files changed

+212
-114
lines changed

src/librustdoc/doctest.rs

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use std::path::{Path, PathBuf};
1111
use std::process::{self, Command, Stdio};
1212
use std::sync::atomic::{AtomicUsize, Ordering};
1313
use std::sync::{Arc, Mutex};
14-
use std::{panic, str};
14+
use std::time::{Duration, Instant};
15+
use std::{fmt, panic, str};
1516

1617
pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder};
1718
pub(crate) use markdown::test as test_markdown;
@@ -36,6 +37,50 @@ use crate::config::{Options as RustdocOptions, OutputFormat};
3637
use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine};
3738
use crate::lint::init_lints;
3839

40+
/// Type used to display times (compilation and total) information for merged doctests.
41+
struct MergedDoctestTimes {
42+
total_time: Instant,
43+
/// Total time spent compiling all merged doctests.
44+
compilation_time: Duration,
45+
/// This field is used to keep track of how many merged doctests we (tried to) compile.
46+
added_compilation_times: usize,
47+
}
48+
49+
impl MergedDoctestTimes {
50+
fn new() -> Self {
51+
Self {
52+
total_time: Instant::now(),
53+
compilation_time: Duration::default(),
54+
added_compilation_times: 0,
55+
}
56+
}
57+
58+
fn add_compilation_time(&mut self, duration: Duration) {
59+
self.compilation_time += duration;
60+
self.added_compilation_times += 1;
61+
}
62+
63+
fn display_times(&self) {
64+
// If no merged doctest was compiled, then there is nothing to display since the numbers
65+
// displayed by `libtest` for standalone tests are already accurate (they include both
66+
// compilation and runtime).
67+
if self.added_compilation_times > 0 {
68+
println!("{self}");
69+
}
70+
}
71+
}
72+
73+
impl fmt::Display for MergedDoctestTimes {
74+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75+
write!(
76+
f,
77+
"all doctests ran in {:.2}s; merged doctests compilation took {:.2}s",
78+
self.total_time.elapsed().as_secs_f64(),
79+
self.compilation_time.as_secs_f64(),
80+
)
81+
}
82+
}
83+
3984
/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
4085
#[derive(Clone)]
4186
pub(crate) struct GlobalTestOptions {
@@ -295,6 +340,7 @@ pub(crate) fn run_tests(
295340

296341
let mut nb_errors = 0;
297342
let mut ran_edition_tests = 0;
343+
let mut times = MergedDoctestTimes::new();
298344
let target_str = rustdoc_options.target.to_string();
299345

300346
for (MergeableTestKey { edition, global_crate_attrs_hash }, mut doctests) in mergeable_tests {
@@ -314,13 +360,15 @@ pub(crate) fn run_tests(
314360
for (doctest, scraped_test) in &doctests {
315361
tests_runner.add_test(doctest, scraped_test, &target_str);
316362
}
317-
if let Ok(success) = tests_runner.run_merged_tests(
363+
let (duration, ret) = tests_runner.run_merged_tests(
318364
rustdoc_test_options,
319365
edition,
320366
&opts,
321367
&test_args,
322368
rustdoc_options,
323-
) {
369+
);
370+
times.add_compilation_time(duration);
371+
if let Ok(success) = ret {
324372
ran_edition_tests += 1;
325373
if !success {
326374
nb_errors += 1;
@@ -354,11 +402,13 @@ pub(crate) fn run_tests(
354402
test::test_main_with_exit_callback(&test_args, standalone_tests, None, || {
355403
// We ensure temp dir destructor is called.
356404
std::mem::drop(temp_dir.take());
405+
times.display_times();
357406
});
358407
}
359408
if nb_errors != 0 {
360409
// We ensure temp dir destructor is called.
361410
std::mem::drop(temp_dir);
411+
times.display_times();
362412
// libtest::ERROR_EXIT_CODE is not public but it's the same value.
363413
std::process::exit(101);
364414
}
@@ -496,16 +546,19 @@ impl RunnableDocTest {
496546
///
497547
/// This is the function that calculates the compiler command line, invokes the compiler, then
498548
/// invokes the test or tests in a separate executable (if applicable).
549+
///
550+
/// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test.
499551
fn run_test(
500552
doctest: RunnableDocTest,
501553
rustdoc_options: &RustdocOptions,
502554
supports_color: bool,
503555
report_unused_externs: impl Fn(UnusedExterns),
504-
) -> Result<(), TestFailure> {
556+
) -> (Duration, Result<(), TestFailure>) {
505557
let langstr = &doctest.langstr;
506558
// Make sure we emit well-formed executable names for our target.
507559
let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target);
508560
let output_file = doctest.test_opts.outdir.path().join(rust_out);
561+
let instant = Instant::now();
509562

510563
// Common arguments used for compiling the doctest runner.
511564
// On merged doctests, the compiler is invoked twice: once for the test code itself,
@@ -589,7 +642,7 @@ fn run_test(
589642
if std::fs::write(&input_file, &doctest.full_test_code).is_err() {
590643
// If we cannot write this file for any reason, we leave. All combined tests will be
591644
// tested as standalone tests.
592-
return Err(TestFailure::CompileError);
645+
return (Duration::default(), Err(TestFailure::CompileError));
593646
}
594647
if !rustdoc_options.nocapture {
595648
// If `nocapture` is disabled, then we don't display rustc's output when compiling
@@ -660,7 +713,7 @@ fn run_test(
660713
if std::fs::write(&runner_input_file, merged_test_code).is_err() {
661714
// If we cannot write this file for any reason, we leave. All combined tests will be
662715
// tested as standalone tests.
663-
return Err(TestFailure::CompileError);
716+
return (instant.elapsed(), Err(TestFailure::CompileError));
664717
}
665718
if !rustdoc_options.nocapture {
666719
// If `nocapture` is disabled, then we don't display rustc's output when compiling
@@ -713,7 +766,7 @@ fn run_test(
713766
let _bomb = Bomb(&out);
714767
match (output.status.success(), langstr.compile_fail) {
715768
(true, true) => {
716-
return Err(TestFailure::UnexpectedCompilePass);
769+
return (instant.elapsed(), Err(TestFailure::UnexpectedCompilePass));
717770
}
718771
(true, false) => {}
719772
(false, true) => {
@@ -729,17 +782,18 @@ fn run_test(
729782
.collect();
730783

731784
if !missing_codes.is_empty() {
732-
return Err(TestFailure::MissingErrorCodes(missing_codes));
785+
return (instant.elapsed(), Err(TestFailure::MissingErrorCodes(missing_codes)));
733786
}
734787
}
735788
}
736789
(false, false) => {
737-
return Err(TestFailure::CompileError);
790+
return (instant.elapsed(), Err(TestFailure::CompileError));
738791
}
739792
}
740793

794+
let duration = instant.elapsed();
741795
if doctest.no_run {
742-
return Ok(());
796+
return (duration, Ok(()));
743797
}
744798

745799
// Run the code!
@@ -771,17 +825,17 @@ fn run_test(
771825
cmd.output()
772826
};
773827
match result {
774-
Err(e) => return Err(TestFailure::ExecutionError(e)),
828+
Err(e) => return (duration, Err(TestFailure::ExecutionError(e))),
775829
Ok(out) => {
776830
if langstr.should_panic && out.status.success() {
777-
return Err(TestFailure::UnexpectedRunPass);
831+
return (duration, Err(TestFailure::UnexpectedRunPass));
778832
} else if !langstr.should_panic && !out.status.success() {
779-
return Err(TestFailure::ExecutionFailure(out));
833+
return (duration, Err(TestFailure::ExecutionFailure(out)));
780834
}
781835
}
782836
}
783837

784-
Ok(())
838+
(duration, Ok(()))
785839
}
786840

787841
/// Converts a path intended to use as a command to absolute if it is
@@ -1071,7 +1125,7 @@ fn doctest_run_fn(
10711125
no_run: scraped_test.no_run(&rustdoc_options),
10721126
merged_test_code: None,
10731127
};
1074-
let res =
1128+
let (_, res) =
10751129
run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs);
10761130

10771131
if let Err(err) = res {

src/librustdoc/doctest/runner.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::fmt::Write;
2+
use std::time::Duration;
23

34
use rustc_data_structures::fx::FxIndexSet;
45
use rustc_span::edition::Edition;
@@ -67,14 +68,18 @@ impl DocTestRunner {
6768
self.nb_tests += 1;
6869
}
6970

71+
/// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test.
72+
///
73+
/// If compilation failed, it will return `Err`, otherwise it will return `Ok` containing if
74+
/// the test ran successfully.
7075
pub(crate) fn run_merged_tests(
7176
&mut self,
7277
test_options: IndividualTestOptions,
7378
edition: Edition,
7479
opts: &GlobalTestOptions,
7580
test_args: &[String],
7681
rustdoc_options: &RustdocOptions,
77-
) -> Result<bool, ()> {
82+
) -> (Duration, Result<bool, ()>) {
7883
let mut code = "\
7984
#![allow(unused_extern_crates)]
8085
#![allow(internal_features)]
@@ -204,9 +209,9 @@ std::process::Termination::report(test::test_main(test_args, tests, None))
204209
no_run: false,
205210
merged_test_code: Some(code),
206211
};
207-
let ret =
212+
let (duration, ret) =
208213
run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {});
209-
if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) }
214+
(duration, if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) })
210215
}
211216
}
212217

tests/rustdoc-ui/2024-doctests-checks.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//@ compile-flags: --test --test-args=--test-threads=1
44
//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
55
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
6+
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
7+
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
68
//@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL"
79

810
/// ```
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11

22
running 1 test
3-
test $DIR/2024-doctests-checks.rs - Foo (line 8) ... ok
3+
test $DIR/2024-doctests-checks.rs - Foo (line 10) ... ok
44

55
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
66

77

88
running 1 test
9-
test $DIR/2024-doctests-checks.rs - Foo (line 15) ... ok
9+
test $DIR/2024-doctests-checks.rs - Foo (line 17) ... ok
1010

1111
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
1212

13+
all doctests ran in $TIME; merged doctests compilation took $TIME

tests/rustdoc-ui/2024-doctests-crate-attribute.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
55
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
66
//@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL"
7+
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
8+
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
79

810
/// This doctest is used to ensure that if a crate attribute is present,
911
/// it will not be part of the merged doctests.
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11

22
running 1 test
3-
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 20) ... ok
3+
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 22) ... ok
44

55
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
66

77

88
running 1 test
9-
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 11) ... ok
9+
test $DIR/2024-doctests-crate-attribute.rs - Foo (line 13) ... ok
1010

1111
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
1212

13+
all doctests ran in $TIME; merged doctests compilation took $TIME

tests/rustdoc-ui/doctest/dead-code-2024.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//@ compile-flags:--test
55
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
66
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
7+
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
8+
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
79
//@ failure-status: 101
810

911
#![doc(test(attr(allow(unused_variables), deny(warnings))))]
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11

22
running 1 test
3-
test $DIR/dead-code-2024.rs - f (line 13) - compile ... FAILED
3+
test $DIR/dead-code-2024.rs - f (line 15) - compile ... FAILED
44

55
failures:
66

7-
---- $DIR/dead-code-2024.rs - f (line 13) stdout ----
7+
---- $DIR/dead-code-2024.rs - f (line 15) stdout ----
88
error: trait `T` is never used
9-
--> $DIR/dead-code-2024.rs:14:7
9+
--> $DIR/dead-code-2024.rs:16:7
1010
|
1111
LL | trait T { fn f(); }
1212
| ^
1313
|
1414
note: the lint level is defined here
15-
--> $DIR/dead-code-2024.rs:12:9
15+
--> $DIR/dead-code-2024.rs:14:9
1616
|
1717
LL | #![deny(warnings)]
1818
| ^^^^^^^^
@@ -23,7 +23,8 @@ error: aborting due to 1 previous error
2323
Couldn't compile the test.
2424

2525
failures:
26-
$DIR/dead-code-2024.rs - f (line 13)
26+
$DIR/dead-code-2024.rs - f (line 15)
2727

2828
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
2929

30+
all doctests ran in $TIME; merged doctests compilation took $TIME

tests/rustdoc-ui/doctest/dead-code-items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//@ compile-flags:--test --test-args=--test-threads=1
55
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
66
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
7+
//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME"
8+
//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME"
79
//@ failure-status: 101
810

911
#![doc(test(attr(deny(warnings))))]

0 commit comments

Comments
 (0)