use log::*;
-use crate::common::{self, CompareMode, Config, Mode, PassMode, FailMode};
-use crate::util;
+use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PassMode};
use crate::extract_gdb_version;
+use crate::util;
#[cfg(test)]
mod tests;
-/// Whether to ignore the test.
-#[derive(Clone, Copy, PartialEq, Debug)]
-pub enum Ignore {
- /// Runs it.
- Run,
- /// Ignore it totally.
- Ignore,
- /// Ignore only the gdb test, but run the lldb test.
- IgnoreGdb,
- /// Ignore only the lldb test, but run the gdb test.
- IgnoreLldb,
-}
-
-impl Ignore {
- pub fn can_run_gdb(&self) -> bool {
- *self == Ignore::Run || *self == Ignore::IgnoreLldb
- }
-
- pub fn can_run_lldb(&self) -> bool {
- *self == Ignore::Run || *self == Ignore::IgnoreGdb
- }
-
- pub fn no_gdb(&self) -> Ignore {
- match *self {
- Ignore::Run => Ignore::IgnoreGdb,
- Ignore::IgnoreGdb => Ignore::IgnoreGdb,
- _ => Ignore::Ignore,
- }
- }
-
- pub fn no_lldb(&self) -> Ignore {
- match *self {
- Ignore::Run => Ignore::IgnoreLldb,
- Ignore::IgnoreLldb => Ignore::IgnoreLldb,
- _ => Ignore::Ignore,
- }
- }
-}
-
/// The result of parse_cfg_name_directive.
#[derive(Clone, Copy, PartialEq, Debug)]
enum ParsedNameDirective {
NoMatch,
/// Match.
Match,
- /// Mode was DebugInfoGdbLldb and this matched gdb.
- MatchGdb,
- /// Mode was DebugInfoGdbLldb and this matched lldb.
- MatchLldb,
}
/// Properties which must be known very early, before actually running
/// the test.
+#[derive(Default)]
pub struct EarlyProps {
- pub ignore: Ignore,
+ pub ignore: bool,
pub should_fail: bool,
pub aux: Vec<String>,
pub aux_crate: Vec<(String, String)>,
impl EarlyProps {
pub fn from_file(config: &Config, testfile: &Path) -> Self {
- let mut props = EarlyProps {
- ignore: Ignore::Run,
- should_fail: false,
- aux: Vec::new(),
- aux_crate: Vec::new(),
- revisions: vec![],
- };
-
- if config.mode == common::DebugInfoGdbLldb {
- if config.lldb_python_dir.is_none() {
- props.ignore = props.ignore.no_lldb();
- }
- if config.gdb_version.is_none() {
- props.ignore = props.ignore.no_gdb();
- }
- } else if config.mode == common::DebugInfoCdb {
- if config.cdb.is_none() {
- props.ignore = Ignore::Ignore;
- }
- }
+ let file = File::open(testfile).unwrap();
+ Self::from_reader(config, testfile, file)
+ }
+ pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self {
+ let mut props = EarlyProps::default();
let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some();
- iter_header(testfile, None, &mut |ln| {
+ iter_header(testfile, None, rdr, &mut |ln| {
// we should check if any only-<platform> exists and if it exists
// and does not matches the current platform, skip the test
- if props.ignore != Ignore::Ignore {
+ if !props.ignore {
props.ignore = match config.parse_cfg_name_directive(ln, "ignore") {
- ParsedNameDirective::Match => Ignore::Ignore,
+ ParsedNameDirective::Match => true,
ParsedNameDirective::NoMatch => props.ignore,
- ParsedNameDirective::MatchGdb => props.ignore.no_gdb(),
- ParsedNameDirective::MatchLldb => props.ignore.no_lldb(),
};
if config.has_cfg_prefix(ln, "only") {
props.ignore = match config.parse_cfg_name_directive(ln, "only") {
ParsedNameDirective::Match => props.ignore,
- ParsedNameDirective::NoMatch => Ignore::Ignore,
- ParsedNameDirective::MatchLldb => props.ignore.no_gdb(),
- ParsedNameDirective::MatchGdb => props.ignore.no_lldb(),
+ ParsedNameDirective::NoMatch => true,
};
}
if ignore_llvm(config, ln) {
- props.ignore = Ignore::Ignore;
+ props.ignore = true;
}
- if config.run_clang_based_tests_with.is_none() &&
- config.parse_needs_matching_clang(ln) {
- props.ignore = Ignore::Ignore;
+ if config.run_clang_based_tests_with.is_none()
+ && config.parse_needs_matching_clang(ln)
+ {
+ props.ignore = true;
}
- if !rustc_has_profiler_support &&
- config.parse_needs_profiler_support(ln) {
- props.ignore = Ignore::Ignore;
+ if !rustc_has_profiler_support && config.parse_needs_profiler_support(ln) {
+ props.ignore = true;
}
- if !rustc_has_sanitizer_support &&
- config.parse_needs_sanitizer_support(ln) {
- props.ignore = Ignore::Ignore;
+ if !rustc_has_sanitizer_support && config.parse_needs_sanitizer_support(ln) {
+ props.ignore = true;
}
if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
- props.ignore = Ignore::Ignore;
+ props.ignore = true;
}
- }
- if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoGdbLldb) &&
- props.ignore.can_run_gdb() && ignore_gdb(config, ln) {
- props.ignore = props.ignore.no_gdb();
- }
+ if config.debugger == Some(Debugger::Gdb) && ignore_gdb(config, ln) {
+ props.ignore = true;
+ }
- if (config.mode == common::DebugInfoLldb || config.mode == common::DebugInfoGdbLldb) &&
- props.ignore.can_run_lldb() && ignore_lldb(config, ln) {
- props.ignore = props.ignore.no_lldb();
+ if config.debugger == Some(Debugger::Lldb) && ignore_lldb(config, ln) {
+ props.ignore = true;
+ }
}
if let Some(s) = config.parse_aux_build(ln) {
fn extract_gdb_version_range(line: &str) -> (u32, u32) {
const ERROR_MESSAGE: &'static str = "Malformed GDB version directive";
- let range_components = line.split(&[' ', '-'][..])
- .filter(|word| !word.is_empty())
- .map(extract_gdb_version)
- .skip_while(Option::is_none)
- .take(3) // 3 or more = invalid, so take at most 3.
- .collect::<Vec<Option<u32>>>();
+ let range_components = line
+ .split(&[' ', '-'][..])
+ .filter(|word| !word.is_empty())
+ .map(extract_gdb_version)
+ .skip_while(Option::is_none)
+ .take(3) // 3 or more = invalid, so take at most 3.
+ .collect::<Vec<Option<u32>>>();
match range_components.len() {
1 => {
fn ignore_lldb(config: &Config, line: &str) -> bool {
if let Some(ref actual_version) = config.lldb_version {
if line.starts_with("min-lldb-version") {
- let min_version = line.trim_end()
+ let min_version = line
+ .trim_end()
.rsplit(' ')
.next()
.expect("Malformed lldb version directive");
}
if let Some(ref actual_version) = config.llvm_version {
if line.starts_with("min-llvm-version") {
- let min_version = line.trim_end()
+ let min_version = line
+ .trim_end()
.rsplit(' ')
.next()
.expect("Malformed llvm version directive");
// version
&actual_version[..] < min_version
} else if line.starts_with("min-system-llvm-version") {
- let min_version = line.trim_end()
+ let min_version = line
+ .trim_end()
.rsplit(' ')
.next()
.expect("Malformed llvm version directive");
config.system_llvm && &actual_version[..] < min_version
} else if line.starts_with("ignore-llvm-version") {
// Syntax is: "ignore-llvm-version <version1> [- <version2>]"
- let range_components = line.split(' ')
+ let range_components = line
+ .split(' ')
.skip(1) // Skip the directive.
.map(|s| s.trim())
.filter(|word| !word.is_empty() && word != &"-")
.take(3) // 3 or more = invalid, so take at most 3.
.collect::<Vec<&str>>();
match range_components.len() {
- 1 => {
- &actual_version[..] == range_components[0]
- }
+ 1 => &actual_version[..] == range_components[0],
2 => {
let v_min = range_components[0];
let v_max = range_components[1];
pub fail_mode: Option<FailMode>,
// rustdoc will test the output of the `--test` option
pub check_test_line_numbers_match: bool,
- // Do not pass `-Z ui-testing` to UI tests
- pub disable_ui_testing_normalization: bool,
// customized normalization rules
pub normalize_stdout: Vec<(String, String)>,
pub normalize_stderr: Vec<(String, String)>,
fail_mode: None,
ignore_pass: false,
check_test_line_numbers_match: false,
- disable_ui_testing_normalization: false,
normalize_stdout: vec![],
normalize_stderr: vec![],
failure_status: -1,
/// `//[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| {
+ let file = File::open(testfile).unwrap();
+ iter_header(testfile, cfg, file, &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(edition) = config.parse_edition(ln) {
self.ignore_pass = config.parse_ignore_pass(ln);
}
- if !self.disable_ui_testing_normalization {
- self.disable_ui_testing_normalization =
- config.parse_disable_ui_testing_normalization(ln);
- }
-
if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") {
self.normalize_stdout.push(rule);
}
}
fn update_fail_mode(&mut self, ln: &str, config: &Config) {
- let check_ui = |mode: &str| if config.mode != Mode::Ui {
- panic!("`{}-fail` header is only supported in UI tests", mode);
+ let check_ui = |mode: &str| {
+ if config.mode != Mode::Ui {
+ panic!("`{}-fail` header is only supported in UI tests", mode);
+ }
};
let fail_mode = if config.parse_name_directive(ln, "check-fail") {
check_ui("check");
if config.mode != Mode::Ui && config.mode != Mode::Incremental {
panic!("`{}` header is only supported in UI and incremental tests", s);
}
- if config.mode == Mode::Incremental &&
- !revision.map_or(false, |r| r.starts_with("cfail")) &&
- !self.revisions.iter().all(|r| r.starts_with("cfail")) {
+ if config.mode == Mode::Incremental
+ && !revision.map_or(false, |r| r.starts_with("cfail"))
+ && !self.revisions.iter().all(|r| r.starts_with("cfail"))
+ {
panic!("`{}` header is only supported in `cfail` incremental tests", s);
}
};
}
}
-fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) {
+fn iter_header<R: Read>(testfile: &Path, cfg: Option<&str>, rdr: R, it: &mut dyn FnMut(&str)) {
if testfile.is_dir() {
return;
}
- let comment = if testfile.to_string_lossy().ends_with(".rs") {
- "//"
- } else {
- "#"
- };
+ let comment = if testfile.to_string_lossy().ends_with(".rs") { "//" } else { "#" };
// FIXME: would be nice to allow some whitespace between comment and brace :)
// It took me like 2 days to debug why compile-flags weren’t taken into account for my test :)
let comment_with_brace = comment.to_string() + "[";
- let rdr = BufReader::new(File::open(testfile).unwrap());
- for ln in rdr.lines() {
+ let mut rdr = BufReader::new(rdr);
+ let mut ln = String::new();
+
+ loop {
+ ln.clear();
+ if rdr.read_line(&mut ln).unwrap() == 0 {
+ break;
+ }
+
// Assume that any directives will be found before the first
// module or function. This doesn't seem to be an optimization
// with a warm page cache. Maybe with a cold one.
- let ln = ln.unwrap();
let ln = ln.trim();
if ln.starts_with("fn") || ln.starts_with("mod") {
return;
// A comment like `//[foo]` is specific to revision `foo`
if let Some(close_brace) = ln.find(']') {
let open_brace = ln.find('[').unwrap();
- let lncfg = &ln[open_brace + 1 .. close_brace];
+ let lncfg = &ln[open_brace + 1..close_brace];
let matches = match cfg {
Some(s) => s == &lncfg[..],
None => false,
it(ln[(close_brace + 1)..].trim_start());
}
} else {
- panic!("malformed condition directive: expected `{}foo]`, found `{}`",
- comment_with_brace, ln)
+ panic!(
+ "malformed condition directive: expected `{}foo]`, found `{}`",
+ comment_with_brace, ln
+ )
}
} else if ln.starts_with(comment) {
- it(ln[comment.len() ..].trim_start());
+ it(ln[comment.len()..].trim_start());
}
}
return;
}
fn parse_aux_build(&self, line: &str) -> Option<String> {
- self.parse_name_value_directive(line, "aux-build")
- .map(|r| r.trim().to_string())
+ self.parse_name_value_directive(line, "aux-build").map(|r| r.trim().to_string())
}
fn parse_aux_crate(&self, line: &str) -> Option<(String, String)> {
}
}
- fn parse_disable_ui_testing_normalization(&self, line: &str) -> bool {
- self.parse_name_directive(line, "disable-ui-testing-normalization")
- }
-
fn parse_check_test_line_numbers_match(&self, line: &str) -> bool {
self.parse_name_directive(line, "check-test-line-numbers-match")
}
}
fn parse_assembly_output(&self, line: &str) -> Option<String> {
- self.parse_name_value_directive(line, "assembly-output")
- .map(|r| r.trim().to_string())
+ self.parse_name_value_directive(line, "assembly-output").map(|r| r.trim().to_string())
}
fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
/// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
/// or `normalize-stderr-32bit`.
fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {
- if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') {
- let name = line[prefix.len() + 1..]
- .split(&[':', ' '][..])
- .next()
- .unwrap();
-
- if name == "test" ||
- util::matches_os(&self.target, name) || // target
- util::matches_env(&self.target, name) || // env
- name == util::get_arch(&self.target) || // architecture
- name == util::get_pointer_width(&self.target) || // pointer width
- name == self.stage_id.split('-').next().unwrap() || // stage
- (self.target != self.host && name == "cross-compile") ||
- match self.compare_mode {
- Some(CompareMode::Nll) => name == "compare-mode-nll",
- Some(CompareMode::Polonius) => name == "compare-mode-polonius",
- None => false,
- } ||
- (cfg!(debug_assertions) && name == "debug") {
- ParsedNameDirective::Match
- } else {
- match self.mode {
- common::DebugInfoGdbLldb => {
- if name == "gdb" {
- ParsedNameDirective::MatchGdb
- } else if name == "lldb" {
- ParsedNameDirective::MatchLldb
- } else {
- ParsedNameDirective::NoMatch
- }
- },
- common::DebugInfoCdb => if name == "cdb" {
- ParsedNameDirective::Match
- } else {
- ParsedNameDirective::NoMatch
- },
- common::DebugInfoGdb => if name == "gdb" {
- ParsedNameDirective::Match
- } else {
- ParsedNameDirective::NoMatch
- },
- common::DebugInfoLldb => if name == "lldb" {
- ParsedNameDirective::Match
- } else {
- ParsedNameDirective::NoMatch
- },
- common::Pretty => if name == "pretty" {
- ParsedNameDirective::Match
- } else {
- ParsedNameDirective::NoMatch
- },
- _ => ParsedNameDirective::NoMatch,
- }
- }
- } else {
- ParsedNameDirective::NoMatch
+ if !line.as_bytes().starts_with(prefix.as_bytes()) {
+ return ParsedNameDirective::NoMatch;
}
+ if line.as_bytes().get(prefix.len()) != Some(&b'-') {
+ return ParsedNameDirective::NoMatch;
+ }
+
+ let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap();
+
+ let is_match = name == "test" ||
+ &self.target == name || // triple
+ util::matches_os(&self.target, name) || // target
+ util::matches_env(&self.target, name) || // env
+ name == util::get_arch(&self.target) || // architecture
+ name == util::get_pointer_width(&self.target) || // pointer width
+ name == self.stage_id.split('-').next().unwrap() || // stage
+ (self.target != self.host && name == "cross-compile") ||
+ match self.compare_mode {
+ Some(CompareMode::Nll) => name == "compare-mode-nll",
+ Some(CompareMode::Polonius) => name == "compare-mode-polonius",
+ None => false,
+ } ||
+ (cfg!(debug_assertions) && name == "debug") ||
+ match self.debugger {
+ Some(Debugger::Cdb) => name == "cdb",
+ Some(Debugger::Gdb) => name == "gdb",
+ Some(Debugger::Lldb) => name == "lldb",
+ None => false,
+ };
+
+ if is_match { ParsedNameDirective::Match } else { ParsedNameDirective::NoMatch }
}
fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool {
fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
// Ensure the directive is a whole word. Do not match "ignore-x86" when
// the line says "ignore-x86_64".
- line.starts_with(directive) && match line.as_bytes().get(directive.len()) {
- None | Some(&b' ') | Some(&b':') => true,
- _ => false,
- }
+ line.starts_with(directive)
+ && match line.as_bytes().get(directive.len()) {
+ None | Some(&b' ') | Some(&b':') => true,
+ _ => false,
+ }
}
pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
}
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)
}