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! {
    /// Every released event is deliverable against the events already released.
    #[test]
    fn prop_release_respects_causality(
        (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();
        for &payload in &released {
            let event = &history[payload as usize];
            prop_assert!(deliverable(event, &delivered));
            delivered = delivered.merge(&event.deps);
        }
    }

    /// A valid history carries the full closure, so every distinct event releases once.
    #[test]
    fn prop_every_event_released_once(
        (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 mut released = drain(&mut buf);

        prop_assert_eq!(buf.pending_len(), 0);
        prop_assert_eq!(released.len(), history.len());
        released.sort_unstable();
        let n = u32::try_from(history.len()).unwrap();
        prop_assert_eq!(released, (0..n).collect::<Vec<u32>>());
    }

    /// Inserting every event twice still releases each exactly once.
    #[test]
    fn prop_duplicates_dropped(
        (history, perm) in arb_history()
            .prop_flat_map(|history| {
                let perm = perm_of(history.len() * 2);
                (Just(history), perm)
            }),
    ) {
        let n = history.len();
        let mut buf = CausalIdeal::new();
        for &index in &perm {
            buf.insert(history[index % n].clone());
        }
        let mut released = drain(&mut buf);

        prop_assert_eq!(released.len(), n);
        released.sort_unstable();
        released.dedup();
        prop_assert_eq!(released.len(), n);
    }

    /// Release order is a function of the events, not their arrival order.
    #[test]
    fn prop_release_order_is_permutation_invariant(
        (history, perm_a, perm_b) in arb_history()
            .prop_flat_map(|history| {
                let len = history.len();
                (Just(history), perm_of(len), perm_of(len))
            }),
    ) {
        let release = |perm: &[usize]| {
            let mut buf = CausalIdeal::new();
            for &index in perm {
                buf.insert(history[index].clone());
            }
            drain(&mut buf)
        };
        prop_assert_eq!(release(&perm_a), release(&perm_b));
    }
}