cranpose-core 0.0.60

Core runtime for a Jetpack Compose inspired UI framework in Rust
Documentation
use super::*;

#[test]
fn slot_v2_empty_table_validates() {
    let table = SlotTable::new();
    assert_eq!(table.validate(), Ok(()));
    assert!(table.groups.is_empty());
    assert!(table.debug_dump_groups().is_empty());
    assert!(table.debug_dump_slot_entries().is_empty());
}

#[test]
fn first_composition_records_group_value_and_node() {
    let mut harness = SlotHarness::new();
    harness.begin_pass(SlotPassMode::Compose);
    let slot = harness.session(|session| {
        let started = begin_unkeyed(session, 1, None);
        assert_eq!(started.kind, GroupStartKind::Inserted);
        let slot = session.value_slot_with_kind(PayloadKind::Internal, || 41_i32);
        session.record_node_with_parent(7, 1, None);
        let result = session.finish_group_body();
        assert!(result.detached_children.is_empty());
        assert!(result.direct_nodes.is_empty());
        session.end_group();
        slot
    });
    harness.finish_pass();

    assert_eq!(*harness.table.read_value::<i32>(slot), 41);
    assert_eq!(harness.table.groups.len(), 1);
    assert_eq!(harness.table.total_payload_count(), 1);
    assert_eq!(harness.table.groups[0].payload_len, 1);
    assert_eq!(harness.table.total_node_count(), 1);
    assert_eq!(harness.table.groups[0].node_len, 1);
    assert_eq!(harness.table.groups[0].subtree_len, 1);
    assert_eq!(harness.table.groups[0].subtree_node_count, 1);
    let payload = harness.table.group_payload_record_at(0, 0);
    assert_eq!(payload.type_id, TypeId::of::<i32>());
    assert_eq!(
        harness.table.group_node_record_at(0, 0).lifecycle,
        super::NodeLifecycle::Active
    );
}

#[test]
fn removing_many_payloads_requests_compaction() {
    const GROUP_KEY: Key = 78;
    const PAYLOAD_COUNT: usize = SlotWriteSessionState::COMPACT_PAYLOAD_THRESHOLD;

    let mut harness = SlotHarness::new();
    harness.begin_pass(SlotPassMode::Compose);
    harness.session(|session| {
        let started = begin_unkeyed(session, GROUP_KEY, None);
        assert_eq!(started.kind, GroupStartKind::Inserted);
        for index in 0..PAYLOAD_COUNT {
            let _ = session.value_slot_with_kind(PayloadKind::Internal, move || index as i32);
        }
        let result = session.finish_group_body();
        assert!(result.detached_children.is_empty());
        session.end_group();
    });
    harness.finish_pass();

    assert!(!harness.state.request_compaction);
    assert_eq!(harness.table.total_payload_count(), PAYLOAD_COUNT);

    harness.begin_pass(SlotPassMode::Compose);
    harness.session(|session| {
        let started = begin_unkeyed(session, GROUP_KEY, None);
        assert_eq!(started.kind, GroupStartKind::Reused);
        let result = session.finish_group_body();
        assert!(result.detached_children.is_empty());
        session.end_group();
    });

    assert_eq!(harness.state.removed_payload_count, PAYLOAD_COUNT);
    assert!(harness.state.request_compaction);
    harness.finish_pass();
}

#[test]
fn slot_write_session_exposes_semantic_operations() {
    const GROUP_KEY: Key = 77;
    const SCOPE_ID: ScopeId = 901;

    let mut harness = SlotHarness::new();
    harness.begin_pass(SlotPassMode::Compose);
    let (composed_group, slot) = harness.session(|session| {
        exercise_slot_write_session_surface(session, GroupKey::new(GROUP_KEY, None, 0), SCOPE_ID)
    });
    harness.finish_pass();
    assert_eq!(*harness.table.read_value::<i32>(slot), 7);
    *harness.table.read_value_mut::<i32>(slot) = 8;
    harness.table.write_value(slot, 9_i32);
    assert_eq!(*harness.table.read_value::<i32>(slot), 9);
    assert_eq!(harness.table.validate(), Ok(()));
    assert_eq!(harness.table.debug_snapshot().active_groups.len(), 1);

    harness.begin_pass(SlotPassMode::Recompose);
    harness.session(|session| {
        let recomposed_group = session
            .begin_recompose_at_scope(SCOPE_ID)
            .expect("session should resolve the indexed scope");
        assert_eq!(recomposed_group, composed_group);
        assert_eq!(node_ids_in_current_subtree(session), vec![55]);
        session.skip_group();
        let result = session.finish_group_body();
        assert!(result.detached_children.is_empty());
        assert_eq!(result.root_nodes, vec![55]);
        assert!(result.was_skipped);
        session.end_recompose();
    });
    harness.finish_pass();
    assert_eq!(harness.table.validate(), Ok(()));
}

