From 30891b84ff3886e076c2fa0a8886ad631196db9a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 24 Jan 2021 13:12:37 -0800 Subject: [PATCH] libtest: allow multiple filters --- library/test/src/cli.rs | 17 ++++----- library/test/src/lib.rs | 4 +-- library/test/src/tests.rs | 35 ++++++++++++++----- .../ui/test-attrs/test-filter-multiple.rs | 17 +++++++++ .../test-filter-multiple.run.stdout | 7 ++++ src/tools/compiletest/src/common.rs | 4 +-- src/tools/compiletest/src/main.rs | 6 ++-- 7 files changed, 63 insertions(+), 27 deletions(-) create mode 100644 src/test/ui/test-attrs/test-filter-multiple.rs create mode 100644 src/test/ui/test-attrs/test-filter-multiple.run.stdout diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 97a659f22d757..5ea0afb0ef30e 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -10,7 +10,7 @@ use super::time::TestTimeOptions; #[derive(Debug)] pub struct TestOpts { pub list: bool, - pub filter: Option, + pub filters: Vec, pub filter_exact: bool, pub force_run_in_process: bool, pub exclude_should_panic: bool, @@ -148,12 +148,13 @@ fn optgroups() -> getopts::Options { } fn usage(binary: &str, options: &getopts::Options) { - let message = format!("Usage: {} [OPTIONS] [FILTER]", binary); + let message = format!("Usage: {} [OPTIONS] [FILTERS...]", binary); println!( r#"{usage} 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 --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 run_ignored = get_run_ignored(&matches, include_ignored)?; - let filter = get_filter(&matches)?; + let filters = matches.free.clone(); let nocapture = get_nocapture(&matches)?; let test_threads = get_test_threads(&matches)?; let color = get_color_config(&matches)?; @@ -253,7 +254,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let test_opts = TestOpts { list, - filter, + filters, filter_exact: exact, force_run_in_process, exclude_should_panic, @@ -397,12 +398,6 @@ fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPart Ok(run_ignored) } -fn get_filter(matches: &getopts::Matches) -> OptPartRes> { - let filter = if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None }; - - Ok(filter) -} - fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes { let mut allow_unstable = false; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 656d9669e81d2..3c4bd3074179f 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -359,8 +359,8 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec TestOpts { TestOpts { list: false, - filter: None, + filters: vec![], filter_exact: false, force_run_in_process: false, exclude_should_panic: false, @@ -469,43 +469,60 @@ pub fn exact_filter_match() { } 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()); + let substr = + filter_tests(&TestOpts { filters: vec!["bas".into()], ..TestOpts::new() }, tests()); assert_eq!(substr.len(), 4); let substr = - filter_tests(&TestOpts { filter: Some("::test".into()), ..TestOpts::new() }, tests()); + filter_tests(&TestOpts { filters: vec!["::test".into()], ..TestOpts::new() }, tests()); assert_eq!(substr.len(), 3); 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); + let substr = filter_tests( + &TestOpts { filters: vec!["test1".into(), "test2".into()], ..TestOpts::new() }, + tests(), + ); + assert_eq!(substr.len(), 2); + 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(), ); assert_eq!(exact.len(), 1); 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(), ); assert_eq!(exact.len(), 0); 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(), ); assert_eq!(exact.len(), 0); 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(), ); 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] diff --git a/src/test/ui/test-attrs/test-filter-multiple.rs b/src/test/ui/test-attrs/test-filter-multiple.rs new file mode 100644 index 0000000000000..04dd83b7fd0f7 --- /dev/null +++ b/src/test/ui/test-attrs/test-filter-multiple.rs @@ -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"); +} diff --git a/src/test/ui/test-attrs/test-filter-multiple.run.stdout b/src/test/ui/test-attrs/test-filter-multiple.run.stdout new file mode 100644 index 0000000000000..1aa684ed5073a --- /dev/null +++ b/src/test/ui/test-attrs/test-filter-multiple.run.stdout @@ -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 + diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5424889a8388b..922f275542b84 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -240,8 +240,8 @@ pub struct Config { /// Run ignored tests pub run_ignored: bool, - /// Only run tests that match this filter - pub filter: Option, + /// Only run tests that match these filters + pub filters: Vec, /// Exactly match the filter, rather than a substring pub filter_exact: bool, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 688cf930033ef..3fde24e8a7fba 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -221,7 +221,7 @@ pub fn parse_config(args: Vec) -> Config { suite: matches.opt_str("suite").unwrap(), debugger: None, run_ignored, - filter: matches.free.first().cloned(), + filters: matches.free.clone(), filter_exact: matches.opt_present("exact"), force_pass_mode: matches.opt_str("pass").map(|mode| { mode.parse::() @@ -280,7 +280,7 @@ pub fn log_config(config: &Config) { logv(c, format!("stage_id: {}", config.stage_id)); logv(c, format!("mode: {}", config.mode)); 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, @@ -465,7 +465,7 @@ fn configure_lldb(config: &Config) -> Option { pub fn test_opts(config: &Config) -> test::TestOpts { test::TestOpts { exclude_should_panic: false, - filter: config.filter.clone(), + filters: config.filters.clone(), filter_exact: config.filter_exact, run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No }, format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },