build(rust): vendor googletest crate
This commit was merged in pull request #76.
This commit is contained in:
1 parent
a6fe84190e
commit
6887b104d6
576 files changed
+284290
-2
No files matched your search
@@ -0,0 +1,3 @@
|
||||
mod regression;
|
||||
#[cfg(not(miri))]
|
||||
mod suite;
|
||||
@@ -0,0 +1,61 @@
|
||||
// Regression test for zero-repetition capture groups,
|
||||
// which caused a panic when the Vec passed into search_slots
|
||||
// contained space for the capture group which would never
|
||||
// have any results.
|
||||
//
|
||||
// See: https://github.com/rust-lang/regex/issues/1327
|
||||
#[test]
|
||||
fn zero_repetition_capture_group() {
|
||||
use regex_automata::{
|
||||
dfa::onepass::DFA, util::primitives::NonMaxUsize, Anchored, Input,
|
||||
};
|
||||
|
||||
let expr = DFA::new(r"(abc)(ABC){0}").unwrap();
|
||||
let s = "abcABC";
|
||||
let input = Input::new(s).span(0..s.len()).anchored(Anchored::Yes);
|
||||
|
||||
// Test with 4 slots, so the whole match plus the first capture group.
|
||||
let mut cache = expr.create_cache();
|
||||
let mut slots: Vec<Option<NonMaxUsize>> = vec![None; 4];
|
||||
let pid = expr.try_search_slots(&mut cache, &input, &mut slots).unwrap();
|
||||
assert_eq!(pid, Some(regex_automata::PatternID::must(0)));
|
||||
assert_eq!(slots[0], Some(NonMaxUsize::new(0).unwrap()));
|
||||
assert_eq!(slots[1], Some(NonMaxUsize::new(3).unwrap()));
|
||||
assert_eq!(slots[2], Some(NonMaxUsize::new(0).unwrap()));
|
||||
assert_eq!(slots[3], Some(NonMaxUsize::new(3).unwrap()));
|
||||
|
||||
// Test with larger slot array, which would fit the
|
||||
// zero-repetition capture group.
|
||||
slots.resize(6, None);
|
||||
let pid = expr.try_search_slots(&mut cache, &input, &mut slots).unwrap();
|
||||
assert_eq!(pid, Some(regex_automata::PatternID::must(0)));
|
||||
// First capture group should match
|
||||
assert_eq!(slots[2], Some(NonMaxUsize::new(0).unwrap()));
|
||||
assert_eq!(slots[3], Some(NonMaxUsize::new(3).unwrap()));
|
||||
// Second capture group with {0} should be None.
|
||||
assert_eq!(slots[4], None);
|
||||
assert_eq!(slots[5], None);
|
||||
}
|
||||
|
||||
// Another regression test for the same case as
|
||||
// `zero_repetition_capture_group`, but uses a simpler pattern. That
|
||||
// is, a zero-repetition capture group is a red herring. The actual bug
|
||||
// is simpler: it happens whenever too many slots are provided by the
|
||||
// caller.
|
||||
#[test]
|
||||
fn too_many_slots_normal_pattern() {
|
||||
use regex_automata::{
|
||||
dfa::onepass::DFA, util::primitives::NonMaxUsize, Anchored, Input,
|
||||
};
|
||||
|
||||
let expr = DFA::new(r"abc").unwrap();
|
||||
let s = "abc";
|
||||
let input = Input::new(s).span(0..s.len()).anchored(Anchored::Yes);
|
||||
|
||||
let mut cache = expr.create_cache();
|
||||
let mut slots: Vec<Option<NonMaxUsize>> = vec![None; 4];
|
||||
let pid = expr.try_search_slots(&mut cache, &input, &mut slots).unwrap();
|
||||
assert_eq!(pid, Some(regex_automata::PatternID::must(0)));
|
||||
assert_eq!(slots[0], Some(NonMaxUsize::new(0).unwrap()));
|
||||
assert_eq!(slots[1], Some(NonMaxUsize::new(3).unwrap()));
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
use {
|
||||
anyhow::Result,
|
||||
regex_automata::{
|
||||
dfa::onepass::{self, DFA},
|
||||
nfa::thompson,
|
||||
util::{iter, syntax},
|
||||
},
|
||||
regex_test::{
|
||||
CompiledRegex, Match, RegexTest, SearchKind, Span, TestResult,
|
||||
TestRunner,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{create_input, suite, testify_captures, untestify_kind};
|
||||
|
||||
const EXPANSIONS: &[&str] = &["is_match", "find", "captures"];
|
||||
|
||||
/// Tests the default configuration of the hybrid NFA/DFA.
|
||||
#[test]
|
||||
fn default() -> Result<()> {
|
||||
let builder = DFA::builder();
|
||||
TestRunner::new()?
|
||||
.expand(EXPANSIONS, |t| t.compiles())
|
||||
.test_iter(suite()?.iter(), compiler(builder))
|
||||
.assert();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests the hybrid NFA/DFA when 'starts_for_each_pattern' is enabled for all
|
||||
/// tests.
|
||||
#[test]
|
||||
fn starts_for_each_pattern() -> Result<()> {
|
||||
let mut builder = DFA::builder();
|
||||
builder.configure(DFA::config().starts_for_each_pattern(true));
|
||||
TestRunner::new()?
|
||||
.expand(EXPANSIONS, |t| t.compiles())
|
||||
.test_iter(suite()?.iter(), compiler(builder))
|
||||
.assert();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests the hybrid NFA/DFA when byte classes are disabled.
|
||||
///
|
||||
/// N.B. Disabling byte classes doesn't avoid any indirection at search time.
|
||||
/// All it does is cause every byte value to be its own distinct equivalence
|
||||
/// class.
|
||||
#[test]
|
||||
fn no_byte_classes() -> Result<()> {
|
||||
let mut builder = DFA::builder();
|
||||
builder.configure(DFA::config().byte_classes(false));
|
||||
TestRunner::new()?
|
||||
.expand(EXPANSIONS, |t| t.compiles())
|
||||
.test_iter(suite()?.iter(), compiler(builder))
|
||||
.assert();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compiler(
|
||||
mut builder: onepass::Builder,
|
||||
) -> impl FnMut(&RegexTest, &[String]) -> Result<CompiledRegex> {
|
||||
move |test, regexes| {
|
||||
// Check if our regex contains things that aren't supported by DFAs.
|
||||
// That is, Unicode word boundaries when searching non-ASCII text.
|
||||
if !configure_onepass_builder(test, &mut builder) {
|
||||
return Ok(CompiledRegex::skip());
|
||||
}
|
||||
let re = match builder.build_many(®exes) {
|
||||
Ok(re) => re,
|
||||
Err(err) => {
|
||||
let msg = err.to_string();
|
||||
// This is pretty gross, but when a regex fails to compile as
|
||||
// a one-pass regex, then we want to be OK with that and just
|
||||
// skip the test. But we have to be careful to only skip it
|
||||
// when the expected result is that the regex compiles. If
|
||||
// the test is specifically checking that the regex does not
|
||||
// compile, then we should bubble up that error and allow the
|
||||
// test to pass.
|
||||
//
|
||||
// Since our error types are all generally opaque, we just
|
||||
// look for an error string. Not great, but not the end of the
|
||||
// world.
|
||||
if test.compiles() && msg.contains("not one-pass") {
|
||||
return Ok(CompiledRegex::skip());
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
let mut cache = re.create_cache();
|
||||
Ok(CompiledRegex::compiled(move |test| -> TestResult {
|
||||
run_test(&re, &mut cache, test)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test(
|
||||
re: &DFA,
|
||||
cache: &mut onepass::Cache,
|
||||
test: &RegexTest,
|
||||
) -> TestResult {
|
||||
let input = create_input(test);
|
||||
match test.additional_name() {
|
||||
"is_match" => {
|
||||
TestResult::matched(re.is_match(cache, input.earliest(true)))
|
||||
}
|
||||
"find" => match test.search_kind() {
|
||||
SearchKind::Earliest | SearchKind::Leftmost => {
|
||||
let input =
|
||||
input.earliest(test.search_kind() == SearchKind::Earliest);
|
||||
let mut caps = re.create_captures();
|
||||
let it = iter::Searcher::new(input)
|
||||
.into_matches_iter(|input| {
|
||||
re.try_search(cache, input, &mut caps)?;
|
||||
Ok(caps.get_match())
|
||||
})
|
||||
.infallible()
|
||||
.take(test.match_limit().unwrap_or(std::usize::MAX))
|
||||
.map(|m| Match {
|
||||
id: m.pattern().as_usize(),
|
||||
span: Span { start: m.start(), end: m.end() },
|
||||
});
|
||||
TestResult::matches(it)
|
||||
}
|
||||
SearchKind::Overlapping => {
|
||||
// The one-pass DFA does not support any kind of overlapping
|
||||
// search. This is not just a matter of not having the API.
|
||||
// It's fundamentally incompatible with the one-pass concept.
|
||||
// If overlapping matches were possible, then the one-pass DFA
|
||||
// would fail to build.
|
||||
TestResult::skip()
|
||||
}
|
||||
},
|
||||
"captures" => match test.search_kind() {
|
||||
SearchKind::Earliest | SearchKind::Leftmost => {
|
||||
let input =
|
||||
input.earliest(test.search_kind() == SearchKind::Earliest);
|
||||
let it = iter::Searcher::new(input)
|
||||
.into_captures_iter(re.create_captures(), |input, caps| {
|
||||
re.try_search(cache, input, caps)
|
||||
})
|
||||
.infallible()
|
||||
.take(test.match_limit().unwrap_or(std::usize::MAX))
|
||||
.map(|caps| testify_captures(&caps));
|
||||
TestResult::captures(it)
|
||||
}
|
||||
SearchKind::Overlapping => {
|
||||
// The one-pass DFA does not support any kind of overlapping
|
||||
// search. This is not just a matter of not having the API.
|
||||
// It's fundamentally incompatible with the one-pass concept.
|
||||
// If overlapping matches were possible, then the one-pass DFA
|
||||
// would fail to build.
|
||||
TestResult::skip()
|
||||
}
|
||||
},
|
||||
name => TestResult::fail(&format!("unrecognized test name: {name}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the given regex builder with all relevant settings on the given
|
||||
/// regex test.
|
||||
///
|
||||
/// If the regex test has a setting that is unsupported, then this returns
|
||||
/// false (implying the test should be skipped).
|
||||
fn configure_onepass_builder(
|
||||
test: &RegexTest,
|
||||
builder: &mut onepass::Builder,
|
||||
) -> bool {
|
||||
if !test.anchored() {
|
||||
return false;
|
||||
}
|
||||
let match_kind = match untestify_kind(test.match_kind()) {
|
||||
None => return false,
|
||||
Some(k) => k,
|
||||
};
|
||||
|
||||
let config = DFA::config().match_kind(match_kind);
|
||||
builder
|
||||
.configure(config)
|
||||
.syntax(config_syntax(test))
|
||||
.thompson(config_thompson(test));
|
||||
true
|
||||
}
|
||||
|
||||
/// Configuration of a Thompson NFA compiler from a regex test.
|
||||
fn config_thompson(test: &RegexTest) -> thompson::Config {
|
||||
let mut lookm = regex_automata::util::look::LookMatcher::new();
|
||||
lookm.set_line_terminator(test.line_terminator());
|
||||
thompson::Config::new().utf8(test.utf8()).look_matcher(lookm)
|
||||
}
|
||||
|
||||
/// Configuration of the regex parser from a regex test.
|
||||
fn config_syntax(test: &RegexTest) -> syntax::Config {
|
||||
syntax::Config::new()
|
||||
.case_insensitive(test.case_insensitive())
|
||||
.unicode(test.unicode())
|
||||
.utf8(test.utf8())
|
||||
.line_terminator(test.line_terminator())
|
||||
}
|
||||
Reference in new issue
Block a user