@@ -43,6 +43,11 @@ use crate::common::{
4343use crate::header::HeadersCache;
4444use crate::util::logv;
4545
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).
4651pub fn parse_config(args: Vec<String>) -> Config {
4752 let mut opts = Options::new();
4853 opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
@@ -413,6 +418,7 @@ pub fn opt_str2(maybestr: Option<String>) -> String {
413418 }
414419}
415420
421+ /// Called by `main` after the config has been parsed.
416422pub fn run_tests(config: Arc<Config>) {
417423 // If we want to collect rustfix coverage information,
418424 // we first make sure that the coverage file does not exist.
@@ -454,6 +460,8 @@ pub fn run_tests(config: Arc<Config>) {
454460 configs.push(config.clone());
455461 };
456462
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).
457465 let mut tests = Vec::new();
458466 for c in configs {
459467 let mut found_paths = HashSet::new();
@@ -463,7 +471,12 @@ pub fn run_tests(config: Arc<Config>) {
463471
464472 tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
465473
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`.
466477 let res = test::run_tests_console(&opts, tests);
478+
479+ // Check the outcome reported by libtest.
467480 match res {
468481 Ok(true) => {}
469482 Ok(false) => {
@@ -532,6 +545,11 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
532545 }
533546}
534547
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.
535553pub fn make_tests(
536554 config: Arc<Config>,
537555 tests: &mut Vec<test::TestDescAndFn>,
@@ -610,10 +628,17 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
610628 stamp
611629}
612630
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.)
613635fn 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.
614638 if !config.only_modified {
615639 return Ok(vec![]);
616640 }
641+
617642 let files =
618643 get_git_modified_files(&config.git_config(), Some(dir), &vec!["rs", "stderr", "fixed"])?
619644 .unwrap_or(vec![]);
@@ -634,6 +659,8 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
634659 Ok(full_paths)
635660}
636661
662+ /// Recursively scans a directory to find test files and create test structures
663+ /// that will be handed over to libtest.
637664fn collect_tests_from_dir(
638665 config: Arc<Config>,
639666 cache: &HeadersCache,
@@ -650,6 +677,8 @@ fn collect_tests_from_dir(
650677 return Ok(());
651678 }
652679
680+ // For run-make tests, a "test file" is actually a directory that contains
681+ // an `rmake.rs` or `Makefile`"
653682 if config.mode == Mode::RunMake {
654683 if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() {
655684 return Err(io::Error::other(
@@ -663,6 +692,7 @@ fn collect_tests_from_dir(
663692 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
664693 };
665694 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.
666696 return Ok(());
667697 }
668698 }
@@ -677,22 +707,27 @@ fn collect_tests_from_dir(
677707 fs::create_dir_all(&build_dir).unwrap();
678708
679709 // 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.
681711 // FIXME: this walks full tests tree, even if we have something to ignore
682712 // use walkdir/ignore like in tidy?
683713 for file in fs::read_dir(dir)? {
684714 let file = file?;
685715 let file_path = file.path();
686716 let file_name = file.file_name();
717+
687718 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.
688720 debug!("found test file: {:?}", file_path.display());
721+
722+ // Record the stem of the test file, to check for overlaps later.
689723 let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap());
690724 found_paths.insert(rel_test_path);
725+
691726 let paths =
692727 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
693-
694728 tests.extend(make_test(config.clone(), cache, &paths, inputs, poisoned))
695729 } else if file_path.is_dir() {
730+ // Recurse to find more tests in a subdirectory.
696731 let relative_file_path = relative_dir_path.join(file.file_name());
697732 if &file_name != "auxiliary" {
698733 debug!("found directory: {:?}", file_path.display());
@@ -728,13 +763,18 @@ pub fn is_test(file_name: &OsString) -> bool {
728763 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
729764}
730765
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.
731768fn make_test(
732769 config: Arc<Config>,
733770 cache: &HeadersCache,
734771 testpaths: &TestPaths,
735772 inputs: &Stamp,
736773 poisoned: &mut bool,
737774) -> 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.
738778 let test_path = if config.mode == Mode::RunMake {
739779 if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() {
740780 panic!("run-make tests cannot have both `rmake.rs` and `Makefile`");
@@ -750,45 +790,66 @@ fn make_test(
750790 } else {
751791 PathBuf::from(&testpaths.file)
752792 };
793+
794+ // Scan the test file to discover its revisions, if any.
753795 let early_props = EarlyProps::from_file(&config, &test_path);
754796
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.
757803 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
758804 vec![None]
759805 } else {
760806 early_props.revisions.iter().map(|r| Some(r.as_str())).collect()
761807 };
762808
809+ // For each revision (or the sole dummy revision), create and return a
810+ // `test::TestDescAndFn` that can be handed over to libtest.
763811 revisions
764812 .into_iter()
765813 .map(|revision| {
814+ // Create a test name and description to hand over to libtest.
766815 let src_file =
767816 std::fs::File::open(&test_path).expect("open test file to parse ignores");
768817 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.
769821 let mut desc = make_test_description(
770822 &config, cache, test_name, &test_path, src_file, revision, poisoned,
771823 );
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.
773827 if !config.force_rerun
774828 && is_up_to_date(&config, testpaths, &early_props, revision, inputs)
775829 {
776830 desc.ignore = true;
777831 // Keep this in sync with the "up-to-date" message detected by bootstrap.
778832 desc.ignore_message = Some("up-to-date");
779833 }
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 }
784839 })
785840 .collect()
786841}
787842
843+ /// The path of the `stamp` file that gets created or updated whenever a
844+ /// particular test completes successfully.
788845fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
789846 output_base_dir(config, testpaths, revision).join("stamp")
790847}
791848
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.)
792853fn files_related_to_test(
793854 config: &Config,
794855 testpaths: &TestPaths,
@@ -824,53 +885,71 @@ fn files_related_to_test(
824885 related
825886}
826887
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.)
827893fn is_up_to_date(
828894 config: &Config,
829895 testpaths: &TestPaths,
830896 props: &EarlyProps,
831897 revision: Option<&str>,
832- inputs: &Stamp,
898+ inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc
833899) -> bool {
834900 let stamp_name = stamp(config, testpaths, revision);
835- // Check hash.
901+ // Check the config hash inside the stamp file .
836902 let contents = match fs::read_to_string(&stamp_name) {
837903 Ok(f) => f,
838904 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.
839906 Err(_) => return false,
840907 };
841908 let expected_hash = runtest::compute_stamp_hash(config);
842909 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.
843912 return false;
844913 }
845914
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.
847917 let mut inputs = inputs.clone();
848918 for path in files_related_to_test(config, testpaths, props, revision) {
849919 inputs.add_path(&path);
850920 }
851921
922+ // If no relevant files have been modified since the stamp file was last
923+ // written, the test is up-to-date.
852924 inputs < Stamp::from_path(&stamp_name)
853925}
854926
927+ /// The maximum of a set of file-modified timestamps.
855928#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
856929struct Stamp {
857930 time: SystemTime,
858931}
859932
860933impl Stamp {
934+ /// Creates a timestamp holding the last-modified time of the specified file.
861935 fn from_path(path: &Path) -> Self {
862936 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
863937 stamp.add_path(path);
864938 stamp
865939 }
866940
941+ /// Updates this timestamp to the last-modified time of the specified file,
942+ /// if it is later than the currently-stored timestamp.
867943 fn add_path(&mut self, path: &Path) {
868944 let modified = fs::metadata(path)
869945 .and_then(|metadata| metadata.modified())
870946 .unwrap_or(SystemTime::UNIX_EPOCH);
871947 self.time = self.time.max(modified);
872948 }
873949
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.
874953 fn add_dir(&mut self, path: &Path) {
875954 for entry in WalkDir::new(path) {
876955 let entry = entry.unwrap();
@@ -886,6 +965,7 @@ impl Stamp {
886965 }
887966}
888967
968+ /// Creates a name for this test/revision that can be handed over to libtest.
889969fn make_test_name(
890970 config: &Config,
891971 testpaths: &TestPaths,
@@ -914,20 +994,41 @@ fn make_test_name(
914994 ))
915995}
916996
997+ /// Creates a callback for this test/revision that libtest will call when it
998+ /// decides to actually run the underlying test.
917999fn make_test_closure(
9181000 config: Arc<Config>,
9191001 testpaths: &TestPaths,
9201002 revision: Option<&str>,
9211003) -> test::TestFn {
922- let config = config.clone();
9231004 let testpaths = testpaths.clone();
9241005 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.
9251009 test::DynTestFn(Box::new(move || {
9261010 runtest::run(config, &testpaths, revision.as_deref());
9271011 Ok(())
9281012 }))
9291013}
9301014
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.
9311032fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) {
9321033 let mut collisions = Vec::new();
9331034 for path in found_paths {
0 commit comments