// SPDX-License-Identifier: AGPL-3.0-only //! Unit tests for the debug chord handling in [`crate::debug`]. use super::*; /// Presses and releases a key, returning the action produced on the press edge. fn tap(controls: &mut DebugControls, code: KeyCode) -> Option { let action = controls.handle_key(code, true); controls.handle_key(code, false); action } #[test] fn chord_key_alone_does_nothing() { let mut controls = DebugControls::default(); assert_eq!(tap(&mut controls, KeyCode::KeyV), None); } #[test] fn modifier_alone_produces_no_action() { let mut controls = DebugControls::default(); assert_eq!(controls.handle_key(DEBUG_MODIFIER, true), None); assert_eq!(controls.handle_key(DEBUG_MODIFIER, false), None); } #[test] fn held_modifier_plus_bound_key_selects_the_mode() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); assert_eq!( tap(&mut controls, KeyCode::KeyV), Some(DebugAction::SetRenderMode(RenderMode::FilledPoints)) ); } #[test] fn each_bound_key_selects_a_distinct_overlay_mode() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); for (code, expected) in [ (KeyCode::KeyV, RenderMode::FilledPoints), (KeyCode::KeyB, RenderMode::FilledWireframe), ] { assert_eq!( tap(&mut controls, code), Some(DebugAction::SetRenderMode(expected)) ); } } #[test] fn solo_modifier_drops_the_filled_pass() { for solo in SOLO_MODIFIER { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); controls.handle_key(solo, true); assert_eq!( tap(&mut controls, KeyCode::KeyV), Some(DebugAction::SetRenderMode(RenderMode::Points)) ); assert_eq!( tap(&mut controls, KeyCode::KeyB), Some(DebugAction::SetRenderMode(RenderMode::Wireframe)) ); } } #[test] fn solo_modifier_is_tracked_before_the_debug_modifier() { let mut controls = DebugControls::default(); controls.handle_key(KeyCode::ShiftLeft, true); controls.handle_key(DEBUG_MODIFIER, true); assert_eq!( tap(&mut controls, KeyCode::KeyV), Some(DebugAction::SetRenderMode(RenderMode::Points)) ); } #[test] fn releasing_the_solo_modifier_restores_the_overlay_form() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); controls.handle_key(KeyCode::ShiftLeft, true); tap(&mut controls, KeyCode::KeyV); controls.handle_key(KeyCode::ShiftLeft, false); assert_eq!( tap(&mut controls, KeyCode::KeyV), Some(DebugAction::SetRenderMode(RenderMode::FilledPoints)) ); } #[test] fn switching_between_modes_does_not_pass_through_filled() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); tap(&mut controls, KeyCode::KeyV); assert_eq!( tap(&mut controls, KeyCode::KeyB), Some(DebugAction::SetRenderMode(RenderMode::FilledWireframe)) ); } #[test] fn repeating_the_chord_toggles_back_to_filled() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); tap(&mut controls, KeyCode::KeyV); assert_eq!( tap(&mut controls, KeyCode::KeyV), Some(DebugAction::SetRenderMode(RenderMode::Filled)) ); } #[test] fn action_fires_on_the_press_edge_only() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); assert!(controls.handle_key(KeyCode::KeyV, true).is_some()); assert_eq!(controls.handle_key(KeyCode::KeyV, false), None); } #[test] fn releasing_the_modifier_disarms_the_chord() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); controls.handle_key(DEBUG_MODIFIER, false); assert_eq!(tap(&mut controls, KeyCode::KeyV), None); } #[test] fn unbound_key_under_the_modifier_is_ignored() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); assert_eq!(tap(&mut controls, KeyCode::KeyW), None); } #[test] fn solo_modifier_alone_produces_no_action() { let mut controls = DebugControls::default(); assert_eq!(controls.handle_key(KeyCode::ShiftLeft, true), None); assert_eq!(tap(&mut controls, KeyCode::KeyV), None); } #[test] fn the_stats_chord_toggles_the_overlay_on_and_off() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); assert_eq!( tap(&mut controls, STATS_KEY), Some(DebugAction::SetStatsOverlay(true)) ); assert_eq!( tap(&mut controls, STATS_KEY), Some(DebugAction::SetStatsOverlay(false)) ); } #[test] fn the_stats_chord_leaves_the_render_mode_untouched() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); tap(&mut controls, KeyCode::KeyV); tap(&mut controls, STATS_KEY); // The raster axis must survive a toggle on the statistics axis; a single-enum design would have reset it. assert_eq!(controls.render_mode, RenderMode::FilledPoints); } #[test] fn a_raster_chord_leaves_the_stats_overlay_untouched() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); tap(&mut controls, STATS_KEY); assert_eq!( tap(&mut controls, KeyCode::KeyB), Some(DebugAction::SetRenderMode(RenderMode::FilledWireframe)) ); assert!(controls.stats_enabled); } #[test] fn the_solo_modifier_does_not_change_the_stats_chord() { let mut controls = DebugControls::default(); controls.handle_key(DEBUG_MODIFIER, true); controls.handle_key(KeyCode::ShiftLeft, true); assert_eq!( tap(&mut controls, STATS_KEY), Some(DebugAction::SetStatsOverlay(true)) ); } #[test] fn the_stats_chord_requires_the_debug_modifier() { let mut controls = DebugControls::default(); assert_eq!(tap(&mut controls, STATS_KEY), None); assert!(!controls.stats_enabled); }