@@ -11,7 +11,8 @@ use std::path::{Path, PathBuf};
11
11
use std:: process:: { self , Command , Stdio } ;
12
12
use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
13
13
use std:: sync:: { Arc , Mutex } ;
14
- use std:: { panic, str} ;
14
+ use std:: time:: { Duration , Instant } ;
15
+ use std:: { fmt, panic, str} ;
15
16
16
17
pub ( crate ) use make:: { BuildDocTestBuilder , DocTestBuilder } ;
17
18
pub ( crate ) use markdown:: test as test_markdown;
@@ -36,6 +37,48 @@ use crate::config::{Options as RustdocOptions, OutputFormat};
36
37
use crate :: html:: markdown:: { ErrorCodes , Ignore , LangString , MdRelLine } ;
37
38
use crate :: lint:: init_lints;
38
39
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.
65
+ if self . added_compilation_times > 0 {
66
+ println ! ( "{self}" ) ;
67
+ }
68
+ }
69
+ }
70
+
71
+ impl fmt:: Display for MergedDoctestTimes {
72
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
73
+ write ! (
74
+ f,
75
+ "all doctests ran in {:.2}s; merged doctests compilation took {:.2}s" ,
76
+ self . total_time. elapsed( ) . as_secs_f64( ) ,
77
+ self . compilation_time. as_secs_f64( ) ,
78
+ )
79
+ }
80
+ }
81
+
39
82
/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
40
83
#[ derive( Clone ) ]
41
84
pub ( crate ) struct GlobalTestOptions {
@@ -295,6 +338,7 @@ pub(crate) fn run_tests(
295
338
296
339
let mut nb_errors = 0 ;
297
340
let mut ran_edition_tests = 0 ;
341
+ let mut times = MergedDoctestTimes :: new ( ) ;
298
342
let target_str = rustdoc_options. target . to_string ( ) ;
299
343
300
344
for ( MergeableTestKey { edition, global_crate_attrs_hash } , mut doctests) in mergeable_tests {
@@ -314,13 +358,15 @@ pub(crate) fn run_tests(
314
358
for ( doctest, scraped_test) in & doctests {
315
359
tests_runner. add_test ( doctest, scraped_test, & target_str) ;
316
360
}
317
- if let Ok ( success ) = tests_runner. run_merged_tests (
361
+ let ( duration , ret ) = tests_runner. run_merged_tests (
318
362
rustdoc_test_options,
319
363
edition,
320
364
& opts,
321
365
& test_args,
322
366
rustdoc_options,
323
- ) {
367
+ ) ;
368
+ times. add_compilation_time ( duration) ;
369
+ if let Ok ( success) = ret {
324
370
ran_edition_tests += 1 ;
325
371
if !success {
326
372
nb_errors += 1 ;
@@ -354,11 +400,13 @@ pub(crate) fn run_tests(
354
400
test:: test_main_with_exit_callback ( & test_args, standalone_tests, None , || {
355
401
// We ensure temp dir destructor is called.
356
402
std:: mem:: drop ( temp_dir. take ( ) ) ;
403
+ times. display_times ( ) ;
357
404
} ) ;
358
405
}
359
406
if nb_errors != 0 {
360
407
// We ensure temp dir destructor is called.
361
408
std:: mem:: drop ( temp_dir) ;
409
+ times. display_times ( ) ;
362
410
// libtest::ERROR_EXIT_CODE is not public but it's the same value.
363
411
std:: process:: exit ( 101 ) ;
364
412
}
@@ -496,16 +544,19 @@ impl RunnableDocTest {
496
544
///
497
545
/// This is the function that calculates the compiler command line, invokes the compiler, then
498
546
/// invokes the test or tests in a separate executable (if applicable).
547
+ ///
548
+ /// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test.
499
549
fn run_test (
500
550
doctest : RunnableDocTest ,
501
551
rustdoc_options : & RustdocOptions ,
502
552
supports_color : bool ,
503
553
report_unused_externs : impl Fn ( UnusedExterns ) ,
504
- ) -> Result < ( ) , TestFailure > {
554
+ ) -> ( Duration , Result < ( ) , TestFailure > ) {
505
555
let langstr = & doctest. langstr ;
506
556
// Make sure we emit well-formed executable names for our target.
507
557
let rust_out = add_exe_suffix ( "rust_out" . to_owned ( ) , & rustdoc_options. target ) ;
508
558
let output_file = doctest. test_opts . outdir . path ( ) . join ( rust_out) ;
559
+ let instant = Instant :: now ( ) ;
509
560
510
561
// Common arguments used for compiling the doctest runner.
511
562
// On merged doctests, the compiler is invoked twice: once for the test code itself,
@@ -589,7 +640,7 @@ fn run_test(
589
640
if std:: fs:: write ( & input_file, & doctest. full_test_code ) . is_err ( ) {
590
641
// If we cannot write this file for any reason, we leave. All combined tests will be
591
642
// tested as standalone tests.
592
- return Err ( TestFailure :: CompileError ) ;
643
+ return ( Duration :: default ( ) , Err ( TestFailure :: CompileError ) ) ;
593
644
}
594
645
if !rustdoc_options. nocapture {
595
646
// If `nocapture` is disabled, then we don't display rustc's output when compiling
@@ -660,7 +711,7 @@ fn run_test(
660
711
if std:: fs:: write ( & runner_input_file, merged_test_code) . is_err ( ) {
661
712
// If we cannot write this file for any reason, we leave. All combined tests will be
662
713
// tested as standalone tests.
663
- return Err ( TestFailure :: CompileError ) ;
714
+ return ( instant . elapsed ( ) , Err ( TestFailure :: CompileError ) ) ;
664
715
}
665
716
if !rustdoc_options. nocapture {
666
717
// If `nocapture` is disabled, then we don't display rustc's output when compiling
@@ -713,7 +764,7 @@ fn run_test(
713
764
let _bomb = Bomb ( & out) ;
714
765
match ( output. status . success ( ) , langstr. compile_fail ) {
715
766
( true , true ) => {
716
- return Err ( TestFailure :: UnexpectedCompilePass ) ;
767
+ return ( instant . elapsed ( ) , Err ( TestFailure :: UnexpectedCompilePass ) ) ;
717
768
}
718
769
( true , false ) => { }
719
770
( false , true ) => {
@@ -729,17 +780,18 @@ fn run_test(
729
780
. collect ( ) ;
730
781
731
782
if !missing_codes. is_empty ( ) {
732
- return Err ( TestFailure :: MissingErrorCodes ( missing_codes) ) ;
783
+ return ( instant . elapsed ( ) , Err ( TestFailure :: MissingErrorCodes ( missing_codes) ) ) ;
733
784
}
734
785
}
735
786
}
736
787
( false , false ) => {
737
- return Err ( TestFailure :: CompileError ) ;
788
+ return ( instant . elapsed ( ) , Err ( TestFailure :: CompileError ) ) ;
738
789
}
739
790
}
740
791
792
+ let duration = instant. elapsed ( ) ;
741
793
if doctest. no_run {
742
- return Ok ( ( ) ) ;
794
+ return ( duration , Ok ( ( ) ) ) ;
743
795
}
744
796
745
797
// Run the code!
@@ -771,17 +823,17 @@ fn run_test(
771
823
cmd. output ( )
772
824
} ;
773
825
match result {
774
- Err ( e) => return Err ( TestFailure :: ExecutionError ( e) ) ,
826
+ Err ( e) => return ( duration , Err ( TestFailure :: ExecutionError ( e) ) ) ,
775
827
Ok ( out) => {
776
828
if langstr. should_panic && out. status . success ( ) {
777
- return Err ( TestFailure :: UnexpectedRunPass ) ;
829
+ return ( duration , Err ( TestFailure :: UnexpectedRunPass ) ) ;
778
830
} else if !langstr. should_panic && !out. status . success ( ) {
779
- return Err ( TestFailure :: ExecutionFailure ( out) ) ;
831
+ return ( duration , Err ( TestFailure :: ExecutionFailure ( out) ) ) ;
780
832
}
781
833
}
782
834
}
783
835
784
- Ok ( ( ) )
836
+ ( duration , Ok ( ( ) ) )
785
837
}
786
838
787
839
/// Converts a path intended to use as a command to absolute if it is
@@ -1071,7 +1123,7 @@ fn doctest_run_fn(
1071
1123
no_run : scraped_test. no_run ( & rustdoc_options) ,
1072
1124
merged_test_code : None ,
1073
1125
} ;
1074
- let res =
1126
+ let ( _ , res) =
1075
1127
run_test ( runnable_test, & rustdoc_options, doctest. supports_color , report_unused_externs) ;
1076
1128
1077
1129
if let Err ( err) = res {
0 commit comments