Skip to content

Commit

Permalink
Rollup merge of #130566 - jieyouxu:breakup-runtest, r=compiler-errors
Browse files Browse the repository at this point in the history
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 PR breaks the `runtest.rs` file up into smaller helper modules, one for each test suite/mode.

> [!NOTE]
> This PR should not contain functional changes, it is intended to be mostly code motion to breakup `runtest.rs` into smaller helper modules to make it easier to digest.
>
> This PR intentionally does not neatly reorganize where all the methods on `TestCx` goes, that is intended for a follow-up PR. Some methods on `TestCx` do not need to be on `TestCx`. It also does not address a weirdness in valgrind, that is intended for a follow-up PR as well.

Part of a series of compiletest cleanups #130565.

Fixes #89475.

r? `@ghost` (I need to do a self-review pass first)
  • Loading branch information
GuillaumeGomez committed Sep 20, 2024
2 parents 9cbb1cb + 60600a6 commit 6a762c9
Show file tree
Hide file tree
Showing 16 changed files with 2,266 additions and 2,151 deletions.
2,361 changes: 212 additions & 2,149 deletions src/tools/compiletest/src/runtest.rs

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions src/tools/compiletest/src/runtest/assembly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::TestCx;

impl TestCx<'_> {
pub(super) fn run_assembly_test(&self) {
if self.config.llvm_filecheck.is_none() {
self.fatal("missing --llvm-filecheck");
}

let (proc_res, output_path) = self.compile_test_and_save_assembly();
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}

let proc_res = self.verify_with_filecheck(&output_path);
if !proc_res.status.success() {
self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
}
}
}
22 changes: 22 additions & 0 deletions src/tools/compiletest/src/runtest/codegen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use super::{PassMode, TestCx};

impl TestCx<'_> {
pub(super) fn run_codegen_test(&self) {
if self.config.llvm_filecheck.is_none() {
self.fatal("missing --llvm-filecheck");
}

let (proc_res, output_path) = self.compile_test_and_save_ir();
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}

if let Some(PassMode::Build) = self.pass_mode() {
return;
}
let proc_res = self.verify_with_filecheck(&output_path);
if !proc_res.status.success() {
self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
}
}
}
191 changes: 191 additions & 0 deletions src/tools/compiletest/src/runtest/codegen_units.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use std::collections::HashSet;

use super::{Emit, TestCx, WillExecute};
use crate::errors;
use crate::util::static_regex;

impl TestCx<'_> {
pub(super) fn run_codegen_units_test(&self) {
assert!(self.revision.is_none(), "revisions not relevant here");

let proc_res = self.compile_test(WillExecute::No, Emit::None);

if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}

self.check_no_compiler_crash(&proc_res, self.props.should_ice);

const PREFIX: &str = "MONO_ITEM ";
const CGU_MARKER: &str = "@@";

// Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs}
// To prevent the current dir from leaking, we just replace the entire path to the test
// file with TEST_PATH.
let actual: Vec<MonoItem> = proc_res
.stdout
.lines()
.filter(|line| line.starts_with(PREFIX))
.map(|line| {
line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string()
})
.map(|line| str_to_mono_item(&line, true))
.collect();

let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
.iter()
.map(|e| str_to_mono_item(&e.msg[..], false))
.collect();

let mut missing = Vec::new();
let mut wrong_cgus = Vec::new();

for expected_item in &expected {
let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name);

if let Some(actual_item) = actual_item_with_same_name {
if !expected_item.codegen_units.is_empty() &&
// Also check for codegen units
expected_item.codegen_units != actual_item.codegen_units
{
wrong_cgus.push((expected_item.clone(), actual_item.clone()));
}
} else {
missing.push(expected_item.string.clone());
}
}

let unexpected: Vec<_> = actual
.iter()
.filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
.map(|acgu| acgu.string.clone())
.collect();

if !missing.is_empty() {
missing.sort();

println!("\nThese items should have been contained but were not:\n");

for item in &missing {
println!("{}", item);
}

println!("\n");
}

