use super::Regex;
use alloc::format;
use alloc::string::ToString;
fn re(pattern: &str, flags: &str) -> Regex {
Regex::new(pattern, flags).expect("compile ok")
}
#[test]
fn literals_and_anchors() {
assert!(re("abc", "").is_match("xxabcyy"));
assert!(!re("abc", "").is_match("ab c"));
assert!(re("^abc$", "").is_match("abc"));
assert!(!re("^abc$", "").is_match("abcd"));
assert_eq!(re("abc", "").find_from("xxabc", 0), Some((2, 5)));
}
#[test]
fn unicode_and_hex_escapes() {
assert!(re(r"A", "").is_match("A"));
assert!(re(r"\x41", "").is_match("A"));
assert!(re(r"σ", "").is_match("\u{03c3}"));
assert!(!re(r"A", "").is_match("B"));
assert!(re("\\u0041", "").is_match("A"));
assert!(re("\\u03c3", "").is_match("\u{03c3}"));
assert!(!re("\\u0041", "").is_match("B"));
assert!(re(r"\u{1F600}", "").is_match("\u{1F600}"));
assert!(re(r"[A-Z]+", "").is_match("HELLO"));
assert!(re(r"[\x61\x62]", "").is_match("b"));
assert!(re(r"\x09", "").is_match("a\tb"));
assert!(re(r"\u00", "").is_match("u00"));
assert!(Regex::new(r"\u00", "u").is_err());
}
#[test]
fn dot_and_classes() {
assert!(re("a.c", "").is_match("axc"));
assert!(!re("a.c", "").is_match("a\nc")); assert!(re("a.c", "s").is_match("a\nc")); assert!(re("[abc]+", "").is_match("cab"));
assert!(re("[a-z]+", "").is_match("hello"));
assert!(!re("[a-z]+", "").is_match("123"));
assert!(re("[^0-9]", "").is_match("a"));
assert!(!re("[^0-9]", "").is_match("5"));
assert!(re(r"\d{3}", "").is_match("a123b"));
assert!(re(r"\w+", "").is_match("foo_bar"));
assert!(re(r"\s", "").is_match("a b"));
}
#[test]
fn quantifiers() {
assert!(re("a*", "").is_match(""));
assert!(re("ab+c", "").is_match("abbbc"));
assert!(!re("ab+c", "").is_match("ac"));
assert!(re("colou?r", "").is_match("color"));
assert!(re("colou?r", "").is_match("colour"));
assert!(re("a{2,4}", "").is_match("aaa"));
assert!(!re("^a{2,4}$", "").is_match("a"));
assert!(!re("^a{2,4}$", "").is_match("aaaaa"));
assert_eq!(
re("a.*b", "").captures_from("axxbxxb", 0).unwrap().whole(),
(0, 7)
);
assert_eq!(
re("a.*?b", "").captures_from("axxbxxb", 0).unwrap().whole(),
(0, 4)
);
}
#[test]
fn groups_and_alternation() {
assert!(re("cat|dog", "").is_match("hotdog"));
assert!(!re("cat|dog", "").is_match("fish"));
let caps = re(r"(\d+)-(\d+)", "")
.captures_from("x 12-34 y", 0)
.unwrap();
assert_eq!(caps.group(1), Some((2, 4)));
assert_eq!(caps.group(2), Some((5, 7)));
assert!(re("(?:ab)+", "").is_match("ababab"));
assert_eq!(re("(?:ab)+", "").group_count(), 0);
assert_eq!(re("(a)(b)", "").group_count(), 2);
}
#[test]
fn word_boundaries() {
assert!(re(r"\bword\b", "").is_match("a word here"));
assert!(!re(r"\bword\b", "").is_match("wordy"));
assert!(re(r"\Bord", "").is_match("word"));
}
#[test]
fn case_insensitive() {
assert!(re("hello", "i").is_match("HELLO"));
assert!(re("[a-z]+", "i").is_match("ABC"));
assert!(!re("hello", "").is_match("HELLO"));
}
#[test]
fn multiline() {
assert!(re("^bar", "m").is_match("foo\nbar"));
assert!(!re("^bar", "").is_match("foo\nbar"));
}
#[test]
fn replace() {
assert_eq!(re("o", "g").replace("foo boo", "0"), "f00 b00");
assert_eq!(re("o", "").replace("foo", "0"), "f0o"); assert_eq!(
re(r"(\w+)@(\w+)", "").replace("user@host", "$2.$1"),
"host.user"
);
assert_eq!(re(r"\d+", "g").replace("a1b22c333", "#"), "a#b#c#");
}
#[test]
fn lookaround_backref_named() {
assert_eq!(re("foo(?=bar)", "").find_from("foobar", 0), Some((0, 3)));
assert!(!re("foo(?=bar)", "").is_match("foobaz"));
assert!(re("foo(?!bar)", "").is_match("foobaz"));
assert_eq!(re("(?<=\\$)\\d+", "").find_from("$100", 0), Some((1, 4)));
assert!(re("(?<!\\$)\\d+", "").is_match("100"));
assert!(re("(ab)\\1", "").is_match("abab"));
assert!(!re("(ab)\\1", "").is_match("abcd"));
let r = re("(?<year>\\d{4})", "");
assert_eq!(r.group_names(), &[(1, alloc::string::String::from("year"))]);
}
#[test]
fn unicode_property_escapes() {
assert!(re("\\p{L}", "u").is_match("a"));
assert!(!re("\\p{L}", "u").is_match("5"));
assert!(re("\\p{L}", "u").is_match("Ω")); assert!(re("^\\p{N}+$", "u").is_match("123"));
assert!(re("\\p{Lu}", "u").is_match("A"));
assert!(!re("\\p{Lu}", "u").is_match("a"));
assert!(re("\\P{L}", "u").is_match("5")); assert!(re("^[\\p{L}\\p{N}]+$", "u").is_match("abc123"));
assert!(re("^\\p{General_Category=Letter}$", "u").is_match("a"));
assert!(re("^\\p{gc=L}$", "u").is_match("a"));
assert!(re("^\\p{Script=Greek}+$", "u").is_match("αβ"));
assert!(re("^\\p{sc=Latn}$", "u").is_match("a"));
assert!(!re("^\\p{Script=Greek}$", "u").is_match("a"));
assert!(re("^\\p{Script_Extensions=Latin}$", "u").is_match("a"));
assert!(re("^\\p{LC}$", "u").is_match("A"));
assert!(re("^\\p{gc=digit}$", "u").is_match("7"));
assert!(re("^\\p{ASCII}$", "u").is_match("a"));
assert!(!re("^\\p{ASCII}$", "u").is_match("é"));
assert!(re("^\\p{ASCII_Hex_Digit}+$", "u").is_match("0aF"));
assert!(re("^\\p{White_Space}$", "u").is_match(" "));
assert!(re("^\\p{Alphabetic}$", "u").is_match("a"));
assert!(Regex::new("\\p{Nonsense}", "u").is_err());
assert!(Regex::new("\\p{Script=Nonsense}", "u").is_err());
assert!(Regex::new("\\p{General_Category}", "u").is_err());
assert!(Regex::new("\\p{Script}", "u").is_err());
assert!(Regex::new("\\p{ASCII=Invalid}", "u").is_err());
assert!(Regex::new("\\p{Alphabetic=Yes}", "u").is_err());
assert!(Regex::new("\\p{}", "u").is_err());
assert!(Regex::new("\\p{=}", "u").is_err());
assert!(Regex::new("\\p{=L}", "u").is_err());
assert!(Regex::new("\\p{^L}", "u").is_err());
assert!(Regex::new("\\p{ L }", "u").is_err()); assert!(Regex::new("\\pL", "u").is_err()); assert!(Regex::new("\\p", "u").is_err());
assert!(Regex::new("\\p{", "u").is_err());
assert!(Regex::new("\\p{Emoji}", "u").is_ok());
assert!(Regex::new("\\p{ID_Start}", "u").is_ok());
assert!(Regex::new("\\p{Nonsense}", "").is_ok());
assert!(Regex::new("\\pL", "").is_ok());
}
#[test]
fn sticky_flag() {
assert!(re("\\d", "y").find_from("1a", 0).is_some());
assert!(re("\\d", "y").find_from("a1", 0).is_none());
assert!(re("\\d", "").find_from("a1", 0).is_some());
assert_eq!(re("\\d", "y").find_from("a1", 1), Some((1, 2)));
assert!(re("abc", "y").find_from("xabc", 0).is_none());
}
#[test]
fn errors() {
assert!(Regex::new("(unterminated", "").is_err());
assert!(Regex::new("[abc", "").is_err());
assert!(Regex::new("a", "z").is_err()); assert!(Regex::new("*abc", "").is_err()); }
#[test]
fn redos_catastrophic_terminates() {
let subject: alloc::string::String = "a".repeat(40) + "!";
assert!(!re("(a+)+$", "").is_match(&subject));
}
#[test]
fn redos_linear_depth_terminates() {
let subject: alloc::string::String = "a".repeat(200_000);
assert!(re("a*", "").is_match(&subject));
assert_eq!(
re("a+", "").captures_from(&subject, 0).unwrap().whole().1,
200_000
);
}
#[test]
fn redos_zero_width_terminates() {
assert!(re("()*", "").is_match("abc"));
assert!(re("(a*)*", "").is_match("aaa"));
assert!(re("(a*)*", "").is_match(""));
assert!(re("(|a)*", "").is_match("aa"));
}
#[test]
fn compile_blowup_rejected() {
assert!(Regex::new("a{99999999999}", "").is_err());
assert!(Regex::new("a{5,2}", "").is_err());
assert!(Regex::new("(a{1000}){1000}", "").is_err());
assert!(Regex::new("a{100}", "").is_ok());
assert!(Regex::new("a{2,4}", "").is_ok());
}
fn u16s(s: &str) -> alloc::vec::Vec<u16> {
s.encode_utf16().collect()
}
#[test]
fn u16_dot_non_unicode_matches_one_code_unit() {
let units = u16s("😀");
assert_eq!(units.len(), 2);
let r = re(".", "");
let m1 = r.find_in_u16(&units, 0).unwrap();
assert_eq!(m1, (0, 1));
let m2 = r.find_in_u16(&units, 1).unwrap();
assert_eq!(m2, (1, 2));
assert!(r.find_in_u16(&units, 2).is_none());
}
#[test]
fn u16_dot_unicode_matches_astral_as_one() {
let units = u16s("😀");
let r = re(".", "u");
let m = r.find_in_u16(&units, 0).unwrap();
assert_eq!(m, (0, 2));
assert!(r.find_in_u16(&units, 2).is_none());
}
#[test]
fn u16_lone_surrogate_matches() {
let units: alloc::vec::Vec<u16> = alloc::vec![0xD83D];
assert_eq!(re(".", "").find_in_u16(&units, 0), Some((0, 1)));
assert_eq!(re(".", "u").find_in_u16(&units, 0), Some((0, 1)));
let r = re(r"\uD83D", "");
assert_eq!(r.find_in_u16(&units, 0), Some((0, 1)));
}
#[test]
fn u16_unicode_escape_astral_in_u_mode() {
let units = u16s("😀");
let r = re(r"\u{1F600}", "u");
assert_eq!(r.find_in_u16(&units, 0), Some((0, 2)));
let r2 = re(r"\u{1F600}", "");
assert_eq!(r2.find_in_u16(&units, 0), Some((0, 2)));
}
#[test]
fn u16_capture_indices_are_code_unit_based() {
let units = u16s("x😀y");
assert_eq!(units.len(), 4);
let r = re(r"x(.)y", "u");
let caps = r.captures_in_u16(&units, 0).unwrap();
assert_eq!(caps.whole(), (0, 4));
assert_eq!(caps.group(1), Some((1, 3)));
}
#[test]
fn u16_astral_quantifier_unicode() {
let units = u16s("😀😁");
assert_eq!(units.len(), 4);
assert_eq!(re(".+", "u").find_in_u16(&units, 0), Some((0, 4)));
let r = re(r"[\u{1F600}-\u{1F610}]+", "u");
assert_eq!(r.find_in_u16(&units, 0), Some((0, 4)));
}
#[test]
fn u16_backtracking_bomb_terminates() {
let subject: alloc::string::String = "a".repeat(40) + "!";
let units = u16s(&subject);
assert!(re("(a+)+$", "").find_in_u16(&units, 0).is_none());
}
#[test]
fn parser_deep_nesting_rejected() {
let pat: alloc::string::String = "(".repeat(100_000) + "a" + &")".repeat(100_000);
assert!(Regex::new(&pat, "").is_err());
let ok: alloc::string::String = "(".repeat(50) + "a" + &")".repeat(50);
assert!(Regex::new(&ok, "").is_ok());
}
#[test]
fn lazy_scalar_prog_reused_is_consistent() {
let r = re(r"(\d+)", "");
for _ in 0..5 {
assert_eq!(r.captures_from("a12b", 0).unwrap().whole(), (1, 3));
assert_eq!(r.find_from("xx99", 0), Some((2, 4)));
assert!(r.is_match("z7"));
}
let g = re(r"(\d+)", "g");
for _ in 0..3 {
let units = u16s("a1b22c333");
let mut pos = 0;
let mut found = alloc::vec::Vec::new();
while let Some((s, e)) = g.find_in_u16(&units, pos) {
found.push((s, e));
pos = if e > s { e } else { e + 1 };
}
assert_eq!(found, alloc::vec![(1, 2), (3, 5), (6, 9)]);
}
let dot = re(".", "");
assert_eq!(dot.find_from("😀x", 0), Some((0, 1)));
}
#[test]
fn positive_lookahead_captures_propagate() {
let units = u16s("123");
let caps = re(r"(?=(\d+))", "").captures_in_u16(&units, 0).unwrap();
assert_eq!(caps.whole(), (0, 0)); assert_eq!(caps.group(1), Some((0, 3)));
let units = u16s("abcabc");
assert!(re(r"(?=(\w{3}))\1", "").find_in_u16(&units, 0).is_some());
let units = u16s("ac");
let caps = re(r"a(?!(b))", "").captures_in_u16(&units, 0).unwrap();
assert_eq!(caps.group(1), None);
}
#[test]
fn positive_lookbehind_captures_propagate() {
let units = u16s("foobar");
let caps = re(r"(?<=(o)(o))bar", "")
.captures_in_u16(&units, 0)
.unwrap();
assert_eq!(caps.group(1), Some((1, 2)));
assert_eq!(caps.group(2), Some((2, 3)));
let units = u16s("za");
let caps = re(r"(?<!(x))a", "").captures_in_u16(&units, 0).unwrap();
assert_eq!(caps.group(1), None);
}
#[test]
fn lookbehind_reverse_captures() {
let grp = |pat: &str, subj: &str, g: usize| -> Option<alloc::string::String> {
let units = u16s(subj);
let caps = re(pat, "").captures_in_u16(&units, 0)?;
caps.group(g)
.map(|(s, e)| alloc::string::String::from_utf16(&units[s..e]).unwrap())
};
assert_eq!(grp(r"(?<=(\w){3})def", "abcdef", 1).as_deref(), Some("a"));
assert_eq!(grp(r"(?<=(\w(\w)))def", "abcdef", 1).as_deref(), Some("bc"));
assert_eq!(grp(r"(?<=(\w(\w)))def", "abcdef", 2).as_deref(), Some("c"));
assert_eq!(grp(r"(?<=(\w{2}))def", "abcdef", 1).as_deref(), Some("bc"));
assert_eq!(grp(r"(?<=(b+))c", "abbbbbbc", 1).as_deref(), Some("bbbbbb"));
assert_eq!(grp(r"(?<=(b\d+))c", "ab1234c", 1).as_deref(), Some("b1234"));
let units = u16s("abcCd");
let caps = re(r"(?<=\1(\w))d", "i").captures_in_u16(&units, 0).unwrap();
assert_eq!(
caps.group(1)
.map(|(s, e)| alloc::string::String::from_utf16(&units[s..e]).unwrap()),
Some(alloc::string::String::from("C"))
);
assert_eq!(grp(r"(?<=(\w+)\1)c", "ababc", 1).as_deref(), Some("abab"));
assert!(
re(r"(?<=abc)def", "")
.captures_in_u16(&u16s("abcdef"), 0)
.is_some()
);
assert!(
re(r"(?<=a(?=b))b", "")
.captures_in_u16(&u16s("ab"), 0)
.is_some()
);
}
#[test]
fn group_name_validation() {
assert!(Regex::new(r"(?<a>x)", "").is_ok());
assert!(Regex::new(r"(?<$_>x)", "").is_ok());
assert!(Regex::new(r"(?<A>x)", "").is_ok());
assert!(Regex::new(r"(?<1a>x)", "").is_err()); assert!(Regex::new(r"(?<a b>x)", "").is_err()); assert!(Regex::new(r"(?<>x)", "").is_err()); }
#[test]
fn duplicate_group_names() {
assert!(Regex::new(r"(?<a>x)(?<a>y)", "").is_err());
assert!(Regex::new(r"(?<a>x)|(?<a>y)", "").is_ok());
assert!(Regex::new(r"(?:(?<a>x)|(?<a>y))", "").is_ok());
assert!(Regex::new(r"(?<a>(?<a>y))", "").is_err());
}
#[test]
fn named_backreference_validation() {
assert!(Regex::new(r"(?<a>x)\k<a>", "").is_ok());
assert!(Regex::new(r"\k<a>(?<a>x)", "").is_ok());
assert!(Regex::new(r"(?<a>x)\k<b>", "").is_err());
assert!(Regex::new(r"\k<a>", "u").is_err());
let r = re(r"\k<a>", "");
assert!(r.is_match("k<a>"));
assert!(!r.is_match("xyz"));
}
#[test]
fn unicode_mode_strict_syntax() {
assert!(Regex::new(r"\1", "u").is_err());
assert!(Regex::new(r"\8", "u").is_err());
assert!(Regex::new(r"(a)\1", "u").is_ok());
assert!(Regex::new(r"\M", "u").is_err());
assert!(re(r"\M", "").is_match("M"));
assert!(Regex::new(r"{", "u").is_err());
assert!(Regex::new(r"}", "u").is_err());
assert!(Regex::new(r"]", "u").is_err());
assert!(re(r"}", "").is_match("}"));
assert!(Regex::new(r"\cA", "u").is_ok());
assert!(Regex::new(r"\n\t\r\f\v\0", "u").is_ok());
assert!(Regex::new(r"\.\*\+\?\(\)\[\]\{\}\|\^\$\\\/", "u").is_ok());
}
#[test]
fn quantifier_on_assertion() {
assert!(Regex::new(r"(?<=a)?b", "").is_err());
assert!(Regex::new(r"(?<=a)?b", "u").is_err());
assert!(Regex::new(r"(?<=a){2}b", "u").is_err());
assert!(Regex::new(r"(?=a)?b", "").is_ok());
assert!(Regex::new(r"(?=a)*b", "").is_ok());
assert!(Regex::new(r"(?=a)?b", "u").is_err());
assert!(Regex::new(r"^?a", "u").is_err());
assert!(Regex::new(r"\b?a", "u").is_err());
assert!(Regex::new(r"(?:^)+", "u").is_ok());
}
#[test]
fn inline_modifier_groups() {
assert!(re("(?i:A)", "").is_match("a"));
assert!(!re("(?-i:A)", "i").is_match("a"));
assert!(re("(?m:^a$)", "").is_match("x\na\ny"));
assert!(re("(?s:.)", "").is_match("\n"));
assert!(re("(?i:a)b", "").is_match("Ab"));
assert!(!re("(?i:a)b", "").is_match("AB"));
assert!(!re("(?i:(?-i:a))", "").is_match("A"));
assert!(re("(?i:(?-i:a))", "").is_match("a"));
assert!(Regex::new("(?i-m:a)", "").is_ok());
assert!(Regex::new("(?-i:a)", "").is_ok());
assert!(Regex::new("(?ims:a)", "").is_ok());
assert!(Regex::new("(?i-:a)", "").is_ok());
for src in [
"(?-:a)", "(?ii:a)", "(?i-mm:a)", "(?ims-m:a)", "(?i-i:a)", "(?d:a)", "(?g:a)",
"(?u:a)",
"(?y:a)",
"(?I:a)",
"(?Q:a)",
"(?1:a)",
"(?i)", "(?ms-i)",
"(?-s)",
"(?i-)", ] {
match Regex::new(src, "") {
Ok(_) => panic!("{src} should be a SyntaxError"),
Err(e) => assert!(!e.is_unsupported(), "{src} should be a hard SyntaxError"),
}
}
}
#[test]
fn ignore_case_unicode_word_carveout() {
assert!(re(r"\w", "iu").is_match("\u{017F}"));
assert!(re(r"\w", "iu").is_match("\u{212A}"));
assert!(!re(r"\w", "u").is_match("\u{017F}"));
}
#[test]
fn case_insensitive_property() {
assert!(re(r"\p{Lu}", "iu").is_match("a"));
assert!(re(r"\P{Lu}", "iu").is_match("A"));
assert!(!re(r"\p{Lu}", "u").is_match("a"));
}
#[test]
fn v_flag_set_operations() {
assert!(re("^[[0-9]&&[0-9]]+$", "v").is_match("123"));
assert!(!re("^[[0-9]&&[a-z]]+$", "v").is_match("1"));
assert!(re(r"^[\d--[0-5]]+$", "v").is_match("789"));
assert!(!re(r"^[\d--[0-5]]+$", "v").is_match("3"));
assert!(re(r"^[\p{ASCII}&&\p{L}]+$", "v").is_match("abc"));
assert!(!re(r"^[\p{ASCII}&&\p{L}]+$", "v").is_match("a1"));
}
#[test]
fn v_flag_string_literals() {
assert!(re(r"^[\q{abc|de}]+$", "v").is_match("abcde"));
assert!(!re(r"^[\q{abc|de}]+$", "v").is_match("abccd"));
assert!(re(r"^[[0-9]\q{ab}]+$", "v").is_match("ab9"));
}
#[test]
fn group_name_surrogate_pairs_non_unicode() {
assert!(Regex::new(r"(?<𝑓>fox)", "").is_ok());
assert!(Regex::new(r"(?<\u{1d453}>fox)", "").is_ok());
assert!(Regex::new("(?<\u{1d453}>fox)", "").is_ok());
assert!(re(r"(?<𝑓>dog)(.*?)(\k<𝑓>)", "").is_match("dog eat dog"));
assert!(Regex::new(r"(?<\ud835x>fox)", "").is_err());
}
#[test]
fn v_flag_property_of_strings_keycap() {
let kc = "0\u{FE0F}\u{20E3}"; let hashkc = "#\u{FE0F}\u{20E3}"; assert!(re(r"^\p{Emoji_Keycap_Sequence}$", "v").is_match(kc));
assert!(re(r"^\p{Emoji_Keycap_Sequence}$", "v").is_match(hashkc));
assert!(!re(r"^\p{Emoji_Keycap_Sequence}$", "v").is_match("0"));
let re1 = re(r"^[\p{Emoji_Keycap_Sequence}\q{0|2}]+$", "v");
assert!(re1.is_match(kc));
assert!(re1.is_match("0"));
let mut combo = alloc::string::String::new();
combo.push_str(kc);
combo.push_str(hashkc);
combo.push('0');
assert!(re1.is_match(&combo));
let d = re(r"^[[0-9]--\p{Emoji_Keycap_Sequence}]+$", "v");
assert!(d.is_match("019"));
assert!(!d.is_match(kc));
let i = re(r"^[\p{Emoji_Keycap_Sequence}&&\q{0️⃣|zz}]+$", "v");
assert!(i.is_match(kc));
assert!(!i.is_match(hashkc));
}
#[test]
fn v_flag_property_of_strings_errors() {
assert!(Regex::new(r"\P{Emoji_Keycap_Sequence}", "v").is_err());
assert!(Regex::new(r"[^\p{Emoji_Keycap_Sequence}]", "v").is_err());
assert!(Regex::new(r"[^\p{RGI_Emoji}]", "v").is_err());
assert!(Regex::new(r"\P{RGI_Emoji}", "v").is_err());
assert!(Regex::new(r"[\p{RGI_Emoji}]", "v").is_ok());
}
#[test]
fn v_flag_syntax_errors() {
assert!(Regex::new(r"[^\q{ab}]", "v").is_err()); assert!(Regex::new("[a~~b]", "v").is_err()); assert!(Regex::new("[a&&]", "v").is_err()); assert!(Regex::new("[(]", "v").is_err()); assert!(Regex::new(r"[\(]", "v").is_ok()); }
#[test]
fn annexb_control_escape_fallback() {
assert!(re(r"\c0", "").is_match(r"\c0"));
assert_eq!(re(r"\c0", "").find_from("\u{0f}\u{10}\u{11}", 0), None);
assert!(re(r"\cА", "").is_match(r"\cА"));
let c = re(r"[\c ]", "");
assert!(c.is_match("\\"));
assert!(c.is_match("c"));
assert_eq!(
re(r"[\c0]", "").find_from("\u{0f}\u{10}\u{11}", 0),
Some((1, 2))
);
assert_eq!(
re(r"[\c_]", "").find_from("\u{1e}\u{1f}\u{20}", 0),
Some((1, 2))
);
assert!(re(r"\cA", "").is_match("\u{1}"));
assert!(re(r"\cA", "u").is_match("\u{1}"));
}
#[test]
fn annexb_legacy_octal_zero_prefixed() {
assert_eq!(re(r"\00", "").find_from("\u{0}", 0), Some((0, 1)));
assert_eq!(re(r"\07", "").find_from("\u{7}", 0), Some((0, 1)));
assert_eq!(re(r"\007", "").find_from("\u{7}", 0), Some((0, 1)));
assert!(re(r"\0111", "").is_match("\u{9}1"));
assert_eq!(re(r"\0", "").find_from("\u{0}", 0), Some((0, 1)));
assert!(Regex::new(r"\00", "u").is_err());
}
#[test]
fn annexb_class_range_shorthand_endpoint() {
assert_eq!(
re(r"[%-\d]+", "").find_from("&%0123456789-&", 0),
Some((1, 13))
);
assert_eq!(
re(r"[--\d]+", "").find_from(".-0123456789-.", 0),
Some((1, 13))
);
assert!(Regex::new(r"[%-\d]", "u").is_err());
}
#[test]
fn v_flag_bare_dash_is_error() {
assert!(Regex::new("[-]", "v").is_err());
assert!(Regex::new(r"[\-]", "v").is_ok());
assert!(Regex::new("[a-z]", "v").is_ok());
}
#[test]
fn whitespace_class_escape_is_not_unicode_white_space() {
assert!(!re(r"\s", "").is_match("\u{0085}"));
assert!(re(r"\S", "").is_match("\u{0085}"));
assert!(re(r"\s", "").is_match("\u{FEFF}"));
assert!(!re(r"\S", "").is_match("\u{FEFF}"));
for c in [
'\t', '\n', '\u{b}', '\u{c}', '\r', ' ', '\u{a0}', '\u{1680}',
] {
assert!(re(r"^\s$", "").is_match(&c.to_string()), "{c:?} is \\s");
}
for c in ['\u{2028}', '\u{2029}', '\u{202f}', '\u{205f}', '\u{3000}'] {
assert!(re(r"^\s$", "").is_match(&c.to_string()), "{c:?} is \\s");
}
}
#[test]
fn unicode_property_tables_match_intl_version() {
assert_eq!(
super::props_data::UNICODE_VERSION,
intl::unicode::UNICODE_VERSION,
);
}
#[test]
fn unicode_property_escapes_table_backed() {
let cases: &[(&str, char, char)] = &[
("ID_Start", 'ª', '_'),
("ID_Continue", '_', '-'),
("Case_Ignorable", '\'', 'a'),
("Changes_When_NFKC_Casefolded", 'A', 'a'),
("Deprecated", 'ʼn', 'a'),
("Emoji", '#', 'a'),
("Emoji_Component", '#', 'a'),
("Emoji_Modifier", '\u{1F3FB}', 'a'),
("Emoji_Modifier_Base", '\u{261D}', 'a'),
("Emoji_Presentation", '\u{231A}', '#'),
("Extended_Pictographic", '\u{00A9}', 'a'),
("Extender", '·', 'a'),
("Grapheme_Base", 'Ό', '\u{0301}'),
("Grapheme_Extend", '\u{05BF}', 'a'),
("IDS_Binary_Operator", '\u{2FF0}', 'a'),
("IDS_Trinary_Operator", '\u{2FF2}', 'a'),
("Ideographic", '\u{16FE4}', 'a'),
("Logical_Order_Exception", '\u{0E40}', 'a'),
("Pattern_Syntax", '`', 'a'),
("Pattern_White_Space", '\u{200E}', 'a'),
("Radical", '\u{2E80}', 'a'),
("Sentence_Terminal", '!', 'a'),
("Soft_Dotted", 'į', 'a'),
("Terminal_Punctuation", '!', 'a'),
("Unified_Ideograph", '\u{FA11}', 'a'),
];
for &(name, yes, no) in cases {
let r = re(&format!(r"^\p{{{name}}}$"), "u");
assert!(
r.is_match(&yes.to_string()),
"\\p{{{name}}} should match {yes:?}"
);
assert!(
!r.is_match(&no.to_string()),
"\\p{{{name}}} should not match {no:?}"
);
}
}
#[test]
fn emoji_property_of_strings_tables() {
assert_eq!(super::props_emoji::EMOJI_VERSION, (17, 0));
assert!(re(r"^\p{RGI_Emoji}$", "v").is_match("\u{1F1E8}\u{1F1F6}"));
assert!(re(r"^\p{RGI_Emoji}$", "v").is_match("\u{1F426}\u{200D}\u{2B1B}"));
assert!(re(r"^\p{Basic_Emoji}$", "v").is_match("\u{231A}"));
assert!(!re(r"^\p{Basic_Emoji}$", "v").is_match("a"));
assert!(re(r"^\p{RGI_Emoji_Flag_Sequence}$", "v").is_match("\u{1F1E8}\u{1F1F6}"));
assert!(re(r"^\p{Emoji_Keycap_Sequence}$", "v").is_match("#\u{FE0F}\u{20E3}"));
assert!(re(r"^\p{RGI_Emoji_Modifier_Sequence}$", "v").is_match("\u{261D}\u{1F3FB}"));
assert!(
re(r"^\p{RGI_Emoji_Tag_Sequence}$", "v")
.is_match("\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}")
);
assert!(Regex::new(r"\P{RGI_Emoji}", "v").is_err());
assert!(Regex::new(r"\p{RGI_Emoji}", "u").is_err());
}