use super::*;
mod unit_tests {
use super::*;
fn span(lo: i32, hi: i32) -> I32CO {
I32CO::try_new(lo, hi).unwrap()
}
#[test]
fn no_overlap() {
let a = span(1, 4);
let b = span(6, 8);
match a.difference(b) {
ZeroOneTwo::One(x) => assert_eq!(x, a),
_ => panic!(),
}
}
#[test]
fn cut_middle() {
let a = span(1, 10);
let b = span(4, 6);
match a.difference(b) {
ZeroOneTwo::Two(l, r) => {
assert_eq!(l, span(1, 4));
assert_eq!(r, span(6, 10));
}
_ => panic!(),
}
}
#[test]
fn cover_all() {
let a = span(3, 6);
let b = span(1, 10);
match a.difference(b) {
ZeroOneTwo::Zero => {}
_ => panic!(),
}
}
#[test]
fn identical_intervals() {
let a = span(3, 6);
match a.difference(a) {
ZeroOneTwo::Zero => {}
_ => panic!(),
}
}
}
mod prop_tests {
use std::{vec, vec::Vec};
use super::*;
use proptest::prelude::*;
fn span(a: i32, b: i32) -> Option<I32CO> {
let lo = a.min(b);
let hi = a.max(b);
I32CO::try_new(lo, hi)
}
fn edge_values() -> Vec<i32> {
let mut v = vec![i32::MIN, i32::MAX, 0, 1];
if i32::MIN < i32::MAX {
v.push(i32::MIN.saturating_add(1));
v.push(i32::MAX.saturating_sub(1));
}
v.sort_unstable();
v.dedup();
v
}
fn edge_scalar() -> impl Strategy<Value = i32> {
prop::sample::select(edge_values())
}
fn mixed_scalar() -> impl Strategy<Value = i32> {
prop_oneof![
3 => edge_scalar(),
7 => any::<i32>(),
]
}
fn span_strategy() -> impl Strategy<Value = I32CO> {
(mixed_scalar(), mixed_scalar()).prop_filter_map("non-empty span", |(a, b)| span(a, b))
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 64,
.. ProptestConfig::default()
})]
#[test]
fn difference_points_are_in_x_and_not_in_y(
x in span_strategy(),
y in span_strategy(),
p in mixed_scalar(),
) {
let in_diff = match x.difference(y) {
ZeroOneTwo::Zero => false,
ZeroOneTwo::One(z) => z.contains(p),
ZeroOneTwo::Two(l, r) => l.contains(p) || r.contains(p),
};
prop_assert_eq!(in_diff, x.contains(p) && !y.contains(p));
}
#[test]
fn difference_two_parts_do_not_overlap(
x in span_strategy(),
y in span_strategy(),
) {
if let ZeroOneTwo::Two(l, r) = x.difference(y) {
prop_assert!(!l.intersects(r));
prop_assert!(!r.intersects(l));
}
}
#[test]
fn difference_is_subset_of_x(
x in span_strategy(),
y in span_strategy(),
p in mixed_scalar(),
) {
let in_diff = match x.difference(y) {
ZeroOneTwo::Zero => false,
ZeroOneTwo::One(z) => z.contains(p),
ZeroOneTwo::Two(l, r) => l.contains(p) || r.contains(p),
};
if in_diff {
prop_assert!(x.contains(p));
}
}
#[test]
fn difference_excludes_y(
x in span_strategy(),
y in span_strategy(),
p in mixed_scalar(),
) {
let in_diff = match x.difference(y) {
ZeroOneTwo::Zero => false,
ZeroOneTwo::One(z) => z.contains(p),
ZeroOneTwo::Two(l, r) => l.contains(p) || r.contains(p),
};
if in_diff {
prop_assert!(!y.contains(p));
}
}
#[test]
fn difference_shape_matches_math(
x in span_strategy(),
y in span_strategy(),
) {
match x.difference(y) {
ZeroOneTwo::Zero => {
prop_assert!(y.start() <= x.start() && y.end_excl() >= x.end_excl());
}
ZeroOneTwo::One(z) => {
prop_assert!(z.start() >= x.start());
prop_assert!(z.end_excl() <= x.end_excl());
}
ZeroOneTwo::Two(l, r) => {
prop_assert!(l.end_excl() <= r.start());
prop_assert!(l.start() >= x.start());
prop_assert!(r.end_excl() <= x.end_excl());
}
}
}
}
}