diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 142ae86c6a30d..5749459a0541e 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -301,6 +301,7 @@ pub enum ColorConfig { pub struct TestOpts { pub filter: Option, + pub filter_exact: bool, pub run_ignored: bool, pub run_tests: bool, pub bench_benchmarks: bool, @@ -317,6 +318,7 @@ impl TestOpts { fn new() -> TestOpts { TestOpts { filter: None, + filter_exact: false, run_ignored: false, run_tests: false, bench_benchmarks: false, @@ -348,6 +350,7 @@ fn optgroups() -> Vec { getopts::optmulti("", "skip", "Skip tests whose names contain FILTER (this flag can \ be used multiple times)","FILTER"), getopts::optflag("q", "quiet", "Display one character per test instead of one line"), + getopts::optflag("", "exact", "Exactly match filters rather than by substring"), getopts::optopt("", "color", "Configure coloring of output: auto = colorize if stdout is a tty and tests are run on serially (default); always = always colorize output; @@ -407,6 +410,7 @@ pub fn parse_opts(args: &[String]) -> Option { let run_ignored = matches.opt_present("ignored"); let quiet = matches.opt_present("quiet"); + let exact = matches.opt_present("exact"); let logfile = matches.opt_str("logfile"); let logfile = logfile.map(|s| PathBuf::from(&s)); @@ -448,6 +452,7 @@ pub fn parse_opts(args: &[String]) -> Option { let test_opts = TestOpts { filter: filter, + filter_exact: exact, run_ignored: run_ignored, run_tests: run_tests, bench_benchmarks: bench_benchmarks, @@ -1118,14 +1123,26 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec filtered, Some(ref filter) => { filtered.into_iter() - .filter(|test| test.desc.name.as_slice().contains(&filter[..])) + .filter(|test| { + if opts.filter_exact { + test.desc.name.as_slice() == &filter[..] + } else { + test.desc.name.as_slice().contains(&filter[..]) + } + }) .collect() } }; // Skip tests that match any of the skip filters filtered = filtered.into_iter() - .filter(|t| !opts.skip.iter().any(|sf| t.desc.name.as_slice().contains(&sf[..]))) + .filter(|t| !opts.skip.iter().any(|sf| { + if opts.filter_exact { + t.desc.name.as_slice() == &sf[..] + } else { + t.desc.name.as_slice().contains(&sf[..]) + } + })) .collect(); // Maybe pull out the ignored test and unignore them @@ -1654,6 +1671,77 @@ mod tests { assert!(!filtered[0].desc.ignore); } + #[test] + pub fn exact_filter_match() { + fn tests() -> Vec { + vec!["base", + "base::test", + "base::test1", + "base::test2", + ].into_iter() + .map(|name| TestDescAndFn { + desc: TestDesc { + name: StaticTestName(name), + ignore: false, + should_panic: ShouldPanic::No, + }, + testfn: DynTestFn(Box::new(move |()| {})) + }) + .collect() + } + + let substr = filter_tests(&TestOpts { + filter: Some("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); + + let substr = filter_tests(&TestOpts { + filter: Some("::test".into()), + ..TestOpts::new() + }, tests()); + assert_eq!(substr.len(), 3); + + let substr = filter_tests(&TestOpts { + filter: Some("base::test".into()), + ..TestOpts::new() + }, tests()); + assert_eq!(substr.len(), 3); + + let exact = filter_tests(&TestOpts { + filter: Some("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() + }, tests()); + assert_eq!(exact.len(), 0); + + let exact = filter_tests(&TestOpts { + filter: Some("::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() + }, tests()); + assert_eq!(exact.len(), 1); + } + #[test] pub fn sort_tests() { let mut opts = TestOpts::new(); diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 34f3837d8bbb0..1aeb76c0a0ece 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -127,6 +127,9 @@ pub struct Config { // Only run tests that match this filter pub filter: Option, + // Exactly match the filter, rather than a substring + pub filter_exact: bool, + // Write out a parseable log of tests that were run pub logfile: Option, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index d8681c9d6ed24..cbdf75eda26d2 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -89,6 +89,7 @@ pub fn parse_config(args: Vec ) -> Config { "(compile-fail|parse-fail|run-fail|run-pass|\ run-pass-valgrind|pretty|debug-info|incremental|mir-opt)"), optflag("", "ignored", "run tests marked as ignored"), + optflag("", "exact", "filters match exactly"), optopt("", "runtool", "supervisor program to run tests under \ (eg. emulator, valgrind)", "PROGRAM"), optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"), @@ -167,6 +168,7 @@ pub fn parse_config(args: Vec ) -> Config { mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"), run_ignored: matches.opt_present("ignored"), filter: matches.free.first().cloned(), + filter_exact: matches.opt_present("exact"), logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)), runtool: matches.opt_str("runtool"), host_rustcflags: matches.opt_str("host-rustcflags"), @@ -216,6 +218,7 @@ pub fn log_config(config: &Config) { opt_str(&config.filter .as_ref() .map(|re| re.to_owned())))); + logv(c, format!("filter_exact: {}", config.filter_exact)); logv(c, format!("runtool: {}", opt_str(&config.runtool))); logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags))); @@ -309,6 +312,7 @@ pub fn run_tests(config: &Config) { pub fn test_opts(config: &Config) -> test::TestOpts { test::TestOpts { filter: config.filter.clone(), + filter_exact: config.filter_exact, run_ignored: config.run_ignored, quiet: config.quiet, logfile: config.logfile.clone(),