use ferroni::oniguruma::*;
use ferroni::regcomp::onig_new;
use ferroni::regexec::onig_search;
use ferroni::regsyntax::OnigSyntaxOniguruma;
fn x2(options: OnigOptionType, pattern: &[u8], input: &[u8], from: i32, to: i32) {
let reg = onig_new(
pattern,
options,
&ferroni::encodings::utf8::ONIG_ENCODING_UTF8,
&OnigSyntaxOniguruma,
)
.unwrap_or_else(|e| {
panic!(
"compile failed for {:?}: error {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
e
)
});
let (result, region) = onig_search(
®,
input,
input.len(),
0,
input.len(),
Some(OnigRegion::new()),
options,
);
assert!(
result >= 0,
"x2: expected match for {:?} against {:?} with options {:#x}, got {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
std::str::from_utf8(input).unwrap_or("<invalid>"),
options,
result
);
let region = region.unwrap();
assert_eq!(
region.beg[0],
from,
"x2: wrong start for {:?} against {:?}: expected {}, got {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
std::str::from_utf8(input).unwrap_or("<invalid>"),
from,
region.beg[0]
);
assert_eq!(
region.end[0],
to,
"x2: wrong end for {:?} against {:?}: expected {}, got {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
std::str::from_utf8(input).unwrap_or("<invalid>"),
to,
region.end[0]
);
}
fn x3(options: OnigOptionType, pattern: &[u8], input: &[u8], from: i32, to: i32, mem: usize) {
let reg = onig_new(
pattern,
options,
&ferroni::encodings::utf8::ONIG_ENCODING_UTF8,
&OnigSyntaxOniguruma,
)
.unwrap_or_else(|e| {
panic!(
"compile failed for {:?}: error {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
e
)
});
let (result, region) = onig_search(
®,
input,
input.len(),
0,
input.len(),
Some(OnigRegion::new()),
options,
);
assert!(
result >= 0,
"x3: expected match for {:?} against {:?} with options {:#x}, got {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
std::str::from_utf8(input).unwrap_or("<invalid>"),
options,
result
);
let region = region.unwrap();
assert!(
mem < region.num_regs as usize,
"x3: group {} not captured for {:?} (num_regs={})",
mem,
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
region.num_regs
);
assert_eq!(
region.beg[mem],
from,
"x3: wrong start for group {} of {:?}: expected {}, got {}",
mem,
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
from,
region.beg[mem]
);
assert_eq!(
region.end[mem],
to,
"x3: wrong end for group {} of {:?}: expected {}, got {}",
mem,
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
to,
region.end[mem]
);
}
fn n(options: OnigOptionType, pattern: &[u8], input: &[u8]) {
let reg = onig_new(
pattern,
options,
&ferroni::encodings::utf8::ONIG_ENCODING_UTF8,
&OnigSyntaxOniguruma,
)
.unwrap_or_else(|e| {
panic!(
"compile failed for {:?}: error {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
e
)
});
let (result, _) = onig_search(
®,
input,
input.len(),
0,
input.len(),
Some(OnigRegion::new()),
options,
);
assert_eq!(
result,
ONIG_MISMATCH,
"n: expected no match for {:?} against {:?} with options {:#x}, got {}",
std::str::from_utf8(pattern).unwrap_or("<invalid>"),
std::str::from_utf8(input).unwrap_or("<invalid>"),
options,
result
);
}
const OIA: OnigOptionType = ONIG_OPTION_IGNORECASE.union(ONIG_OPTION_IGNORECASE_IS_ASCII);
#[test]
fn option_ignorecase_hex_escape() {
x2(ONIG_OPTION_IGNORECASE, b"\\x61", b"A", 0, 1);
x2(ONIG_OPTION_IGNORECASE, b"\\x61b", b"AB", 0, 2);
x2(
ONIG_OPTION_IGNORECASE,
b"\\x61\\x61\\x61\\x61\\x61\\x61",
b"xAaAaAa",
1,
7,
);
x2(ONIG_OPTION_IGNORECASE, b"\\xC3\\xA9", "É".as_bytes(), 0, 2);
n(ONIG_OPTION_IGNORECASE, b"(?-i)\\x61", b"A");
}
#[test]
fn option_ignorecase_basic() {
x2(ONIG_OPTION_IGNORECASE, b"a", b"A", 0, 1);
}
#[test]
fn option_ignorecase_is_ascii_no_fold() {
n(ONIG_OPTION_IGNORECASE_IS_ASCII, b"a", b"A");
}
#[test]
fn option_ignorecase_kelvin_sign_to_k() {
x2(ONIG_OPTION_IGNORECASE, b"\xe2\x84\xaa", b"k", 0, 1);
}
#[test]
fn option_ignorecase_k_to_kelvin_sign() {
x2(ONIG_OPTION_IGNORECASE, b"k", b"\xe2\x84\xaa", 0, 3);
}
#[test]
fn option_oia_no_kelvin_to_k() {
n(OIA, b"\xe2\x84\xaa", b"k");
}
#[test]
fn option_oia_no_k_to_kelvin() {
n(OIA, b"k", b"\xe2\x84\xaa");
}
#[test]
fn option_oia_a_to_a() {
x2(OIA, b"a", b"a", 0, 1);
}
#[test]
fn option_oia_upper_a() {
x2(OIA, b"A", b"A", 0, 1);
}
#[test]
fn option_oia_lower_a_to_upper_a() {
x2(OIA, b"a", b"A", 0, 1);
}
#[test]
fn option_oia_upper_a_to_lower_a() {
x2(OIA, b"A", b"a", 0, 1);
}
#[test]
fn option_oia_full_alphabet_upper_to_lower() {
x2(
OIA,
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
b"abcdefghijklmnopqrstuvwxyz",
0,
26,
);
}
#[test]
fn option_oia_full_alphabet_lower_to_upper() {
x2(
OIA,
b"abcdefghijklmnopqrstuvwxyz",
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
0,
26,
);
}
#[test]
fn option_oia_upper_alpha_partial_match() {
x2(
OIA,
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
b"ABCabcdefghijklmnopqrstuvwxyz",
3,
29,
);
}
#[test]
fn option_oia_lower_alpha_partial_match() {
x2(
OIA,
b"abcdefghijklmnopqrstuvwxyz",
b"abcABCDEFGHIJKLMNOPQRSTUVWXYZ",
3,
29,
);
}
#[test]
fn option_oia_capture_group() {
x3(OIA, b"#%(a!;)(b&)", b"#%A!;B&", 5, 7, 2);
}
#[test]
fn option_ignorecase_ss_to_sharp_s() {
x2(ONIG_OPTION_IGNORECASE, b"ss", b"\xc3\x9f", 0, 2);
}
#[test]
fn option_ignorecase_sharp_s_to_ss() {
x2(ONIG_OPTION_IGNORECASE, b"\xc3\x9f", b"SS", 0, 2);
}
#[test]
fn option_oia_no_ss_to_sharp_s() {
n(OIA, b"ss", b"\xc3\x9f");
}
#[test]
fn option_oia_no_sharp_s_to_ss() {
n(OIA, b"\xc3\x9f", b"ss");
}
#[test]
fn option_oia_ss_to_upper_ss() {
x2(OIA, b"ss", b"SS", 0, 2);
}
#[test]
fn option_oia_mixed_case_ss() {
x2(OIA, b"Ss", b"sS", 0, 2);
}
#[test]
fn option_ignorecase_inline_lookbehind_cclass() {
x2(ONIG_OPTION_NONE, b"(?i)(?<![-\\w])x", b" X", 1, 2);
}
#[test]
fn option_notbol_caret() {
n(ONIG_OPTION_NOTBOL, b"^ab", b"ab");
}
#[test]
fn option_notbol_begin_buf() {
n(ONIG_OPTION_NOTBOL, b"\\Aab", b"ab");
}
#[test]
fn option_noteol_dollar() {
n(ONIG_OPTION_NOTEOL, b"ab$", b"ab");
}
#[test]
fn option_noteol_end_buf_z() {
n(ONIG_OPTION_NOTEOL, b"ab\\z", b"ab");
}
#[test]
fn option_noteol_end_buf_big_z() {
n(ONIG_OPTION_NOTEOL, b"ab\\Z", b"ab");
}
#[test]
fn option_noteol_end_buf_big_z_newline() {
n(ONIG_OPTION_NOTEOL, b"ab\\Z", b"ab\n");
}
#[test]
fn option_not_begin_string() {
n(ONIG_OPTION_NOT_BEGIN_STRING, b"\\Aab", b"ab");
}
#[test]
fn option_not_end_string_z() {
n(ONIG_OPTION_NOT_END_STRING, b"ab\\z", b"ab");
}
#[test]
fn option_not_end_string_big_z() {
n(ONIG_OPTION_NOT_END_STRING, b"ab\\Z", b"ab");
}
#[test]
fn option_not_end_string_big_z_newline() {
n(ONIG_OPTION_NOT_END_STRING, b"ab\\Z", b"ab\n");
}
#[test]
fn option_none_alternation_partial() {
x2(ONIG_OPTION_NONE, b"a|abc", b"abc", 0, 1);
}
#[test]
fn option_none_alternation_with_end_anchor() {
x2(ONIG_OPTION_NONE, b"(a|abc)\\Z", b"abc", 0, 3);
}
#[test]
fn option_match_whole_string_abc() {
x2(ONIG_OPTION_MATCH_WHOLE_STRING, b"a|abc", b"abc", 0, 3);
}
#[test]
fn option_match_whole_string_a() {
x2(ONIG_OPTION_MATCH_WHOLE_STRING, b"a|abc", b"a", 0, 1);
}
#[test]
fn option_word_is_ascii_match() {
x2(ONIG_OPTION_WORD_IS_ASCII, b"\\w", b"@g", 1, 2);
}
#[test]
fn option_word_is_ascii_no_hiragana() {
n(ONIG_OPTION_WORD_IS_ASCII, b"\\w", "あ".as_bytes());
}
#[test]
fn option_none_digit_fullwidth() {
x2(ONIG_OPTION_NONE, b"\\d", "1".as_bytes(), 0, 3);
}
#[test]
fn option_digit_is_ascii_no_fullwidth() {
n(ONIG_OPTION_DIGIT_IS_ASCII, b"\\d", "1".as_bytes());
}
#[test]
fn option_space_is_ascii_space() {
x2(ONIG_OPTION_SPACE_IS_ASCII, b"\\s", b" ", 0, 1);
}
#[test]
fn option_none_space_fullwidth() {
x2(ONIG_OPTION_NONE, b"\\s", " ".as_bytes(), 0, 3);
}
#[test]
fn option_space_is_ascii_no_fullwidth() {
n(ONIG_OPTION_SPACE_IS_ASCII, b"\\s", " ".as_bytes());
}
#[test]
fn option_posix_is_ascii_match() {
x2(ONIG_OPTION_POSIX_IS_ASCII, b"\\w\\d\\s", b"c3 ", 0, 3);
}
#[test]
fn option_posix_is_ascii_no_fullwidth() {
n(
ONIG_OPTION_POSIX_IS_ASCII,
b"\\w|\\d|\\s",
"あ4 ".as_bytes(),
);
}
#[test]
fn option_extend_whitespace_ignored() {
x2(ONIG_OPTION_EXTEND, b" abc \n def", b"abcdef", 0, 6);
}
#[test]
fn option_find_longest() {
x2(ONIG_OPTION_FIND_LONGEST, b"\\w+", b"abc defg hij", 4, 8);
}
#[test]
fn option_find_longest_respects_search_upper_range() {
for (pattern, input, start, range, span) in [
(&b"(?m)^.*$"[..], &b"a\nb"[..], 1, 0, (0, 1)),
(b"^\\s*$", b"\n\n\n", 1, 0, (0, 2)),
(b"(?m).*$", b"a\nb", 0, 2, (0, 1)),
] {
let reg = onig_new(
pattern,
ONIG_OPTION_FIND_LONGEST,
&ferroni::encodings::utf8::ONIG_ENCODING_UTF8,
&OnigSyntaxOniguruma,
)
.unwrap();
let (result, region) = onig_search(
®,
input,
input.len(),
start,
range,
Some(OnigRegion::new()),
ONIG_OPTION_FIND_LONGEST,
);
let region = region.unwrap();
assert_eq!(
(result, (region.beg[0], region.end[0])),
(span.0, span),
"{:?} on {input:?} start={start} range={range}",
std::str::from_utf8(pattern).unwrap()
);
}
}
#[test]
fn option_find_longest_at_search_only_does_not_match() {
for (pattern, input, start, range) in [
(&b"\\Aab"[..], &b"ab"[..], 0, 2),
(b"ab", b"xab", 0, 3),
(b"ab", b"xab", 3, 0),
(b"a", b"ab", 0, 0),
(b"", b"", 0, 0),
] {
let reg = onig_new(
pattern,
ONIG_OPTION_NONE,
&ferroni::encodings::utf8::ONIG_ENCODING_UTF8,
&OnigSyntaxOniguruma,
)
.unwrap();
for option in [ONIG_OPTION_FIND_LONGEST, ONIG_OPTION_NONE] {
let (result, _) = onig_search(
®,
input,
input.len(),
start,
range,
Some(OnigRegion::new()),
option,
);
assert_eq!(
result >= 0,
option == ONIG_OPTION_NONE,
"{:?} on {input:?} start={start} range={range} option={:#x}",
std::str::from_utf8(pattern).unwrap(),
option.bits()
);
}
}
}
#[cfg(feature = "ffi")]
#[test]
fn option_find_longest_placement_agrees_with_c() {
use ferroni::ffi::{CRegex, CRegion};
let patterns: [&[u8]; 10] = [
b"\\Aab", b"\\Gab", b"ab", b"^ab", b"a|ab", b"\\w+", b"", b"a*", b"(?m).*$", b"x*\\Z",
];
let subjects: [&[u8]; 5] = [b"", b"ab", b"xab", b"abab", b"a\nb\n"];
let mut compared = 0;
for pattern in patterns {
for (compiled, searched) in [
(ONIG_OPTION_FIND_LONGEST, ONIG_OPTION_NONE),
(ONIG_OPTION_NONE, ONIG_OPTION_FIND_LONGEST),
(ONIG_OPTION_FIND_LONGEST, ONIG_OPTION_FIND_LONGEST),
] {
let c = CRegex::new(pattern, compiled.bits()).unwrap();
let reg = onig_new(
pattern,
compiled,
&ferroni::encodings::utf8::ONIG_ENCODING_UTF8,
&OnigSyntaxOniguruma,
)
.unwrap();
for input in subjects {
for start in 0..=input.len() {
for range in 0..=input.len() {
let mut c_region = CRegion::new();
let c_result =
c.search(input, start, range, Some(&mut c_region), searched.bits());
let c_span = if c_result >= 0 {
c_region.match_range()
} else {
None
};
let (result, region) = onig_search(
®,
input,
input.len(),
start,
range,
Some(OnigRegion::new()),
searched,
);
let span = region
.filter(|_| result >= 0)
.map(|region| (region.beg[0], region.end[0]));
assert_eq!(
(result, span),
(c_result, c_span),
"{:?} on {input:?} start={start} range={range} \
compiled={:#x} searched={:#x}",
std::str::from_utf8(pattern).unwrap(),
compiled.bits(),
searched.bits()
);
compared += 1;
}
}
}
}
}
assert!(compared > 1_000, "{compared} comparisons");
}
#[test]
fn option_find_not_empty() {
x2(
ONIG_OPTION_FIND_NOT_EMPTY,
b"\\w*",
b"@@@ abc defg hij",
4,
7,
);
}