extern crate alloc;
use super::super::support::{arb_history, perm_of};
use crate::metis::{BoundedInsert, CausalIdeal, Event};
use alloc::vec::Vec;
use core::num::NonZeroUsize;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_try_insert_never_exceeds_capacity(
(history, perm) in arb_history()
.prop_flat_map(|history| {
let perm = perm_of(history.len());
(Just(history), perm)
}),
cap in 1usize..6,
) {
let cap = NonZeroUsize::new(cap).unwrap();
let mut buf = CausalIdeal::new();
for &index in &perm {
if let Err(at) = buf.try_insert(history[index].clone(), cap) {
prop_assert_eq!(at.event.payload, history[index].payload);
}
prop_assert!(buf.pending_len() <= cap.get());
}
}
}
proptest! {
#[test]
fn prop_try_insert_evicting_where_preserves_causal_closure(
(history, perm) in arb_history()
.prop_flat_map(|history| {
let perm = perm_of(history.len());
(Just(history), perm)
}),
cap in 1usize..6,
) {
let cap = NonZeroUsize::new(cap).unwrap();
let mut buf = CausalIdeal::new();
let mut pending = Vec::new();
for &index in &perm {
let arrival = history[index].clone();
let before = buf.delivered().clone();
let outcome = buf.try_insert_evicting_where(
arrival.clone(),
cap,
|_| true,
);
prop_assert!(buf.pending_len() <= cap.get());
prop_assert_eq!(buf.delivered(), &before);
match outcome {
Ok(BoundedInsert::Buffered) => pending.push(arrival),
Ok(BoundedInsert::DroppedStale) => {
prop_assert!(false, "a valid unreleased history event is not stale");
}
Ok(BoundedInsert::Evicted { evicted }) => {
let victim = pending
.iter()
.position(|resident| resident.stamp == evicted.stamp)
.expect("the returned victim was pending");
prop_assert!(is_causally_maximal(victim, &pending, &arrival));
let _ = pending.swap_remove(victim);
pending.push(arrival);
}
Err(refused) => {
prop_assert_eq!(refused.event.stamp, arrival.stamp);
prop_assert!(pending
.iter()
.enumerate()
.all(|(candidate, _)| !is_causally_maximal(candidate, &pending, &arrival)));
}
}
prop_assert_eq!(buf.pending_len(), pending.len());
}
}
}
fn is_causally_maximal(candidate: usize, pending: &[Event<u32>], arrival: &Event<u32>) -> bool {
let victim = &pending[candidate];
!depends_on(arrival, victim)
&& pending
.iter()
.enumerate()
.all(|(index, event)| index == candidate || !depends_on(event, victim))
}
fn depends_on(event: &Event<u32>, predecessor: &Event<u32>) -> bool {
let station = predecessor.stamp.station_id();
let sequence = predecessor.deps.get(station);
let event_dot = (
event.stamp.station_id(),
event.deps.get(event.stamp.station_id()),
);
event_dot != (station, sequence) && event.deps.get(station) >= sequence
}