Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compiletest: Custom differ #131181

Merged
merged 5 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@
# passed to cargo invocations.
#jobs = 0

# What custom diff tool to use for displaying compiletest tests.
#compiletest-diff-tool = <none>

# =============================================================================
# General install configuration options
# =============================================================================
Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
if builder.config.cmd.only_modified() {
cmd.arg("--only-modified");
}
if let Some(compiletest_diff_tool) = &builder.config.compiletest_diff_tool {
cmd.arg("--compiletest-diff-tool").arg(compiletest_diff_tool);
}

let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] };
flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests));
Expand Down
6 changes: 6 additions & 0 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,9 @@ pub struct Config {
/// The paths to work with. For example: with `./x check foo bar` we get
/// `paths=["foo", "bar"]`.
pub paths: Vec<PathBuf>,

/// Command for visual diff display, e.g. `diff-tool --color=always`.
pub compiletest_diff_tool: Option<String>,
}

#[derive(Clone, Debug, Default)]
Expand Down Expand Up @@ -892,6 +895,7 @@ define_config! {
android_ndk: Option<PathBuf> = "android-ndk",
optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
jobs: Option<u32> = "jobs",
compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
}
}

Expand Down Expand Up @@ -1512,6 +1516,7 @@ impl Config {
android_ndk,
optimized_compiler_builtins,
jobs,
compiletest_diff_tool,
} = toml.build.unwrap_or_default();

config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0))));
Expand Down Expand Up @@ -2158,6 +2163,7 @@ impl Config {
config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(DebuginfoLevel::None);
config.optimized_compiler_builtins =
optimized_compiler_builtins.unwrap_or(config.channel != "dev");
config.compiletest_diff_tool = compiletest_diff_tool;

let download_rustc = config.download_rustc_commit.is_some();
// See https://github.com/rust-lang/compiler-team/issues/326
Expand Down
5 changes: 5 additions & 0 deletions src/bootstrap/src/utils/change_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
severity: ChangeSeverity::Info,
summary: "Allow setting `--jobs` in config.toml with `build.jobs`.",
},
ChangeInfo {
change_id: 131181,
severity: ChangeSeverity::Info,
summary: "New option `build.compiletest-diff-tool` that adds support for a custom differ for compiletest",
},
];
3 changes: 3 additions & 0 deletions src/tools/compiletest/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ pub struct Config {
/// True if the profiler runtime is enabled for this target.
/// Used by the "needs-profiler-runtime" directive in test files.
pub profiler_runtime: bool,

/// Command for visual diff display, e.g. `diff-tool --color=always`.
pub diff_command: Option<String>,
dev-ardi marked this conversation as resolved.
Show resolved Hide resolved
}

impl Config {
Expand Down
7 changes: 7 additions & 0 deletions src/tools/compiletest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
"git-merge-commit-email",
"email address used for finding merge commits",
"EMAIL",
)
.optopt(
"",
"compiletest-diff-tool",
"What custom diff tool to use for displaying compiletest tests.",
"COMMAND",
);

let (argv0, args_) = args.split_first().unwrap();
Expand Down Expand Up @@ -364,6 +370,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),

profiler_runtime: matches.opt_present("profiler-runtime"),
diff_command: matches.opt_str("compiletest-diff-tool"),
}
}

Expand Down
84 changes: 47 additions & 37 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2459,7 +2459,7 @@ impl<'test> TestCx<'test> {
}
}

fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
fn compare_output(&self, stream: &str, actual: &str, expected: &str) -> usize {
let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) {
// FIXME: We ignore the first line of SVG files
// because the width parameter is non-deterministic.
Expand Down Expand Up @@ -2499,56 +2499,66 @@ impl<'test> TestCx<'test> {
(expected, actual)
};

// Write the actual output to a file in build/
let test_name = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
let actual_path = self
.output_base_name()
.with_extra_extension(self.revision.unwrap_or(""))
.with_extra_extension(test_name)
.with_extra_extension(stream);

if let Err(err) = fs::write(&actual_path, &actual) {
self.fatal(&format!("failed to write {stream} to `{actual_path:?}`: {err}",));
}
println!("Saved the actual {stream} to {actual_path:?}");

let expected_path =
expected_output_path(self.testpaths, self.revision, &self.config.compare_mode, stream);

if !self.config.bless {
if expected.is_empty() {
println!("normalized {}:\n{}\n", kind, actual);
println!("normalized {}:\n{}\n", stream, actual);
} else {
println!("diff of {}:\n", kind);
print!("{}", write_diff(expected, actual, 3));
println!("diff of {stream}:\n");
if let Some(diff_command) = self.config.diff_command.as_deref() {
let mut args = diff_command.split_whitespace();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: technically this is very wonky and not robust, i.e. diff-tool --arg="string-delimited whitespace" will get incorrectly split, but whatever.

let name = args.next().unwrap();
match Command::new(name)
.args(args)
.args([&expected_path, &actual_path])
.output()
{
Err(err) => {
self.fatal(&format!(
"failed to call custom diff command `{diff_command}`: {err}"
));
}
Ok(output) => {
let output = String::from_utf8_lossy(&output.stdout);
print!("{output}");
}
}
} else {
print!("{}", write_diff(expected, actual, 3));
}
}
}

let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
let output_file = self
.output_base_name()
.with_extra_extension(self.revision.unwrap_or(""))
.with_extra_extension(mode)
.with_extra_extension(kind);

let mut files = vec![output_file];
if self.config.bless {
} else {
// Delete non-revision .stderr/.stdout file if revisions are used.
// Without this, we'd just generate the new files and leave the old files around.
if self.revision.is_some() {
let old =
expected_output_path(self.testpaths, None, &self.config.compare_mode, kind);
expected_output_path(self.testpaths, None, &self.config.compare_mode, stream);
self.delete_file(&old);
}
files.push(expected_output_path(
self.testpaths,
self.revision,
&self.config.compare_mode,
kind,
));
}

for output_file in &files {
if actual.is_empty() {
self.delete_file(output_file);
} else if let Err(err) = fs::write(&output_file, &actual) {
self.fatal(&format!(
"failed to write {} to `{}`: {}",
kind,
output_file.display(),
err,
));
if let Err(err) = fs::write(&expected_path, &actual) {
self.fatal(&format!("failed to write {stream} to `{expected_path:?}`: {err}"));
}
println!("Blessing the {stream} of {test_name} in {expected_path:?}");
}

println!("\nThe actual {0} differed from the expected {0}.", kind);
for output_file in files {
println!("Actual {} saved to {}", kind, output_file.display());
}
jieyouxu marked this conversation as resolved.
Show resolved Hide resolved
println!("\nThe actual {0} differed from the expected {0}.", stream);

if self.config.bless { 0 } else { 1 }
}

Expand Down
Loading