Skip to content

Commit

Permalink
Make glob detection apply more modularly
Browse files Browse the repository at this point in the history
The filter and each skip rule are now all interpreted as test name
patterns separately. This means that, unless `--glob` is passed, only
patterns containing glob characters are considered glob patterns. For
example:

    foo --skip *_slow

Will consider `foo` a substring pattern, and `*_slow` a glob pattern.
Similarly

    *_fast --skip bar

Will run `foo_fast`, but not `bar_fast`.
  • Loading branch information
jonhoo committed Dec 1, 2017
1 parent d04e128 commit 0b83c56
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 70 deletions.
131 changes: 64 additions & 67 deletions src/libtest/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub mod test {
pub use {Bencher, TestName, TestResult, TestDesc, TestDescAndFn, TestOpts, TrFailed,
TrFailedMsg, TrIgnored, TrOk, Metric, MetricMap, StaticTestFn, StaticTestName,
DynTestName, DynTestFn, run_test, test_main, test_main_static, filter_tests,
parse_opts, StaticBenchFn, ShouldPanic, Options};
parse_opts, StaticBenchFn, ShouldPanic, Options, TestNamePattern};
}

pub mod stats;
Expand Down Expand Up @@ -335,12 +335,40 @@ pub enum ColorConfig {
NeverColor,
}

#[derive(Debug)]
pub enum TestNamePattern {
Exact(String),
Substring(String),
Glob(glob::Pattern),
}

impl TestNamePattern {
pub fn new(pattern: String, force_exact: bool, force_glob: bool) -> Self {
if force_exact {
return TestNamePattern::Exact(pattern);
}
if force_glob || pattern.chars().any(|c| ['*', '?', '['].contains(&c)) {
if let Ok(g) = glob::Pattern::new(&pattern) {
return TestNamePattern::Glob(g);
}
}
TestNamePattern::Substring(pattern)
}

fn matches(&self, test: &TestDescAndFn) -> bool {
let name = test.desc.name.as_slice();
match *self {
TestNamePattern::Exact(ref filter) => name == filter,
TestNamePattern::Substring(ref filter) => name.contains(&filter[..]),
TestNamePattern::Glob(ref g) => g.matches(name),
}
}
}

