Skip to content

Commit 43e5093

Browse files
authored
Rollup merge of #131870 - Zalathar:test-collector, r=jieyouxu
compiletest: Store test collection context/state in two structs This is another incremental cleanup that untangles some of the parameter passing during test collection, making it easier to see which pieces of context information are read-only, and making it easier to find where each field is used.
2 parents 765e8c7 + 5540976 commit 43e5093

File tree

2 files changed

+98
-104
lines changed

2 files changed

+98
-104
lines changed

src/tools/compiletest/src/lib.rs

+95-101
Original file line numberDiff line numberDiff line change
@@ -464,9 +464,7 @@ pub fn run_tests(config: Arc<Config>) {
464464
// structure for each test (or each revision of a multi-revision test).
465465
let mut tests = Vec::new();
466466
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));
470468
}
471469

472470
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 {
545543
}
546544
}
547545

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+
548561
/// Creates libtest structures for every test/revision in the test suite directory.
549562
///
550563
/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
551564
/// regardless of whether any filters/tests were specified on the command-line,
552565
/// 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> {
558567
debug!("making tests from {:?}", config.src_base.display());
559-
let inputs = common_inputs_stamp(&config);
568+
let common_inputs_stamp = common_inputs_stamp(&config);
560569
let modified_tests = modified_tests(&config, &config.src_base).unwrap_or_else(|err| {
561570
panic!("modified_tests got error from dir: {}, error: {}", config.src_base.display(), err)
562571
});
563-
564572
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;
580584

581585
if poisoned {
582586
eprintln!();
583587
panic!("there are errors in tests");
584588
}
589+
590+
check_for_overlapping_test_paths(&found_path_stems);
591+
592+
tests
585593
}
586594

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.)
588602
fn common_inputs_stamp(config: &Config) -> Stamp {
589603
let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
590604

@@ -662,15 +676,10 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> {
662676
/// Recursively scans a directory to find test files and create test structures
663677
/// that will be handed over to libtest.
664678
fn collect_tests_from_dir(
665-
config: Arc<Config>,
666-
cache: &HeadersCache,
679+
cx: &TestCollectorCx,
680+
collector: &mut TestCollector,
667681
dir: &Path,
668682
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,
674683
) -> io::Result<()> {
675684
// Ignore directories that contain a file named `compiletest-ignore-dir`.
676685
if dir.join("compiletest-ignore-dir").exists() {
@@ -679,7 +688,7 @@ fn collect_tests_from_dir(
679688

680689
// For run-make tests, a "test file" is actually a directory that contains
681690
// an `rmake.rs` or `Makefile`"
682-
if config.mode == Mode::RunMake {
691+
if cx.config.mode == Mode::RunMake {
683692
if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() {
684693
return Err(io::Error::other(
685694
"run-make tests cannot have both `Makefile` and `rmake.rs`",
@@ -691,7 +700,7 @@ fn collect_tests_from_dir(
691700
file: dir.to_path_buf(),
692701
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
693702
};
694-
tests.extend(make_test(config, cache, &paths, inputs, poisoned));
703+
make_test(cx, collector, &paths);
695704
// This directory is a test, so don't try to find other tests inside it.
696705
return Ok(());
697706
}
@@ -703,7 +712,7 @@ fn collect_tests_from_dir(
703712
// sequential loop because otherwise, if we do it in the
704713
// tests themselves, they race for the privilege of
705714
// 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);
707716
fs::create_dir_all(&build_dir).unwrap();
708717

709718
// Add each `.rs` file as a test, and recurse further on any
@@ -715,33 +724,25 @@ fn collect_tests_from_dir(
715724
let file_path = file.path();
716725
let file_name = file.file_name();
717726

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+
{
719730
// We found a test file, so create the corresponding libtest structures.
720731
debug!("found test file: {:?}", file_path.display());
721732

722733
// Record the stem of the test file, to check for overlaps later.
723734
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);
725736

726737
let paths =
727738
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);
729740
} else if file_path.is_dir() {
730741
// Recurse to find more tests in a subdirectory.
731742
let relative_file_path = relative_dir_path.join(file.file_name());
732743
if &file_name != "auxiliary" {
733744
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)?;
745746
}
746747
} else {
747748
debug!("found other file/directory: {:?}", file_path.display());
@@ -765,17 +766,11 @@ pub fn is_test(file_name: &OsString) -> bool {
765766

766767
/// For a single test file, creates one or more test structures (one per revision)
767768
/// 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) {
775770
// For run-make tests, each "test file" is actually a _directory_ containing
776771
// an `rmake.rs` or `Makefile`. But for the purposes of directive parsing,
777772
// 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 {
779774
if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() {
780775
panic!("run-make tests cannot have both `rmake.rs` and `Makefile`");
781776
}
@@ -792,57 +787,57 @@ fn make_test(
792787
};
793788

794789
// 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);
796791

797792
// Normally we create one libtest structure per revision, with two exceptions:
798793
// - If a test doesn't use revisions, create a dummy revision (None) so that
799794
// the test can still run.
800795
// - Incremental tests inherently can't run their revisions in parallel, so
801796
// we treat them like non-revisioned tests here. Incremental revisions are
802797
// 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 {
804799
vec![None]
805800
} else {
806801
early_props.revisions.iter().map(|r| Some(r.as_str())).collect()
807802
};
808803

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
810805
// `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+
);
824822

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+
}
834830

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);
837833

838-
test::TestDescAndFn { desc, testfn }
839-
})
840-
.collect()
834+
test::TestDescAndFn { desc, testfn }
835+
}));
841836
}
842837

