Skip to content

Commit a67227e

Browse files
authored
Rollup merge of rust-lang#131181 - dev-ardi:custom-differ, r=jieyouxu
Compiletest: Custom differ This adds support for a custom differ for compiletests. It’s purely visual and helps produce cleaner output when UI tests fail. I’m using an environment variable for now since it’s experimental and I don’t want to drill the cli arguments all the way down. Also did a bit of general cleanup while I was at it. This is how it looks [with debug info silenced](rust-lang#131182) (rust-lang#131182) `COMPILETEST_DIFF_TOOL="/usr/bin/env difft --color always --background light --display side-by-side" ./x test tests/ui/parser` ![image](https://github.com/user-attachments/assets/f740ce50-7564-4469-be0a-86e24bc50eb8)
2 parents aacf015 + 0e33163 commit a67227e

File tree

7 files changed

+74
-37
lines changed

7 files changed

+74
-37
lines changed

config.example.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@
414414
# Specify the location of the Android NDK. Used when targeting Android.
415415
#android-ndk = "/path/to/android-ndk-r26d"
416416

417+
# What custom diff tool to use for displaying compiletest tests.
418+
#compiletest-diff-tool = <none>
419+
417420
# =============================================================================
418421
# General install configuration options
419422
# =============================================================================

src/bootstrap/src/core/build_steps/test.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
18341834
if builder.config.cmd.only_modified() {
18351835
cmd.arg("--only-modified");
18361836
}
1837+
if let Some(compiletest_diff_tool) = &builder.config.compiletest_diff_tool {
1838+
cmd.arg("--compiletest-diff-tool").arg(compiletest_diff_tool);
1839+
}
18371840

18381841
let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] };
18391842
flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests));

src/bootstrap/src/core/config/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ pub struct Config {
368368
/// The paths to work with. For example: with `./x check foo bar` we get
369369
/// `paths=["foo", "bar"]`.
370370
pub paths: Vec<PathBuf>,
371+
372+
/// Command for visual diff display, e.g. `diff-tool --color=always`.
373+
pub compiletest_diff_tool: Option<String>,
371374
}
372375

373376
#[derive(Clone, Debug, Default)]
@@ -891,6 +894,7 @@ define_config! {
891894
metrics: Option<bool> = "metrics",
892895
android_ndk: Option<PathBuf> = "android-ndk",
893896
optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
897+
compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
894898
}
895899
}
896900

@@ -1511,6 +1515,7 @@ impl Config {
15111515
metrics: _,
15121516
android_ndk,
15131517
optimized_compiler_builtins,
1518+
compiletest_diff_tool,
15141519
} = toml.build.unwrap_or_default();
15151520

15161521
if let Some(file_build) = build {
@@ -2155,6 +2160,7 @@ impl Config {
21552160
config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(DebuginfoLevel::None);
21562161
config.optimized_compiler_builtins =
21572162
optimized_compiler_builtins.unwrap_or(config.channel != "dev");
2163+
config.compiletest_diff_tool = compiletest_diff_tool;
21582164

21592165
let download_rustc = config.download_rustc_commit.is_some();
21602166
// See https://github.com/rust-lang/compiler-team/issues/326

src/bootstrap/src/utils/change_tracker.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,4 +275,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
275275
severity: ChangeSeverity::Info,
276276
summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.",
277277
},
278+
ChangeInfo {
279+
change_id: 131181,
280+
severity: ChangeSeverity::Info,
281+
summary: "New option `build.compiletest-diff-tool` that adds support for a custom differ for compiletest",
282+
},
278283
];

src/tools/compiletest/src/common.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ pub struct Config {
387387
/// True if the profiler runtime is enabled for this target.
388388
/// Used by the "needs-profiler-runtime" directive in test files.
389389
pub profiler_runtime: bool,
390+
391+
/// Command for visual diff display, e.g. `diff-tool --color=always`.
392+
pub diff_command: Option<String>,
390393
}
391394

392395
impl Config {

src/tools/compiletest/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
175175
"git-merge-commit-email",
176176
"email address used for finding merge commits",
177177
"EMAIL",
178+
)
179+
.optopt(
180+
"",
181+
"compiletest-diff-tool",
182+
"What custom diff tool to use for displaying compiletest tests.",
183+
"COMMAND",
178184
);
179185

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

