From 43bc3cb02952d9ba2784587633b2a2daff01c2df Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Wed, 21 Feb 2024 12:37:09 +0000 Subject: [PATCH] Added some tests to `dynatos_reactive::{effect, trigger}`. --- dynatos-reactive/src/effect.rs | 76 +++++++++++++++++++++++++++++++++ dynatos-reactive/src/trigger.rs | 40 +++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/dynatos-reactive/src/effect.rs b/dynatos-reactive/src/effect.rs index 0da0c2e..f48d5b1 100644 --- a/dynatos-reactive/src/effect.rs +++ b/dynatos-reactive/src/effect.rs @@ -195,3 +195,79 @@ impl fmt::Debug for WeakEffect { f.debug_struct("WeakEffect").finish_non_exhaustive() } } + +#[cfg(test)] +mod test { + // Imports + use {super::*, std::cell::OnceCell}; + + /// Leaks a value and returns a `&'static T` + /// + /// This is useful because `&'static T` is `Copy`, + /// so we don't need to worry about cloning `Rc`s to + /// pass variables to effects. + fn leaked(value: T) -> &'static T { + Box::leak(Box::new(value)) + } + + /// Ensures the function returned by `Effect::running` is the same as the future being run. + #[test] + fn running() { + // Create an effect, and save the running effect within it to `running`. + let running = self::leaked(OnceCell::new()); + let effect = Effect::new(move || { + running + .set(Effect::running().expect("Future wasn't running")) + .expect("Unable to set running effect"); + }); + + // Then ensure the running effect is the same as the one created. + let running = running + .get() + .expect("Running effect missing") + .upgrade() + .expect("Running effect was dropped"); + assert_eq!(effect, running); + } + + /// Ensures the function returned by `Effect::running` is the same as the future being run, + /// while running stacked futures + #[test] + fn running_stacked() { + // Create 2 stacked effects, saving the running within each to `running1` and `running2`. + // `running1` contains the top-level effect, while `running2` contains the inner one. + let running_top = self::leaked(OnceCell::new()); + let running_bottom = self::leaked(OnceCell::new()); + let effect = Effect::new(move || { + running_top + .set(Effect::running().expect("Future wasn't running")) + .expect("Unable to set running effect"); + + let effect = Effect::new(move || { + running_bottom + .set(Effect::running().expect("Future wasn't running")) + .expect("Unable to set running effect"); + }); + + // Then ensure the bottom-level running effect is the same as the one created. + let running_bottom = running_bottom + .get() + .expect("Running effect missing") + .upgrade() + .expect("Running effect was dropped"); + assert_eq!(effect, running_bottom); + }); + + // Then ensure the top-level running effect is the same as the one created. + let running_top = running_top + .get() + .expect("Running effect missing") + .upgrade() + .expect("Running effect was dropped"); + assert_eq!(effect, running_top); + + // And that the bottom-level running effect was already dropped + let running_bottom = running_bottom.get().expect("Running effect missing").upgrade(); + assert_eq!(running_bottom, None); + } +} diff --git a/dynatos-reactive/src/trigger.rs b/dynatos-reactive/src/trigger.rs index d029b7c..12b2bae 100644 --- a/dynatos-reactive/src/trigger.rs +++ b/dynatos-reactive/src/trigger.rs @@ -106,3 +106,43 @@ impl IntoSubscriber for T { body } } + +#[cfg(test)] +mod test { + // Imports + use { + super::*, + std::{cell::Cell, mem}, + }; + + #[test] + fn basic() { + thread_local! { + /// Counts the number of times the effect was run + static TRIGGERS: Cell = const { Cell::new(0) }; + } + + // Create the effect and reset the flag + let effect = Effect::new(move || TRIGGERS.set(TRIGGERS.get() + 1)); + + // Then create the trigger, and ensure it wasn't triggered + // by just creating it and adding the subscriber + let trigger = Trigger::new(); + trigger.add_subscriber(effect.downgrade()); + assert_eq!(TRIGGERS.get(), 1, "Trigger was triggered early"); + + // Then trigger and ensure it was triggered + trigger.trigger(); + assert_eq!(TRIGGERS.get(), 2, "Trigger was not triggered"); + + // Then add the subscriber again and ensure the effect isn't run twice + trigger.add_subscriber(effect.downgrade()); + trigger.trigger(); + assert_eq!(TRIGGERS.get(), 3, "Trigger ran effect multiple times"); + + // Finally drop the effect and try again + mem::drop(effect); + trigger.trigger(); + assert_eq!(TRIGGERS.get(), 3, "Trigger was triggered after effect was dropped"); + } +}