From 24384340548e9af2ce85f9dd91858cac30166fee Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 26 Apr 2018 15:16:27 -0700 Subject: [PATCH 1/4] compiletest: rustfmt --- src/tools/compiletest/src/common.rs | 23 ++-- src/tools/compiletest/src/errors.rs | 70 +++++++----- src/tools/compiletest/src/header.rs | 71 +++++------- src/tools/compiletest/src/json.rs | 71 +++++++----- src/tools/compiletest/src/main.rs | 47 ++++---- src/tools/compiletest/src/raise_fd_limit.rs | 15 ++- src/tools/compiletest/src/read2.rs | 60 +++++----- src/tools/compiletest/src/runtest.rs | 118 +++++++++----------- src/tools/compiletest/src/util.rs | 4 +- 9 files changed, 247 insertions(+), 232 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 2df5281659934..508cbbc7634a2 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -10,8 +10,8 @@ pub use self::Mode::*; use std::fmt; -use std::str::FromStr; use std::path::PathBuf; +use std::str::FromStr; use test::ColorConfig; @@ -103,7 +103,7 @@ pub enum CompareMode { impl CompareMode { pub(crate) fn to_str(&self) -> &'static str { match *self { - CompareMode::Nll => "nll" + CompareMode::Nll => "nll", } } @@ -253,16 +253,21 @@ pub struct TestPaths { } /// Used by `ui` tests to generate things like `foo.stderr` from `foo.rs`. -pub fn expected_output_path(testpaths: &TestPaths, - revision: Option<&str>, - compare_mode: &Option, - kind: &str) -> PathBuf { - +pub fn expected_output_path( + testpaths: &TestPaths, + revision: Option<&str>, + compare_mode: &Option, + kind: &str, +) -> PathBuf { assert!(UI_EXTENSIONS.contains(&kind)); let mut parts = Vec::new(); - if let Some(x) = revision { parts.push(x); } - if let Some(ref x) = *compare_mode { parts.push(x.to_str()); } + if let Some(x) = revision { + parts.push(x); + } + if let Some(ref x) = *compare_mode { + parts.push(x.to_str()); + } parts.push(kind); let extension = parts.join("."); diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index 251dd4d5edb4e..dd2e5557c16d4 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -11,8 +11,8 @@ use self::WhichLine::*; use std::fmt; use std::fs::File; -use std::io::BufReader; use std::io::prelude::*; +use std::io::BufReader; use std::path::Path; use std::str::FromStr; @@ -35,8 +35,7 @@ impl FromStr for ErrorKind { "ERROR" => Ok(ErrorKind::Error), "NOTE" => Ok(ErrorKind::Note), "SUGGESTION" => Ok(ErrorKind::Suggestion), - "WARN" | - "WARNING" => Ok(ErrorKind::Warning), + "WARN" | "WARNING" => Ok(ErrorKind::Warning), _ => Err(()), } } @@ -101,23 +100,25 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec { rdr.lines() .enumerate() .filter_map(|(line_num, line)| { - parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag) - .map(|(which, error)| { + parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag).map( + |(which, error)| { match which { FollowPrevious(_) => {} _ => last_nonfollow_error = Some(error.line_num), } error - }) + }, + ) }) .collect() } -fn parse_expected(last_nonfollow_error: Option, - line_num: usize, - line: &str, - tag: &str) - -> Option<(WhichLine, Error)> { +fn parse_expected( + last_nonfollow_error: Option, + line_num: usize, + line: &str, + tag: &str, +) -> Option<(WhichLine, Error)> { let start = match line.find(tag) { Some(i) => i, None => return None, @@ -125,7 +126,13 @@ fn parse_expected(last_nonfollow_error: Option, let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' { (true, 0) } else { - (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count()) + ( + false, + line[start + tag.len()..] + .chars() + .take_while(|c| *c == '^') + .count(), + ) }; let kind_start = start + tag.len() + adjusts + (follow as usize); let (kind, msg); @@ -133,12 +140,14 @@ fn parse_expected(last_nonfollow_error: Option, .split_whitespace() .next() .expect("Encountered unexpected empty comment") - .parse::() { + .parse::() + { Ok(k) => { // If we find `//~ ERROR foo` or something like that: kind = Some(k); let letters = line[kind_start..].chars(); - msg = letters.skip_while(|c| c.is_whitespace()) + msg = letters + .skip_while(|c| c.is_whitespace()) .skip_while(|c| !c.is_whitespace()) .collect::(); } @@ -146,7 +155,8 @@ fn parse_expected(last_nonfollow_error: Option, // Otherwise we found `//~ foo`: kind = None; let letters = line[kind_start..].chars(); - msg = letters.skip_while(|c| c.is_whitespace()) + msg = letters + .skip_while(|c| c.is_whitespace()) .collect::(); } } @@ -154,8 +164,10 @@ fn parse_expected(last_nonfollow_error: Option, let (which, line_num) = if follow { assert_eq!(adjusts, 0, "use either //~| or //~^, not both."); - let line_num = last_nonfollow_error.expect("encountered //~| without \ - preceding //~^ line."); + let line_num = last_nonfollow_error.expect( + "encountered //~| without \ + preceding //~^ line.", + ); (FollowPrevious(line_num), line_num) } else { let which = if adjusts > 0 { @@ -167,16 +179,16 @@ fn parse_expected(last_nonfollow_error: Option, (which, line_num) }; - debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}", - line_num, - tag, - which, - kind, - msg); - Some((which, - Error { - line_num, - kind, - msg, - })) + debug!( + "line={} tag={:?} which={:?} kind={:?} msg={:?}", + line_num, tag, which, kind, msg + ); + Some(( + which, + Error { + line_num, + kind, + msg, + }, + )) } diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 32980a513f6f9..06ec9e893f068 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -10,12 +10,12 @@ use std::env; use std::fs::File; -use std::io::BufReader; use std::io::prelude::*; +use std::io::BufReader; use std::path::{Path, PathBuf}; -use common::Config; use common; +use common::Config; use util; use extract_gdb_version; @@ -38,19 +38,14 @@ impl EarlyProps { revisions: vec![], }; - iter_header(testfile, - None, - &mut |ln| { + iter_header(testfile, None, &mut |ln| { // we should check if any only- exists and if it exists // and does not matches the current platform, skip the test - props.ignore = - props.ignore || - config.parse_cfg_name_directive(ln, "ignore") || - (config.has_cfg_prefix(ln, "only") && - !config.parse_cfg_name_directive(ln, "only")) || - ignore_gdb(config, ln) || - ignore_lldb(config, ln) || - ignore_llvm(config, ln); + props.ignore = props.ignore || config.parse_cfg_name_directive(ln, "ignore") + || (config.has_cfg_prefix(ln, "only") + && !config.parse_cfg_name_directive(ln, "only")) + || ignore_gdb(config, ln) || ignore_lldb(config, ln) + || ignore_llvm(config, ln); if let Some(s) = config.parse_aux_build(ln) { props.aux.push(s); @@ -149,7 +144,7 @@ impl EarlyProps { fn ignore_llvm(config: &Config, line: &str) -> bool { if config.system_llvm && line.starts_with("no-system-llvm") { - return true; + return true; } if let Some(ref actual_version) = config.llvm_version { if line.starts_with("min-llvm-version") { @@ -272,11 +267,7 @@ impl TestProps { } } - pub fn from_aux_file(&self, - testfile: &Path, - cfg: Option<&str>, - config: &Config) - -> Self { + pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self { let mut props = TestProps::new(); // copy over select properties to the aux build: @@ -296,20 +287,15 @@ impl TestProps { /// tied to a particular revision `foo` (indicated by writing /// `//[foo]`), then the property is ignored unless `cfg` is /// `Some("foo")`. - fn load_from(&mut self, - testfile: &Path, - cfg: Option<&str>, - config: &Config) { - iter_header(testfile, - cfg, - &mut |ln| { + fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) { + iter_header(testfile, cfg, &mut |ln| { if let Some(ep) = config.parse_error_pattern(ln) { self.error_patterns.push(ep); } if let Some(flags) = config.parse_compile_flags(ln) { - self.compile_flags.extend(flags.split_whitespace() - .map(|s| s.to_owned())); + self.compile_flags + .extend(flags.split_whitespace().map(|s| s.to_owned())); } if let Some(r) = config.parse_revisions(ln) { @@ -382,8 +368,7 @@ impl TestProps { if !self.compile_pass { // run-pass implies must_compile_sucessfully - self.compile_pass = - config.parse_compile_pass(ln) || self.run_pass; + self.compile_pass = config.parse_compile_pass(ln) || self.run_pass; } if !self.skip_trans { @@ -453,7 +438,7 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) { None => false, }; if matches { - it(ln[(close_brace + 1) ..].trim_left()); + it(ln[(close_brace + 1)..].trim_left()); } } else { panic!("malformed condition directive: expected `{}foo]`, found `{}`", @@ -554,9 +539,7 @@ impl Config { fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> { self.parse_name_value_directive(line, name).map(|nv| { // nv is either FOO or FOO=BAR - let mut strs: Vec = nv.splitn(2, '=') - .map(str::to_owned) - .collect(); + let mut strs: Vec = nv.splitn(2, '=').map(str::to_owned).collect(); match strs.len() { 1 => (strs.pop().unwrap(), "".to_owned()), @@ -599,7 +582,10 @@ impl Config { /// or `normalize-stderr-32bit`. Returns `true` if the line matches it. fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> bool { if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') { - let name = line[prefix.len()+1 ..].split(&[':', ' '][..]).next().unwrap(); + let name = line[prefix.len() + 1..] + .split(&[':', ' '][..]) + .next() + .unwrap(); name == "test" || util::matches_os(&self.target, name) || // target @@ -612,8 +598,7 @@ impl Config { common::DebugInfoLldb => name == "lldb", common::Pretty => name == "pretty", _ => false, - } || - (self.target != self.host && name == "cross-compile") + } || (self.target != self.host && name == "cross-compile") } else { false } @@ -631,14 +616,14 @@ impl Config { // the line says "ignore-x86_64". line.starts_with(directive) && match line.as_bytes().get(directive.len()) { None | Some(&b' ') | Some(&b':') => true, - _ => false + _ => false, } } pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option { let colon = directive.len(); if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { - let value = line[(colon + 1) ..].to_owned(); + let value = line[(colon + 1)..].to_owned(); debug!("{}: {}", directive, value); Some(expand_variables(value, self)) } else { @@ -665,8 +650,10 @@ impl Config { } pub fn lldb_version_to_int(version_string: &str) -> isize { - let error_string = format!("Encountered LLDB version string with unexpected format: {}", - version_string); + let error_string = format!( + "Encountered LLDB version string with unexpected format: {}", + version_string + ); version_string.parse().expect(&error_string) } @@ -713,6 +700,6 @@ fn parse_normalization_string(line: &mut &str) -> Option { None => return None, }; let result = line[begin..end].to_owned(); - *line = &line[end+1..]; + *line = &line[end + 1..]; Some(result) } diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 761d1e511d52b..165f2914ae246 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -9,10 +9,10 @@ // except according to those terms. use errors::{Error, ErrorKind}; +use runtest::ProcRes; use serde_json; -use std::str::FromStr; use std::path::Path; -use runtest::ProcRes; +use std::str::FromStr; // These structs are a subset of the ones found in // `syntax::json`. @@ -58,26 +58,30 @@ struct DiagnosticCode { } pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String { - output.lines() - .filter_map(|line| if line.starts_with('{') { - match serde_json::from_str::(line) { - Ok(diagnostic) => diagnostic.rendered, - Err(error) => { - proc_res.fatal(Some(&format!("failed to decode compiler output as json: \ - `{}`\noutput: {}\nline: {}", - error, - line, - output))); + output + .lines() + .filter_map(|line| { + if line.starts_with('{') { + match serde_json::from_str::(line) { + Ok(diagnostic) => diagnostic.rendered, + Err(error) => { + proc_res.fatal(Some(&format!( + "failed to decode compiler output as json: \ + `{}`\noutput: {}\nline: {}", + error, line, output + ))); + } } + } else { + None } - } else { - None }) .collect() } pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec { - output.lines() + output + .lines() .flat_map(|line| parse_line(file_name, line, output, proc_res)) .collect() } @@ -93,11 +97,11 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) -> expected_errors } Err(error) => { - proc_res.fatal(Some(&format!("failed to decode compiler output as json: \ - `{}`\noutput: {}\nline: {}", - error, - line, - output))); + proc_res.fatal(Some(&format!( + "failed to decode compiler output as json: \ + `{}`\noutput: {}\nline: {}", + error, line, output + ))); } } } else { @@ -105,11 +109,14 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) -> } } -fn push_expected_errors(expected_errors: &mut Vec, - diagnostic: &Diagnostic, - default_spans: &[&DiagnosticSpan], - file_name: &str) { - let spans_in_this_file: Vec<_> = diagnostic.spans +fn push_expected_errors( + expected_errors: &mut Vec, + diagnostic: &Diagnostic, + default_spans: &[&DiagnosticSpan], + file_name: &str, +) { + let spans_in_this_file: Vec<_> = diagnostic + .spans .iter() .filter(|span| Path::new(&span.file_name) == Path::new(&file_name)) .collect(); @@ -204,8 +211,10 @@ fn push_expected_errors(expected_errors: &mut Vec, } // Add notes for any labels that appear in the message. - for span in spans_in_this_file.iter() - .filter(|span| span.label.is_some()) { + for span in spans_in_this_file + .iter() + .filter(|span| span.label.is_some()) + { expected_errors.push(Error { line_num: span.line_start, kind: Some(ErrorKind::Note), @@ -219,9 +228,11 @@ fn push_expected_errors(expected_errors: &mut Vec, } } -fn push_backtrace(expected_errors: &mut Vec, - expansion: &DiagnosticSpanMacroExpansion, - file_name: &str) { +fn push_backtrace( + expected_errors: &mut Vec, + expansion: &DiagnosticSpanMacroExpansion, + file_name: &str, +) { if Path::new(&expansion.span.file_name) == Path::new(&file_name) { expected_errors.push(Error { line_num: expansion.span.line_start, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 0899ebd5d80f7..0fd7567d4f21d 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -31,31 +31,31 @@ extern crate serde_json; extern crate test; extern crate rustfix; +use common::CompareMode; +use common::{expected_output_path, UI_EXTENSIONS}; +use common::{Config, TestPaths}; +use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty}; +use filetime::FileTime; +use getopts::Options; use std::env; use std::ffi::OsString; use std::fs; use std::io; use std::path::{Path, PathBuf}; use std::process::Command; -use filetime::FileTime; -use getopts::Options; -use common::{Config, TestPaths}; -use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty}; -use common::{expected_output_path, UI_EXTENSIONS}; -use common::CompareMode; use test::ColorConfig; use util::logv; use self::header::EarlyProps; -pub mod util; -mod json; -pub mod header; -pub mod runtest; pub mod common; pub mod errors; +pub mod header; +mod json; mod raise_fd_limit; mod read2; +pub mod runtest; +pub mod util; fn main() { env_logger::init(); @@ -236,7 +236,7 @@ pub fn parse_config(args: Vec) -> Config { "", "compare-mode", "mode describing what file the actual ui output will be compared to", - "COMPARE MODE" + "COMPARE MODE", ) .optflag("h", "help", "show this message"); @@ -501,7 +501,11 @@ pub fn test_opts(config: &Config) -> test::TestOpts { filter: config.filter.clone(), filter_exact: config.filter_exact, run_ignored: config.run_ignored, - format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty }, + format: if config.quiet { + test::OutputFormat::Terse + } else { + test::OutputFormat::Pretty + }, logfile: config.logfile.clone(), run_tests: true, bench_benchmarks: true, @@ -634,8 +638,7 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn }; // Debugging emscripten code doesn't make sense today - let ignore = early_props.ignore - || !up_to_date(config, testpaths, &early_props) + let ignore = early_props.ignore || !up_to_date(config, testpaths, &early_props) || (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb) && config.target.contains("emscripten"); @@ -694,8 +697,7 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo for pretty_printer_file in &pretty_printer_files { inputs.push(mtime(&rust_src_dir.join(pretty_printer_file))); } - let mut entries = config.run_lib_path.read_dir().unwrap() - .collect::>(); + let mut entries = config.run_lib_path.read_dir().unwrap().collect::>(); while let Some(entry) = entries.pop() { let entry = entry.unwrap(); let path = entry.path(); @@ -713,10 +715,8 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo // UI test files. for extension in UI_EXTENSIONS { for revision in &props.revisions { - let path = &expected_output_path(testpaths, - Some(revision), - &config.compare_mode, - extension); + let path = + &expected_output_path(testpaths, Some(revision), &config.compare_mode, extension); inputs.push(mtime(path)); } @@ -746,7 +746,12 @@ pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName Some(ref mode) => format!(" ({})", mode.to_str()), None => format!(""), }; - test::DynTestName(format!("[{}{}] {}", config.mode, mode_suffix, path.display())) + test::DynTestName(format!( + "[{}{}] {}", + config.mode, + mode_suffix, + path.display() + )) } pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn { diff --git a/src/tools/compiletest/src/raise_fd_limit.rs b/src/tools/compiletest/src/raise_fd_limit.rs index fcc5a727cf2d6..220082799a8b0 100644 --- a/src/tools/compiletest/src/raise_fd_limit.rs +++ b/src/tools/compiletest/src/raise_fd_limit.rs @@ -34,12 +34,15 @@ pub unsafe fn raise_fd_limit() { let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC]; let mut maxfiles: libc::c_int = 0; let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; - if libc::sysctl(&mut mib[0], - 2, - &mut maxfiles as *mut _ as *mut _, - &mut size, - null_mut(), - 0) != 0 { + if libc::sysctl( + &mut mib[0], + 2, + &mut maxfiles as *mut _ as *mut _, + &mut size, + null_mut(), + 0, + ) != 0 + { let err = io::Error::last_os_error(); panic!("raise_fd_limit: error calling sysctl: {}", err); } diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs index 4f55edc58ef2a..5bf898f5f1baf 100644 --- a/src/tools/compiletest/src/read2.rs +++ b/src/tools/compiletest/src/read2.rs @@ -16,11 +16,13 @@ pub use self::imp::read2; #[cfg(not(any(unix, windows)))] mod imp { use std::io::{self, Read}; - use std::process::{ChildStdout, ChildStderr}; + use std::process::{ChildStderr, ChildStdout}; - pub fn read2(out_pipe: ChildStdout, - err_pipe: ChildStderr, - data: &mut FnMut(bool, &mut Vec, bool)) -> io::Result<()> { + pub fn read2( + out_pipe: ChildStdout, + err_pipe: ChildStderr, + data: &mut FnMut(bool, &mut Vec, bool), + ) -> io::Result<()> { let mut buffer = Vec::new(); out_pipe.read_to_end(&mut buffer)?; data(true, &mut buffer, true); @@ -33,16 +35,18 @@ mod imp { #[cfg(unix)] mod imp { - use std::io::prelude::*; + use libc; use std::io; + use std::io::prelude::*; use std::mem; use std::os::unix::prelude::*; - use std::process::{ChildStdout, ChildStderr}; - use libc; + use std::process::{ChildStderr, ChildStdout}; - pub fn read2(mut out_pipe: ChildStdout, - mut err_pipe: ChildStderr, - data: &mut FnMut(bool, &mut Vec, bool)) -> io::Result<()> { + pub fn read2( + mut out_pipe: ChildStdout, + mut err_pipe: ChildStderr, + data: &mut FnMut(bool, &mut Vec, bool), + ) -> io::Result<()> { unsafe { libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); @@ -67,9 +71,9 @@ mod imp { if r == -1 { let err = io::Error::last_os_error(); if err.kind() == io::ErrorKind::Interrupted { - continue + continue; } - return Err(err) + return Err(err); } // Read as much as we can from each pipe, ignoring EWOULDBLOCK or @@ -77,15 +81,13 @@ mod imp { // reader will return Ok(0), in which case we'll see `Ok` ourselves. In // this case we flip the other fd back into blocking mode and read // whatever's leftover on that file descriptor. - let handle = |res: io::Result<_>| { - match res { - Ok(_) => Ok(true), - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - Ok(false) - } else { - Err(e) - } + let handle = |res: io::Result<_>| match res { + Ok(_) => Ok(true), + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock { + Ok(false) + } else { + Err(e) } } }; @@ -113,7 +115,7 @@ mod imp { use std::io; use std::os::windows::prelude::*; - use std::process::{ChildStdout, ChildStderr}; + use std::process::{ChildStderr, ChildStdout}; use std::slice; use self::miow::iocp::{CompletionPort, CompletionStatus}; @@ -128,9 +130,11 @@ mod imp { done: bool, } - pub fn read2(out_pipe: ChildStdout, - err_pipe: ChildStderr, - data: &mut FnMut(bool, &mut Vec, bool)) -> io::Result<()> { + pub fn read2( + out_pipe: ChildStdout, + err_pipe: ChildStderr, + data: &mut FnMut(bool, &mut Vec, bool), + ) -> io::Result<()> { let mut out = Vec::new(); let mut err = Vec::new(); @@ -206,7 +210,9 @@ mod imp { if v.capacity() == v.len() { v.reserve(1); } - slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize), - v.capacity() - v.len()) + slice::from_raw_parts_mut( + v.as_mut_ptr().offset(v.len() as isize), + v.capacity() - v.len(), + ) } } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 4f18f238cab90..a3ce4d490713c 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -8,28 +8,28 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use common::{Config, TestPaths}; -use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; +use common::CompareMode; +use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED}; use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc}; +use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; +use common::{Config, TestPaths}; use common::{Incremental, MirOpt, RunMake, Ui}; -use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED}; -use common::CompareMode; use diff; use errors::{self, Error, ErrorKind}; use filetime::FileTime; -use json; use header::TestProps; -use util::logv; +use json; use regex::Regex; use rustfix::{apply_suggestions, get_suggestions_from_json}; +use util::logv; -use std::collections::VecDeque; use std::collections::HashMap; use std::collections::HashSet; +use std::collections::VecDeque; use std::env; use std::ffi::{OsStr, OsString}; -use std::fs::{self, create_dir_all, File}; use std::fmt; +use std::fs::{self, create_dir_all, File}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::{Path, PathBuf}; @@ -355,9 +355,10 @@ impl<'test> TestCx<'test> { if expected_status != received_status { self.fatal_proc_rec( - &format!("Error: expected failure status ({:?}) but received status {:?}.", - expected_status, - received_status), + &format!( + "Error: expected failure status ({:?}) but received status {:?}.", + expected_status, received_status + ), proc_res, ); } @@ -440,8 +441,7 @@ impl<'test> TestCx<'test> { self.config, format!( "pretty-printing round {} revision {:?}", - round, - self.revision + round, self.revision ), ); let proc_res = self.print_source(srcs[round].to_owned(), &self.props.pretty_mode); @@ -450,8 +450,7 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec( &format!( "pretty-printing failed in round {} revision {:?}", - round, - self.revision + round, self.revision ), &proc_res, ); @@ -555,8 +554,7 @@ impl<'test> TestCx<'test> { {}\n\ ------------------------------------------\n\ \n", - expected, - actual + expected, actual ); } } @@ -661,8 +659,7 @@ impl<'test> TestCx<'test> { script_str.push_str(&format!( "set solib-search-path \ ./{}/stage2/lib/rustlib/{}/lib/\n", - self.config.host, - self.config.target + self.config.host, self.config.target )); for line in &breakpoint_lines { script_str.push_str( @@ -881,7 +878,6 @@ impl<'test> TestCx<'test> { ..self.config.clone() }; - let test_cx = TestCx { config: &config, ..*self @@ -952,8 +948,7 @@ impl<'test> TestCx<'test> { for line in &breakpoint_lines { script_str.push_str(&format!( "breakpoint set --file '{}' --line {}\n", - source_file_name, - line + source_file_name, line )); } @@ -1028,9 +1023,7 @@ impl<'test> TestCx<'test> { fn parse_debugger_commands(&self, debugger_prefixes: &[&str]) -> DebuggerCommands { let directives = debugger_prefixes .iter() - .map(|prefix| { - (format!("{}-command", prefix), format!("{}-check", prefix)) - }) + .map(|prefix| (format!("{}-command", prefix), format!("{}-check", prefix))) .collect::>(); let mut breakpoint_lines = vec![]; @@ -1041,12 +1034,11 @@ impl<'test> TestCx<'test> { for line in reader.lines() { match line { Ok(line) => { - let line = - if line.starts_with("//") { - line[2..].trim_left() - } else { - line.as_str() - }; + let line = if line.starts_with("//") { + line[2..].trim_left() + } else { + line.as_str() + }; if line.contains("#break") { breakpoint_lines.push(counter); @@ -1645,7 +1637,10 @@ impl<'test> TestCx<'test> { let mut rustc = if !is_rustdoc { Command::new(&self.config.rustc_path) } else { - Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet")) + Command::new(&self.config + .rustdoc_path + .clone() + .expect("no rustdoc built yet")) }; rustc.arg(input_file).arg("-L").arg(&self.config.build_base); @@ -1671,10 +1666,7 @@ impl<'test> TestCx<'test> { if !is_rustdoc { if let Some(ref incremental_dir) = self.props.incremental_dir { - rustc.args(&[ - "-C", - &format!("incremental={}", incremental_dir.display()), - ]); + rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]); rustc.args(&["-Z", "incremental-verify-ich"]); rustc.args(&["-Z", "incremental-queries"]); } @@ -1697,7 +1689,11 @@ impl<'test> TestCx<'test> { } } Ui => { - if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) { + if !self.props + .compile_flags + .iter() + .any(|s| s.starts_with("--error-format")) + { rustc.args(&["--error-format", "json"]); } if !self.props.disable_ui_testing_normalization { @@ -1720,16 +1716,8 @@ impl<'test> TestCx<'test> { rustc.arg(dir_opt); } - RunPass | - RunFail | - RunPassValgrind | - Pretty | - DebugInfoGdb | - DebugInfoLldb | - Codegen | - Rustdoc | - RunMake | - CodegenUnits => { + RunPass | RunFail | RunPassValgrind | Pretty | DebugInfoGdb | DebugInfoLldb + | Codegen | Rustdoc | RunMake | CodegenUnits => { // do not use JSON output } } @@ -1759,8 +1747,8 @@ impl<'test> TestCx<'test> { match self.config.compare_mode { Some(CompareMode::Nll) => { rustc.args(&["-Zborrowck=mir", "-Ztwo-phase-borrows"]); - }, - None => {}, + } + None => {} } if self.props.force_host { @@ -1923,7 +1911,8 @@ impl<'test> TestCx<'test> { /// Same as `output_base_name`, but includes the stage ID as an extension, /// such as: `.../compile-fail/foo/bar.stage1-` fn output_base_name_stage(&self) -> PathBuf { - self.output_base_name().with_extension(&self.config.stage_id) + self.output_base_name() + .with_extension(&self.config.stage_id) } fn maybe_dump_to_stdout(&self, out: &str, err: &str) { @@ -2417,8 +2406,7 @@ impl<'test> TestCx<'test> { if self.config.verbose { print!( "revision={:?} revision_props={:#?}", - revision, - revision_props + revision, revision_props ); } @@ -2731,8 +2719,7 @@ impl<'test> TestCx<'test> { if source_time > output_time { debug!( "source file time: {:?} output file time: {:?}", - source_time, - output_time + source_time, output_time ); panic!( "test source file `{}` is newer than potentially stale output file `{}`.", @@ -2906,10 +2893,12 @@ impl<'test> TestCx<'test> { } fn expected_output_path(&self, kind: &str) -> PathBuf { - let mut path = expected_output_path(&self.testpaths, - self.revision, - &self.config.compare_mode, - kind); + let mut path = expected_output_path( + &self.testpaths, + self.revision, + &self.config.compare_mode, + kind, + ); if !path.exists() && self.config.compare_mode.is_some() { // fallback! path = expected_output_path(&self.testpaths, self.revision, &None, kind); @@ -2959,14 +2948,14 @@ impl<'test> TestCx<'test> { 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!(""); @@ -3025,10 +3014,7 @@ impl ProcRes { {}\n\ ------------------------------------------\n\ \n", - self.status, - self.cmdline, - self.stdout, - self.stderr + self.status, self.cmdline, self.stdout, self.stderr ); panic!(); } @@ -3072,8 +3058,8 @@ fn nocomment_mir_line(line: &str) -> &str { } fn read2_abbreviated(mut child: Child) -> io::Result { - use std::mem::replace; use read2::read2; + use std::mem::replace; const HEAD_LEN: usize = 160 * 1024; const TAIL_LEN: usize = 256 * 1024; diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index c612f0117aaf7..77b991ae3c3fd 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::env; use common::Config; +use std::env; /// Conversion table from triple OS name to Rust SYSNAME const OS_TABLE: &'static [(&'static str, &'static str)] = &[ @@ -73,7 +73,7 @@ pub fn matches_os(triple: &str, name: &str) -> bool { // For the wasm32 bare target we ignore anything also ignored on emscripten // and then we also recognize `wasm32-bare` as the os for the target if triple == "wasm32-unknown-unknown" { - return name == "emscripten" || name == "wasm32-bare" + return name == "emscripten" || name == "wasm32-bare"; } let triple: Vec<_> = triple.split('-').collect(); for &(triple_os, os) in OS_TABLE { From e3d8fc50444a268a572117669e0fb3f9eabd1c29 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 26 Apr 2018 15:27:31 -0700 Subject: [PATCH 2/4] compiletest: Run revisions as independent tests. - The output of each test is now in its own directory. - "auxiliary" output is now under the respective test directory. - `stage_id` removed from filenames, and instead placed in the stamp file as a hash. This helps keep path lengths down for Windows. In brief, the new layout looks like this: ``` //../ stamp .err .out a (binary) auxiliary/lib.dylib auxiliary//.err auxiliary//.out ``` (revision and mode are optional) --- src/test/ui-fulldeps/update-references.sh | 23 ++- src/test/ui/update-references.sh | 2 +- src/tools/compiletest/src/common.rs | 41 +++++- src/tools/compiletest/src/main.rs | 146 ++++++++++--------- src/tools/compiletest/src/runtest.rs | 166 +++++++++++----------- src/tools/compiletest/src/util.rs | 24 +++- 6 files changed, 234 insertions(+), 168 deletions(-) diff --git a/src/test/ui-fulldeps/update-references.sh b/src/test/ui-fulldeps/update-references.sh index c2c842fcc4984..86f555620eeb9 100755 --- a/src/test/ui-fulldeps/update-references.sh +++ b/src/test/ui-fulldeps/update-references.sh @@ -31,18 +31,17 @@ MYDIR=$(dirname $0) BUILD_DIR="$1" shift +shopt -s nullglob + while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" + for EXT in "stderr" "stdout"; do + for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do + OUT_BASE=`basename "$OUT_NAME"` + if ! (diff $OUT_NAME $MYDIR/$OUT_BASE >& /dev/null); then + echo updating $MYDIR/$OUT_BASE + cp $OUT_NAME $MYDIR + fi + done + done shift - if [ -f $BUILD_DIR/$STDOUT_NAME ] && \ - ! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then - echo updating $MYDIR/$STDOUT_NAME - cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME - fi - if [ -f $BUILD_DIR/$STDERR_NAME ] && \ - ! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then - echo updating $MYDIR/$STDERR_NAME - cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME - fi done diff --git a/src/test/ui/update-references.sh b/src/test/ui/update-references.sh index 00b4b5c5caa78..f3c5997fc3f53 100755 --- a/src/test/ui/update-references.sh +++ b/src/test/ui/update-references.sh @@ -35,7 +35,7 @@ shopt -s nullglob while [[ "$1" != "" ]]; do for EXT in "stderr" "stdout" "fixed"; do - for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do + for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do OUT_DIR=`dirname "$1"` OUT_BASE=`basename "$OUT_NAME"` if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 508cbbc7634a2..812e9c5f39d87 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -10,10 +10,11 @@ pub use self::Mode::*; use std::fmt; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::str::FromStr; use test::ColorConfig; +use util::PathBufExt; #[derive(Clone, Copy, PartialEq, Debug)] pub enum Mode { @@ -245,10 +246,9 @@ pub struct Config { pub nodejs: Option, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct TestPaths { pub file: PathBuf, // e.g., compile-test/foo/bar/baz.rs - pub base: PathBuf, // e.g., compile-test, auxiliary pub relative_dir: PathBuf, // e.g., foo/bar } @@ -278,3 +278,38 @@ pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED]; pub const UI_STDERR: &str = "stderr"; pub const UI_STDOUT: &str = "stdout"; pub const UI_FIXED: &str = "fixed"; + +/// Absolute path to the directory where all output for all tests in the given +/// `relative_dir` group should reside. Example: +/// /path/to/build/host-triple/test/ui/relative/ +/// This is created early when tests are collected to avoid race conditions. +pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf { + config.build_base.join(relative_dir) +} + +/// Generates a unique name for the test, such as `testname.revision.mode`. +pub fn output_testname_unique( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> PathBuf { + let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str()); + PathBuf::from(&testpaths.file.file_stem().unwrap()) + .with_extra_extension(revision.unwrap_or("")) + .with_extra_extension(mode) +} + +/// Absolute path to the directory where all output for the given +/// test/revision should reside. Example: +/// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/ +pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { + output_relative_path(config, &testpaths.relative_dir) + .join(output_testname_unique(config, testpaths, revision)) +} + +/// Absolute path to the base filename used as output for the given +/// test/revision. Example: +/// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/testname +pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { + output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap()) +} diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 0fd7567d4f21d..42a2cdfa55b5a 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -32,7 +32,7 @@ extern crate test; extern crate rustfix; use common::CompareMode; -use common::{expected_output_path, UI_EXTENSIONS}; +use common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS}; use common::{Config, TestPaths}; use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty}; use filetime::FileTime; @@ -40,7 +40,7 @@ use getopts::Options; use std::env; use std::ffi::OsString; use std::fs; -use std::io; +use std::io::{self, Read}; use std::path::{Path, PathBuf}; use std::process::Command; use test::ColorConfig; @@ -552,10 +552,9 @@ fn collect_tests_from_dir( if name == *"Makefile" && config.mode == Mode::RunMake { let paths = TestPaths { file: dir.to_path_buf(), - base: base.to_path_buf(), relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), }; - tests.push(make_test(config, &paths)); + tests.extend(make_test(config, &paths)); return Ok(()); } } @@ -566,7 +565,7 @@ fn collect_tests_from_dir( // sequential loop because otherwise, if we do it in the // tests themselves, they race for the privilege of // creating the directories and sometimes fail randomly. - let build_dir = config.build_base.join(&relative_dir_path); + let build_dir = output_relative_path(config, relative_dir_path); fs::create_dir_all(&build_dir).unwrap(); // Add each `.rs` file as a test, and recurse further on any @@ -580,21 +579,12 @@ fn collect_tests_from_dir( debug!("found test file: {:?}", file_path.display()); let paths = TestPaths { file: file_path, - base: base.to_path_buf(), relative_dir: relative_dir_path.to_path_buf(), }; - tests.push(make_test(config, &paths)) + tests.extend(make_test(config, &paths)) } else if file_path.is_dir() { let relative_file_path = relative_dir_path.join(file.file_name()); - if &file_name == "auxiliary" { - // `aux` directories contain other crates used for - // cross-crate tests. Don't search them for tests, but - // do create a directory in the build dir for them, - // since we will dump intermediate output in there - // sometimes. - let build_dir = config.build_base.join(&relative_file_path); - fs::create_dir_all(&build_dir).unwrap(); - } else { + if &file_name != "auxiliary" { debug!("found directory: {:?}", file_path.display()); collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?; } @@ -617,7 +607,7 @@ pub fn is_test(file_name: &OsString) -> bool { !invalid_prefixes.iter().any(|p| file_name.starts_with(p)) } -pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn { +pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec { let early_props = if config.mode == Mode::RunMake { // Allow `ignore` directives to be in the Makefile. EarlyProps::from_file(config, &testpaths.file.join("Makefile")) @@ -637,46 +627,68 @@ pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn }, }; - // Debugging emscripten code doesn't make sense today - let ignore = early_props.ignore || !up_to_date(config, testpaths, &early_props) - || (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb) - && config.target.contains("emscripten"); - - test::TestDescAndFn { - desc: test::TestDesc { - name: make_test_name(config, testpaths), - ignore, - should_panic, - allow_fail: false, - }, - testfn: make_test_closure(config, testpaths), - } + // Incremental tests are special, they inherently cannot be run in parallel. + // `runtest::run` will be responsible for iterating over revisions. + let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental { + vec![None] + } else { + early_props.revisions.iter().map(|r| Some(r)).collect() + }; + revisions + .into_iter() + .map(|revision| { + // Debugging emscripten code doesn't make sense today + let ignore = early_props.ignore + || !up_to_date( + config, + testpaths, + &early_props, + revision.map(|s| s.as_str()), + ) + || (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb) + && config.target.contains("emscripten"); + test::TestDescAndFn { + desc: test::TestDesc { + name: make_test_name(config, testpaths, revision), + ignore, + should_panic, + allow_fail: false, + }, + testfn: make_test_closure(config, testpaths, revision), + } + }) + .collect() } -fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf { - let mode_suffix = match config.compare_mode { - Some(ref mode) => format!("-{}", mode.to_str()), - None => format!(""), - }; - let stamp_name = format!( - "{}-{}{}.stamp", - testpaths.file.file_name().unwrap().to_str().unwrap(), - config.stage_id, - mode_suffix - ); - config - .build_base - .canonicalize() - .unwrap_or_else(|_| config.build_base.clone()) - .join(&testpaths.relative_dir) - .join(stamp_name) +fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { + output_base_dir(config, testpaths, revision).join("stamp") } -fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> bool { +fn up_to_date( + config: &Config, + testpaths: &TestPaths, + props: &EarlyProps, + revision: Option<&str>, +) -> bool { + let stamp_name = stamp(config, testpaths, revision); + // Check hash. + let mut f = match fs::File::open(&stamp_name) { + Ok(f) => f, + Err(_) => return true, + }; + let mut contents = String::new(); + f.read_to_string(&mut contents) + .expect("Can't read stamp contents"); + let expected_hash = runtest::compute_stamp_hash(config); + if contents != expected_hash { + return true; + } + + // Check timestamps. let rust_src_dir = config .find_rust_src_root() .expect("Could not find Rust source root"); - let stamp = mtime(&stamp(config, testpaths)); + let stamp = mtime(&stamp_name); let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)]; for aux in props.aux.iter() { inputs.push(mtime(&testpaths @@ -714,16 +726,8 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo // UI test files. for extension in UI_EXTENSIONS { - for revision in &props.revisions { - let path = - &expected_output_path(testpaths, Some(revision), &config.compare_mode, extension); - inputs.push(mtime(path)); - } - - if props.revisions.is_empty() { - let path = &expected_output_path(testpaths, None, &config.compare_mode, extension); - inputs.push(mtime(path)); - } + let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension); + inputs.push(mtime(path)); } inputs.iter().any(|input| *input > stamp) @@ -735,7 +739,11 @@ fn mtime(path: &Path) -> FileTime { .unwrap_or_else(|_| FileTime::zero()) } -pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName { +fn make_test_name( + config: &Config, + testpaths: &TestPaths, + revision: Option<&String>, +) -> test::TestName { // Convert a complete path to something like // // run-pass/foo/bar/baz.rs @@ -747,17 +755,25 @@ pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName None => format!(""), }; test::DynTestName(format!( - "[{}{}] {}", + "[{}{}] {}{}", config.mode, mode_suffix, - path.display() + path.display(), + revision.map_or("".to_string(), |rev| format!("#{}", rev)) )) } -pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn { +fn make_test_closure( + config: &Config, + testpaths: &TestPaths, + revision: Option<&String>, +) -> test::TestFn { let config = config.clone(); let testpaths = testpaths.clone(); - test::DynTestFn(Box::new(move || runtest::run(config, &testpaths))) + let revision = revision.cloned(); + test::DynTestFn(Box::new(move || { + runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str())) + })) } /// Returns (Path to GDB, GDB Version, GDB has Rust Support) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index a3ce4d490713c..2a76113460fc7 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -10,6 +10,7 @@ use common::CompareMode; use common::{expected_output_path, UI_STDERR, UI_STDOUT, UI_FIXED}; +use common::{output_base_dir, output_base_name, output_testname_unique}; use common::{Codegen, CodegenUnits, DebugInfoGdb, DebugInfoLldb, Rustdoc}; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; use common::{Config, TestPaths}; @@ -21,15 +22,15 @@ use header::TestProps; use json; use regex::Regex; use rustfix::{apply_suggestions, get_suggestions_from_json}; -use util::logv; +use util::{logv, PathBufExt}; -use std::collections::HashMap; -use std::collections::HashSet; -use std::collections::VecDeque; +use std::collections::{HashMap, HashSet, VecDeque}; +use std::collections::hash_map::DefaultHasher; use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::fmt; use std::fs::{self, create_dir_all, File}; +use std::hash::{Hash, Hasher}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::{Path, PathBuf}; @@ -106,26 +107,6 @@ impl Mismatch { } } -trait PathBufExt { - /// Append an extension to the path, even if it already has one. - fn with_extra_extension>(&self, extension: S) -> PathBuf; -} - -impl PathBufExt for PathBuf { - fn with_extra_extension>(&self, extension: S) -> PathBuf { - if extension.as_ref().len() == 0 { - self.clone() - } else { - let mut fname = self.file_name().unwrap().to_os_string(); - if !extension.as_ref().to_str().unwrap().starts_with(".") { - fname.push("."); - } - fname.push(extension); - self.with_file_name(fname) - } - } -} - // Produces a diff between the expected output and actual output. pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec { let mut line_number = 1; @@ -186,7 +167,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec) { match &*config.target { "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => { if !config.adb_device_status { @@ -207,20 +188,25 @@ pub fn run(config: Config, testpaths: &TestPaths) { print!("\n\n"); } debug!("running {:?}", testpaths.file.display()); - let base_props = TestProps::from_file(&testpaths.file, None, &config); + let props = TestProps::from_file(&testpaths.file, revision, &config); - let base_cx = TestCx { + let cx = TestCx { config: &config, - props: &base_props, + props: &props, testpaths, - revision: None, + revision: revision, }; - base_cx.init_all(); + create_dir_all(&cx.output_base_dir()).unwrap(); - if base_props.revisions.is_empty() { - base_cx.run_revision() - } else { - for revision in &base_props.revisions { + if config.mode == Incremental { + // Incremental tests are special because they cannot be run in + // parallel. + assert!( + !props.revisions.is_empty(), + "Incremental tests require revisions." + ); + cx.init_incremental_test(); + for revision in &props.revisions { let revision_props = TestProps::from_file(&testpaths.file, Some(revision), &config); let rev_cx = TestCx { config: &config, @@ -230,11 +216,17 @@ pub fn run(config: Config, testpaths: &TestPaths) { }; rev_cx.run_revision(); } + } else { + cx.run_revision(); } - base_cx.complete_all(); + cx.create_stamp(); +} - File::create(::stamp(&config, testpaths)).unwrap(); +pub fn compute_stamp_hash(config: &Config) -> String { + let mut hash = DefaultHasher::new(); + config.stage_id.hash(&mut hash); + format!("{:x}", hash.finish()) } struct TestCx<'test> { @@ -251,14 +243,6 @@ struct DebuggerCommands { } impl<'test> TestCx<'test> { - /// invoked once before any revisions have been processed - fn init_all(&self) { - assert!(self.revision.is_none(), "init_all invoked for a revision"); - if let Incremental = self.config.mode { - self.init_incremental_test() - } - } - /// Code executed for each revision in turn (or, if there are no /// revisions, exactly once, with revision == None). fn run_revision(&self) { @@ -280,11 +264,6 @@ impl<'test> TestCx<'test> { } } - /// Invoked after all revisions have executed. - fn complete_all(&self) { - assert!(self.revision.is_none(), "init_all invoked for a revision"); - } - fn check_if_test_should_compile(&self, proc_res: &ProcRes) { if self.props.compile_pass { if !proc_res.status.success() { @@ -1361,6 +1340,8 @@ impl<'test> TestCx<'test> { testpaths: &aux_testpaths, revision: self.revision, }; + // Create the directory for the stdout/stderr files. + create_dir_all(aux_cx.output_base_dir()).unwrap(); let auxres = aux_cx.document(out_dir); if !auxres.status.success() { return auxres; @@ -1445,7 +1426,7 @@ impl<'test> TestCx<'test> { let mut program = Command::new(&prog); program .args(args) - .current_dir(&self.output_base_name().parent().unwrap()) + .current_dir(&self.output_base_dir()) .envs(env.clone()); self.compose_and_run( program, @@ -1483,9 +1464,9 @@ impl<'test> TestCx<'test> { TestPaths { file: test_ab, - base: self.testpaths.base.clone(), relative_dir: self.testpaths .relative_dir + .join(self.output_testname_unique()) .join("auxiliary") .join(rel_ab) .parent() @@ -1506,17 +1487,15 @@ impl<'test> TestCx<'test> { let aux_props = self.props .from_aux_file(&aux_testpaths.file, self.revision, self.config); - let aux_output = { - let f = self.make_lib_name(&self.testpaths.file); - let parent = f.parent().unwrap(); - TargetLocation::ThisDirectory(parent.to_path_buf()) - }; + let aux_output = TargetLocation::ThisDirectory(self.aux_output_dir_name()); let aux_cx = TestCx { config: self.config, props: &aux_props, testpaths: &aux_testpaths, revision: self.revision, }; + // Create the directory for the stdout/stderr files. + create_dir_all(aux_cx.output_base_dir()).unwrap(); let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output); let crate_type = if aux_props.no_prefer_dynamic { @@ -1642,7 +1621,8 @@ impl<'test> TestCx<'test> { .clone() .expect("no rustdoc built yet")) }; - rustc.arg(input_file).arg("-L").arg(&self.config.build_base); + // FIXME Why is -L here? + rustc.arg(input_file);//.arg("-L").arg(&self.config.build_base); // Optionally prevent default --target if specified in test compile-flags. let custom_target = self.props @@ -1767,15 +1747,12 @@ impl<'test> TestCx<'test> { rustc } - fn make_lib_name(&self, auxfile: &Path) -> PathBuf { - // what we return here is not particularly important, as it - // happens; rustc ignores everything except for the directory. - let auxname = self.output_testname(auxfile); - self.aux_output_dir_name().join(&auxname) - } - fn make_exe_name(&self) -> PathBuf { - let mut f = self.output_base_name_stage(); + // Using a single letter here to keep the path length down for + // Windows. Some test names get very long. rustc creates `rcgu` + // files with the module name appended to it which can more than + // double the length. + let mut f = self.output_base_dir().join("a"); // FIXME: This is using the host architecture exe suffix, not target! if self.config.target.contains("emscripten") { f = f.with_extra_extension("js"); @@ -1885,34 +1862,47 @@ impl<'test> TestCx<'test> { .unwrap(); } + /// Create a filename for output with the given extension. Example: + /// /.../testname.revision.mode/testname.extension fn make_out_name(&self, extension: &str) -> PathBuf { self.output_base_name().with_extension(extension) } + /// Directory where auxiliary files are written. Example: + /// /.../testname.revision.mode/auxiliary/ fn aux_output_dir_name(&self) -> PathBuf { - self.output_base_name_stage() + self.output_base_dir() + .join("auxiliary") .with_extra_extension(self.config.mode.disambiguator()) - .with_extra_extension(".aux") } - fn output_testname(&self, filepath: &Path) -> PathBuf { - PathBuf::from(filepath.file_stem().unwrap()) + /// Generates a unique name for the test, such as `testname.revision.mode`. + fn output_testname_unique(&self) -> PathBuf { + output_testname_unique(self.config, self.testpaths, self.safe_revision()) } - /// Given a test path like `compile-fail/foo/bar.rs` returns a name like - /// `/path/to/build//test/compile-fail/foo/bar`. - fn output_base_name(&self) -> PathBuf { - let dir = self.config.build_base.join(&self.testpaths.relative_dir); + /// The revision, ignored for Incremental since it wants all revisions in + /// the same directory. + fn safe_revision(&self) -> Option<&str> { + if self.config.mode == Incremental { + None + } else { + self.revision + } + } - // Note: The directory `dir` is created during `collect_tests_from_dir` - dir.join(&self.output_testname(&self.testpaths.file)) + /// Absolute path to the directory where all output for the given + /// test/revision should reside. Example: + /// /path/to/build/host-triple/test/ui/relative/testname.revision.mode/ + fn output_base_dir(&self) -> PathBuf { + output_base_dir(self.config, self.testpaths, self.safe_revision()) } - /// Same as `output_base_name`, but includes the stage ID as an extension, - /// such as: `.../compile-fail/foo/bar.stage1-` - fn output_base_name_stage(&self) -> PathBuf { - self.output_base_name() - .with_extension(&self.config.stage_id) + /// Absolute path to the base filename used as output for the given + /// test/revision. Example: + /// /.../relative/testname.revision.mode/testname + fn output_base_name(&self) -> PathBuf { + output_base_name(self.config, self.testpaths, self.safe_revision()) } fn maybe_dump_to_stdout(&self, out: &str, err: &str) { @@ -1987,8 +1977,7 @@ impl<'test> TestCx<'test> { fn compile_test_and_save_ir(&self) -> ProcRes { let aux_dir = self.aux_output_dir_name(); - let output_file = - TargetLocation::ThisDirectory(self.output_base_name().parent().unwrap().to_path_buf()); + let output_file = TargetLocation::ThisDirectory(self.output_base_dir()); let mut rustc = self.make_compile_args(&self.testpaths.file, output_file); rustc.arg("-L").arg(aux_dir).arg("--emit=llvm-ir"); @@ -2037,7 +2026,7 @@ impl<'test> TestCx<'test> { fn run_rustdoc_test(&self) { assert!(self.revision.is_none(), "revisions not relevant here"); - let out_dir = self.output_base_name_stage(); + let out_dir = self.output_base_dir(); let _ = fs::remove_dir_all(&out_dir); create_dir_all(&out_dir).unwrap(); @@ -2365,7 +2354,7 @@ impl<'test> TestCx<'test> { fn run_incremental_test(&self) { // Basic plan for a test incremental/foo/bar.rs: // - load list of revisions rpass1, cfail2, rpass3 - // - each should begin with `rpass`, `cfail`, or `cfail` + // - each should begin with `rpass`, `cfail`, or `rfail` // - if `rpass`, expect compile and execution to succeed // - if `cfail`, expect compilation to fail // - if `rfail`, expect execution to fail @@ -2438,7 +2427,7 @@ impl<'test> TestCx<'test> { .unwrap(); let src_root = cwd.join(&src_root); - let tmpdir = cwd.join(self.output_base_name_stage()); + let tmpdir = cwd.join(self.output_base_name()); if tmpdir.exists() { self.aggressive_rm_rf(&tmpdir).unwrap(); } @@ -2982,6 +2971,11 @@ impl<'test> TestCx<'test> { println!("Actual {} saved to {}", kind, output_file.display()); 1 } + + fn create_stamp(&self) { + let mut f = File::create(::stamp(&self.config, self.testpaths, self.revision)).unwrap(); + f.write_all(compute_stamp_hash(&self.config).as_bytes()).unwrap(); + } } struct ProcArgs { diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 77b991ae3c3fd..91e7399f1f492 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -8,8 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use common::Config; +use std::ffi::OsStr; use std::env; +use std::path::PathBuf; +use common::Config; /// Conversion table from triple OS name to Rust SYSNAME const OS_TABLE: &'static [(&'static str, &'static str)] = &[ @@ -128,3 +130,23 @@ pub fn logv(config: &Config, s: String) { println!("{}", s); } } + +pub trait PathBufExt { + /// Append an extension to the path, even if it already has one. + fn with_extra_extension>(&self, extension: S) -> PathBuf; +} + +impl PathBufExt for PathBuf { + fn with_extra_extension>(&self, extension: S) -> PathBuf { + if extension.as_ref().len() == 0 { + self.clone() + } else { + let mut fname = self.file_name().unwrap().to_os_string(); + if !extension.as_ref().to_str().unwrap().starts_with(".") { + fname.push("."); + } + fname.push(extension); + self.with_file_name(fname) + } + } +} From 6f414b1408c004e32387d7fb563a9cc5cab28526 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 4 May 2018 14:31:43 -0700 Subject: [PATCH 3/4] Fix update-references for tests within subdirectories. --- src/test/ui-fulldeps/update-references.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/ui-fulldeps/update-references.sh b/src/test/ui-fulldeps/update-references.sh index 86f555620eeb9..48e7ec8c25a22 100755 --- a/src/test/ui-fulldeps/update-references.sh +++ b/src/test/ui-fulldeps/update-references.sh @@ -36,10 +36,11 @@ shopt -s nullglob while [[ "$1" != "" ]]; do for EXT in "stderr" "stdout"; do for OUT_NAME in $BUILD_DIR/${1%.rs}*/*$EXT; do + OUT_DIR=`dirname "$1"` OUT_BASE=`basename "$OUT_NAME"` - if ! (diff $OUT_NAME $MYDIR/$OUT_BASE >& /dev/null); then - echo updating $MYDIR/$OUT_BASE - cp $OUT_NAME $MYDIR + if ! (diff $OUT_NAME $MYDIR/$OUT_DIR/$OUT_BASE >& /dev/null); then + echo updating $MYDIR/$OUT_DIR/$OUT_BASE + cp $OUT_NAME $MYDIR/$OUT_DIR fi done done From b8473de7b9309b750fdfafa8f40f3e7302e056f3 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 12 May 2018 22:18:55 -0700 Subject: [PATCH 4/4] Fix running multiple targets. The aux dir, which previously had the `stage_id` embedded in it, was picking up remnants from previous runs. --- src/tools/compiletest/src/runtest.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 2a76113460fc7..59d94e1fa51e1 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1476,12 +1476,13 @@ impl<'test> TestCx<'test> { } fn compose_and_run_compiler(&self, mut rustc: Command, input: Option) -> ProcRes { + let aux_dir = self.aux_output_dir_name(); + if !self.props.aux_builds.is_empty() { - create_dir_all(&self.aux_output_dir_name()).unwrap(); + let _ = fs::remove_dir_all(&aux_dir); + create_dir_all(&aux_dir).unwrap(); } - let aux_dir = self.aux_output_dir_name(); - for rel_ab in &self.props.aux_builds { let aux_testpaths = self.compute_aux_test_paths(rel_ab); let aux_props =