use pattern_core::Pattern;
use proptest::prelude::*;
fn arb_atomic_pattern() -> impl Strategy<Value = Pattern<i32>> {
any::<i32>().prop_map(|v| Pattern::point(v))
}
fn arb_nested_pattern() -> impl Strategy<Value = Pattern<i32>> {
let leaf = any::<i32>().prop_map(|v| Pattern::point(v));
leaf.prop_recursive(
3, 10, 3, |inner| {
(any::<i32>(), prop::collection::vec(inner, 0..=3))
.prop_map(|(v, elements)| Pattern::pattern(v, elements))
},
)
}
proptest! {
#[test]
fn prop_find_first_consistent_with_filter(pattern in arb_nested_pattern()) {
let predicate = |p: &Pattern<i32>| p.value > 0;
let first = pattern.find_first(predicate);
let all = pattern.filter(predicate);
if first.is_some() {
prop_assert!(!all.is_empty());
prop_assert_eq!(first.unwrap().value, all[0].value);
}
if first.is_none() {
prop_assert!(all.is_empty());
}
}
}
proptest! {
#[test]
fn prop_find_first_none_iff_filter_empty(pattern in arb_nested_pattern()) {
let predicate = |p: &Pattern<i32>| p.value < -1000;
let first = pattern.find_first(predicate);
let all = pattern.filter(predicate);
prop_assert_eq!(first.is_none(), all.is_empty());
}
}
proptest! {
#[test]
fn prop_find_first_is_first_in_filter(pattern in arb_nested_pattern()) {
let predicate = |p: &Pattern<i32>| p.value % 2 == 0;
let first = pattern.find_first(predicate);
let all = pattern.filter(predicate);
if let Some(f) = first {
prop_assert!(!all.is_empty());
prop_assert!(std::ptr::eq(f, all[0]));
}
}
}
proptest! {
#[test]
fn prop_find_first_satisfies_predicate(pattern in arb_nested_pattern()) {
let predicate = |p: &Pattern<i32>| p.value > 10;
if let Some(found) = pattern.find_first(predicate) {
prop_assert!(predicate(found));
}
}
}
proptest! {
#[test]
fn prop_matches_reflexive(pattern in arb_nested_pattern()) {
prop_assert!(pattern.matches(&pattern));
}
}
proptest! {
#[test]
fn prop_matches_reflexive_atomic(value in any::<i32>()) {
let pattern = Pattern::point(value);
prop_assert!(pattern.matches(&pattern));
}
}
proptest! {
#[test]
fn prop_matches_symmetric(p1 in arb_nested_pattern(), p2 in arb_nested_pattern()) {
prop_assert_eq!(p1.matches(&p2), p2.matches(&p1));
}
}
proptest! {
#[test]
fn prop_matches_symmetric_atomic(v1 in any::<i32>(), v2 in any::<i32>()) {
let p1 = Pattern::point(v1);
let p2 = Pattern::point(v2);
prop_assert_eq!(p1.matches(&p2), p2.matches(&p1));
}
}
proptest! {
#[test]
fn prop_matches_identical_patterns(pattern in arb_nested_pattern()) {
let clone = Pattern {
value: pattern.value,
elements: pattern.elements.clone(),
};
prop_assert!(pattern.matches(&clone));
prop_assert!(clone.matches(&pattern));
}
}
proptest! {
#[test]
fn prop_contains_reflexive(pattern in arb_nested_pattern()) {
prop_assert!(pattern.contains(&pattern));
}
}
proptest! {
#[test]
fn prop_contains_reflexive_atomic(value in any::<i32>()) {
let pattern = Pattern::point(value);
prop_assert!(pattern.contains(&pattern));
}
}
#[test]
fn test_contains_transitive_simple() {
let c = Pattern::point(1);
let b = Pattern::pattern(2, vec![c.clone()]);
let a = Pattern::pattern(3, vec![b.clone()]);
assert!(a.contains(&b));
assert!(b.contains(&c));
assert!(a.contains(&c)); }
proptest! {
#[test]
fn prop_contains_transitive_nested(inner in arb_atomic_pattern()) {
let middle = Pattern::pattern(42, vec![inner.clone()]);
let outer = Pattern::pattern(99, vec![middle.clone()]);
prop_assert!(outer.contains(&middle));
prop_assert!(middle.contains(&inner));
prop_assert!(outer.contains(&inner)); }
}
proptest! {
#[test]
fn prop_matches_implies_contains(p1 in arb_nested_pattern(), p2 in arb_nested_pattern()) {
if p1.matches(&p2) {
prop_assert!(p1.contains(&p2));
prop_assert!(p2.contains(&p1));
}
}
}
proptest! {
#[test]
fn prop_matches_implies_contains_atomic(v1 in any::<i32>(), v2 in any::<i32>()) {
let p1 = Pattern::point(v1);
let p2 = Pattern::point(v2);
if p1.matches(&p2) {
prop_assert!(p1.contains(&p2));
prop_assert!(p2.contains(&p1));
}
}
}
proptest! {
#[test]
fn prop_contains_element_means_contains_pattern(pattern in arb_nested_pattern()) {
for element in &pattern.elements {
prop_assert!(pattern.contains(element));
}
}
}
proptest! {
#[test]
fn prop_find_first_always_returns_valid_ref(pattern in arb_nested_pattern()) {
let predicate = |_: &Pattern<i32>| true;
let result = pattern.find_first(predicate);
prop_assert!(result.is_some());
if let Some(found) = result {
let _ = found.value; }
}
}
proptest! {
#[test]
fn prop_matches_distinguishes_different_structures(
v in any::<i32>(),
elements1 in prop::collection::vec(any::<i32>(), 1..=3),
elements2 in prop::collection::vec(any::<i32>(), 1..=3)
) {
let p1 = Pattern::pattern(v, elements1.iter().map(|&e| Pattern::point(e)).collect());
let p2 = Pattern::pattern(v, elements2.iter().map(|&e| Pattern::point(e)).collect());
if elements1.len() != elements2.len() {
prop_assert!(!p1.matches(&p2));
}
}
}
proptest! {
#[test]
fn prop_contains_not_symmetric(inner in arb_atomic_pattern()) {
let outer = Pattern::pattern(42, vec![inner.clone()]);
prop_assert!(outer.contains(&inner));
prop_assert!(!inner.contains(&outer));
}
}