843838
/// The path of the `stamp` file that gets created or updated whenever a
844839
/// 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 {
846841
output_base_dir(config, testpaths, revision).join("stamp")
847842
}
848843

@@ -891,21 +886,20 @@ fn files_related_to_test(
891886
/// (This is not very reliable in some circumstances, so the `--force-rerun`
892887
/// flag can be used to ignore up-to-date checking and always re-run tests.)
893888
fn is_up_to_date(
894-
config: &Config,
889+
cx: &TestCollectorCx,
895890
testpaths: &TestPaths,
896891
props: &EarlyProps,
897892
revision: Option<&str>,
898-
inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc
899893
) -> bool {
900-
let stamp_name = stamp(config, testpaths, revision);
894+
let stamp_file_path = stamp_file_path(&cx.config, testpaths, revision);
901895
// 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) {
903897
Ok(f) => f,
904898
Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
905899
// The test hasn't succeeded yet, so it is not up-to-date.
906900
Err(_) => return false,
907901
};
908-
let expected_hash = runtest::compute_stamp_hash(config);
902+
let expected_hash = runtest::compute_stamp_hash(&cx.config);
909903
if contents != expected_hash {
910904
// Some part of compiletest configuration has changed since the test
911905
// last succeeded, so it is not up-to-date.
@@ -914,14 +908,14 @@ fn is_up_to_date(
914908

915909
// Check the timestamp of the stamp file against the last modified time
916910
// 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);
920914
}
921915

922916
// If no relevant files have been modified since the stamp file was last
923917
// written, the test is up-to-date.
924-
inputs < Stamp::from_path(&stamp_name)
918+
inputs_stamp < Stamp::from_path(&stamp_file_path)
925919
}
926920

927921
/// The maximum of a set of file-modified timestamps.
@@ -1029,11 +1023,11 @@ fn make_test_closure(
10291023
/// To avoid problems, we forbid test names from overlapping in this way.
10301024
///
10311025
/// 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>) {
10331027
let mut collisions = Vec::new();
1034-
for path in found_paths {
1028+
for path in found_path_stems {
10351029
for ancestor in path.ancestors().skip(1) {
1036-
if found_paths.contains(ancestor) {
1030+
if found_path_stems.contains(ancestor) {
10371031
collisions.push((path, ancestor));
10381032
}
10391033
}

src/tools/compiletest/src/runtest.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::errors::{self, Error, ErrorKind};
2929
use crate::header::TestProps;
3030
use crate::read2::{Truncated, read2_abbreviated};
3131
use crate::util::{PathBufExt, add_dylib_path, logv, static_regex};
32-
use crate::{ColorConfig, json};
32+
use crate::{ColorConfig, json, stamp_file_path};
3333

3434
mod debugger;
3535

@@ -2595,8 +2595,8 @@ impl<'test> TestCx<'test> {
25952595
}
25962596

25972597
fn create_stamp(&self) {
2598-
let stamp = crate::stamp(&self.config, self.testpaths, self.revision);
2599-
fs::write(&stamp, compute_stamp_hash(&self.config)).unwrap();
2598+
let stamp_file_path = stamp_file_path(&self.config, self.testpaths, self.revision);
2599+
fs::write(&stamp_file_path, compute_stamp_hash(&self.config)).unwrap();
26002600
}
26012601

26022602
fn init_incremental_test(&self) {

0 commit comments

Comments
 (0)