use crate::errors::{self, Error, ErrorKind};
use crate::header::TestProps;
use crate::json;
+use crate::util::get_pointer_width;
use crate::util::{logv, PathBufExt};
use diff;
use regex::{Captures, Regex};
use std::collections::{HashMap, HashSet, VecDeque};
use std::env;
use std::ffi::{OsStr, OsString};
-use std::fmt;
use std::fs::{self, create_dir_all, File, OpenOptions};
use std::hash::{Hash, Hasher};
use std::io::prelude::*;
results
}
+fn print_diff(expected: &str, actual: &str, context_size: usize) {
+ let diff_results = make_diff(expected, actual, context_size);
+ for result in diff_results {
+ let mut line_number = result.line_number;
+ for line in result.lines {
+ match line {
+ DiffLine::Expected(e) => {
+ println!("-\t{}", e);
+ line_number += 1;
+ }
+ DiffLine::Context(c) => {
+ println!("{}\t{}", line_number, c);
+ line_number += 1;
+ }
+ DiffLine::Resulting(r) => {
+ println!("+\t{}", r);
+ }
+ }
+ }
+ println!();
+ }
+}
+
pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
match &*config.target {
"arm-linux-androideabi"
Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => {
WillExecute::Yes
}
- Ui => WillExecute::No,
+ MirOpt if pm == Some(PassMode::Run) => WillExecute::Yes,
+ Ui | MirOpt => WillExecute::No,
mode => panic!("unimplemented for mode {:?}", mode),
}
}
fn should_run_successfully(&self, pm: Option<PassMode>) -> bool {
match self.config.mode {
- Ui => pm == Some(PassMode::Run),
+ Ui | MirOpt => pm == Some(PassMode::Run),
mode => panic!("unimplemented for mode {:?}", mode),
}
}
if let Some(ref incremental_dir) = self.props.incremental_dir {
rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]);
rustc.args(&["-Z", "incremental-verify-ich"]);
- rustc.args(&["-Z", "incremental-queries"]);
}
if self.config.mode == CodegenUnits {
None => {}
}
+ // Add `-A unused` before `config` flags and in-test (`props`) flags, so that they can
+ // overwrite this.
+ if let AllowUnused::Yes = allow_unused {
+ rustc.args(&["-A", "unused"]);
+ }
+
if self.props.force_host {
self.maybe_add_external_args(
&mut rustc,
rustc.arg("-Ctarget-feature=-crt-static");
}
- if let AllowUnused::Yes = allow_unused {
- rustc.args(&["-A", "unused"]);
- }
-
rustc.args(&self.props.compile_flags);
rustc
.filter(|s| !s.is_empty())
.map(|s| {
if cgu_has_crate_disambiguator {
- remove_crate_disambiguator_from_cgu(s)
+ remove_crate_disambiguators_from_set_of_cgu_names(s)
} else {
s.to_string()
}
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(|cgu| remove_crate_disambiguator_from_cgu(cgu))
+ .collect::<Vec<_>>()
+ .join("--")
+ }
}
fn init_incremental_test(&self) {
// - if `cfail`, expect compilation to fail
// - if `rfail`, expect execution to fail
// - create a directory build/foo/bar.incremental
- // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass1
+ // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1
// - because name of revision starts with "rpass", expect success
- // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C cfail2
+ // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C cfail2
// - because name of revision starts with "cfail", expect an error
// - load expected errors as usual, but filter for those that end in `[rfail2]`
- // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass3
+ // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass3
// - because name of revision starts with "rpass", expect success
// - execute build/foo/bar.exe and save output
//
self.document(&out_dir);
let root = self.config.find_rust_src_root().unwrap();
+ let file_stem =
+ self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem");
let res = self.cmd2procres(
Command::new(&nodejs)
.arg(root.join("src/tools/rustdoc-js/tester.js"))
- .arg(out_dir.parent().expect("no parent"))
- .arg(&self.testpaths.file.file_stem().expect("couldn't get file stem")),
+ .arg("--doc-folder")
+ .arg(out_dir)
+ .arg("--crate-name")
+ .arg(file_stem.replace("-", "_"))
+ .arg("--test-file")
+ .arg(self.testpaths.file.with_extension("js")),
);
if !res.status.success() {
self.fatal_proc_rec("rustdoc-js test failed!", &res);
}
fn run_mir_opt_test(&self) {
- let proc_res = self.compile_test(WillExecute::Yes, EmitMetadata::No);
+ let pm = self.pass_mode();
+ let should_run = self.should_run(pm);
+ let emit_metadata = self.should_emit_metadata(pm);
+ let proc_res = self.compile_test(should_run, emit_metadata);
if !proc_res.status.success() {
self.fatal_proc_rec("compilation failed!", &proc_res);
}
- let proc_res = self.exec_compiled_test();
+ self.check_mir_dump();
- if !proc_res.status.success() {
- self.fatal_proc_rec("test run failed!", &proc_res);
+ if let WillExecute::Yes = should_run {
+ let proc_res = self.exec_compiled_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("test run failed!", &proc_res);
+ }
}
- self.check_mir_dump();
}
fn check_mir_dump(&self) {
let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap();
- if let Some(idx) = test_file_contents.find("// END RUST SOURCE") {
- let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len());
- let tests_text_str = String::from(tests_text);
- let mut curr_test: Option<&str> = None;
- let mut curr_test_contents = vec![ExpectedLine::Elision];
- for l in tests_text_str.lines() {
- debug!("line: {:?}", l);
- if l.starts_with("// START ") {
- let (_, t) = l.split_at("// START ".len());
- curr_test = Some(t);
- } else if l.starts_with("// END") {
- let (_, t) = l.split_at("// END ".len());
- if Some(t) != curr_test {
- panic!("mismatched START END test name");
+
+ let mut test_dir = self.testpaths.file.with_extension("");
+
+ if test_file_contents.lines().any(|l| l == "// EMIT_MIR_FOR_EACH_BIT_WIDTH") {
+ test_dir.push(get_pointer_width(&self.config.target))
+ }
+
+ if self.config.bless {
+ let _ = std::fs::remove_dir_all(&test_dir);
+ }
+ for l in test_file_contents.lines() {
+ if l.starts_with("// EMIT_MIR ") {
+ let test_name = l.trim_start_matches("// EMIT_MIR ");
+ let expected_file = test_dir.join(test_name);
+
+ let dumped_string = if test_name.ends_with(".diff") {
+ let test_name = test_name.trim_end_matches(".diff");
+ let before = format!("{}.before.mir", test_name);
+ let after = format!("{}.after.mir", test_name);
+ let before = self.get_mir_dump_dir().join(before);
+ let after = self.get_mir_dump_dir().join(after);
+ debug!(
+ "comparing the contents of: {} with {}",
+ before.display(),
+ after.display()
+ );
+ let before = fs::read_to_string(before).unwrap();
+ let after = fs::read_to_string(after).unwrap();
+ let before = self.normalize_output(&before, &[]);
+ let after = self.normalize_output(&after, &[]);
+ let mut dumped_string = String::new();
+ for result in diff::lines(&before, &after) {
+ use std::fmt::Write;
+ match result {
+ diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(),
+ diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(),
+ diff::Result::Both(s, _) => writeln!(dumped_string, " {}", s).unwrap(),
+ }
+ }
+ dumped_string
+ } else {
+ let mut output_file = PathBuf::new();
+ output_file.push(self.get_mir_dump_dir());
+ output_file.push(test_name);
+ debug!(
+ "comparing the contents of: {} with {}",
+ output_file.display(),
+ expected_file.display()
+ );
+ if !output_file.exists() {
+ panic!(
+ "Output file `{}` from test does not exist, available files are in `{}`",
+ output_file.display(),
+ output_file.parent().unwrap().display()
+ );
+ }
+ self.check_mir_test_timestamp(test_name, &output_file);
+ let dumped_string = fs::read_to_string(&output_file).unwrap();
+ self.normalize_output(&dumped_string, &[])
+ };
+ if self.config.bless {
+ let _ = std::fs::create_dir_all(&test_dir);
+ let _ = std::fs::remove_file(&expected_file);
+ std::fs::write(expected_file, dumped_string.as_bytes()).unwrap();
+ } else {
+ if !expected_file.exists() {
+ panic!(
+ "Output file `{}` from test does not exist",
+ expected_file.display()
+ );
+ }
+ let expected_string = fs::read_to_string(&expected_file).unwrap();
+ if dumped_string != expected_string {
+ print_diff(&expected_string, &dumped_string, 3);
+ panic!(
+ "Actual MIR output differs from expected MIR output {}",
+ expected_file.display()
+ );
}
- self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents);
- curr_test = None;
- curr_test_contents.clear();
- curr_test_contents.push(ExpectedLine::Elision);
- } else if l.is_empty() {
- // ignore
- } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." {
- curr_test_contents.push(ExpectedLine::Elision)
- } else if l.starts_with("// ") {
- let (_, test_content) = l.split_at("// ".len());
- curr_test_contents.push(ExpectedLine::Text(test_content));
}
}
}
}
}
- fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) {
- let mut output_file = PathBuf::new();
- output_file.push(self.get_mir_dump_dir());
- output_file.push(test_name);
- debug!("comparing the contests of: {:?}", output_file);
- debug!("with: {:?}", expected_content);
- if !output_file.exists() {
- panic!(
- "Output file `{}` from test does not exist",
- output_file.into_os_string().to_string_lossy()
- );
- }
- self.check_mir_test_timestamp(test_name, &output_file);
-
- let dumped_string = fs::read_to_string(&output_file).unwrap();
- let mut dumped_lines =
- dumped_string.lines().map(|l| nocomment_mir_line(l)).filter(|l| !l.is_empty());
- let mut expected_lines = expected_content
- .iter()
- .filter(|&l| if let &ExpectedLine::Text(l) = l { !l.is_empty() } else { true })
- .peekable();
-
- let compare = |expected_line, dumped_line| {
- let e_norm = normalize_mir_line(expected_line);
- let d_norm = normalize_mir_line(dumped_line);
- debug!("found: {:?}", d_norm);
- debug!("expected: {:?}", e_norm);
- e_norm == d_norm
- };
-
- let error = |expected_line, extra_msg| {
- let normalize_all = dumped_string
- .lines()
- .map(nocomment_mir_line)
- .filter(|l| !l.is_empty())
- .collect::<Vec<_>>()
- .join("\n");
- let f = |l: &ExpectedLine<_>| match l {
- &ExpectedLine::Elision => "... (elided)".into(),
- &ExpectedLine::Text(t) => t,
- };
- let expected_content =
- expected_content.iter().map(|l| f(l)).collect::<Vec<_>>().join("\n");
- panic!(
- "Did not find expected line, error: {}\n\
- Expected Line: {:?}\n\
- Test Name: {}\n\
- Expected:\n{}\n\
- Actual:\n{}",
- extra_msg, expected_line, test_name, expected_content, normalize_all
- );
- };
-
- // We expect each non-empty line to appear consecutively, non-consecutive lines
- // must be separated by at least one Elision
- let mut start_block_line = None;
- while let Some(dumped_line) = dumped_lines.next() {
- match expected_lines.next() {
- Some(&ExpectedLine::Text(expected_line)) => {
- let normalized_expected_line = normalize_mir_line(expected_line);
- if normalized_expected_line.contains(":{") {
- start_block_line = Some(expected_line);
- }
-
- if !compare(expected_line, dumped_line) {
- error!("{:?}", start_block_line);
- error(
- expected_line,
- format!(
- "Mismatch in lines\n\
- Current block: {}\n\
- Actual Line: {:?}",
- start_block_line.unwrap_or("None"),
- dumped_line
- ),
- );
- }
- }
- Some(&ExpectedLine::Elision) => {
- // skip any number of elisions in a row.
- while let Some(&&ExpectedLine::Elision) = expected_lines.peek() {
- expected_lines.next();
- }
- if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() {
- let mut found = compare(expected_line, dumped_line);
- if found {
- continue;
- }
- while let Some(dumped_line) = dumped_lines.next() {
- found = compare(expected_line, dumped_line);
- if found {
- break;
- }
- }
- if !found {
- error(expected_line, "ran out of mir dump to match against".into());
- }
- }
- }
- None => {}
- }
- }
- }
-
fn get_mir_dump_dir(&self) -> PathBuf {
let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
debug!("input_file: {:?}", self.testpaths.file);
let json = cflags.contains("--error-format json")
|| cflags.contains("--error-format pretty-json")
|| cflags.contains("--error-format=json")
- || cflags.contains("--error-format=pretty-json");
+ || cflags.contains("--error-format=pretty-json")
+ || cflags.contains("--output-format json")
+ || cflags.contains("--output-format=json");
let mut normalized = output.to_string();
// with placeholders as we do not want tests needing updated when compiler source code
// changes.
// eg. $SRC_DIR/libcore/mem.rs:323:14 becomes $SRC_DIR/libcore/mem.rs:LL:COL
- normalized = Regex::new("SRC_DIR(.+):\\d+:\\d+")
+ normalized = Regex::new("SRC_DIR(.+):\\d+:\\d+(: \\d+:\\d+)?")
.unwrap()
.replace_all(&normalized, "SRC_DIR$1:LL:COL")
.into_owned();
println!("normalized {}:\n{}\n", kind, actual);
} else {
println!("diff of {}:\n", kind);
- let diff_results = make_diff(expected, actual, 3);
- for result in diff_results {
- let mut line_number = result.line_number;
- for line in result.lines {
- match line {
- DiffLine::Expected(e) => {
- println!("-\t{}", e);
- line_number += 1;
- }
- DiffLine::Context(c) => {
- println!("{}\t{}", line_number, c);
- line_number += 1;
- }
- DiffLine::Resulting(r) => {
- println!("+\t{}", r);
- }
- }
- }
- println!();
- }
+ print_diff(expected, actual, 3);
}
}
ThisDirectory(PathBuf),
}
-#[derive(Clone, PartialEq, Eq)]
-enum ExpectedLine<T: AsRef<str>> {
- Elision,
- Text(T),
-}
-
enum AllowUnused {
Yes,
No,
}
-impl<T> fmt::Debug for ExpectedLine<T>
-where
- T: AsRef<str> + fmt::Debug,
-{
- fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- if let &ExpectedLine::Text(ref t) = self {
- write!(formatter, "{:?}", t)
- } else {
- write!(formatter, "\"...\" (Elision)")
- }
- }
-}
-
-fn normalize_mir_line(line: &str) -> String {
- nocomment_mir_line(line).replace(char::is_whitespace, "")
-}
-
-fn nocomment_mir_line(line: &str) -> &str {
- if let Some(idx) = line.find("//") {
- let (l, _) = line.split_at(idx);
- l.trim_end()
- } else {
- line
- }
-}
-
fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
use crate::read2::read2;
use std::mem::replace;