Skip to content

Commit 2a3defd

Browse files
committed
Add basic bless support
1 parent 139dfdb commit 2a3defd

File tree

3 files changed

+127
-33
lines changed

3 files changed

+127
-33
lines changed

src/common.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ impl fmt::Display for Mode {
102102

103103
#[derive(Clone)]
104104
pub struct Config {
105+
/// `true` to overwrite stderr/stdout/fixed files instead of complaining about changes in output.
106+
pub bless: bool,
107+
105108
/// The library paths required for running the compiler
106109
pub compile_lib_path: PathBuf,
107110

@@ -239,6 +242,25 @@ pub struct TestPaths {
239242
pub relative_dir: PathBuf, // e.g., foo/bar
240243
}
241244

245+
/// Used by `ui` tests to generate things like `foo.stderr` from `foo.rs`.
246+
pub fn expected_output_path(
247+
testpaths: &TestPaths,
248+
revision: Option<&str>,
249+
kind: &str,
250+
) -> PathBuf {
251+
assert!(UI_EXTENSIONS.contains(&kind));
252+
let mut parts = Vec::new();
253+
254+
if let Some(x) = revision {
255+
parts.push(x);
256+
}
257+
parts.push(kind);
258+
259+
let extension = parts.join(".");
260+
testpaths.file.with_extension(extension)
261+
}
262+
263+
pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
242264
pub const UI_STDERR: &str = "stderr";
243265
pub const UI_STDOUT: &str = "stdout";
244266
pub const UI_FIXED: &str = "fixed";
@@ -335,6 +357,7 @@ impl Default for Config {
335357
let platform = rustc_session::config::host_triple().to_string();
336358

337359
Config {
360+
bless: false,
338361
compile_lib_path: PathBuf::from(""),
339362
run_lib_path: PathBuf::from(""),
340363
rustc_path: PathBuf::from("rustc"),

src/runtest.rs

Lines changed: 82 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use common::{Config, TestPaths};
12-
use common::{UI_FIXED, UI_STDERR, UI_STDOUT};
12+
use common::{expected_output_path, UI_FIXED, UI_STDERR, UI_STDOUT};
1313
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
1414
use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
1515
use common::{Incremental, RunMake, Ui, MirOpt};
@@ -20,7 +20,7 @@ use json;
2020
use regex::Regex;
2121
use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
2222
use header::TestProps;
23-
use util::logv;
23+
use crate::util::{logv, PathBufExt};
2424

2525
use std::collections::HashMap;
2626
use std::collections::HashSet;
@@ -2167,6 +2167,18 @@ actual:\n\
21672167
// compiler flags set in the test cases:
21682168
cmd.env_remove("RUSTFLAGS");
21692169

2170+
if self.config.bless {
2171+
cmd.env("RUSTC_BLESS_TEST", "--bless");
2172+
// Assume this option is active if the environment variable is "defined", with _any_ value.
2173+
// As an example, a `Makefile` can use this option by:
2174+
//
2175+
// ifdef RUSTC_BLESS_TEST
2176+
// cp "$(TMPDIR)"/actual_something.ext expected_something.ext
2177+
// else
2178+
// $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
2179+
// endif
2180+
}
2181+
21702182
if self.config.target.contains("msvc") {
21712183
// We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
21722184
// and that `lib.exe` lives next to it.
@@ -2323,16 +2335,17 @@ actual:\n\
23232335
}
23242336

23252337
if errors > 0 {
2326-
println!("To update references, run this command from build directory:");
2338+
println!("To update references, rerun the tests and pass the `--bless` flag");
23272339
let relative_path_to_file =
2328-
self.testpaths.relative_dir
2329-
.join(self.testpaths.file.file_name().unwrap());
2330-
println!("{}/update-references.sh '{}' '{}'",
2331-
self.config.src_base.display(),
2332-
self.config.build_base.display(),
2333-
relative_path_to_file.display());
2334-
self.fatal_proc_rec(&format!("{} errors occurred comparing output.", errors),
2335-
&proc_res);
2340+
self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
2341+
println!(
2342+
"To only update this specific test, also pass `--test-args {}`",
2343+
relative_path_to_file.display(),
2344+
);
2345+
self.fatal_proc_rec(
2346+
&format!("{} errors occurred comparing output.", errors),
2347+
&proc_res,
2348+
);
23362349
}
23372350

23382351
if self.props.run_pass {
@@ -2566,11 +2579,14 @@ actual:\n\
25662579
}
25672580

25682581
fn expected_output_path(&self, kind: &str) -> PathBuf {
2569-
let extension = match self.revision {
2570-
Some(r) => format!("{}.{}", r, kind),
2571-
None => kind.to_string(),
2572-
};
2573-
self.testpaths.file.with_extension(extension)
2582+
let mut path =
2583+
expected_output_path(&self.testpaths, self.revision, kind);
2584+
2585+
if !path.exists() {
2586+
path = expected_output_path(&self.testpaths, self.revision, kind);
2587+
}
2588+
2589+
path
25742590
}
25752591

25762592
fn load_expected_output(&self, path: &Path) -> String {
@@ -2594,35 +2610,68 @@ actual:\n\
25942610
})
25952611
}
25962612

2613+
fn delete_file(&self, file: &PathBuf) {
2614+
if !file.exists() {
2615+
// Deleting a nonexistant file would error.
2616+
return;
2617+
}
2618+
if let Err(e) = fs::remove_file(file) {
2619+
self.fatal(&format!("failed to delete `{}`: {}", file.display(), e,));
2620+
}
2621+
}
2622+
25972623
fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
25982624
if actual == expected {
25992625
return 0;
26002626
}
26012627

2602-
println!("normalized {}:\n{}\n", kind, actual);
2603-
println!("expected {}:\n{}\n", kind, expected);
2604-
println!("diff of {}:\n", kind);
2605-
2606-
for diff in diff::lines(expected, actual) {
2607-
match diff {
2608-
diff::Result::Left(l) => println!("-{}", l),
2609-
diff::Result::Both(l, _) => println!(" {}", l),
2610-
diff::Result::Right(r) => println!("+{}", r),
2628+
if !self.config.bless {
2629+
if expected.is_empty() {
2630+
println!("normalized {}:\n{}\n", kind, actual);
2631+
} else {
2632+
println!("diff of {}:\n", kind);
2633+
for diff in diff::lines(expected, actual) {
2634+
match diff {
2635+
diff::Result::Left(l) => println!("-{}", l),
2636+
diff::Result::Both(l, _) => println!(" {}", l),
2637+
diff::Result::Right(r) => println!("+{}", r),
2638+
}
2639+
}
26112640
}
26122641
}
26132642

2614-
let output_file = self.output_base_name().with_extension(kind);
2615-
match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) {
2616-
Ok(()) => { }
2617-
Err(e) => {
2618-
self.fatal(&format!("failed to write {} to `{}`: {}",
2619-
kind, output_file.display(), e))
2643+
let output_file = self
2644+
.output_base_name()
2645+
.with_extra_extension(self.revision.unwrap_or(""))
2646+
.with_extra_extension(kind);
2647+
2648+
let mut files = vec![output_file];
2649+
if self.config.bless {
2650+
files.push(expected_output_path(
2651+
self.testpaths,
2652+
self.revision,
2653+
kind,
2654+
));
2655+
}
2656+
2657+
for output_file in &files {
2658+
if actual.is_empty() {
2659+
self.delete_file(output_file);
2660+
} else if let Err(err) = fs::write(&output_file, &actual) {
2661+
self.fatal(&format!(
2662+
"failed to write {} to `{}`: {}",
2663+
kind,
2664+
output_file.display(),
2665+
err,
2666+
));
26202667
}
26212668
}
26222669

26232670
println!("\nThe actual {0} differed from the expected {0}.", kind);
2624-
println!("Actual {} saved to {}", kind, output_file.display());
2625-
1
2671+
for output_file in files {
2672+
println!("Actual {} saved to {}", kind, output_file.display());
2673+
}
2674+
if self.config.bless { 0 } else { 1 }
26262675
}
26272676
}
26282677

src/util.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
use std::env;
1212
use common::Config;
13+
use std::ffi::OsStr;
14+
use std::path::PathBuf;
1315

1416
/// Conversion table from triple OS name to Rust SYSNAME
1517
const OS_TABLE: &'static [(&'static str, &'static str)] = &[
@@ -108,3 +110,23 @@ pub fn logv(config: &Config, s: String) {
108110
println!("{}", s);
109111
}
110112
}
113+
114+
pub trait PathBufExt {
115+
/// Append an extension to the path, even if it already has one.
116+
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;
117+
}
118+
119+
impl PathBufExt for PathBuf {
120+
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
121+
if extension.as_ref().is_empty() {
122+
self.clone()
123+
} else {
124+
let mut fname = self.file_name().unwrap().to_os_string();
125+
if !extension.as_ref().to_str().unwrap().starts_with('.') {
126+
fname.push(".");
127+
}
128+
fname.push(extension);
129+
self.with_file_name(fname)
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)