@@ -43,6 +43,11 @@ use crate::common::{
43
43
use crate :: header:: HeadersCache ;
44
44
use crate :: util:: logv;
45
45
46
+ /// Creates the `Config` instance for this invocation of compiletest.
47
+ ///
48
+ /// The config mostly reflects command-line arguments, but there might also be
49
+ /// some code here that inspects environment variables or even runs executables
50
+ /// (e.g. when discovering debugger versions).
46
51
pub fn parse_config ( args : Vec < String > ) -> Config {
47
52
let mut opts = Options :: new ( ) ;
48
53
opts. reqopt ( "" , "compile-lib-path" , "path to host shared libraries" , "PATH" )
@@ -413,6 +418,7 @@ pub fn opt_str2(maybestr: Option<String>) -> String {
413
418
}
414
419
}
415
420
421
+ /// Called by `main` after the config has been parsed.
416
422
pub fn run_tests ( config : Arc < Config > ) {
417
423
// If we want to collect rustfix coverage information,
418
424
// we first make sure that the coverage file does not exist.
@@ -454,6 +460,8 @@ pub fn run_tests(config: Arc<Config>) {
454
460
configs. push ( config. clone ( ) ) ;
455
461
} ;
456
462
463
+ // Discover all of the tests in the test suite directory, and build a libtest
464
+ // structure for each test (or each revision of a multi-revision test).
457
465
let mut tests = Vec :: new ( ) ;
458
466
for c in configs {
459
467
let mut found_paths = HashSet :: new ( ) ;
@@ -463,7 +471,12 @@ pub fn run_tests(config: Arc<Config>) {
463
471
464
472
tests. sort_by ( |a, b| a. desc . name . as_slice ( ) . cmp ( & b. desc . name . as_slice ( ) ) ) ;
465
473
474
+ // Delegate to libtest to filter and run the big list of structures created
475
+ // during test discovery. When libtest decides to run a test, it will invoke
476
+ // the corresponding closure created by `make_test_closure`.
466
477
let res = test:: run_tests_console ( & opts, tests) ;
478
+
479
+ // Check the outcome reported by libtest.
467
480
match res {
468
481
Ok ( true ) => { }
469
482
Ok ( false ) => {
@@ -532,6 +545,11 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
532
545
}
533
546
}
534
547
548
+ /// Creates libtest structures for every test/revision in the test suite directory.
549
+ ///
550
+ /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
551
+ /// regardless of whether any filters/tests were specified on the command-line,
552
+ /// because filtering is handled later by libtest.
535
553
pub fn make_tests (
536
554
config : Arc < Config > ,
537
555
tests : & mut Vec < test:: TestDescAndFn > ,
@@ -610,10 +628,17 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
610
628
stamp
611
629
}
612
630
631
+ /// Returns a list of modified/untracked test files that should be run when
632
+ /// the `--only-modified` flag is in use.
633
+ ///
634
+ /// (Might be inaccurate in some cases.)
613
635
fn modified_tests ( config : & Config , dir : & Path ) -> Result < Vec < PathBuf > , String > {
636
+ // If `--only-modified` wasn't passed, the list of modified tests won't be
637
+ // used for anything, so avoid some work and just return an empty list.
614
638
if !config. only_modified {
615
639
return Ok ( vec ! [ ] ) ;
616
640
}
641
+
617
642
let files =
618
643
get_git_modified_files ( & config. git_config ( ) , Some ( dir) , & vec ! [ "rs" , "stderr" , "fixed" ] ) ?
619
644
. unwrap_or ( vec ! [ ] ) ;
@@ -634,6 +659,8 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
634
659
Ok ( full_paths)
635
660
}
636
661
662
+ /// Recursively scans a directory to find test files and create test structures
663
+ /// that will be handed over to libtest.
637
664
fn collect_tests_from_dir (
638
665
config : Arc < Config > ,
639
666
cache : & HeadersCache ,
@@ -650,6 +677,8 @@ fn collect_tests_from_dir(
650
677
return Ok ( ( ) ) ;
651
678
}
652
679
680
+ // For run-make tests, a "test file" is actually a directory that contains
681
+ // an `rmake.rs` or `Makefile`"
653
682
if config. mode == Mode :: RunMake {
654
683
if dir. join ( "Makefile" ) . exists ( ) && dir. join ( "rmake.rs" ) . exists ( ) {
655
684
return Err ( io:: Error :: other (
@@ -663,6 +692,7 @@ fn collect_tests_from_dir(
663
692
relative_dir : relative_dir_path. parent ( ) . unwrap ( ) . to_path_buf ( ) ,
664
693
} ;
665
694
tests. extend ( make_test ( config, cache, & paths, inputs, poisoned) ) ;
695
+ // This directory is a test, so don't try to find other tests inside it.
666
696
return Ok ( ( ) ) ;
667
697
}
668
698
}
@@ -677,22 +707,27 @@ fn collect_tests_from_dir(
677
707
fs:: create_dir_all ( & build_dir) . unwrap ( ) ;
678
708
679
709
// Add each `.rs` file as a test, and recurse further on any
680
- // subdirectories we find, except for `aux ` directories.
710
+ // subdirectories we find, except for `auxiliary ` directories.
681
711
// FIXME: this walks full tests tree, even if we have something to ignore
682
712
// use walkdir/ignore like in tidy?
683
713
for file in fs:: read_dir ( dir) ? {
684
714
let file = file?;
685
715
let file_path = file. path ( ) ;
686
716
let file_name = file. file_name ( ) ;
717
+
687
718
if is_test ( & file_name) && ( !config. only_modified || modified_tests. contains ( & file_path) ) {
719
+ // We found a test file, so create the corresponding libtest structures.
688
720
debug ! ( "found test file: {:?}" , file_path. display( ) ) ;
721
+
722
+ // Record the stem of the test file, to check for overlaps later.
689
723
let rel_test_path = relative_dir_path. join ( file_path. file_stem ( ) . unwrap ( ) ) ;
690
724
found_paths. insert ( rel_test_path) ;
725
+
691
726
let paths =
692
727
TestPaths { file : file_path, relative_dir : relative_dir_path. to_path_buf ( ) } ;
693
-
694
728
tests. extend ( make_test ( config. clone ( ) , cache, & paths, inputs, poisoned) )
695
729
} else if file_path. is_dir ( ) {
730
+ // Recurse to find more tests in a subdirectory.
696
731
let relative_file_path = relative_dir_path. join ( file. file_name ( ) ) ;
697
732
if & file_name != "auxiliary" {
698
733
debug ! ( "found directory: {:?}" , file_path. display( ) ) ;
@@ -728,13 +763,18 @@ pub fn is_test(file_name: &OsString) -> bool {
728
763
!invalid_prefixes. iter ( ) . any ( |p| file_name. starts_with ( p) )
729
764
}
730
765
766
+ /// For a single test file, creates one or more test structures (one per revision)
767
+ /// that can be handed over to libtest to run, possibly in parallel.
731
768
fn make_test (
732
769
config : Arc < Config > ,
733
770
cache : & HeadersCache ,
734
771
testpaths : & TestPaths ,
735
772
inputs : & Stamp ,
736
773
poisoned : & mut bool ,
737
774
) -> Vec < test:: TestDescAndFn > {
775
+ // For run-make tests, each "test file" is actually a _directory_ containing
776
+ // an `rmake.rs` or `Makefile`. But for the purposes of directive parsing,
777
+ // we want to look at that recipe file, not the directory itself.
738
778
let test_path = if config. mode == Mode :: RunMake {
739
779
if testpaths. file . join ( "rmake.rs" ) . exists ( ) && testpaths. file . join ( "Makefile" ) . exists ( ) {
740
780
panic ! ( "run-make tests cannot have both `rmake.rs` and `Makefile`" ) ;
@@ -750,45 +790,66 @@ fn make_test(
750
790
} else {
751
791
PathBuf :: from ( & testpaths. file )
752
792
} ;
793
+
794
+ // Scan the test file to discover its revisions, if any.
753
795
let early_props = EarlyProps :: from_file ( & config, & test_path) ;
754
796
755
- // Incremental tests are special, they inherently cannot be run in parallel.
756
- // `runtest::run` will be responsible for iterating over revisions.
797
+ // Normally we create one libtest structure per revision, with two exceptions:
798
+ // - If a test doesn't use revisions, create a dummy revision (None) so that
799
+ // the test can still run.
800
+ // - Incremental tests inherently can't run their revisions in parallel, so
801
+ // we treat them like non-revisioned tests here. Incremental revisions are
802
+ // handled internally by `runtest::run` instead.
757
803
let revisions = if early_props. revisions . is_empty ( ) || config. mode == Mode :: Incremental {
758
804
vec ! [ None ]
759
805
} else {
760
806
early_props. revisions . iter ( ) . map ( |r| Some ( r. as_str ( ) ) ) . collect ( )
761
807
} ;
762
808
809
+ // For each revision (or the sole dummy revision), create and return a
810
+ // `test::TestDescAndFn` that can be handed over to libtest.
763
811
revisions
764
812
. into_iter ( )
765
813
. map ( |revision| {
814
+ // Create a test name and description to hand over to libtest.
766
815
let src_file =
767
816
std:: fs:: File :: open ( & test_path) . expect ( "open test file to parse ignores" ) ;
768
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.
769
821
let mut desc = make_test_description (
770
822
& config, cache, test_name, & test_path, src_file, revision, poisoned,
771
823
) ;
772
- // Ignore tests that already run and are up to date with respect to inputs.
824
+
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.
773
827
if !config. force_rerun
774
828
&& is_up_to_date ( & config, testpaths, & early_props, revision, inputs)
775
829
{
776
830
desc. ignore = true ;
777
831
// Keep this in sync with the "up-to-date" message detected by bootstrap.
778
832
desc. ignore_message = Some ( "up-to-date" ) ;
779
833
}
780
- test:: TestDescAndFn {
781
- desc,
782
- testfn : make_test_closure ( config. clone ( ) , testpaths, revision) ,
783
- }
834
+
835
+ // Create the callback that will run this test/revision when libtest calls it.
836
+ let testfn = make_test_closure ( config. clone ( ) , testpaths, revision) ;
837
+
838
+ test:: TestDescAndFn { desc, testfn }
784
839
} )
785
840
. collect ( )
786
841
}
787
842
843
+ /// The path of the `stamp` file that gets created or updated whenever a
844
+ /// particular test completes successfully.
788
845
fn stamp ( config : & Config , testpaths : & TestPaths , revision : Option < & str > ) -> PathBuf {
789
846
output_base_dir ( config, testpaths, revision) . join ( "stamp" )
790
847
}
791
848
849
+ /// Returns a list of files that, if modified, would cause this test to no
850
+ /// longer be up-to-date.
851
+ ///
852
+ /// (Might be inaccurate in some cases.)
792
853
fn files_related_to_test (
793
854
config : & Config ,
794
855
testpaths : & TestPaths ,
@@ -824,53 +885,71 @@ fn files_related_to_test(
824
885
related
825
886
}
826
887
888
+ /// Checks whether a particular test/revision is "up-to-date", meaning that no
889
+ /// relevant files/settings have changed since the last time the test succeeded.
890
+ ///
891
+ /// (This is not very reliable in some circumstances, so the `--force-rerun`
892
+ /// flag can be used to ignore up-to-date checking and always re-run tests.)
827
893
fn is_up_to_date (
828
894
config : & Config ,
829
895
testpaths : & TestPaths ,
830
896
props : & EarlyProps ,
831
897
revision : Option < & str > ,
832
- inputs : & Stamp ,
898
+ inputs : & Stamp , // Last-modified timestamp of the compiler, compiletest etc
833
899
) -> bool {
834
900
let stamp_name = stamp ( config, testpaths, revision) ;
835
- // Check hash.
901
+ // Check the config hash inside the stamp file .
836
902
let contents = match fs:: read_to_string ( & stamp_name) {
837
903
Ok ( f) => f,
838
904
Err ( ref e) if e. kind ( ) == ErrorKind :: InvalidData => panic ! ( "Can't read stamp contents" ) ,
905
+ // The test hasn't succeeded yet, so it is not up-to-date.
839
906
Err ( _) => return false ,
840
907
} ;
841
908
let expected_hash = runtest:: compute_stamp_hash ( config) ;
842
909
if contents != expected_hash {
910
+ // Some part of compiletest configuration has changed since the test
911
+ // last succeeded, so it is not up-to-date.
843
912
return false ;
844
913
}
845
914
846
- // Check timestamps.
915
+ // Check the timestamp of the stamp file against the last modified time
916
+ // of all files known to be relevant to the test.
847
917
let mut inputs = inputs. clone ( ) ;
848
918
for path in files_related_to_test ( config, testpaths, props, revision) {
849
919
inputs. add_path ( & path) ;
850
920
}
851
921
922
+ // If no relevant files have been modified since the stamp file was last
923
+ // written, the test is up-to-date.
852
924
inputs < Stamp :: from_path ( & stamp_name)
853
925
}
854
926
927
+ /// The maximum of a set of file-modified timestamps.
855
928
#[ derive( Clone , Debug , PartialEq , Eq , PartialOrd , Ord ) ]
856
929
struct Stamp {
857
930
time : SystemTime ,
858
931
}
859
932
860
933
impl Stamp {
934
+ /// Creates a timestamp holding the last-modified time of the specified file.
861
935
fn from_path ( path : & Path ) -> Self {
862
936
let mut stamp = Stamp { time : SystemTime :: UNIX_EPOCH } ;
863
937
stamp. add_path ( path) ;
864
938
stamp
865
939
}
866
940
941
+ /// Updates this timestamp to the last-modified time of the specified file,
942
+ /// if it is later than the currently-stored timestamp.
867
943
fn add_path ( & mut self , path : & Path ) {
868
944
let modified = fs:: metadata ( path)
869
945
. and_then ( |metadata| metadata. modified ( ) )
870
946
. unwrap_or ( SystemTime :: UNIX_EPOCH ) ;
871
947
self . time = self . time . max ( modified) ;
872
948
}
873
949
950
+ /// Updates this timestamp to the most recent last-modified time of all files
951
+ /// recursively contained in the given directory, if it is later than the
952
+ /// currently-stored timestamp.
874
953
fn add_dir ( & mut self , path : & Path ) {
875
954
for entry in WalkDir :: new ( path) {
876
955
let entry = entry. unwrap ( ) ;
@@ -886,6 +965,7 @@ impl Stamp {
886
965
}
887
966
}
888
967
968
+ /// Creates a name for this test/revision that can be handed over to libtest.
889
969
fn make_test_name (
890
970
config : & Config ,
891
971
testpaths : & TestPaths ,
@@ -914,20 +994,41 @@ fn make_test_name(
914
994
) )
915
995
}
916
996
997
+ /// Creates a callback for this test/revision that libtest will call when it
998
+ /// decides to actually run the underlying test.
917
999
fn make_test_closure (
918
1000
config : Arc < Config > ,
919
1001
testpaths : & TestPaths ,
920
1002
revision : Option < & str > ,
921
1003
) -> test:: TestFn {
922
- let config = config. clone ( ) ;
923
1004
let testpaths = testpaths. clone ( ) ;
924
1005
let revision = revision. map ( str:: to_owned) ;
1006
+
1007
+ // This callback is the link between compiletest's test discovery code,
1008
+ // and the parts of compiletest that know how to run an individual test.
925
1009
test:: DynTestFn ( Box :: new ( move || {
926
1010
runtest:: run ( config, & testpaths, revision. as_deref ( ) ) ;
927
1011
Ok ( ( ) )
928
1012
} ) )
929
1013
}
930
1014
1015
+ /// Checks that test discovery didn't find any tests whose name stem is a prefix
1016
+ /// of some other tests's name.
1017
+ ///
1018
+ /// For example, suppose the test suite contains these two test files:
1019
+ /// - `tests/rustdoc/primitive.rs`
1020
+ /// - `tests/rustdoc/primitive/no_std.rs`
1021
+ ///
1022
+ /// The test runner might put the output from those tests in these directories:
1023
+ /// - `$build/test/rustdoc/primitive/`
1024
+ /// - `$build/test/rustdoc/primitive/no_std/`
1025
+ ///
1026
+ /// Because one output path is a subdirectory of the other, the two tests might
1027
+ /// interfere with each other in unwanted ways, especially if the test runner
1028
+ /// decides to delete test output directories to clean them between runs.
1029
+ /// To avoid problems, we forbid test names from overlapping in this way.
1030
+ ///
1031
+ /// See <https://github.com/rust-lang/rust/pull/109509> for more context.
931
1032
fn check_overlapping_tests ( found_paths : & HashSet < PathBuf > ) {
932
1033
let mut collisions = Vec :: new ( ) ;
933
1034
for path in found_paths {
0 commit comments