Rollup merge of #81356 - ehuss:libtest-filters, r=m-ou-se
libtest: allow multiple filters Libtest ignores any filters after the first. This changes it so that if multiple filters are passed, it will test against all of them. This also affects compiletest to do the same. Closes #30422
This commit is contained in:
@@ -10,7 +10,7 @@ use super::time::TestTimeOptions;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TestOpts {
|
pub struct TestOpts {
|
||||||
pub list: bool,
|
pub list: bool,
|
||||||
pub filter: Option<String>,
|
pub filters: Vec<String>,
|
||||||
pub filter_exact: bool,
|
pub filter_exact: bool,
|
||||||
pub force_run_in_process: bool,
|
pub force_run_in_process: bool,
|
||||||
pub exclude_should_panic: bool,
|
pub exclude_should_panic: bool,
|
||||||
@@ -148,12 +148,13 @@ fn optgroups() -> getopts::Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(binary: &str, options: &getopts::Options) {
|
fn usage(binary: &str, options: &getopts::Options) {
|
||||||
let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
|
let message = format!("Usage: {} [OPTIONS] [FILTERS...]", binary);
|
||||||
println!(
|
println!(
|
||||||
r#"{usage}
|
r#"{usage}
|
||||||
|
|
||||||
The FILTER string is tested against the name of all tests, and only those
|
The FILTER string is tested against the name of all tests, and only those
|
||||||
tests whose names contain the filter are run.
|
tests whose names contain the filter are run. Multiple filter strings may
|
||||||
|
be passed, which will run all tests matching any of the filters.
|
||||||
|
|
||||||
By default, all tests are run in parallel. This can be altered with the
|
By default, all tests are run in parallel. This can be altered with the
|
||||||
--test-threads flag or the RUST_TEST_THREADS environment variable when running
|
--test-threads flag or the RUST_TEST_THREADS environment variable when running
|
||||||
@@ -243,7 +244,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
|
|||||||
|
|
||||||
let logfile = get_log_file(&matches)?;
|
let logfile = get_log_file(&matches)?;
|
||||||
let run_ignored = get_run_ignored(&matches, include_ignored)?;
|
let run_ignored = get_run_ignored(&matches, include_ignored)?;
|
||||||
let filter = get_filter(&matches)?;
|
let filters = matches.free.clone();
|
||||||
let nocapture = get_nocapture(&matches)?;
|
let nocapture = get_nocapture(&matches)?;
|
||||||
let test_threads = get_test_threads(&matches)?;
|
let test_threads = get_test_threads(&matches)?;
|
||||||
let color = get_color_config(&matches)?;
|
let color = get_color_config(&matches)?;
|
||||||
@@ -253,7 +254,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
|
|||||||
|
|
||||||
let test_opts = TestOpts {
|
let test_opts = TestOpts {
|
||||||
list,
|
list,
|
||||||
filter,
|
filters,
|
||||||
filter_exact: exact,
|
filter_exact: exact,
|
||||||
force_run_in_process,
|
force_run_in_process,
|
||||||
exclude_should_panic,
|
exclude_should_panic,
|
||||||
@@ -397,12 +398,6 @@ fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPart
|
|||||||
Ok(run_ignored)
|
Ok(run_ignored)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
|
|
||||||
let filter = if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None };
|
|
||||||
|
|
||||||
Ok(filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
|
fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
|
||||||
let mut allow_unstable = false;
|
let mut allow_unstable = false;
|
||||||
|
|
||||||
|
|||||||
@@ -396,8 +396,8 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Remove tests that don't match the test filter
|
// Remove tests that don't match the test filter
|
||||||
if let Some(ref filter) = opts.filter {
|
if !opts.filters.is_empty() {
|
||||||
filtered.retain(|test| matches_filter(test, filter));
|
filtered.retain(|test| opts.filters.iter().any(|filter| matches_filter(test, filter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip tests that match any of the skip filters
|
// Skip tests that match any of the skip filters
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ impl TestOpts {
|
|||||||
fn new() -> TestOpts {
|
fn new() -> TestOpts {
|
||||||
TestOpts {
|
TestOpts {
|
||||||
list: false,
|
list: false,
|
||||||
filter: None,
|
filters: vec![],
|
||||||
filter_exact: false,
|
filter_exact: false,
|
||||||
force_run_in_process: false,
|
force_run_in_process: false,
|
||||||
exclude_should_panic: false,
|
exclude_should_panic: false,
|
||||||
@@ -473,43 +473,60 @@ pub fn exact_filter_match() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let substr =
|
let substr =
|
||||||
filter_tests(&TestOpts { filter: Some("base".into()), ..TestOpts::new() }, tests());
|
filter_tests(&TestOpts { filters: vec!["base".into()], ..TestOpts::new() }, tests());
|
||||||
assert_eq!(substr.len(), 4);
|
|
||||||
|
|
||||||
let substr = filter_tests(&TestOpts { filter: Some("bas".into()), ..TestOpts::new() }, tests());
|
|
||||||
assert_eq!(substr.len(), 4);
|
assert_eq!(substr.len(), 4);
|
||||||
|
|
||||||
let substr =
|
let substr =
|
||||||
filter_tests(&TestOpts { filter: Some("::test".into()), ..TestOpts::new() }, tests());
|
filter_tests(&TestOpts { filters: vec!["bas".into()], ..TestOpts::new() }, tests());
|
||||||
|
assert_eq!(substr.len(), 4);
|
||||||
|
|
||||||
|
let substr =
|
||||||
|
filter_tests(&TestOpts { filters: vec!["::test".into()], ..TestOpts::new() }, tests());
|
||||||
assert_eq!(substr.len(), 3);
|
assert_eq!(substr.len(), 3);
|
||||||
|
|
||||||
let substr =
|
let substr =
|
||||||
filter_tests(&TestOpts { filter: Some("base::test".into()), ..TestOpts::new() }, tests());
|
filter_tests(&TestOpts { filters: vec!["base::test".into()], ..TestOpts::new() }, tests());
|
||||||
assert_eq!(substr.len(), 3);
|
assert_eq!(substr.len(), 3);
|
||||||
|
|
||||||
|
let substr = filter_tests(
|
||||||
|
&TestOpts { filters: vec!["test1".into(), "test2".into()], ..TestOpts::new() },
|
||||||
|
tests(),
|
||||||
|
);
|
||||||
|
assert_eq!(substr.len(), 2);
|
||||||
|
|
||||||
let exact = filter_tests(
|
let exact = filter_tests(
|
||||||
&TestOpts { filter: Some("base".into()), filter_exact: true, ..TestOpts::new() },
|
&TestOpts { filters: vec!["base".into()], filter_exact: true, ..TestOpts::new() },
|
||||||
tests(),
|
tests(),
|
||||||
);
|
);
|
||||||
assert_eq!(exact.len(), 1);
|
assert_eq!(exact.len(), 1);
|
||||||
|
|
||||||
let exact = filter_tests(
|
let exact = filter_tests(
|
||||||
&TestOpts { filter: Some("bas".into()), filter_exact: true, ..TestOpts::new() },
|
&TestOpts { filters: vec!["bas".into()], filter_exact: true, ..TestOpts::new() },
|
||||||
tests(),
|
tests(),
|
||||||
);
|
);
|
||||||
assert_eq!(exact.len(), 0);
|
assert_eq!(exact.len(), 0);
|
||||||
|
|
||||||
let exact = filter_tests(
|
let exact = filter_tests(
|
||||||
&TestOpts { filter: Some("::test".into()), filter_exact: true, ..TestOpts::new() },
|
&TestOpts { filters: vec!["::test".into()], filter_exact: true, ..TestOpts::new() },
|
||||||
tests(),
|
tests(),
|
||||||
);
|
);
|
||||||
assert_eq!(exact.len(), 0);
|
assert_eq!(exact.len(), 0);
|
||||||
|
|
||||||
let exact = filter_tests(
|
let exact = filter_tests(
|
||||||
&TestOpts { filter: Some("base::test".into()), filter_exact: true, ..TestOpts::new() },
|
&TestOpts { filters: vec!["base::test".into()], filter_exact: true, ..TestOpts::new() },
|
||||||
tests(),
|
tests(),
|
||||||
);
|
);
|
||||||
assert_eq!(exact.len(), 1);
|
assert_eq!(exact.len(), 1);
|
||||||
|
|
||||||
|
let exact = filter_tests(
|
||||||
|
&TestOpts {
|
||||||
|
filters: vec!["base".into(), "base::test".into()],
|
||||||
|
filter_exact: true,
|
||||||
|
..TestOpts::new()
|
||||||
|
},
|
||||||
|
tests(),
|
||||||
|
);
|
||||||
|
assert_eq!(exact.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
17
src/test/ui/test-attrs/test-filter-multiple.rs
Normal file
17
src/test/ui/test-attrs/test-filter-multiple.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// run-pass
|
||||||
|
// compile-flags: --test
|
||||||
|
// run-flags: --test-threads=1 test1 test2
|
||||||
|
// check-run-results
|
||||||
|
// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||||
|
// ignore-emscripten no threads support
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test1() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test2() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test3() {
|
||||||
|
panic!("this should not run");
|
||||||
|
}
|
||||||
7
src/test/ui/test-attrs/test-filter-multiple.run.stdout
Normal file
7
src/test/ui/test-attrs/test-filter-multiple.run.stdout
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
running 2 tests
|
||||||
|
test test1 ... ok
|
||||||
|
test test2 ... ok
|
||||||
|
|
||||||
|
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in $TIME
|
||||||
|
|
||||||
@@ -240,8 +240,8 @@ pub struct Config {
|
|||||||
/// Run ignored tests
|
/// Run ignored tests
|
||||||
pub run_ignored: bool,
|
pub run_ignored: bool,
|
||||||
|
|
||||||
/// Only run tests that match this filter
|
/// Only run tests that match these filters
|
||||||
pub filter: Option<String>,
|
pub filters: Vec<String>,
|
||||||
|
|
||||||
/// Exactly match the filter, rather than a substring
|
/// Exactly match the filter, rather than a substring
|
||||||
pub filter_exact: bool,
|
pub filter_exact: bool,
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
|||||||
suite: matches.opt_str("suite").unwrap(),
|
suite: matches.opt_str("suite").unwrap(),
|
||||||
debugger: None,
|
debugger: None,
|
||||||
run_ignored,
|
run_ignored,
|
||||||
filter: matches.free.first().cloned(),
|
filters: matches.free.clone(),
|
||||||
filter_exact: matches.opt_present("exact"),
|
filter_exact: matches.opt_present("exact"),
|
||||||
force_pass_mode: matches.opt_str("pass").map(|mode| {
|
force_pass_mode: matches.opt_str("pass").map(|mode| {
|
||||||
mode.parse::<PassMode>()
|
mode.parse::<PassMode>()
|
||||||
@@ -280,7 +280,7 @@ pub fn log_config(config: &Config) {
|
|||||||
logv(c, format!("stage_id: {}", config.stage_id));
|
logv(c, format!("stage_id: {}", config.stage_id));
|
||||||
logv(c, format!("mode: {}", config.mode));
|
logv(c, format!("mode: {}", config.mode));
|
||||||
logv(c, format!("run_ignored: {}", config.run_ignored));
|
logv(c, format!("run_ignored: {}", config.run_ignored));
|
||||||
logv(c, format!("filter: {}", opt_str(&config.filter)));
|
logv(c, format!("filters: {:?}", config.filters));
|
||||||
logv(c, format!("filter_exact: {}", config.filter_exact));
|
logv(c, format!("filter_exact: {}", config.filter_exact));
|
||||||
logv(
|
logv(
|
||||||
c,
|
c,
|
||||||
@@ -465,7 +465,7 @@ fn configure_lldb(config: &Config) -> Option<Config> {
|
|||||||
pub fn test_opts(config: &Config) -> test::TestOpts {
|
pub fn test_opts(config: &Config) -> test::TestOpts {
|
||||||
test::TestOpts {
|
test::TestOpts {
|
||||||
exclude_should_panic: false,
|
exclude_should_panic: false,
|
||||||
filter: config.filter.clone(),
|
filters: config.filters.clone(),
|
||||||
filter_exact: config.filter_exact,
|
filter_exact: config.filter_exact,
|
||||||
run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
|
run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
|
||||||
format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
|
format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
|
||||||
|
|||||||
Reference in New Issue
Block a user