366372
profiler_runtime: matches.opt_present("profiler-runtime"),
373+
diff_command: matches.opt_str("compiletest-diff-tool"),
367374
}
368375
}
369376

src/tools/compiletest/src/runtest.rs

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,7 +2449,7 @@ impl<'test> TestCx<'test> {
24492449
}
24502450
}
24512451

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

2492+
// Write the actual output to a file in build/
2493+
let test_name = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
2494+
let actual_path = self
2495+
.output_base_name()
2496+
.with_extra_extension(self.revision.unwrap_or(""))
2497+
.with_extra_extension(test_name)
2498+
.with_extra_extension(stream);
2499+
2500+
if let Err(err) = fs::write(&actual_path, &actual) {
2501+
self.fatal(&format!("failed to write {stream} to `{actual_path:?}`: {err}",));
2502+
}
2503+
println!("Saved the actual {stream} to {actual_path:?}");
2504+
2505+
let expected_path =
2506+
expected_output_path(self.testpaths, self.revision, &self.config.compare_mode, stream);
2507+
24922508
if !self.config.bless {
24932509
if expected.is_empty() {
2494-
println!("normalized {}:\n{}\n", kind, actual);
2510+
println!("normalized {}:\n{}\n", stream, actual);
24952511
} else {
2496-
println!("diff of {}:\n", kind);
2497-
print!("{}", write_diff(expected, actual, 3));
2512+
println!("diff of {stream}:\n");
2513+
if let Some(diff_command) = self.config.diff_command.as_deref() {
2514+
let mut args = diff_command.split_whitespace();
2515+
let name = args.next().unwrap();
2516+
match Command::new(name)
2517+
.args(args)
2518+
.args([&expected_path, &actual_path])
2519+
.output()
2520+
{
2521+
Err(err) => {
2522+
self.fatal(&format!(
2523+
"failed to call custom diff command `{diff_command}`: {err}"
2524+
));
2525+
}
2526+
Ok(output) => {
2527+
let output = String::from_utf8_lossy(&output.stdout);
2528+
print!("{output}");
2529+
}
2530+
}
2531+
} else {
2532+
print!("{}", write_diff(expected, actual, 3));
2533+
}
24982534
}
2499-
}
2500-
2501-
let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
2502-
let output_file = self
2503-
.output_base_name()
2504-
.with_extra_extension(self.revision.unwrap_or(""))
2505-
.with_extra_extension(mode)
2506-
.with_extra_extension(kind);
2507-
2508-
let mut files = vec![output_file];
2509-
if self.config.bless {
2535+
} else {
25102536
// Delete non-revision .stderr/.stdout file if revisions are used.
25112537
// Without this, we'd just generate the new files and leave the old files around.
25122538
if self.revision.is_some() {
25132539
let old =
2514-
expected_output_path(self.testpaths, None, &self.config.compare_mode, kind);
2540+
expected_output_path(self.testpaths, None, &self.config.compare_mode, stream);
25152541
self.delete_file(&old);
25162542
}
2517-
files.push(expected_output_path(
2518-
self.testpaths,
2519-
self.revision,
2520-
&self.config.compare_mode,
2521-
kind,
2522-
));
2523-
}
25242543

2525-
for output_file in &files {
2526-
if actual.is_empty() {
2527-
self.delete_file(output_file);
2528-
} else if let Err(err) = fs::write(&output_file, &actual) {
2529-
self.fatal(&format!(
2530-
"failed to write {} to `{}`: {}",
2531-
kind,
2532-
output_file.display(),
2533-
err,
2534-
));
2544+
if let Err(err) = fs::write(&expected_path, &actual) {
2545+
self.fatal(&format!("failed to write {stream} to `{expected_path:?}`: {err}"));
25352546
}
2547+
println!("Blessing the {stream} of {test_name} in {expected_path:?}");
25362548
}
25372549

2538-
println!("\nThe actual {0} differed from the expected {0}.", kind);
2539-
for output_file in files {
2540-
println!("Actual {} saved to {}", kind, output_file.display());
2541-
}
2550+
println!("\nThe actual {0} differed from the expected {0}.", stream);
2551+
25422552
if self.config.bless { 0 } else { 1 }
25432553
}
25442554

0 commit comments

Comments
 (0)