cranpose-core 0.1.164

Core runtime for a Jetpack Compose inspired UI framework in Rust
Documentation
use std::{cell::Cell, rc::Rc};

use super::{CallbackHolder, CallbackHolder1, ParamSlot};
use crate::{RecomposeScope, runtime::TestRuntime};

#[test]
fn param_slot_take_reports_absence_instead_of_panicking() {
    let slot = ParamSlot::default();

    assert_eq!(slot.take(), None);
    slot.set(11);
    assert_eq!(slot.take(), Some(11));
    assert_eq!(slot.take(), None);
}

#[test]
fn callback_holder_default_forwarder_is_noop() {
    let forwarder = CallbackHolder::new().clone_rc();
    forwarder();
}

#[test]
fn callback_holder_forwarder_uses_latest_callback() {
    let holder = CallbackHolder::new();
    let total = Rc::new(Cell::new(0));
    let forwarder = holder.clone_rc();

    let first_total = Rc::clone(&total);
    holder.update(move || first_total.set(first_total.get() + 1));
    forwarder();

    let second_total = Rc::clone(&total);
    holder.update(move || second_total.set(second_total.get() + 10));
    forwarder();

    assert_eq!(total.get(), 11);
}

#[test]
fn callback_holder_does_not_invoke_after_creator_scope_deactivation() {
    let runtime = TestRuntime::new();
    let scope = RecomposeScope::new_for_test(runtime.handle());
    let holder = CallbackHolder::new();
    let invocations = Rc::new(Cell::new(0));
    let invocations_for_callback = Rc::clone(&invocations);
    holder.update(move || invocations_for_callback.set(invocations_for_callback.get() + 1));
    holder.creator_scope.replace(Some(scope.clone()));
    let forwarder = holder.clone_rc();

    forwarder();
    assert_eq!(invocations.get(), 1);

    scope.deactivate();
    forwarder();
    assert_eq!(
        invocations.get(),
        1,
        "callbacks cannot outlive the active composition scope that owns them",
    );
}

#[test]
fn callback_holder_does_not_invoke_under_inactive_creator_ancestor() {
    let runtime = TestRuntime::new();
    let ancestor = RecomposeScope::new_for_test(runtime.handle());
    let creator = RecomposeScope::new_for_test(runtime.handle());
    creator.set_parent_scope(Some(ancestor.clone()));
    let holder = CallbackHolder::new();
    let invocations = Rc::new(Cell::new(0));
    let invocations_for_callback = Rc::clone(&invocations);
    holder.update(move || invocations_for_callback.set(invocations_for_callback.get() + 1));
    holder.creator_scope.replace(Some(creator));
    let forwarder = holder.clone_rc();

    forwarder();
    assert_eq!(invocations.get(), 1);

    ancestor.deactivate();
    forwarder();
    assert_eq!(
        invocations.get(),
        1,
        "callbacks cannot run beneath an inactive composition ancestor",
    );
}

#[test]
fn callback_holder1_default_forwarder_is_noop() {
    let forwarder = CallbackHolder1::<i32>::new().clone_rc();
    forwarder(7);
}

#[test]
fn callback_holder1_forwarder_uses_latest_callback() {
    let holder = CallbackHolder1::<i32>::new();
    let total = Rc::new(Cell::new(0));
    let forwarder = holder.clone_rc();

    let first_total = Rc::clone(&total);
    holder.update(move |value| first_total.set(first_total.get() + value));
    forwarder(2);

    let second_total = Rc::clone(&total);
    holder.update(move |value| second_total.set(second_total.get() + value * 5));
    forwarder(3);

    assert_eq!(total.get(), 17);
}

#[test]
fn callback_holder1_does_not_invoke_after_creator_scope_deactivation() {
    let runtime = TestRuntime::new();
    let scope = RecomposeScope::new_for_test(runtime.handle());
    let holder = CallbackHolder1::<i32>::new();
    let total = Rc::new(Cell::new(0));
    let total_for_callback = Rc::clone(&total);
    holder.update(move |value| total_for_callback.set(total_for_callback.get() + value));
    holder.creator_scope.replace(Some(scope.clone()));
    let forwarder = holder.clone_rc();

    forwarder(3);
    assert_eq!(total.get(), 3);

    scope.deactivate();
    forwarder(5);
    assert_eq!(
        total.get(),
        3,
        "argument callbacks cannot outlive their active composition owner",
    );
}