From 2f0b9825c6a31b7412babeda2a95b00f6273a271 Mon Sep 17 00:00:00 2001 From: Ryan Walters Date: Fri, 5 Sep 2025 19:46:52 -0500 Subject: [PATCH] test: blinking system tests --- tests/blinking.rs | 316 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 tests/blinking.rs diff --git a/tests/blinking.rs b/tests/blinking.rs new file mode 100644 index 0000000..5bcfe1d --- /dev/null +++ b/tests/blinking.rs @@ -0,0 +1,316 @@ +use bevy_ecs::{entity::Entity, system::RunSystemOnce, world::World}; +use pacman::systems::{ + blinking::{blinking_system, Blinking}, + components::{DeltaTime, Renderable}, + Frozen, Hidden, +}; +use speculoos::prelude::*; + +mod common; + +/// Creates a test world with blinking system resources +fn create_blinking_test_world() -> World { + let mut world = World::new(); + world.insert_resource(DeltaTime::from_ticks(1)); + world +} + +/// Spawns a test entity with blinking and renderable components +fn spawn_blinking_entity(world: &mut World, interval_ticks: u32) -> Entity { + world + .spawn(( + Blinking::new(interval_ticks), + Renderable { + sprite: common::mock_atlas_tile(1), + layer: 0, + }, + )) + .id() +} + +/// Spawns a test entity with blinking, renderable, and hidden components +fn spawn_hidden_blinking_entity(world: &mut World, interval_ticks: u32) -> Entity { + world + .spawn(( + Blinking::new(interval_ticks), + Renderable { + sprite: common::mock_atlas_tile(1), + layer: 0, + }, + Hidden, + )) + .id() +} + +/// Spawns a test entity with blinking, renderable, and frozen components +fn spawn_frozen_blinking_entity(world: &mut World, interval_ticks: u32) -> Entity { + world + .spawn(( + Blinking::new(interval_ticks), + Renderable { + sprite: common::mock_atlas_tile(1), + layer: 0, + }, + Frozen, + )) + .id() +} + +/// Spawns a test entity with blinking, renderable, hidden, and frozen components +fn spawn_frozen_hidden_blinking_entity(world: &mut World, interval_ticks: u32) -> Entity { + world + .spawn(( + Blinking::new(interval_ticks), + Renderable { + sprite: common::mock_atlas_tile(1), + layer: 0, + }, + Hidden, + Frozen, + )) + .id() +} + +/// Runs the blinking system with the given delta time +fn run_blinking_system(world: &mut World, delta_ticks: u32) { + world.resource_mut::().ticks = delta_ticks; + world.run_system_once(blinking_system).unwrap(); +} + +/// Checks if an entity has the Hidden component +fn has_hidden_component(world: &World, entity: Entity) -> bool { + world.entity(entity).contains::() +} + +/// Checks if an entity has the Frozen component +fn has_frozen_component(world: &World, entity: Entity) -> bool { + world.entity(entity).contains::() +} + +#[test] +fn test_blinking_component_creation() { + let blinking = Blinking::new(10); + + assert_that(&blinking.tick_timer).is_equal_to(0); + assert_that(&blinking.interval_ticks).is_equal_to(10); +} + +#[test] +fn test_blinking_system_normal_interval_no_toggle() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 5); + + // Run system with 3 ticks (less than interval) + run_blinking_system(&mut world, 3); + + // Entity should not be hidden yet + assert_that(&has_hidden_component(&world, entity)).is_false(); + + // Check that timer was updated + let blinking = world.entity(entity).get::().unwrap(); + assert_that(&blinking.tick_timer).is_equal_to(3); +} + +#[test] +fn test_blinking_system_normal_interval_first_toggle() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 5); + + // Run system with 5 ticks (exactly one interval) + run_blinking_system(&mut world, 5); + + // Entity should now be hidden + assert_that(&has_hidden_component(&world, entity)).is_true(); + + // Check that timer was reset + let blinking = world.entity(entity).get::().unwrap(); + assert_that(&blinking.tick_timer).is_equal_to(0); +} + +#[test] +fn test_blinking_system_normal_interval_second_toggle() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 5); + + // First toggle: 5 ticks + run_blinking_system(&mut world, 5); + assert_that(&has_hidden_component(&world, entity)).is_true(); + + // Second toggle: another 5 ticks + run_blinking_system(&mut world, 5); + assert_that(&has_hidden_component(&world, entity)).is_false(); +} + +#[test] +fn test_blinking_system_normal_interval_multiple_intervals() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 3); + + // Run system with 7 ticks (2 complete intervals + 1 remainder) + run_blinking_system(&mut world, 7); + + // Should toggle twice (even number), so back to original state (not hidden) + assert_that(&has_hidden_component(&world, entity)).is_false(); + + // Check that timer was updated to remainder + let blinking = world.entity(entity).get::().unwrap(); + assert_that(&blinking.tick_timer).is_equal_to(1); +} + +#[test] +fn test_blinking_system_normal_interval_odd_intervals() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 2); + + // Run system with 5 ticks (2 complete intervals + 1 remainder) + run_blinking_system(&mut world, 5); + + // Should toggle twice (even number), so back to original state (not hidden) + assert_that(&has_hidden_component(&world, entity)).is_false(); + + // Check that timer was updated to remainder + let blinking = world.entity(entity).get::().unwrap(); + assert_that(&blinking.tick_timer).is_equal_to(1); +} + +#[test] +fn test_blinking_system_zero_interval_with_ticks() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 0); + + // Run system with any positive ticks + run_blinking_system(&mut world, 1); + + // Entity should be hidden immediately + assert_that(&has_hidden_component(&world, entity)).is_true(); +} + +#[test] +fn test_blinking_system_zero_interval_no_ticks() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 0); + + // Run system with 0 ticks + run_blinking_system(&mut world, 0); + + // Entity should not be hidden (no time passed) + assert_that(&has_hidden_component(&world, entity)).is_false(); +} + +#[test] +fn test_blinking_system_zero_interval_toggle_back() { + let mut world = create_blinking_test_world(); + let entity = spawn_hidden_blinking_entity(&mut world, 0); + + // Run system with any positive ticks + run_blinking_system(&mut world, 1); + + // Entity should be unhidden + assert_that(&has_hidden_component(&world, entity)).is_false(); +} + +#[test] +fn test_blinking_system_frozen_entity_unhidden() { + let mut world = create_blinking_test_world(); + let entity = spawn_frozen_hidden_blinking_entity(&mut world, 5); + + // Run system with ticks + run_blinking_system(&mut world, 10); + + // Frozen entity should be unhidden and stay unhidden + assert_that(&has_hidden_component(&world, entity)).is_false(); + assert_that(&has_frozen_component(&world, entity)).is_true(); +} + +#[test] +fn test_blinking_system_frozen_entity_no_blinking() { + let mut world = create_blinking_test_world(); + let entity = spawn_frozen_blinking_entity(&mut world, 5); + + // Run system with ticks + run_blinking_system(&mut world, 10); + + // Frozen entity should not be hidden (blinking disabled) + assert_that(&has_hidden_component(&world, entity)).is_false(); + assert_that(&has_frozen_component(&world, entity)).is_true(); +} + +#[test] +fn test_blinking_system_frozen_entity_timer_not_updated() { + let mut world = create_blinking_test_world(); + let entity = spawn_frozen_blinking_entity(&mut world, 5); + + // Run system with ticks + run_blinking_system(&mut world, 10); + + // Timer should not be updated for frozen entities + let blinking = world.entity(entity).get::().unwrap(); + assert_that(&blinking.tick_timer).is_equal_to(0); +} + +#[test] +fn test_blinking_system_entity_without_renderable_ignored() { + let mut world = create_blinking_test_world(); + + // Spawn entity with only Blinking component (no Renderable) + let entity = world.spawn(Blinking::new(5)).id(); + + // Run system + run_blinking_system(&mut world, 10); + + // Entity should not be affected (not in query) + assert_that(&has_hidden_component(&world, entity)).is_false(); +} + +#[test] +fn test_blinking_system_entity_without_blinking_ignored() { + let mut world = create_blinking_test_world(); + + // Spawn entity with only Renderable component (no Blinking) + let entity = world + .spawn(Renderable { + sprite: common::mock_atlas_tile(1), + layer: 0, + }) + .id(); + + // Run system + run_blinking_system(&mut world, 10); + + // Entity should not be affected (not in query) + assert_that(&has_hidden_component(&world, entity)).is_false(); +} + +#[test] +fn test_blinking_system_large_interval() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 1000); + + // Run system with 500 ticks (less than interval) + run_blinking_system(&mut world, 500); + + // Entity should not be hidden yet + assert_that(&has_hidden_component(&world, entity)).is_false(); + + // Check that timer was updated + let blinking = world.entity(entity).get::().unwrap(); + assert_that(&blinking.tick_timer).is_equal_to(500); +} + +#[test] +fn test_blinking_system_very_small_interval() { + let mut world = create_blinking_test_world(); + let entity = spawn_blinking_entity(&mut world, 1); + + // Run system with 1 tick + run_blinking_system(&mut world, 1); + + // Entity should be hidden + assert_that(&has_hidden_component(&world, entity)).is_true(); + + // Run system with another 1 tick + run_blinking_system(&mut world, 1); + + // Entity should be unhidden + assert_that(&has_hidden_component(&world, entity)).is_false(); +}