@@ -543,6 +543,21 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
543543 }
544544}
545545
546+ /// Read-only context data used during test collection.
547+ struct TestCollectorCx {
548+ config: Arc<Config>,
549+ cache: HeadersCache,
550+ inputs: 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_paths: HashSet<PathBuf>,
558+ poisoned: bool,
559+ }
560+
546561/// Creates libtest structures for every test/revision in the test suite directory.
547562///
548563/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
@@ -556,24 +571,16 @@ pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> {
556571 });
557572 let cache = HeadersCache::load(&config);
558573
559- let mut tests = vec![];
560- let mut found_paths = HashSet::new();
561- let mut poisoned = false;
562-
563- collect_tests_from_dir(
564- config.clone(),
565- &cache,
566- &config.src_base,
567- &PathBuf::new(),
568- &inputs,
569- &mut tests,
570- &mut found_paths,
571- &modified_tests,
572- &mut poisoned,
573- )
574- .unwrap_or_else(|reason| {
575- panic!("Could not read tests from {}: {reason}", config.src_base.display())
576- });
574+ let cx = TestCollectorCx { config, cache, inputs, modified_tests };
575+ let mut collector =
576+ TestCollector { tests: vec![], found_paths: 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_paths, poisoned } = collector;
577584
578585 if poisoned {
579586 eprintln!();
@@ -663,15 +670,10 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
663670/// Recursively scans a directory to find test files and create test structures
664671/// that will be handed over to libtest.
665672fn collect_tests_from_dir(
666- config: Arc<Config> ,
667- cache : &HeadersCache ,
673+ cx: &TestCollectorCx ,
674+ collector : &mut TestCollector ,
668675 dir: &Path,
669676 relative_dir_path: &Path,
670- inputs: &Stamp,
671- tests: &mut Vec<test::TestDescAndFn>,
672- found_paths: &mut HashSet<PathBuf>,
673- modified_tests: &Vec<PathBuf>,
674- poisoned: &mut bool,
675677) -> io::Result<()> {
676678 // Ignore directories that contain a file named `compiletest-ignore-dir`.
677679 if dir.join("compiletest-ignore-dir").exists() {
@@ -680,7 +682,7 @@ fn collect_tests_from_dir(
680682
681683 // For run-make tests, a "test file" is actually a directory that contains
682684 // an `rmake.rs` or `Makefile`"
683- if config.mode == Mode::RunMake {
685+ if cx. config.mode == Mode::RunMake {
684686 if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() {
685687 return Err(io::Error::other(
686688 "run-make tests cannot have both `Makefile` and `rmake.rs`",
@@ -692,7 +694,7 @@ fn collect_tests_from_dir(
692694 file: dir.to_path_buf(),
693695 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
694696 };
695- tests.extend( make_test(config, cache , &paths, inputs, poisoned) );
697+ make_test(cx, collector , &paths);
696698 // This directory is a test, so don't try to find other tests inside it.
697699 return Ok(());
698700 }
@@ -704,7 +706,7 @@ fn collect_tests_from_dir(
704706 // sequential loop because otherwise, if we do it in the
705707 // tests themselves, they race for the privilege of
706708 // creating the directories and sometimes fail randomly.
707- let build_dir = output_relative_path(&config, relative_dir_path);
709+ let build_dir = output_relative_path(&cx. config, relative_dir_path);
708710 fs::create_dir_all(&build_dir).unwrap();
709711
710712 // Add each `.rs` file as a test, and recurse further on any
@@ -716,33 +718,25 @@ fn collect_tests_from_dir(
716718 let file_path = file.path();
717719 let file_name = file.file_name();
718720
719- if is_test(&file_name) && (!config.only_modified || modified_tests.contains(&file_path)) {
721+ if is_test(&file_name)
722+ && (!cx.config.only_modified || cx.modified_tests.contains(&file_path))
723+ {
720724 // We found a test file, so create the corresponding libtest structures.
721725 debug!("found test file: {:?}", file_path.display());
722726
723727 // Record the stem of the test file, to check for overlaps later.
724728 let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap());
725- found_paths.insert(rel_test_path);
729+ collector. found_paths.insert(rel_test_path);
726730
727731 let paths =
728732 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
729- tests.extend( make_test(config.clone(), cache , &paths, inputs, poisoned))
733+ make_test(cx, collector , &paths);
730734 } else if file_path.is_dir() {
731735 // Recurse to find more tests in a subdirectory.
732736 let relative_file_path = relative_dir_path.join(file.file_name());
733737 if &file_name != "auxiliary" {
734738 debug!("found directory: {:?}", file_path.display());
735- collect_tests_from_dir(
736- config.clone(),
737- cache,
738- &file_path,
739- &relative_file_path,
740- inputs,
741- tests,
742- found_paths,
743- modified_tests,
744- poisoned,
745- )?;
739+ collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?;
746740 }
747741 } else {
748742 debug!("found other file/directory: {:?}", file_path.display());
@@ -766,17 +760,11 @@ pub fn is_test(file_name: &OsString) -> bool {
766760
767761/// For a single test file, creates one or more test structures (one per revision)
768762/// that can be handed over to libtest to run, possibly in parallel.
769- fn make_test(
770- config: Arc<Config>,
771- cache: &HeadersCache,
772- testpaths: &TestPaths,
773- inputs: &Stamp,
774- poisoned: &mut bool,
775- ) -> Vec<test::TestDescAndFn> {
763+ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &TestPaths) {
776764 // For run-make tests, each "test file" is actually a _directory_ containing
777765 // an `rmake.rs` or `Makefile`. But for the purposes of directive parsing,
778766 // we want to look at that recipe file, not the directory itself.
779- let test_path = if config.mode == Mode::RunMake {
767+ let test_path = if cx. config.mode == Mode::RunMake {
780768 if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() {
781769 panic!("run-make tests cannot have both `rmake.rs` and `Makefile`");
782770 }
@@ -793,52 +781,52 @@ fn make_test(
793781 };
794782
795783 // Scan the test file to discover its revisions, if any.
796- let early_props = EarlyProps::from_file(&config, &test_path);
784+ let early_props = EarlyProps::from_file(&cx. config, &test_path);
797785
798786 // Normally we create one libtest structure per revision, with two exceptions:
799787 // - If a test doesn't use revisions, create a dummy revision (None) so that
800788 // the test can still run.
801789 // - Incremental tests inherently can't run their revisions in parallel, so
802790 // we treat them like non-revisioned tests here. Incremental revisions are
803791 // handled internally by `runtest::run` instead.
804- let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
792+ let revisions = if early_props.revisions.is_empty() || cx. config.mode == Mode::Incremental {
805793 vec![None]
806794 } else {
807795 early_props.revisions.iter().map(|r| Some(r.as_str())).collect()
808796 };
809797
810- // For each revision (or the sole dummy revision), create and return a
798+ // For each revision (or the sole dummy revision), create and append a
811799 // `test::TestDescAndFn` that can be handed over to libtest.
812- revisions
813- .into_iter()
814- .map(|revision| {
815- // Create a test name and description to hand over to libtest.
816- let src_file =
817- std::fs::File::open(&test_path).expect("open test file to parse ignores");
818- let test_name = crate::make_test_name(&config, testpaths, revision);
819- // Create a libtest description for the test/revision.
820- // This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
821- // because they need to set the libtest ignored flag.
822- let mut desc = make_test_description(
823- &config, cache, test_name, &test_path, src_file, revision, poisoned,
824- );
800+ collector.tests.extend(revisions.into_iter().map(|revision| {
801+ // Create a test name and description to hand over to libtest.
802+ let src_file = fs::File::open(&test_path).expect("open test file to parse ignores");
803+ let test_name = make_test_name(&cx.config, testpaths, revision);
804+ // Create a libtest description for the test/revision.
805+ // This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
806+ // because they need to set the libtest ignored flag.
807+ let mut desc = make_test_description(
808+ &cx.config,
809+ &cx.cache,
810+ test_name,
811+ &test_path,
812+ src_file,
813+ revision,
814+ &mut collector.poisoned,
815+ );
825816
826- // If a test's inputs haven't changed since the last time it ran,
827- // mark it as ignored so that libtest will skip it.
828- if !config.force_rerun
829- && is_up_to_date(&config, testpaths, &early_props, revision, inputs)
830- {
831- desc.ignore = true;
832- // Keep this in sync with the "up-to-date" message detected by bootstrap.
833- desc.ignore_message = Some("up-to-date");
834- }
817+ // If a test's inputs haven't changed since the last time it ran,
818+ // mark it as ignored so that libtest will skip it.
819+ if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) {
820+ desc.ignore = true;
821+ // Keep this in sync with the "up-to-date" message detected by bootstrap.
822+ desc.ignore_message = Some("up-to-date");
823+ }
835824
836- // Create the callback that will run this test/revision when libtest calls it.
837- let testfn = make_test_closure(config. clone(), testpaths, revision);
825+ // Create the callback that will run this test/revision when libtest calls it.
826+ let testfn = make_test_closure(Arc:: clone(&cx.config ), testpaths, revision);
838827
839- test::TestDescAndFn { desc, testfn }
840- })
841- .collect()
828+ test::TestDescAndFn { desc, testfn }
829+ }));
842830}
843831
844832/// The path of the `stamp` file that gets created or updated whenever a
@@ -892,21 +880,20 @@ fn files_related_to_test(
892880/// (This is not very reliable in some circumstances, so the `--force-rerun`
893881/// flag can be used to ignore up-to-date checking and always re-run tests.)
894882fn is_up_to_date(
895- config : &Config ,
883+ cx : &TestCollectorCx ,
896884 testpaths: &TestPaths,
897885 props: &EarlyProps,
898886 revision: Option<&str>,
899- inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc
900887) -> bool {
901- let stamp_name = stamp(config, testpaths, revision);
888+ let stamp_name = stamp(&cx. config, testpaths, revision);
902889 // Check the config hash inside the stamp file.
903890 let contents = match fs::read_to_string(&stamp_name) {
904891 Ok(f) => f,
905892 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
906893 // The test hasn't succeeded yet, so it is not up-to-date.
907894 Err(_) => return false,
908895 };
909- let expected_hash = runtest::compute_stamp_hash(config);
896+ let expected_hash = runtest::compute_stamp_hash(&cx. config);
910897 if contents != expected_hash {
911898 // Some part of compiletest configuration has changed since the test
912899 // last succeeded, so it is not up-to-date.
@@ -915,8 +902,8 @@ fn is_up_to_date(
915902
916903 // Check the timestamp of the stamp file against the last modified time
917904 // of all files known to be relevant to the test.
918- let mut inputs = inputs.clone();
919- for path in files_related_to_test(config, testpaths, props, revision) {
905+ let mut inputs = cx. inputs.clone();
906+ for path in files_related_to_test(&cx. config, testpaths, props, revision) {
920907 inputs.add_path(&path);
921908 }
922909
0 commit comments