From 717c09c4a33b774f9b6899eecd50f070391a1d39 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 7 Aug 2023 11:52:54 -0700 Subject: [PATCH] create tests for signals --- packages/copy/src/lib.rs | 15 +++++ packages/signals/Cargo.toml | 5 ++ packages/signals/src/impls.rs | 8 +-- packages/signals/src/lib.rs | 10 ++++ packages/signals/src/rt.rs | 2 +- packages/signals/tests/create.rs | 60 +++++++++++++++++++ packages/signals/tests/effect.rs | 47 +++++++++++++++ packages/signals/tests/subscribe.rs | 92 +++++++++++++++++++++++++++++ 8 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 packages/signals/tests/create.rs create mode 100644 packages/signals/tests/effect.rs create mode 100644 packages/signals/tests/subscribe.rs diff --git a/packages/copy/src/lib.rs b/packages/copy/src/lib.rs index 3b091d30a..149c14434 100644 --- a/packages/copy/src/lib.rs +++ b/packages/copy/src/lib.rs @@ -1,5 +1,6 @@ use std::{ cell::{Cell, Ref, RefCell, RefMut}, + fmt::Debug, marker::PhantomData, rc::Rc, }; @@ -151,6 +152,20 @@ pub struct CopyHandle { _marker: PhantomData, } +impl Debug for CopyHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + #[cfg(any(debug_assertions, feature = "check_generation"))] + f.write_fmt(format_args!( + "{:?}@{:?}", + self.raw.data.as_ptr(), + self.generation + ))?; + #[cfg(not(any(debug_assertions, feature = "check_generation")))] + f.write_fmt(format_args!("{:?}", self.raw.data.as_ptr()))?; + Ok(()) + } +} + impl CopyHandle { #[inline(always)] fn validate(&self) -> bool { diff --git a/packages/signals/Cargo.toml b/packages/signals/Cargo.toml index 11d5542d7..19f2039cc 100644 --- a/packages/signals/Cargo.toml +++ b/packages/signals/Cargo.toml @@ -9,3 +9,8 @@ edition = "2018" [dependencies] dioxus-core = { workspace = true } dioxus-copy = { workspace = true } +log.workspace = true +simple_logger = "4.2.0" + +[dev-dependencies] +dioxus = { workspace = true } diff --git a/packages/signals/src/impls.rs b/packages/signals/src/impls.rs index d54e5251f..8a3c9db8c 100644 --- a/packages/signals/src/impls.rs +++ b/packages/signals/src/impls.rs @@ -38,25 +38,25 @@ macro_rules! impls { impl + Copy + 'static> std::ops::AddAssign for $ty { fn add_assign(&mut self, rhs: T) { - self.set(self.value() + rhs); + self.with_mut(|v| *v = *v + rhs) } } impl + Copy + 'static> std::ops::SubAssign for $ty { fn sub_assign(&mut self, rhs: T) { - self.set(self.value() - rhs); + self.with_mut(|v| *v = *v - rhs) } } impl + Copy + 'static> std::ops::MulAssign for $ty { fn mul_assign(&mut self, rhs: T) { - self.set(self.value() * rhs); + self.with_mut(|v| *v = *v * rhs) } } impl + Copy + 'static> std::ops::DivAssign for $ty { fn div_assign(&mut self, rhs: T) { - self.set(self.value() / rhs); + self.with_mut(|v| *v = *v / rhs) } } diff --git a/packages/signals/src/lib.rs b/packages/signals/src/lib.rs index a49a38a18..c0d081f7d 100644 --- a/packages/signals/src/lib.rs +++ b/packages/signals/src/lib.rs @@ -77,6 +77,11 @@ impl Signal { pub fn read(&self) -> Ref { let inner = self.inner.read(); if let Some(current_scope_id) = current_scope_id() { + log::trace!( + "{:?} subscribed to {:?}", + self.inner.value, + current_scope_id + ); let mut subscribers = inner.subscribers.borrow_mut(); if !subscribers.contains(¤t_scope_id) { subscribers.push(current_scope_id); @@ -98,6 +103,11 @@ impl Signal { { let inner = self.inner.read(); for &scope_id in &*inner.subscribers.borrow() { + log::trace!( + "Write on {:?} triggered update on {:?}", + self.inner.value, + scope_id + ); (inner.update_any)(scope_id); } } diff --git a/packages/signals/src/rt.rs b/packages/signals/src/rt.rs index ca5d5ec9f..b37a46a53 100644 --- a/packages/signals/src/rt.rs +++ b/packages/signals/src/rt.rs @@ -41,7 +41,7 @@ fn owner_in_scope(scope: ScopeId) -> Rc { } pub struct CopyValue { - pub value: CopyHandle, + pub(crate) value: CopyHandle, origin_scope: ScopeId, } diff --git a/packages/signals/tests/create.rs b/packages/signals/tests/create.rs new file mode 100644 index 000000000..0681263ea --- /dev/null +++ b/packages/signals/tests/create.rs @@ -0,0 +1,60 @@ +#![allow(unused, non_upper_case_globals, non_snake_case)] + +use dioxus::prelude::*; +use dioxus_core::ElementId; +use dioxus_signals::*; + +#[test] +fn create_signals_global() { + let mut dom = VirtualDom::new(|cx| { + render! { + for _ in 0..10 { + Child {} + } + } + }); + + fn Child(cx: Scope) -> Element { + let signal = create_without_cx(); + + render! { + "{signal}" + } + } + + let _edits = dom.rebuild().santize(); + + fn create_without_cx() -> Signal { + Signal::new("hello world".to_string()) + } +} + +#[test] +fn drop_signals() { + let mut dom = VirtualDom::new(|cx| { + let generation = cx.generation(); + + let count = if generation % 2 == 0 { 10 } else { 0 }; + render! { + for _ in 0..count { + Child {} + } + } + }); + + fn Child(cx: Scope) -> Element { + let signal = create_without_cx(); + + render! { + "{signal}" + } + } + + let _ = dom.rebuild().santize(); + dom.mark_dirty(ScopeId(0)); + dom.render_immediate(); + + fn create_without_cx() -> Signal { + Signal::new("hello world".to_string()) + } +} diff --git a/packages/signals/tests/effect.rs b/packages/signals/tests/effect.rs new file mode 100644 index 000000000..513b7e929 --- /dev/null +++ b/packages/signals/tests/effect.rs @@ -0,0 +1,47 @@ +#![allow(unused, non_upper_case_globals, non_snake_case)] +use std::collections::HashMap; +use std::rc::Rc; + +use dioxus::prelude::*; +use dioxus_core::ElementId; +use dioxus_signals::*; + +#[test] +fn effects_rerun() { + simple_logger::SimpleLogger::new().init().unwrap(); + + #[derive(Default)] + struct RunCounter { + component: usize, + effect: usize, + } + + let counter = Rc::new(RefCell::new(RunCounter::default())); + let mut dom = VirtualDom::new_with_props( + |cx| { + let counter = cx.props; + counter.borrow_mut().component += 1; + + let mut signal = use_signal(cx, || 0); + cx.use_hook(move || { + to_owned![counter]; + Effect::new(move || { + counter.borrow_mut().effect += 1; + println!("Signal: {:?}", signal); + }) + }); + signal += 1; + + render! { + div {} + } + }, + counter.clone(), + ); + + let _ = dom.rebuild().santize(); + + let current_counter = counter.borrow(); + assert_eq!(current_counter.component, 1); + assert_eq!(current_counter.effect, 2); +} diff --git a/packages/signals/tests/subscribe.rs b/packages/signals/tests/subscribe.rs new file mode 100644 index 000000000..5c1a99347 --- /dev/null +++ b/packages/signals/tests/subscribe.rs @@ -0,0 +1,92 @@ +#![allow(unused, non_upper_case_globals, non_snake_case)] +use std::collections::HashMap; +use std::rc::Rc; + +use dioxus::prelude::*; +use dioxus_core::ElementId; +use dioxus_signals::*; + +#[test] +fn reading_subscribes() { + simple_logger::SimpleLogger::new().init().unwrap(); + + #[derive(Default)] + struct RunCounter { + parent: usize, + children: HashMap, + } + + let counter = Rc::new(RefCell::new(RunCounter::default())); + let mut dom = VirtualDom::new_with_props( + |cx| { + let mut signal = use_signal(cx, || 0); + + println!("Parent: {:?}", cx.scope_id()); + if cx.generation() == 1 { + signal += 1; + } + + cx.props.borrow_mut().parent += 1; + + render! { + for id in 0..10 { + Child { + signal: signal, + counter: cx.props.clone() + } + } + } + }, + counter.clone(), + ); + + #[derive(Props, Clone)] + struct ChildProps { + signal: Signal, + counter: Rc>, + } + + impl PartialEq for ChildProps { + fn eq(&self, other: &Self) -> bool { + self.signal == other.signal + } + } + + fn Child(cx: Scope) -> Element { + println!("Child: {:?}", cx.scope_id()); + *cx.props + .counter + .borrow_mut() + .children + .entry(cx.scope_id()) + .or_default() += 1; + + render! { + "{cx.props.signal}" + } + } + + let _ = dom.rebuild().santize(); + + { + let current_counter = counter.borrow(); + assert_eq!(current_counter.parent, 1); + + for (scope_id, rerun_count) in current_counter.children.iter() { + assert_eq!(rerun_count, &1); + } + } + + dom.mark_dirty(ScopeId(0)); + dom.render_immediate(); + dom.render_immediate(); + + { + let current_counter = counter.borrow(); + assert_eq!(current_counter.parent, 2); + + for (scope_id, rerun_count) in current_counter.children.iter() { + assert_eq!(rerun_count, &2); + } + } +}