Skip to content

Commit 60600a6

Browse files
committed
Break up compiletest runtest.rs into smaller helper modules
Previously compiletest's `runtest.rs` was a massive 4700 lines file that made reading and navigation very awkward. This commit intentionally does not neatly reorganize where all the methods on `TestCx` goes, that is intended for a follow-up PR.
1 parent b7b9453 commit 60600a6

16 files changed

+2266
-2151
lines changed

src/tools/compiletest/src/runtest.rs

+212-2,149
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use super::TestCx;
2+
3+
impl TestCx<'_> {
4+
pub(super) fn run_assembly_test(&self) {
5+
if self.config.llvm_filecheck.is_none() {
6+
self.fatal("missing --llvm-filecheck");
7+
}
8+
9+
let (proc_res, output_path) = self.compile_test_and_save_assembly();
10+
if !proc_res.status.success() {
11+
self.fatal_proc_rec("compilation failed!", &proc_res);
12+
}
13+
14+
let proc_res = self.verify_with_filecheck(&output_path);
15+
if !proc_res.status.success() {
16+
self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use super::{PassMode, TestCx};
2+
3+
impl TestCx<'_> {
4+
pub(super) fn run_codegen_test(&self) {
5+
if self.config.llvm_filecheck.is_none() {
6+
self.fatal("missing --llvm-filecheck");
7+
}
8+
9+
let (proc_res, output_path) = self.compile_test_and_save_ir();
10+
if !proc_res.status.success() {
11+
self.fatal_proc_rec("compilation failed!", &proc_res);
12+
}
13+
14+
if let Some(PassMode::Build) = self.pass_mode() {
15+
return;
16+
}
17+
let proc_res = self.verify_with_filecheck(&output_path);
18+
if !proc_res.status.success() {
19+
self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
20+
}
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
use std::collections::HashSet;
2+
3+
use super::{Emit, TestCx, WillExecute};
4+
use crate::errors;
5+
use crate::util::static_regex;
6+
7+
impl TestCx<'_> {
8+
pub(super) fn run_codegen_units_test(&self) {
9+
assert!(self.revision.is_none(), "revisions not relevant here");
10+
11+
let proc_res = self.compile_test(WillExecute::No, Emit::None);
12+
13+
if !proc_res.status.success() {
14+
self.fatal_proc_rec("compilation failed!", &proc_res);
15+
}
16+
17+
self.check_no_compiler_crash(&proc_res, self.props.should_ice);
18+
19+
const PREFIX: &str = "MONO_ITEM ";
20+
const CGU_MARKER: &str = "@@";
21+
22+
// Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs}
23+
// To prevent the current dir from leaking, we just replace the entire path to the test
24+
// file with TEST_PATH.
25+
let actual: Vec<MonoItem> = proc_res
26+
.stdout
27+
.lines()
28+
.filter(|line| line.starts_with(PREFIX))
29+
.map(|line| {
30+
line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string()
31+
})
32+
.map(|line| str_to_mono_item(&line, true))
33+
.collect();
34+
35+
let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
36+
.iter()
37+
.map(|e| str_to_mono_item(&e.msg[..], false))
38+
.collect();
39+
40+
let mut missing = Vec::new();
41+
let mut wrong_cgus = Vec::new();
42+
43+
for expected_item in &expected {
44+
let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name);
45+
46+
if let Some(actual_item) = actual_item_with_same_name {
47+
if !expected_item.codegen_units.is_empty() &&
48+
// Also check for codegen units
49+
expected_item.codegen_units != actual_item.codegen_units
50+
{
51+
wrong_cgus.push((expected_item.clone(), actual_item.clone()));
52+
}
53+
} else {
54+
missing.push(expected_item.string.clone());
55+
}
56+
}
57+
58+
let unexpected: Vec<_> = actual
59+
.iter()
60+
.filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
61+
.map(|acgu| acgu.string.clone())
62+
.collect();
63+
64+
if !missing.is_empty() {
65+
missing.sort();
66+
67+
println!("\nThese items should have been contained but were not:\n");
68+
69+
for item in &missing {
70+
println!("{}", item);
71+
}
72+
73+
println!("\n");
74+
}
75+
76+
if !unexpected.is_empty() {
77+
let sorted = {
78+
let mut sorted = unexpected.clone();
79+
sorted.sort();
80+
sorted
81+
};
82+
83+
println!("\nThese items were contained but should not have been:\n");
84+
85+
for item in sorted {
86+
println!("{}", item);
87+
}
88+
89+
println!("\n");
90+
}
91+
92+
if !wrong_cgus.is_empty() {
93+
wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
94+
println!("\nThe following items were assigned to wrong codegen units:\n");
95+
96+
for &(ref expected_item, ref actual_item) in &wrong_cgus {
97+
println!("{}", expected_item.name);
98+
println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units));
99+
println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units));
100+
println!();
101+
}
102+
}
103+
104+
if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) {
105+
panic!();
106+
}
107+
108+
#[derive(Clone, Eq, PartialEq)]
109+
struct MonoItem {
110+
name: String,
111+
codegen_units: HashSet<String>,
112+
string: String,
113+
}
114+
115+
// [MONO_ITEM] name [@@ (cgu)+]
116+
fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem {
117+
let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() };
118+
119+
let full_string = format!("{}{}", PREFIX, s);
120+
121+
let parts: Vec<&str> =
122+
s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect();
123+
124+
let name = parts[0].trim();
125+
126+
let cgus = if parts.len() > 1 {
127+
let cgus_str = parts[1];
128+
129+
cgus_str
130+
.split(' ')
131+
.map(str::trim)
132+
.filter(|s| !s.is_empty())
133+
.map(|s| {
134+
if cgu_has_crate_disambiguator {
135+
remove_crate_disambiguators_from_set_of_cgu_names(s)
136+
} else {
137+
s.to_string()
138+
}
139+
})
140+
.collect()
141+
} else {
142+
HashSet::new()
143+
};
144+
145+
MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string }
146+
}
147+
148+
fn codegen_units_to_str(cgus: &HashSet<String>) -> String {
149+
let mut cgus: Vec<_> = cgus.iter().collect();
150+
cgus.sort();
151+
152+
let mut string = String::new();
153+
for cgu in cgus {
154+
string.push_str(&cgu[..]);
155+
string.push(' ');
156+
}
157+
158+
string
159+
}
160+
161+
// Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or
162+
// the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>,
163+
// remove all crate-disambiguators.
164+
fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String {
165+
let Some(captures) =
166+
static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?")
167+
.captures(cgu)
168+
else {
169+
panic!("invalid cgu name encountered: {cgu}");
170+
};
171+
172+
let mut new_name = cgu.to_owned();
173+
174+
if let Some(d2) = captures.name("d2") {
175+
new_name.replace_range(d2.start()..d2.end(), "");
176+
}
177+
178+
let d1 = captures.name("d1").unwrap();
179+
new_name.replace_range(d1.start()..d1.end(), "");
180+
181+
new_name
182+
}
183+
184+
// The name of merged CGUs is constructed as the names of the original
185+
// CGUs joined with "--". This function splits such composite CGU names
186+
// and handles each component individually.
187+
fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String {
188+
cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--")
189+
}
190+
}
191+
}

