use crate::regex::Regex;
use crate::typing::Pattern;
use crate::typing::pattern::Match;
use proptest::prelude::*;
fn leaf() -> impl Strategy<Value = String> {
"[A-Za-z][A-Za-z0-9]{0,3}"
}
fn lit_ty() -> impl Strategy<Value = Pattern> {
leaf().prop_map(Pattern::raw)
}
fn set_ty() -> impl Strategy<Value = Pattern> {
prop::collection::vec(leaf(), 1..4)
.prop_map(|ps| Pattern::closed(Regex::union_many(ps.iter().map(|p| Regex::literal(p)))))
}
fn concrete_ty() -> impl Strategy<Value = Pattern> {
prop_oneof![
6 => lit_ty(),
5 => set_ty(),
1 => Just(Pattern::bottom()),
]
}
fn solved(m: &Match) -> bool {
matches!(m, Match::Solved)
}
proptest! {
#[test]
fn meet_reflexive(t in concrete_ty()) {
prop_assert_eq!(solved(&t.unify(&t)), t != Pattern::bottom());
}
#[test]
fn unify_symmetric(a in concrete_ty(), b in concrete_ty()) {
prop_assert_eq!(a.unify(&b), b.unify(&a));
}
#[test]
fn meet_is_intersection(a in concrete_ty(), b in concrete_ty()) {
prop_assert_eq!(solved(&a.unify(&b)), a.head.has_intersection(&b.head));
}
#[test]
fn unify_total(a in concrete_ty(), b in concrete_ty()) {
let m = a.unify(&b);
prop_assert!(matches!(m, Match::Solved | Match::Empty));
}
}
proptest! {
#[test]
fn bottom_always_empty(t in concrete_ty()) {
prop_assert_eq!(Pattern::bottom().unify(&t), Match::Empty);
}
}
proptest! {
#[test]
fn subset_reflexive(t in concrete_ty()) {
prop_assert!(t.subset(&t));
}
#[test]
fn bottom_below_all(t in concrete_ty()) {
prop_assert!(Pattern::bottom().subset(&t));
}
#[test]
fn subset_transitive(a in set_ty(), b in set_ty(), c in set_ty()) {
if a.subset(&b) && b.subset(&c) {
prop_assert!(a.subset(&c));
}
}
#[test]
fn subset_antisymmetric(a in set_ty(), b in set_ty()) {
if a.subset(&b) && b.subset(&a) {
prop_assert!(solved(&a.unify(&b)));
}
}
#[test]
fn subset_implies_meet(a in set_ty(), b in set_ty()) {
if a.subset(&b) {
prop_assert!(solved(&a.unify(&b)));
}
}
}
proptest! {
#[test]
fn raw_is_canonical(s in leaf()) {
prop_assert_eq!(Pattern::raw(&s), Pattern::lit(&s));
}
}