@@ -464,9 +464,7 @@ pub fn run_tests(config: Arc<Config>) {
464
464
// structure for each test (or each revision of a multi-revision test).
465
465
let mut tests = Vec :: new ( ) ;
466
466
for c in configs {
467
- let mut found_paths = HashSet :: new ( ) ;
468
- make_tests ( c, & mut tests, & mut found_paths) ;
469
- check_overlapping_tests ( & found_paths) ;
467
+ tests. extend ( collect_and_make_tests ( c) ) ;
470
468
}
471
469
472
470
tests. sort_by ( |a, b| a. desc . name . as_slice ( ) . cmp ( & b. desc . name . as_slice ( ) ) ) ;
@@ -545,46 +543,62 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
545
543
}
546
544
}
547
545
546
+ /// Read-only context data used during test collection.
547
+ struct TestCollectorCx {
548
+ config : Arc < Config > ,
549
+ cache : HeadersCache ,
550
+ common_inputs_stamp : Stamp ,
551
+ modified_tests : Vec < PathBuf > ,
552
+ }
553
+
554
+ /// Mutable state used during test collection.
555
+ struct TestCollector {
556
+ tests : Vec < test:: TestDescAndFn > ,
557
+ found_path_stems : HashSet < PathBuf > ,
558
+ poisoned : bool ,
559
+ }
560
+
548
561
/// Creates libtest structures for every test/revision in the test suite directory.
549
562
///
550
563
/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
551
564
/// regardless of whether any filters/tests were specified on the command-line,
552
565
/// because filtering is handled later by libtest.
553
- pub fn make_tests (
554
- config : Arc < Config > ,
555
- tests : & mut Vec < test:: TestDescAndFn > ,
556
- found_paths : & mut HashSet < PathBuf > ,
557
- ) {
566
+ pub fn collect_and_make_tests ( config : Arc < Config > ) -> Vec < test:: TestDescAndFn > {
558
567
debug ! ( "making tests from {:?}" , config. src_base. display( ) ) ;
559
- let inputs = common_inputs_stamp ( & config) ;
568
+ let common_inputs_stamp = common_inputs_stamp ( & config) ;
560
569
let modified_tests = modified_tests ( & config, & config. src_base ) . unwrap_or_else ( |err| {
561
570
panic ! ( "modified_tests got error from dir: {}, error: {}" , config. src_base. display( ) , err)
562
571
} ) ;
563
-
564
572
let cache = HeadersCache :: load ( & config) ;
565
- let mut poisoned = false ;
566
- collect_tests_from_dir (
567
- config. clone ( ) ,
568
- & cache,
569
- & config. src_base ,
570
- & PathBuf :: new ( ) ,
571
- & inputs,
572
- tests,
573
- found_paths,
574
- & modified_tests,
575
- & mut poisoned,
576
- )
577
- . unwrap_or_else ( |reason| {
578
- panic ! ( "Could not read tests from {}: {reason}" , config. src_base. display( ) )
579
- } ) ;
573
+
574
+ let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests } ;
575
+ let mut collector =
576
+ TestCollector { tests : vec ! [ ] , found_path_stems : HashSet :: new ( ) , poisoned : false } ;
577
+
578
+ collect_tests_from_dir ( & cx, & mut collector, & cx. config . src_base , & PathBuf :: new ( ) )
579
+ . unwrap_or_else ( |reason| {
580
+ panic ! ( "Could not read tests from {}: {reason}" , cx. config. src_base. display( ) )
581
+ } ) ;
582
+
583
+ let TestCollector { tests, found_path_stems, poisoned } = collector;
580
584
581
585
if poisoned {
582
586
eprintln ! ( ) ;
583
587
panic ! ( "there are errors in tests" ) ;
584
588
}
589
+
590
+ check_for_overlapping_test_paths ( & found_path_stems) ;
591
+
592
+ tests
585
593
}
586
594
587
- /// Returns a stamp constructed from input files common to all test cases.
595
+ /// Returns the most recent last-modified timestamp from among the input files
596
+ /// that are considered relevant to all tests (e.g. the compiler, std, and
597
+ /// compiletest itself).
598
+ ///
599
+ /// (Some of these inputs aren't actually relevant to _all_ tests, but they are
600
+ /// common to some subset of tests, and are hopefully unlikely to be modified
601
+ /// while working on other tests.)
588
602
fn common_inputs_stamp ( config : & Config ) -> Stamp {
589
603
let rust_src_dir = config. find_rust_src_root ( ) . expect ( "Could not find Rust source root" ) ;
590
604
@@ -662,15 +676,10 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
662
676
/// Recursively scans a directory to find test files and create test structures
663
677
/// that will be handed over to libtest.
664
678
fn collect_tests_from_dir (
665
- config : Arc < Config > ,
666
- cache : & HeadersCache ,
679
+ cx : & TestCollectorCx ,
680
+ collector : & mut TestCollector ,
667
681
dir : & Path ,
668
682
relative_dir_path : & Path ,
669
- inputs : & Stamp ,
670
- tests : & mut Vec < test:: TestDescAndFn > ,
671
- found_paths : & mut HashSet < PathBuf > ,
672
- modified_tests : & Vec < PathBuf > ,
673
- poisoned : & mut bool ,
674
683
) -> io:: Result < ( ) > {
675
684
// Ignore directories that contain a file named `compiletest-ignore-dir`.
676
685
if dir. join ( "compiletest-ignore-dir" ) . exists ( ) {
@@ -679,7 +688,7 @@ fn collect_tests_from_dir(
679
688
680
689
// For run-make tests, a "test file" is actually a directory that contains
681
690
// an `rmake.rs` or `Makefile`"
682
- if config. mode == Mode :: RunMake {
691
+ if cx . config . mode == Mode :: RunMake {
683
692
if dir. join ( "Makefile" ) . exists ( ) && dir. join ( "rmake.rs" ) . exists ( ) {
684
693
return Err ( io:: Error :: other (
685
694
"run-make tests cannot have both `Makefile` and `rmake.rs`" ,
@@ -691,7 +700,7 @@ fn collect_tests_from_dir(
691
700
file : dir. to_path_buf ( ) ,
692
701
relative_dir : relative_dir_path. parent ( ) . unwrap ( ) . to_path_buf ( ) ,
693
702
} ;
694
- tests . extend ( make_test ( config , cache , & paths, inputs , poisoned ) ) ;
703
+ make_test ( cx , collector , & paths) ;
695
704
// This directory is a test, so don't try to find other tests inside it.
696
705
return Ok ( ( ) ) ;
697
706
}
@@ -703,7 +712,7 @@ fn collect_tests_from_dir(
703
712
// sequential loop because otherwise, if we do it in the
704
713
// tests themselves, they race for the privilege of
705
714
// creating the directories and sometimes fail randomly.
706
- let build_dir = output_relative_path ( & config, relative_dir_path) ;
715
+ let build_dir = output_relative_path ( & cx . config , relative_dir_path) ;
707
716
fs:: create_dir_all ( & build_dir) . unwrap ( ) ;
708
717
709
718
// Add each `.rs` file as a test, and recurse further on any
@@ -715,33 +724,25 @@ fn collect_tests_from_dir(
715
724
let file_path = file. path ( ) ;
716
725
let file_name = file. file_name ( ) ;
717
726
718
- if is_test ( & file_name) && ( !config. only_modified || modified_tests. contains ( & file_path) ) {
727
+ if is_test ( & file_name)
728
+ && ( !cx. config . only_modified || cx. modified_tests . contains ( & file_path) )
729
+ {
719
730
// We found a test file, so create the corresponding libtest structures.
720
731
debug ! ( "found test file: {:?}" , file_path. display( ) ) ;
721
732
722
733
// Record the stem of the test file, to check for overlaps later.
723
734
let rel_test_path = relative_dir_path. join ( file_path. file_stem ( ) . unwrap ( ) ) ;
724
- found_paths . insert ( rel_test_path) ;
735
+ collector . found_path_stems . insert ( rel_test_path) ;
725
736
726
737
let paths =
727
738
TestPaths { file : file_path, relative_dir : relative_dir_path. to_path_buf ( ) } ;
728
- tests . extend ( make_test ( config . clone ( ) , cache , & paths, inputs , poisoned ) )
739
+ make_test ( cx , collector , & paths) ;
729
740
} else if file_path. is_dir ( ) {
730
741
// Recurse to find more tests in a subdirectory.
731
742
let relative_file_path = relative_dir_path. join ( file. file_name ( ) ) ;
732
743
if & file_name != "auxiliary" {
733
744
debug ! ( "found directory: {:?}" , file_path. display( ) ) ;
734
- collect_tests_from_dir (
735
- config. clone ( ) ,
736
- cache,
737
- & file_path,
738
- & relative_file_path,
739
- inputs,
740
- tests,
741
- found_paths,
742
- modified_tests,
743
- poisoned,
744
- ) ?;
745
+ collect_tests_from_dir ( cx, collector, & file_path, & relative_file_path) ?;
745
746
}
746
747
} else {
747
748
debug ! ( "found other file/directory: {:?}" , file_path. display( ) ) ;
@@ -765,17 +766,11 @@ pub fn is_test(file_name: &OsString) -> bool {
765
766
766
767
/// For a single test file, creates one or more test structures (one per revision)
767
768
/// that can be handed over to libtest to run, possibly in parallel.
768
- fn make_test (
769
- config : Arc < Config > ,
770
- cache : & HeadersCache ,
771
- testpaths : & TestPaths ,
772
- inputs : & Stamp ,
773
- poisoned : & mut bool ,
774
- ) -> Vec < test:: TestDescAndFn > {
769
+ fn make_test ( cx : & TestCollectorCx , collector : & mut TestCollector , testpaths : & TestPaths ) {
775
770
// For run-make tests, each "test file" is actually a _directory_ containing
776
771
// an `rmake.rs` or `Makefile`. But for the purposes of directive parsing,
777
772
// we want to look at that recipe file, not the directory itself.
778
- let test_path = if config. mode == Mode :: RunMake {
773
+ let test_path = if cx . config . mode == Mode :: RunMake {
779
774
if testpaths. file . join ( "rmake.rs" ) . exists ( ) && testpaths. file . join ( "Makefile" ) . exists ( ) {
780
775
panic ! ( "run-make tests cannot have both `rmake.rs` and `Makefile`" ) ;
781
776
}
@@ -792,57 +787,57 @@ fn make_test(
792
787
} ;
793
788
794
789
// Scan the test file to discover its revisions, if any.
795
- let early_props = EarlyProps :: from_file ( & config, & test_path) ;
790
+ let early_props = EarlyProps :: from_file ( & cx . config , & test_path) ;
796
791
797
792
// Normally we create one libtest structure per revision, with two exceptions:
798
793
// - If a test doesn't use revisions, create a dummy revision (None) so that
799
794
// the test can still run.
800
795
// - Incremental tests inherently can't run their revisions in parallel, so
801
796
// we treat them like non-revisioned tests here. Incremental revisions are
802
797
// handled internally by `runtest::run` instead.
803
- let revisions = if early_props. revisions . is_empty ( ) || config. mode == Mode :: Incremental {
798
+ let revisions = if early_props. revisions . is_empty ( ) || cx . config . mode == Mode :: Incremental {
804
799
vec ! [ None ]
805
800
} else {
806
801
early_props. revisions . iter ( ) . map ( |r| Some ( r. as_str ( ) ) ) . collect ( )
807
802
} ;
808
803
809
- // For each revision (or the sole dummy revision), create and return a
804
+ // For each revision (or the sole dummy revision), create and append a
810
805
// `test::TestDescAndFn` that can be handed over to libtest.
811
- revisions
812
- . into_iter ( )
813
- . map ( |revision| {
814
- // Create a test name and description to hand over to libtest.
815
- let src_file =
816
- std:: fs:: File :: open ( & test_path) . expect ( "open test file to parse ignores" ) ;
817
- let test_name = crate :: make_test_name ( & config, testpaths, revision) ;
818
- // Create a libtest description for the test/revision.
819
- // This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
820
- // because they need to set the libtest ignored flag.
821
- let mut desc = make_test_description (
822
- & config, cache, test_name, & test_path, src_file, revision, poisoned,
823
- ) ;
806
+ collector. tests . extend ( revisions. into_iter ( ) . map ( |revision| {
807
+ // Create a test name and description to hand over to libtest.
808
+ let src_file = fs:: File :: open ( & test_path) . expect ( "open test file to parse ignores" ) ;
809
+ let test_name = make_test_name ( & cx. config , testpaths, revision) ;
810
+ // Create a libtest description for the test/revision.
811
+ // This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
812
+ // because they need to set the libtest ignored flag.
813
+ let mut desc = make_test_description (
814
+ & cx. config ,
815
+ & cx. cache ,
816
+ test_name,
817
+ & test_path,
818
+ src_file,
819
+ revision,
820
+ & mut collector. poisoned ,
821
+ ) ;
824
822
825
- // If a test's inputs haven't changed since the last time it ran,
826
- // mark it as ignored so that libtest will skip it.
827
- if !config. force_rerun
828
- && is_up_to_date ( & config, testpaths, & early_props, revision, inputs)
829
- {
830
- desc. ignore = true ;
831
- // Keep this in sync with the "up-to-date" message detected by bootstrap.
832
- desc. ignore_message = Some ( "up-to-date" ) ;
833
- }
823
+ // If a test's inputs haven't changed since the last time it ran,
824
+ // mark it as ignored so that libtest will skip it.
825
+ if !cx. config . force_rerun && is_up_to_date ( cx, testpaths, & early_props, revision) {
826
+ desc. ignore = true ;
827
+ // Keep this in sync with the "up-to-date" message detected by bootstrap.
828
+ desc. ignore_message = Some ( "up-to-date" ) ;
829
+ }
834
830
835
- // Create the callback that will run this test/revision when libtest calls it.
836
- let testfn = make_test_closure ( config . clone ( ) , testpaths, revision) ;
831
+ // Create the callback that will run this test/revision when libtest calls it.
832
+ let testfn = make_test_closure ( Arc :: clone ( & cx . config ) , testpaths, revision) ;
837
833
838
- test:: TestDescAndFn { desc, testfn }
839
- } )
840
- . collect ( )
834
+ test:: TestDescAndFn { desc, testfn }
835
+ } ) ) ;
841
836
}
842
837
843
838
/// The path of the `stamp` file that gets created or updated whenever a
844
839
/// particular test completes successfully.
845
- fn stamp ( config : & Config , testpaths : & TestPaths , revision : Option < & str > ) -> PathBuf {
840
+ fn stamp_file_path ( config : & Config , testpaths : & TestPaths , revision : Option < & str > ) -> PathBuf {
846
841
output_base_dir ( config, testpaths, revision) . join ( "stamp" )
847
842
}
848
843
@@ -891,21 +886,20 @@ fn files_related_to_test(
891
886
/// (This is not very reliable in some circumstances, so the `--force-rerun`
892
887
/// flag can be used to ignore up-to-date checking and always re-run tests.)
893
888
fn is_up_to_date (
894
- config : & Config ,
889
+ cx : & TestCollectorCx ,
895
890
testpaths : & TestPaths ,
896
891
props : & EarlyProps ,
897
892
revision : Option < & str > ,
898
- inputs : & Stamp , // Last-modified timestamp of the compiler, compiletest etc
899
893
) -> bool {
900
- let stamp_name = stamp ( config, testpaths, revision) ;
894
+ let stamp_file_path = stamp_file_path ( & cx . config , testpaths, revision) ;
901
895
// Check the config hash inside the stamp file.
902
- let contents = match fs:: read_to_string ( & stamp_name ) {
896
+ let contents = match fs:: read_to_string ( & stamp_file_path ) {
903
897
Ok ( f) => f,
904
898
Err ( ref e) if e. kind ( ) == ErrorKind :: InvalidData => panic ! ( "Can't read stamp contents" ) ,
905
899
// The test hasn't succeeded yet, so it is not up-to-date.
906
900
Err ( _) => return false ,
907
901
} ;
908
- let expected_hash = runtest:: compute_stamp_hash ( config) ;
902
+ let expected_hash = runtest:: compute_stamp_hash ( & cx . config ) ;
909
903
if contents != expected_hash {
910
904
// Some part of compiletest configuration has changed since the test
911
905
// last succeeded, so it is not up-to-date.
@@ -914,14 +908,14 @@ fn is_up_to_date(
914
908
915
909
// Check the timestamp of the stamp file against the last modified time
916
910
// of all files known to be relevant to the test.
917
- let mut inputs = inputs . clone ( ) ;
918
- for path in files_related_to_test ( config, testpaths, props, revision) {
919
- inputs . add_path ( & path) ;
911
+ let mut inputs_stamp = cx . common_inputs_stamp . clone ( ) ;
912
+ for path in files_related_to_test ( & cx . config , testpaths, props, revision) {
913
+ inputs_stamp . add_path ( & path) ;
920
914
}
921
915
922
916
// If no relevant files have been modified since the stamp file was last
923
917
// written, the test is up-to-date.
924
- inputs < Stamp :: from_path ( & stamp_name )
918
+ inputs_stamp < Stamp :: from_path ( & stamp_file_path )
925
919
}
926
920
927
921
/// The maximum of a set of file-modified timestamps.
@@ -1029,11 +1023,11 @@ fn make_test_closure(
1029
1023
/// To avoid problems, we forbid test names from overlapping in this way.
1030
1024
///
1031
1025
/// See <https://github.com/rust-lang/rust/pull/109509> for more context.
1032
- fn check_overlapping_tests ( found_paths : & HashSet < PathBuf > ) {
1026
+ fn check_for_overlapping_test_paths ( found_path_stems : & HashSet < PathBuf > ) {
1033
1027
let mut collisions = Vec :: new ( ) ;
1034
- for path in found_paths {
1028
+ for path in found_path_stems {
1035
1029
for ancestor in path. ancestors ( ) . skip ( 1 ) {
1036
- if found_paths . contains ( ancestor) {
1030
+ if found_path_stems . contains ( ancestor) {
1037
1031
collisions. push ( ( path, ancestor) ) ;
1038
1032
}
1039
1033
}
0 commit comments