cranpose-core 0.21.2

Composition runtime and reactive state for Cranpose
Documentation
use super::*;

#[test]
fn test_empty_heap() {
    let heap = SnapshotDoubleIndexHeap::new();
    assert_eq!(heap.len(), 0);
    assert!(heap.is_empty());
    assert_eq!(heap.lowest_or_default(SnapshotId(999)), SnapshotId(999));
}

#[test]
fn test_add_single_element() {
    let mut heap = SnapshotDoubleIndexHeap::new();
    let handle = heap.add(SnapshotId(42));

    assert_eq!(heap.len(), 1);
    assert!(!heap.is_empty());
    assert_eq!(heap.lowest_or_default(SnapshotId(999)), SnapshotId(42));

    heap.remove(handle);
    assert_eq!(heap.len(), 0);
    assert_eq!(heap.lowest_or_default(SnapshotId(999)), SnapshotId(999));
}

#[test]
fn test_add_multiple_maintains_min() {
    let mut heap = SnapshotDoubleIndexHeap::new();

    heap.add(SnapshotId(50));
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(50));

    heap.add(SnapshotId(30));
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(30));

    heap.add(SnapshotId(70));
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(30));

    heap.add(SnapshotId(10));
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(10));

    assert_eq!(heap.len(), 4);
}

#[test]
fn test_remove_maintains_heap_invariant() {
    let mut heap = SnapshotDoubleIndexHeap::new();

    let h1 = heap.add(SnapshotId(50));
    let h2 = heap.add(SnapshotId(30));
    let h3 = heap.add(SnapshotId(70));
    let h4 = heap.add(SnapshotId(10));

    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(10));

    heap.remove(h4);
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(30));
    assert_eq!(heap.len(), 3);

    heap.remove(h1);
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(30));
    assert_eq!(heap.len(), 2);

    heap.remove(h2);
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(70));
    assert_eq!(heap.len(), 1);

    heap.remove(h3);
    assert!(heap.is_empty());
    assert_eq!(heap.lowest_or_default(SnapshotId(999)), SnapshotId(999));
}

#[test]
fn test_heap_invariant_after_operations() {
    let mut heap = SnapshotDoubleIndexHeap::new();

    let values = vec![100, 20, 80, 5, 60, 15, 90, 3, 40];
    let mut handles = Vec::new();

    for &v in &values {
        handles.push(heap.add(SnapshotId(v)));
    }

    fn verify_heap_invariant(heap: &SnapshotDoubleIndexHeap) {
        for i in 0..heap.size {
            let left_child = 2 * i + 1;
            let right_child = 2 * i + 2;

            if left_child < heap.size {
                assert!(
                    heap.values[i] <= heap.values[left_child],
                    "Parent {} > left child {} at positions {}, {}",
                    heap.values[i],
                    heap.values[left_child],
                    i,
                    left_child
                );
            }

            if right_child < heap.size {
                assert!(
                    heap.values[i] <= heap.values[right_child],
                    "Parent {} > right child {} at positions {}, {}",
                    heap.values[i],
                    heap.values[right_child],
                    i,
                    right_child
                );
            }
        }
    }

    verify_heap_invariant(&heap);
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(3));

    heap.remove(handles[3]);
    verify_heap_invariant(&heap);
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(3));

    heap.remove(handles[7]);
    verify_heap_invariant(&heap);
    assert_eq!(heap.lowest_or_default(SnapshotId(0)), SnapshotId(15));

    heap.remove(handles[1]);
    verify_heap_invariant(&heap);
}

#[test]
fn test_handle_reuse() {
    let mut heap = SnapshotDoubleIndexHeap::new();

    let h1 = heap.add(SnapshotId(1));
    let h2 = heap.add(SnapshotId(2));
    let h3 = heap.add(SnapshotId(3));

    heap.remove(h2);
    heap.remove(h1);

    let h4 = heap.add(SnapshotId(4));
    let h5 = heap.add(SnapshotId(5));

    assert_eq!(heap.len(), 3);
    heap.remove(h3);
    heap.remove(h4);
    heap.remove(h5);
    assert!(heap.is_empty());
}

#[test]
fn test_capacity_growth() {
    let mut heap = SnapshotDoubleIndexHeap::with_capacity(2);

    let mut handles = Vec::new();
    for i in 0..20 {
        handles.push(heap.add(SnapshotId(i)));
    }

    assert_eq!(heap.len(), 20);
    assert_eq!(heap.lowest_or_default(SnapshotId(999)), SnapshotId(0));

    for handle in handles {
        heap.remove(handle);
    }

    assert!(heap.is_empty());
}

#[test]
fn test_stress_random_operations() {
    let mut heap = SnapshotDoubleIndexHeap::new();
    let mut handles = Vec::new();

    for i in 0..100 {
        handles.push(heap.add(SnapshotId(i * 7 % 97)));
    }

    for i in (0..handles.len()).step_by(2) {
        heap.remove(handles[i]);
    }

    assert_eq!(heap.len(), 50);

    for i in 100..150 {
        handles.push(heap.add(SnapshotId(i * 3 % 89)));
    }

    assert_eq!(heap.len(), 100);

    let _ = heap.lowest_or_default(SnapshotId(0));
}