@@ -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