use std::env;
use std::ffi::OsString;
use std::fs::{self, File, create_dir_all};
+use std::fmt;
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::{Path, PathBuf};
None => {
if self.is_unexpected_compiler_message(actual_error, expect_help, expect_note) {
self.error(
- &format!("{}:{}: unexpected {:?}: '{}'",
+ &format!("{}:{}: unexpected {}: '{}'",
file_name,
actual_error.line_num,
actual_error.kind.as_ref()
.arg("-o").arg(out_dir)
.arg(&self.testpaths.file)
.args(&self.props.compile_flags);
+ if let Some(ref linker) = self.config.linker {
+ rustdoc.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
+ }
self.compose_and_run_compiler(rustdoc, None)
}
} else {
rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
}
+ if let Some(ref linker) = self.config.linker {
+ rustc.arg(format!("-Clinker={}", linker));
+ }
rustc.args(&self.props.compile_flags);
.env("LLVM_COMPONENTS", &self.config.llvm_components)
.env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags);
+ if let Some(ref linker) = self.config.linker {
+ cmd.env("RUSTC_LINKER", linker);
+ }
+
// We don't want RUSTFLAGS set from the outside to interfere with
// compiler flags set in the test cases:
cmd.env_remove("RUSTFLAGS");
.env("CXX", &self.config.cxx);
} else {
cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
- .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags));
+ .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))
+ .env("AR", &self.config.ar);
if self.config.target.contains("windows") {
cmd.env("IS_WINDOWS", "1");
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::new();
+ let mut curr_test_contents = vec![ExpectedLine::Elision];
for l in tests_text_str.lines() {
debug!("line: {:?}", l);
if l.starts_with("// START ") {
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(test_content);
+ curr_test_contents.push(ExpectedLine::Text(test_content));
}
}
}
}
}
- fn compare_mir_test_output(&self, test_name: &str, expected_content: &[&str]) {
+ 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);
let mut dumped_string = String::new();
dumped_file.read_to_string(&mut dumped_string).unwrap();
let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty());
- let mut expected_lines = expected_content.iter().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();
- // We expect each non-empty line from expected_content to appear
- // in the dump in order, but there may be extra lines interleaved
- while let Some(expected_line) = expected_lines.next() {
+ let compare = |expected_line, dumped_line| {
let e_norm = normalize_mir_line(expected_line);
- if e_norm.is_empty() {
- continue;
+ 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 mut found = false;
- while let Some(dumped_line) = dumped_lines.next() {
- let d_norm = normalize_mir_line(dumped_line);
- debug!("found: {:?}", d_norm);
- debug!("expected: {:?}", e_norm);
- if e_norm == d_norm {
- found = true;
- break;
- };
- }
- if !found {
- let normalize_all = dumped_string.lines()
- .map(nocomment_mir_line)
- .filter(|l| !l.is_empty())
- .collect::<Vec<_>>()
- .join("\n");
- panic!("ran out of mir dump output to match against.\n\
- Did not find expected line: {:?}\n\
- Expected:\n{}\n\
- Actual:\n{}",
- expected_line,
- expected_content.join("\n"),
- normalize_all);
+ let expected_content = expected_content.iter()
+ .map(|l| f(l))
+ .collect::<Vec<_>>()
+ .join("\n");
+ panic!("Did not find expected line, error: {}\n\
+ Actual Line: {:?}\n\
+ Expected:\n{}\n\
+ Actual:\n{}",
+ extra_msg,
+ expected_line,
+ expected_content,
+ normalize_all);
+ };
+
+ // We expect each non-empty line to appear consecutively, non-consecutive lines
+ // must be separated by at least one Elision
+ while let Some(dumped_line) = dumped_lines.next() {
+ match expected_lines.next() {
+ Some(&ExpectedLine::Text(expected_line)) =>
+ if !compare(expected_line, dumped_line) {
+ error(expected_line,
+ format!("Mismatch in lines\nExpected Line: {:?}", 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 normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
let parent_dir = self.testpaths.file.parent().unwrap();
- let parent_dir_str = parent_dir.display().to_string();
+ let cflags = self.props.compile_flags.join(" ");
+ let parent_dir_str = if cflags.contains("--error-format json") {
+ parent_dir.display().to_string().replace("\\", "\\\\")
+ } else {
+ parent_dir.display().to_string()
+ };
+
let mut normalized = output.replace(&parent_dir_str, "$DIR")
+ .replace("\\\\", "\\") // denormalize for paths on windows
.replace("\\", "/") // normalize for paths on windows
.replace("\r\n", "\n") // normalize for linebreaks on windows
.replace("\t", "\\t"); // makes tabs visible
ThisDirectory(PathBuf),
}
+#[derive(Clone, PartialEq, Eq)]
+enum ExpectedLine<T: AsRef<str>> {
+ Elision,
+ Text(T)
+}
+
+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, "")
}