src/tools/compiletest/src/runtest/coverage.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl<'test> TestCx<'test> {
1818
.unwrap_or_else(|| self.fatal("missing --coverage-dump"))
1919
}
2020

21-
pub(crate) fn run_coverage_map_test(&self) {
21+
pub(super) fn run_coverage_map_test(&self) {
2222
let coverage_dump_path = self.coverage_dump_path();
2323

2424
let (proc_res, llvm_ir_path) = self.compile_test_and_save_ir();
@@ -50,7 +50,7 @@ impl<'test> TestCx<'test> {
5050
}
5151
}
5252

53-
pub(crate) fn run_coverage_run_test(&self) {
53+
pub(super) fn run_coverage_run_test(&self) {
5454
let should_run = self.run_if_enabled();
5555
let proc_res = self.compile_test(should_run, Emit::None);
5656

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use super::{TestCx, WillExecute};
2+
3+
impl TestCx<'_> {
4+
pub(super) fn run_crash_test(&self) {
5+
let pm = self.pass_mode();
6+
let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
7+
8+
if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() {
9+
eprintln!("{}", proc_res.status);
10+
eprintln!("{}", proc_res.stdout);
11+
eprintln!("{}", proc_res.stderr);
12+
eprintln!("{}", proc_res.cmdline);
13+
}
14+
15+
// if a test does not crash, consider it an error
16+
if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) {
17+
self.fatal(&format!(
18+
"crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful name, \
19+
add a doc-comment to the start of the test explaining why it exists and \
20+
move it to tests/ui or wherever you see fit. Adding 'Fixes #<issueNr>' to your PR description \
21+
ensures that the corresponding ticket is auto-closed upon merge."
22+
));
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)