if !unexpected.is_empty() {
let sorted = {
let mut sorted = unexpected.clone();
sorted.sort();
sorted
};

println!("\nThese items were contained but should not have been:\n");

for item in sorted {
println!("{}", item);
}

println!("\n");
}

if !wrong_cgus.is_empty() {
wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
println!("\nThe following items were assigned to wrong codegen units:\n");

for &(ref expected_item, ref actual_item) in &wrong_cgus {
println!("{}", expected_item.name);
println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units));
println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units));
println!();
}
}

if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) {
panic!();
}

#[derive(Clone, Eq, PartialEq)]
struct MonoItem {
name: String,
codegen_units: HashSet<String>,
string: String,
}

// [MONO_ITEM] name [@@ (cgu)+]
fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem {
let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() };

let full_string = format!("{}{}", PREFIX, s);

let parts: Vec<&str> =
s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect();

let name = parts[0].trim();

let cgus = if parts.len() > 1 {
let cgus_str = parts[1];

cgus_str
.split(' ')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(|s| {
if cgu_has_crate_disambiguator {
remove_crate_disambiguators_from_set_of_cgu_names(s)
} else {
s.to_string()
}
})
.collect()
} else {
HashSet::new()
};

MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string }
}

fn codegen_units_to_str(cgus: &HashSet<String>) -> String {
let mut cgus: Vec<_> = cgus.iter().collect();
cgus.sort();

let mut string = String::new();
for cgu in cgus {
string.push_str(&cgu[..]);
string.push(' ');
}

string
}

// Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or
// the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>,
// remove all crate-disambiguators.
fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String {
let Some(captures) =
static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?")
.captures(cgu)
else {
panic!("invalid cgu name encountered: {cgu}");
};

let mut new_name = cgu.to_owned();

if let Some(d2) = captures.name("d2") {
new_name.replace_range(d2.start()..d2.end(), "");
}

let d1 = captures.name("d1").unwrap();
new_name.replace_range(d1.start()..d1.end(), "");

new_name
}

// The name of merged CGUs is constructed as the names of the original
// CGUs joined with "--". This function splits such composite CGU names
// and handles each component individually.
fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String {
cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--")
}
}
}
4 changes: 2 additions & 2 deletions src/tools/compiletest/src/runtest/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl<'test> TestCx<'test> {
.unwrap_or_else(|| self.fatal("missing --coverage-dump"))
}

pub(crate) fn run_coverage_map_test(&self) {
pub(super) fn run_coverage_map_test(&self) {
let coverage_dump_path = self.coverage_dump_path();

let (proc_res, llvm_ir_path) = self.compile_test_and_save_ir();
Expand Down Expand Up @@ -50,7 +50,7 @@ impl<'test> TestCx<'test> {
}
}

pub(crate) fn run_coverage_run_test(&self) {
pub(super) fn run_coverage_run_test(&self) {
let should_run = self.run_if_enabled();
let proc_res = self.compile_test(should_run, Emit::None);

Expand Down
25 changes: 25 additions & 0 deletions src/tools/compiletest/src/runtest/crash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use super::{TestCx, WillExecute};

impl TestCx<'_> {
pub(super) fn run_crash_test(&self) {
let pm = self.pass_mode();
let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));

if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() {
eprintln!("{}", proc_res.status);
eprintln!("{}", proc_res.stdout);
eprintln!("{}", proc_res.stderr);
eprintln!("{}", proc_res.cmdline);
}

// if a test does not crash, consider it an error
if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) {
self.fatal(&format!(
"crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful name, \
add a doc-comment to the start of the test explaining why it exists and \
move it to tests/ui or wherever you see fit. Adding 'Fixes #<issueNr>' to your PR description \
ensures that the corresponding ticket is auto-closed upon merge."
));
}
}
}
Loading

0 comments on commit 6a762c9

Please sign in to comment.