#[derive(Debug)]
pub struct TestOpts {
pub list: bool,
pub filter: Option<String>,
pub filter_exact: bool,
pub filter_glob: bool,
pub filter: Option<TestNamePattern>,
pub run_ignored: bool,
pub run_tests: bool,
pub bench_benchmarks: bool,
Expand All @@ -349,7 +377,7 @@ pub struct TestOpts {
pub color: ColorConfig,
pub quiet: bool,
pub test_threads: Option<usize>,
pub skip: Vec<String>,
pub skip: Vec<TestNamePattern>,
pub options: Options,
}

Expand All @@ -359,8 +387,6 @@ impl TestOpts {
TestOpts {
list: false,
filter: None,
filter_exact: false,
filter_glob: false,
run_ignored: false,
run_tests: false,
bench_benchmarks: false,
Expand Down Expand Up @@ -449,19 +475,16 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> {
return None;
}

let mut glob = matches.opt_present("glob");
let glob = matches.opt_present("glob");
let exact = matches.opt_present("exact");
let filter = if !matches.free.is_empty() {
if matches.free[0].chars().any(|c| ['*', '?', '['].contains(&c)) {
glob = true;
}
Some(matches.free[0].clone())
Some(TestNamePattern::new(matches.free[0].clone(), exact, glob))
} else {
None
};

let run_ignored = matches.opt_present("ignored");
let quiet = matches.opt_present("quiet");
let exact = matches.opt_present("exact");
let list = matches.opt_present("list");

let logfile = matches.opt_str("logfile");
Expand Down Expand Up @@ -504,11 +527,14 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> {
}
};

let skip = matches
.opt_strs("skip")
.into_iter().map(|s| TestNamePattern::new(s, exact, glob))
.collect();

let test_opts = TestOpts {
list,
filter,
filter_exact: exact,
filter_glob: glob,
run_ignored,
run_tests,
bench_benchmarks,
Expand All @@ -517,7 +543,7 @@ pub fn parse_opts(args: &[String]) -> Option<OptRes> {
color,
quiet,
test_threads,
skip: matches.opt_strs("skip"),
skip,
options: Options::new(),
};

Expand Down Expand Up @@ -1333,41 +1359,15 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescA
filtered = match opts.filter {
None => filtered,
Some(ref filter) => {
let glob = if opts.filter_glob && !opts.filter_exact {
glob::Pattern::new(filter).ok()
} else {
None
};

filtered.into_iter()
.filter(|test| {
if let Some(ref glob) = glob {
glob.matches(&test.desc.name.as_slice())
} else if opts.filter_exact {
test.desc.name.as_slice() == &filter[..]
} else {
test.desc.name.as_slice().contains(&filter[..])
}
})
.filter(|test| filter.matches(test))
.collect()
}
};

// Skip tests that match any of the skip filters
filtered = filtered.into_iter()
.filter(|t| !opts.skip.iter().any(|sf| {
if opts.filter_glob && !opts.filter_exact {
if let Ok(glob) = glob::Pattern::new(sf) {
return glob.matches(&t.desc.name.as_slice());
}
}

if opts.filter_exact {
t.desc.name.as_slice() == &sf[..]
} else {
t.desc.name.as_slice().contains(&sf[..])
}
}))
.filter(|t| !opts.skip.iter().any(|sf| sf.matches(t)))
.collect();

// Maybe pull out the ignored test and unignore them
Expand Down Expand Up @@ -1775,7 +1775,7 @@ pub mod bench {
mod tests {
use test::{TrFailed, TrFailedMsg, TrIgnored, TrOk, filter_tests, parse_opts, TestDesc,
TestDescAndFn, TestOpts, run_test, MetricMap, StaticTestName, DynTestName,
DynTestFn, ShouldPanic};
DynTestFn, ShouldPanic, TestNamePattern};
use std::sync::mpsc::channel;
use bench;
use Bencher;
Expand Down Expand Up @@ -1963,83 +1963,80 @@ mod tests {
}

let substr = filter_tests(&TestOpts {
filter: Some("base".into()),
filter: Some(TestNamePattern::Substring("base".into())),
..TestOpts::new()
}, tests());
assert_eq!(substr.len(), 4);

let substr = filter_tests(&TestOpts {
filter: Some("bas".into()),
filter: Some(TestNamePattern::Substring("bas".into())),
..TestOpts::new()
}, tests());
assert_eq!(substr.len(), 4);

let substr = filter_tests(&TestOpts {
filter: Some("::test".into()),
filter: Some(TestNamePattern::Substring("::test".into())),
..TestOpts::new()
}, tests());
assert_eq!(substr.len(), 3);

let substr = filter_tests(&TestOpts {
filter: Some("base::test".into()),
filter: Some(TestNamePattern::Substring("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()
filter: Some(TestNamePattern::Exact("base".into())),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 1);

let exact = filter_tests(&TestOpts {
filter: Some("bas".into()),
filter_exact: true,
filter: Some(TestNamePattern::Exact("bas".into())),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 0);

let exact = filter_tests(&TestOpts {
filter: Some("::test".into()),
filter_exact: true,
filter: Some(TestNamePattern::Exact("::test".into())),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 0);

let exact = filter_tests(&TestOpts {
filter: Some("base::test".into()),
filter_exact: true,
filter: Some(TestNamePattern::Exact("base::test".into())),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 1);

let exact = filter_tests(&TestOpts {
filter: Some("b".into()),
filter_glob: true, ..TestOpts::new()
filter: Some(TestNamePattern::new("b".into(), false, true)),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 0);

let exact = filter_tests(&TestOpts {
filter: Some("base".into()),
filter_glob: true, ..TestOpts::new()
filter: Some(TestNamePattern::new("base".into(), false, true)),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 1);

let exact = filter_tests(&TestOpts {
filter: Some("base*".into()),
filter_glob: true, ..TestOpts::new()
filter: Some(TestNamePattern::new("base*".into(), false, true)),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 4);

let exact = filter_tests(&TestOpts {
filter: Some("base::test?".into()),
filter_glob: true, ..TestOpts::new()
filter: Some(TestNamePattern::new("base::test?".into(), false, true)),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 2);

let exact = filter_tests(&TestOpts {
filter: Some("base::test[2-9]".into()),
filter_glob: true, ..TestOpts::new()
filter: Some(TestNamePattern::new("base::test[2-9]".into(), false, true)),
..TestOpts::new()
}, tests());
assert_eq!(exact.len(), 1);
}
Expand Down
12 changes: 9 additions & 3 deletions src/tools/compiletest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,16 @@ pub fn run_tests(config: &Config) {
}

pub fn test_opts(config: &Config) -> test::TestOpts {
let filter = config.filter.clone().map(|filter| {
test::TestNamePattern::new(
filter,
config.filter_exact,
config.filter_glob
)
});

test::TestOpts {
filter: config.filter.clone(),
filter_exact: config.filter_exact,
filter_glob: config.filter_glob
filter: filter,
run_ignored: config.run_ignored,
quiet: config.quiet,
logfile: config.logfile.clone(),
Expand Down

0 comments on commit 0b83c56

Please sign in to comment.