cranpose-core 0.21.0

Composition runtime and reactive state for Cranpose
Documentation
use std::rc::Rc;

use super::*;
use crate::{state::StateObject, tests::state_mock::MockState};

#[test]
fn test_readonly_snapshot_creation() {
    let snapshot = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), None);
    assert_eq!(snapshot.snapshot_id(), SnapshotId(1));
    assert!(!snapshot.is_disposed());
}

#[test]
fn test_readonly_snapshot_is_valid() {
    let invalid = SnapshotIdSet::new().set(SnapshotId(5));
    let snapshot = ReadonlySnapshot::new(SnapshotId(10), invalid, None);

    let any_snapshot = AnySnapshot::Readonly(snapshot);
    assert!(any_snapshot.is_valid(SnapshotId(1)));
    assert!(any_snapshot.is_valid(SnapshotId(10)));
    assert!(!any_snapshot.is_valid(SnapshotId(5)));
    assert!(!any_snapshot.is_valid(SnapshotId(11)));
}

#[test]
fn test_readonly_snapshot_no_pending_changes() {
    let snapshot = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), None);
    assert!(!snapshot.has_pending_changes());
}

#[test]
fn test_readonly_snapshot_enter() {
    let snapshot = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), None);

    set_current_snapshot(None);
    assert!(current_snapshot().is_none());

    snapshot.enter(|| {
        let current = current_snapshot();
        assert!(current.is_some());
        assert_eq!(current.unwrap().snapshot_id(), SnapshotId(1));
    });

    assert!(current_snapshot().is_none());
}

#[test]
fn test_readonly_snapshot_enter_restores_previous() {
    let snapshot1 = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), None);
    let snapshot2 = ReadonlySnapshot::new(SnapshotId(2), SnapshotIdSet::new(), None);

    snapshot1.enter(|| {
        snapshot2.enter(|| {
            let current = current_snapshot();
            assert_eq!(current.unwrap().snapshot_id(), SnapshotId(2));
        });

        let current = current_snapshot();
        assert_eq!(current.unwrap().snapshot_id(), SnapshotId(1));
    });
}

#[test]
fn test_readonly_snapshot_nested() {
    let parent = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), None);
    let nested = parent.take_nested_snapshot(None);

    assert_eq!(nested.snapshot_id(), SnapshotId(1));
}

#[test]
fn test_readonly_snapshot_read_observer() {
    use std::{rc::Rc as ObserverRef, sync::Mutex};

    let read_count = ObserverRef::new(Mutex::new(0));
    let read_count_clone = Rc::clone(&read_count);

    let observer = Rc::new(move |_: &dyn StateObject| {
        *read_count_clone.lock().unwrap() += 1;
    });

    let snapshot = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), Some(observer));
    let mock_state = MockState::with_id(0);

    snapshot.record_read(&mock_state);
    snapshot.record_read(&mock_state);

    assert_eq!(*read_count.lock().unwrap(), 2);
}

#[test]
fn test_readonly_snapshot_nested_with_observer() {
    use std::{rc::Rc as ObserverRef, sync::Mutex};

    let parent_reads = ObserverRef::new(Mutex::new(0));
    let parent_reads_clone = Rc::clone(&parent_reads);
    let parent_observer = Rc::new(move |_: &dyn StateObject| {
        *parent_reads_clone.lock().unwrap() += 1;
    });

    let nested_reads = ObserverRef::new(Mutex::new(0));
    let nested_reads_clone = Rc::clone(&nested_reads);
    let nested_observer = Rc::new(move |_: &dyn StateObject| {
        *nested_reads_clone.lock().unwrap() += 1;
    });

    let parent = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), Some(parent_observer));
    let nested = parent.take_nested_snapshot(Some(nested_observer));

    let mock_state = MockState::with_id(0);

    nested.record_read(&mock_state);

    assert_eq!(*parent_reads.lock().unwrap(), 1);
    assert_eq!(*nested_reads.lock().unwrap(), 1);
}

#[test]
#[should_panic(expected = "Cannot write to a read-only snapshot")]
fn test_readonly_snapshot_write_panics() {
    let snapshot = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), None);
    let mock_state = Rc::new(MockState::with_id(0));
    snapshot.record_write(mock_state);
}

#[test]
fn test_readonly_snapshot_dispose() {
    let snapshot = ReadonlySnapshot::new(SnapshotId(1), SnapshotIdSet::new(), None);
    assert!(!snapshot.is_disposed());

    snapshot.dispose();
    assert!(snapshot.is_disposed());
}