extern crate alloc;
use super::super::support::{arb_history, deliverable, drain, perm_of};
use crate::metis::{CausalIdeal, VersionVector};
use alloc::vec::Vec;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_concurrent_frontier_in_kairos_order(
(history, perm) in arb_history()
.prop_flat_map(|history| {
let perm = perm_of(history.len());
(Just(history), perm)
}),
) {
let mut buf = CausalIdeal::new();
for &index in &perm {
buf.insert(history[index].clone());
}
let released = drain(&mut buf);
let mut delivered = VersionVector::new();
let mut done = (0..history.len()).map(|_| false).collect::<Vec<bool>>();
for &payload in &released {
let chosen = &history[payload as usize];
prop_assert!(deliverable(chosen, &delivered));
for (i, event) in history.iter().enumerate() {
if !done[i] && deliverable(event, &delivered) {
prop_assert!(chosen.stamp <= event.stamp);
}
}
delivered = delivered.merge(&chosen.deps);
done[payload as usize] = true;
}
}
#[test]
fn prop_pop_ready_releases_frontier_minimum(
(history, perm) in arb_history()
.prop_flat_map(|history| {
let perm = perm_of(history.len());
(Just(history), perm)
}),
) {
let mut buf = CausalIdeal::new();
for &index in &perm {
buf.insert(history[index].clone());
}
loop {
let front_min = buf.frontier().min_by_key(|e| e.stamp).map(|e| e.payload);
match buf.pop_ready() {
None => {
prop_assert!(front_min.is_none());
break;
}
Some(payload) => prop_assert_eq!(Some(payload), front_min),
}
}
}
#[test]
fn prop_selective_release_is_frontier_filtering(
(history, perm) in arb_history()
.prop_flat_map(|history| {
let perm = perm_of(history.len());
(Just(history), perm)
}),
parity in any::<bool>(),
) {
let mut buf = CausalIdeal::new();
for &index in &perm {
buf.insert(history[index].clone());
}
let eligible = |payload: u32| payload.is_multiple_of(2) == parity;
let expected = buf
.frontier()
.filter(|event| eligible(event.payload))
.min_by_key(|event| event.stamp)
.map(|event| event.payload);
let released = buf
.pop_ready_event_where(|event| eligible(event.payload))
.map(|release| *release.payload());
prop_assert_eq!(released, expected);
}
#[test]
fn prop_selective_release_predicate_order_is_permutation_invariant(
(history, perm) in arb_history()
.prop_flat_map(|history| {
let perm = perm_of(history.len());
(Just(history), perm)
}),
) {
let mut buf = CausalIdeal::new();
for &index in &perm {
buf.insert(history[index].clone());
}
let expected = buf
.frontier()
.min_by_key(|event| event.stamp)
.map(|event| event.payload);
let mut calls = 0usize;
let released = buf
.pop_ready_event_where(|_| {
calls += 1;
calls == 1
})
.map(|release| *release.payload());
prop_assert_eq!(released, expected);
prop_assert_eq!(calls, usize::from(expected.is_some()));
}
#[test]
fn prop_frontier_members_are_pairwise_concurrent(
(history, perm) in arb_history()
.prop_flat_map(|history| {
let perm = perm_of(history.len());
(Just(history), perm)
}),
) {
let mut buf = CausalIdeal::new();
for &index in &perm {
buf.insert(history[index].clone());
}
loop {
let front: Vec<VersionVector> =
buf.frontier().map(|e| e.deps.clone()).collect();
for (i, a) in front.iter().enumerate() {
for b in &front[i + 1..] {
prop_assert!(a.concurrent(b));
}
}
if buf.pop_ready().is_none() {
break;
}
}
}
}