minerva 0.2.0

Causal ordering for distributed systems
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! {
    /// At every release, the chosen event is the Kairos-minimal deliverable event.
    #[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;
        }
    }

    /// `pop_ready` releases the `Kairos`-minimal member of `frontier`.
    #[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),
            }
        }
    }

    /// Selective release chooses the `Kairos`-minimal eligible frontier member.
    #[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);
    }

    /// A stateful predicate observes ready events in canonical order, never arrival order.
    #[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()));
    }

    /// In a valid history, every ready frontier is pairwise concurrent.
    #[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;
            }
        }
    }
}