#[test]
fn debug_snapshot_reports_active_groups_anchors_and_scopes() {
    const ROOT_KEY: Key = 41;
    const CHILD_KEY: Key = 42;
    const ROOT_SCOPE: ScopeId = 101;
    const CHILD_SCOPE: ScopeId = 202;

    let mut harness = SlotHarness::new();

    harness.begin_pass(SlotPassMode::Compose);
    harness.session(|session| {
        let root = begin_unkeyed(session, ROOT_KEY, None);
        session.set_group_scope(root.group, ROOT_SCOPE);
        let _ = session.value_slot_with_kind(PayloadKind::Internal, || 7_i32);
        session.record_node_with_parent(11, 1, None);

        let child = begin_unkeyed(session, CHILD_KEY, None);
        session.set_group_scope(child.group, CHILD_SCOPE);
        let _ = session.value_slot_with_kind(PayloadKind::Internal, || 9_i32);
        let child_result = session.finish_group_body();
        assert!(child_result.detached_children.is_empty());
        session.end_group();

        let root_result = session.finish_group_body();
        assert!(root_result.detached_children.is_empty());
        session.end_group();
    });
    harness.finish_pass();

    let snapshot = harness.table.debug_snapshot();
    assert_eq!(snapshot.active_groups.len(), 2);
    assert_eq!(snapshot.anchors.len(), 2);
    assert_eq!(snapshot.scopes.len(), 2);
    assert_eq!(snapshot.active_payload_count, 2);
    assert_eq!(snapshot.active_node_count, 1);
    assert_eq!(snapshot.active_scope_count, 2);
    assert_eq!(snapshot.scope_index_count, 2);
    assert_eq!(snapshot.runtime_scope_registry_count, None);
    assert_eq!(snapshot.retained_subtree_count, 0);

    let root = snapshot
        .active_groups
        .iter()
        .find(|group| group.static_key == ROOT_KEY)
        .expect("root group snapshot");
    assert_eq!(root.scope_id, Some(ROOT_SCOPE));
    assert_eq!(root.subtree_len, 2);
    assert_eq!(root.payload_len, 1);
    assert_eq!(root.node_len, 1);

    let child = snapshot
        .active_groups
        .iter()
        .find(|group| group.static_key == CHILD_KEY)
        .expect("child group snapshot");
    assert_eq!(child.parent_anchor, root.anchor);
    assert_eq!(child.scope_id, Some(CHILD_SCOPE));
    assert_eq!(child.subtree_len, 1);
    assert_eq!(child.payload_len, 1);
}

#[test]
fn debug_dump_slot_entries_use_v2_native_labels() {
    const GROUP_KEY: Key = 51;

    let table = composed_group_with_value_and_node_table(GROUP_KEY);
    let rows = table.debug_dump_slot_entries();

    assert!(
        rows.iter()
            .any(|entry| entry.kind == super::SlotDebugEntryKind::Payload),
        "payload entries should still be visible in debug output"
    );
    assert!(
        rows.iter()
            .any(|entry| entry.line.contains("kind=internal")),
        "debug output must expose the stored payload classification"
    );
    assert!(
        rows.iter().all(|entry| {
            !entry.line.starts_with("Value(") && !entry.line.starts_with("PayloadKind(")
        }),
        "debug output must speak in V2 entry labels rather than fake slot-stream wrappers"
    );
}

#[test]
fn take_all_drops_reserves_only_payload_count() {
    let mut table = composed_group_with_value_and_node_table(23);
    let drops = table.take_all_drops();

    assert_eq!(drops.len(), 1);
    assert_eq!(drops.capacity(), drops.len());
    assert!(table.groups.is_empty());
    assert!(table.payloads.is_empty());
    assert!(table.nodes.is_empty());
}

#[test]
fn debug_stats_report_explicit_v2_table_local_counts() {
    const GROUP_KEY: Key = 34;

    let table = composed_group_with_value_and_node_table(GROUP_KEY);
    let stats = table.debug_stats();

    assert_eq!(stats.group_count, 1);
    assert_eq!(stats.payload_count, 1);
    assert_eq!(stats.active_payload_anchor_count, 1);
    assert_eq!(stats.payload_anchor_slot_count, 1);
    assert_eq!(stats.detached_payload_anchor_count, 0);
    assert_eq!(stats.invalidated_payload_anchor_count, 0);
    assert_eq!(stats.free_payload_anchor_count, 0);
    assert_eq!(stats.node_count, 1);
    assert_eq!(stats.active_anchor_count, 1);
    assert_eq!(stats.group_record_size, mem::size_of::<GroupRecord>());
    assert_eq!(
        stats.group_heap_bytes,
        stats.group_capacity * stats.group_record_size
    );
    assert_eq!(stats.anchor_slot_count, 1);
    assert_eq!(stats.anchor_sparse_count, 0);
    assert_eq!(stats.detached_anchor_count, 0);
    assert_eq!(stats.invalidated_anchor_count, 0);
    assert_eq!(stats.free_anchor_count, 0);
    assert_eq!(stats.scope_index_count, 0);
    assert!(stats.group_capacity >= stats.group_count);
    assert!(stats.payload_capacity >= stats.payload_count);
    assert!(stats.payload_anchor_capacity >= stats.active_payload_anchor_count);
    assert!(stats.payload_anchor_heap_bytes > 0);
    assert!(stats.node_capacity >= stats.node_count);
    assert!(stats.anchor_capacity >= stats.active_anchor_count);
    assert!(stats.anchor_heap_bytes > 0);
    assert!(stats.scope_index_capacity >= stats.scope_index_count);
}

#[test]
fn group_record_footprint_stays_bounded_until_profiling_justifies_split_storage() {
    let group_record_size = mem::size_of::<GroupRecord>();

    assert!(
        group_record_size <= 128,
        "GroupRecord is {group_record_size} bytes; revisit field packing with perf data before accepting more group-table bandwidth"
    );
}