diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 2281a45e014a9..f6c41a0a6371d 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1932,6 +1932,7 @@ mod __test { bless: false, compare_mode: None, rustfix_coverage: false, + filter_mode: None, }; let build = Build::new(config); @@ -1974,6 +1975,7 @@ mod __test { bless: false, compare_mode: None, rustfix_coverage: false, + filter_mode: None, }; let build = Build::new(config); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 4774c0a51c09a..c78e626b3e15e 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -58,6 +58,7 @@ pub enum Subcommand { /// Whether to automatically update stderr/stdout files bless: bool, compare_mode: Option, + filter_mode: Option, test_args: Vec, rustc_args: Vec, fail_fast: bool, @@ -199,6 +200,12 @@ To learn more about a subcommand, run `./x.py -h`" "mode describing what file the actual ui output will be compared to", "COMPARE MODE", ); + opts.optopt( + "", + "filter-mode", + "only test files matching any of the comma separated list of modes", + "MODES", + ); opts.optflag( "", "rustfix-coverage", @@ -401,6 +408,7 @@ Arguments: paths, bless: matches.opt_present("bless"), compare_mode: matches.opt_str("compare-mode"), + filter_mode: matches.opt_str("filter-mode"), test_args: matches.opt_strs("test-args"), rustc_args: matches.opt_strs("rustc-args"), fail_fast: !matches.opt_present("no-fail-fast"), @@ -524,6 +532,15 @@ impl Subcommand { _ => None, } } + + pub fn filter_mode(&self) -> Option<&str> { + match *self { + Subcommand::Test { + ref filter_mode, .. + } => filter_mode.as_ref().map(|s| &s[..]), + _ => None, + } + } } fn split(s: &[String]) -> Vec { diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 74caaae2840c5..9469ac7d020f1 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1065,6 +1065,11 @@ impl Step for Compiletest { } }); + if let Some(ref filter_mode) = builder.config.cmd.filter_mode() { + cmd.arg("--filter-mode"); + cmd.arg(filter_mode); + } + if let Some(ref nodejs) = builder.config.nodejs { cmd.arg("--nodejs").arg(nodejs); } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 722979c3c1402..c14855c0bad73 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -99,6 +99,33 @@ impl fmt::Display for Mode { } } +/// Mode with extra forms. +/// Used for `--filter-mode` to allow `compile-pass` to be used. +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum ExtraMode { + Mode(Mode), + CompilePass, +} + +impl FromStr for ExtraMode { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "compile-pass" => Ok(ExtraMode::CompilePass), + s => Mode::from_str(s).map(ExtraMode::Mode), + } + } +} + +impl fmt::Display for ExtraMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ExtraMode::CompilePass => fmt::Display::fmt("compile-pass", f), + ExtraMode::Mode(mode) => mode.fmt(f), + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum CompareMode { Nll, @@ -184,6 +211,9 @@ pub struct Config { /// Exactly match the filter, rather than a substring pub filter_exact: bool, + /// Only run tests that match any of these modes. + pub filter_mode: Option>, + /// Write out a parseable log of tests that were run pub logfile: Option, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index ab5594f36050d..57cc07568e1d7 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -6,7 +6,7 @@ use std::path::{Path, PathBuf}; use log::*; -use crate::common::{self, CompareMode, Config, Mode}; +use crate::common::{self, CompareMode, Config, Mode, ExtraMode}; use crate::util; use crate::extract_gdb_version; @@ -97,7 +97,24 @@ impl EarlyProps { 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(); + let mut is_run_pass = false; + let mut is_compile_pass = false; + let is_mode_ui = config.mode == Mode::Ui; + + // Apply `--filter-mode` for non-ui tests. + if let Some(fm) = &config.filter_mode { + if !is_mode_ui && !fm.contains(&ExtraMode::Mode(config.mode)) { + props.ignore = Ignore::Ignore; + } + } + iter_header(testfile, None, &mut |ln| { + // Are we instructed by this line to treat file as run-pass/compile-pass? + if is_mode_ui { + is_run_pass |= config.parse_run_pass(ln); + is_compile_pass |= config.parse_compile_pass(ln); + } + // we should check if any only- exists and if it exists // and does not matches the current platform, skip the test if props.ignore != Ignore::Ignore { @@ -158,6 +175,19 @@ impl EarlyProps { props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail"); }); + // Apply `--filter-mode` for ui tests. + if let (Some(fm), true) = (&config.filter_mode, is_mode_ui) { + let keep = fm.contains(&ExtraMode::Mode(Mode::Ui)) + || match (is_run_pass, is_compile_pass) { + (true, _) => fm.contains(&ExtraMode::Mode(Mode::RunPass)), + (_, true) => fm.contains(&ExtraMode::CompilePass), + (_, _) => fm.contains(&ExtraMode::Mode(Mode::CompileFail)), + }; + if !keep { + props.ignore = Ignore::Ignore; + } + } + return props; fn ignore_gdb(config: &Config, line: &str) -> bool { diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index d0dc9d11d3963..e4419426ce18d 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -8,7 +8,8 @@ extern crate test; use crate::common::CompareMode; use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS}; use crate::common::{Config, TestPaths}; -use crate::common::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb, Mode, Pretty}; +use crate::common::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb}; +use crate::common::{Mode, ExtraMode, Pretty}; use getopts::Options; use std::env; use std::ffi::OsString; @@ -128,6 +129,12 @@ pub fn parse_config(args: Vec) -> Config { "(compile-fail|run-fail|run-pass|\ run-pass-valgrind|pretty|debug-info|incremental|mir-opt)", ) + .optopt( + "", + "filter-mode", + "only test files matching any of the comma separated list of modes", + "MODES" + ) .optflag("", "ignored", "run tests marked as ignored") .optflag("", "exact", "filters match exactly") .optopt( @@ -320,6 +327,17 @@ pub fn parse_config(args: Vec) -> Config { run_ignored, filter: matches.free.first().cloned(), filter_exact: matches.opt_present("exact"), + filter_mode: matches.opt_str("filter-mode").map(|fm| { + fm.split(',') + .map(|m| match m.parse::() { + Ok(m) => m, + _ => panic!( + "--filter-mode only accepts valid modes; `{}` is not valid", + m + ), + }) + .collect() + }), logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)), runtool: matches.opt_str("runtool"), host_rustcflags: matches.opt_str("host-rustcflags"), @@ -382,6 +400,18 @@ pub fn log_config(config: &Config) { ), ); logv(c, format!("filter_exact: {}", config.filter_exact)); + logv( + c, + format!( + "filter_mode: {}", + opt_str(&config.filter_mode.as_ref().map(|fm| { + fm.iter() + .map(|m| format!("{}", m)) + .collect::>() + .join(",") + })) + ), + ); logv(c, format!("runtool: {}", opt_str(&config.runtool))); logv( c,