use std::time::Instant;
#[test]
fn an_oversized_repetition_bound_is_refused() {
for pattern in [
r"\w{200000,}",
r"a{70000}",
r"(?:ab){65536}",
r"[a-z]{0,99999}",
] {
let error = regexr::Regex::new(pattern)
.expect_err(&format!("{pattern} must not compile"))
.to_string();
assert!(
error.contains("exceed"),
"{pattern}: expected a limit error, got {error:?}"
);
}
}
#[test]
fn nested_repetitions_are_bounded_by_their_product() {
for pattern in [
r"(?:a{1000}){1000}",
r"(?:(?:a{100}){100}){100}",
r"(?:\w{500}){500}",
] {
let error = regexr::Regex::new(pattern)
.expect_err(&format!("{pattern} must not compile"))
.to_string();
assert!(
error.contains("expands to"),
"{pattern}: expected an expansion error, got {error:?}"
);
}
}
#[test]
fn ordinary_repetitions_still_compile() {
const PATTERNS: &[&str] = &[
r"\w+",
r"a{2,4}",
r"(?:ab){100}",
r"\d{1,3}(\.\d{1,3}){3}",
r"[a-z]{500}",
r"(?:\w{50}){50}",
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
];
for pattern in PATTERNS {
assert!(
regexr::Regex::new(pattern).is_ok(),
"{pattern} must still compile"
);
}
}
#[test]
fn the_expansion_limit_can_be_raised() {
let pattern = r"a{50000}";
assert!(
regexr::Regex::new(pattern).is_err(),
"{pattern} is past the default"
);
let raised = regexr::RegexBuilder::new(pattern)
.size_limit(100_000)
.build()
.expect("a caller who owns the pattern may pay for it");
assert!(raised.is_match(&"a".repeat(50_000)));
assert!(!raised.is_match(&"a".repeat(49_999)));
assert!(regexr::RegexBuilder::new(r"(?:ab){100}")
.size_limit(10)
.build()
.is_err());
}
#[test]
fn the_largest_accepted_pattern_compiles_promptly() {
fn fastest_compile_ms(pattern: &str) -> f64 {
(0..5)
.map(|_| {
let start = Instant::now();
let regex = regexr::Regex::new(pattern);
let ms = start.elapsed().as_secs_f64() * 1000.0;
assert!(regex.is_ok(), "{pattern} should be within the limit");
ms
})
.fold(f64::INFINITY, f64::min)
}
let full = fastest_compile_ms(r"(?:[a-z]{999}){10}");
let tenth = fastest_compile_ms(r"(?:[a-z]{999}){1}");
let ratio = full / tenth;
assert!(
ratio <= 40.0,
"compiling the largest accepted pattern took {full:.0} ms against \
{tenth:.0} ms for a tenth of the expansion ({ratio:.1}x for 10x the \
elements); the expansion limit is no longer bounding compile time"
);
}