use openjd_expr::{ParsedExpression, SymbolTable};
fn eval_expr(expr: &str) -> Result<openjd_expr::ExprValue, openjd_expr::ExpressionError> {
ParsedExpression::new(expr).and_then(|p| p.evaluate(&SymbolTable::new()))
}
#[test]
fn reject_positive_lookahead() {
let err = eval_expr(r"re_match('foo', 'foo(?=bar)')").unwrap_err();
assert!(
err.message().contains("lookahead") || err.message().contains("look-around"),
"got: {}",
err.message()
);
}
#[test]
fn reject_negative_lookahead() {
let err = eval_expr(r"re_match('foo', 'foo(?!bar)')").unwrap_err();
assert!(
err.message().contains("lookahead") || err.message().contains("look-around"),
"got: {}",
err.message()
);
}
#[test]
fn reject_positive_lookbehind() {
let err = eval_expr(r"re_match('foo', '(?<=bar)foo')").unwrap_err();
assert!(
err.message().contains("lookbehind") || err.message().contains("look-around"),
"got: {}",
err.message()
);
}
#[test]
fn reject_negative_lookbehind() {
let err = eval_expr(r"re_match('foo', '(?<!bar)foo')").unwrap_err();
assert!(
err.message().contains("lookbehind") || err.message().contains("look-around"),
"got: {}",
err.message()
);
}
#[test]
fn reject_empty_pattern() {
let err = eval_expr(r"re_match('foo', '')").unwrap_err();
assert!(
err.message().to_lowercase().contains("empty"),
"got: {}",
err.message()
);
}
#[test]
fn allow_question_mark_in_character_class() {
let result = eval_expr(r"re_match('?', '[(?=]')").unwrap();
match &result {
openjd_expr::ExprValue::ListString(v, _) => {
assert_eq!(v.len(), 1);
assert_eq!(v[0], "?");
}
other => panic!("expected ListString, got: {:?}", other),
}
}
#[test]
fn allow_regex_comment_containing_lookahead_syntax() {
let result = eval_expr(r"re_match('abc', '(?#comment)abc')");
match result {
Ok(_) => {} Err(e) => assert!(
!e.message().to_lowercase().contains("lookahead"),
"spurious lookahead rejection: {}",
e.message()
),
}
}
#[test]
fn allow_pattern_with_escaped_question_mark_followed_by_equals() {
let result = eval_expr(r"re_match('a?=b', 'a\?=b')").unwrap();
match &result {
openjd_expr::ExprValue::ListString(v, _) => {
assert_eq!(v[0], "a?=b");
}
other => panic!("expected match, got: {:?}", other),
}
}
#[test]
fn allow_simple_patterns() {
assert!(eval_expr(r"re_match('abc', 'abc')").is_ok());
assert!(eval_expr(r"re_match('abc', '[a-z]+')").is_ok());
assert!(eval_expr(r"re_match('abc123', '\w+')").is_ok());
assert!(eval_expr(r"re_match('abc', '(a)(b)(c)')").is_ok());
}
#[test]
fn allow_named_groups() {
assert!(eval_expr(r"re_search('foo123', '(?P<name>\d+)')").is_ok());
}
#[test]
fn allow_non_capturing_groups() {
assert!(eval_expr(r"re_match('abab', '(?:ab)+')").is_ok());
}
#[test]
fn allow_inline_flags() {
assert!(eval_expr(r"re_match('ABC', '(?i)abc')").is_ok());
assert!(eval_expr(r"re_match('abc', '(?m)^abc$')").is_ok());
}
#[test]
fn allow_unicode_patterns() {
assert!(eval_expr(r"re_match('héllo', 'h.llo')").is_ok());
}
#[test]
fn reject_backreference_in_replacement() {
let err = eval_expr(r"re_sub('aaa', '(a)', r'\1')").unwrap_err();
assert!(
err.message().to_lowercase().contains("group reference")
|| err.message().to_lowercase().contains("backreference"),
"got: {}",
err.message()
);
}
#[test]
fn reject_backslash_lower_z_end_anchor() {
let err = eval_expr(r#"re_search('hello', r'llo\z')"#).unwrap_err();
assert!(
err.message()
.to_lowercase()
.contains("end-of-string anchor")
|| err.message().contains(r"\z"),
"got: {}",
err.message()
);
}
#[test]
fn reject_backslash_upper_z_end_anchor() {
let err = eval_expr(r#"re_search('hello', r'llo\Z')"#).unwrap_err();
assert!(
err.message()
.to_lowercase()
.contains("end-of-string anchor")
|| err.message().contains(r"\Z"),
"got: {}",
err.message()
);
}
#[test]
fn reject_unicode_brace_lowercase_x() {
let err = eval_expr(r"re_match('A', r'\x{0041}')").unwrap_err();
assert!(
err.message().to_lowercase().contains("unicode brace") || err.message().contains(r"\x{"),
"got: {}",
err.message()
);
}
#[test]
fn reject_unicode_brace_lowercase_u() {
let err = eval_expr(r"re_match('A', r'\u{0041}')").unwrap_err();
assert!(
err.message().to_lowercase().contains("unicode brace") || err.message().contains(r"\u{"),
"got: {}",
err.message()
);
}
#[test]
fn reject_unicode_brace_uppercase_u() {
let err = eval_expr(r"re_match('A', r'\U{0041}')").unwrap_err();
assert!(
err.message().to_lowercase().contains("unicode brace") || err.message().contains(r"\U{"),
"got: {}",
err.message()
);
}
#[test]
fn allow_non_brace_hex_escape() {
let result = eval_expr(r"re_match('A', r'\x41')").unwrap();
match &result {
openjd_expr::ExprValue::ListString(v, _) => assert_eq!(v[0], "A"),
other => panic!("expected match, got: {:?}", other),
}
}
#[test]
fn allow_non_brace_unicode_escape() {
let result = eval_expr(r"re_match('A', r'\u0041')").unwrap();
match &result {
openjd_expr::ExprValue::ListString(v, _) => assert_eq!(v[0], "A"),
other => panic!("expected match, got: {:?}", other),
}
}
#[test]
fn allow_dollar_end_anchor() {
let result = eval_expr(r"re_search('hello', 'llo$')").unwrap();
match &result {
openjd_expr::ExprValue::ListString(v, _) => assert_eq!(v[0], "llo"),
other => panic!("expected match, got: {:?}", other),
}
}