use proptest::prelude::*;
use super::parse_tag;
fn fragment() -> impl Strategy<Value = &'static str> {
prop::sample::select(vec![
"foo", "bar-1", "0", "$foo", "@foo", ".cls", "#id", "primary", "f(", ")", "(", "[", "]",
"{", "}", ",", ":", "=", ".", "/", "-", "\"", "\"a\"", "\"\\", "\\n", "1", "-1.5", "1a",
"true", "false", "null", "$$mdtype", " ", " ", "\n", "\t", "\r", "é", "\u{0}",
])
}
fn tag_shaped() -> impl Strategy<Value = String> {
prop::collection::vec(fragment(), 0..32).prop_map(|parts| parts.concat())
}
fn check_error_invariants(input: &str) {
let Err(error) = parse_tag(input) else {
return;
};
assert!(
error.start() <= error.end(),
"start {} is past end {} for {input:?}",
error.start(),
error.end()
);
assert!(
error.end() <= input.len(),
"end {} is past the input length {} for {input:?}",
error.end(),
input.len()
);
assert!(
input.get(error.start()..error.end()).is_some(),
"offsets {}..{} do not land on character boundaries for {input:?}",
error.start(),
error.end()
);
assert!(
!error.message().is_empty(),
"an error with no message for {input:?}"
);
}
proptest! {
#[test]
fn never_panics_on_arbitrary_input(input in any::<String>()) {
let _ = parse_tag(&input);
}
#[test]
fn never_panics_on_tag_shaped_input(input in tag_shaped()) {
let _ = parse_tag(&input);
}
#[test]
fn errors_carry_sliceable_offsets(input in any::<String>()) {
check_error_invariants(&input);
}
#[test]
fn errors_carry_sliceable_offsets_for_tag_shaped_input(input in tag_shaped()) {
check_error_invariants(&input);
}
#[test]
fn parsing_is_deterministic(input in tag_shaped()) {
let first = parse_tag(&input);
let second = parse_tag(&input);
match (first, second) {
(Ok(first), Ok(second)) => prop_assert_eq!(first, second),
(Err(first), Err(second)) => {
prop_assert_eq!(first.message(), second.message());
prop_assert_eq!(first.start(), second.start());
}
_ => prop_assert!(false, "one run parsed and the other did not"),
}
}
#[test]
fn deep_nesting_returns_rather_than_overflowing(depth in 1usize..2_000) {
let input = format!("a={}{}", "[".repeat(depth), "]".repeat(depth));
let _ = parse_tag(&input);
let unbalanced = format!("a={}", "{".repeat(depth));
let _ = parse_tag(&unbalanced);
}
}