#!/usr/bin/env python3 import unittest from pathlib import Path from unittest.mock import MagicMock from engine import controls class DummyBindings(controls.KeyBindings): def __init__(self, game=None): if game is None: game = MagicMock() game.menu_up = lambda: None game.menu_down = lambda: None game.menu_left = lambda: None game.menu_right = lambda: None game.reset_game = lambda: None super().__init__(game) def spawn_rat(self): pass def toggle_audio(self): pass def toggle_full_screen(self): pass def start_scrolling(self, direction): pass def stop_scrolling(self): pass def spawn_new_bomb(self): pass def spawn_new_nuclear_bomb(self): pass def spawn_new_mine(self): pass def spawn_new_gas(self): pass def spawn_gas(self, parent_id=None): pass def toggle_pause(self): pass def reset_game(self): pass def quit_game(self): pass def menu_up(self): pass def menu_down(self): pass def menu_left(self): pass def menu_right(self): pass class KeybindingProfileTests(unittest.TestCase): def test_shipped_profiles_expose_all_weapon_actions(self): dummy = DummyBindings() conf_dir = Path(__file__).resolve().parent / "conf" weapon_actions = { "spawn_new_bomb", "spawn_new_nuclear_bomb", "spawn_new_mine", "spawn_new_gas", } for config_path in sorted(conf_dir.glob("keybindings*.json")) + sorted(conf_dir.glob("keybindings*.yaml")): bindings = controls._load_bindings_from_file(config_path) validated = dummy._validate_bindings(bindings, config_path) game_bindings = validated.get("keybinding_game", {}) self.assertTrue(game_bindings, f"missing keybinding_game in {config_path.name}") actions = set(game_bindings.values()) self.assertTrue( weapon_actions.issubset(actions), f"incomplete weapon bindings in {config_path.name}: {sorted(actions)}", ) def test_shipped_profiles_define_level_dialog_bindings(self): dummy = DummyBindings() conf_dir = Path(__file__).resolve().parent / "conf" required_sections = { "keybinding_start_menu", # Corrected from level_intro since we removed it "keybinding_level_clear", "keybinding_defeat", "keybinding_run_complete", } for config_path in sorted(conf_dir.glob("keybindings*.json")) + sorted(conf_dir.glob("keybindings*.yaml")): bindings = controls._load_bindings_from_file(config_path) validated = dummy._validate_bindings(bindings, config_path) for section_name in required_sections: section = validated.get(section_name, {}) self.assertTrue(section, f"missing {section_name} in {config_path.name}") values = set(section.values()) self.assertIn("reset_game", values, f"missing confirm binding in {section_name} for {config_path.name}") self.assertIn("quit_game", values, f"missing quit binding in {section_name} for {config_path.name}") def test_trigger_uses_start_menu_context(self): game = MagicMock() dummy = DummyBindings(game) calls = [] # Override action_dispatcher for testing dummy.action_dispatcher["reset_game"] = lambda: calls.append("reset") dummy.action_dispatcher["quit_game"] = lambda: calls.append("quit") dummy.bindings = { "keybinding_start_menu": {"keydown_Return": "reset_game"}, } game.game_status = "start_menu" game.menu_screen = "start" game.game_end = (False, None) dummy.trigger("keydown_Return") self.assertEqual(calls, ["reset"]) def test_trigger_uses_level_clear_context(self): game = MagicMock() dummy = DummyBindings(game) calls = [] dummy.action_dispatcher["reset_game"] = lambda: calls.append("reset") dummy.action_dispatcher["quit_game"] = lambda: calls.append("quit") dummy.bindings = { "keybinding_level_clear": {"keydown_Return": "reset_game"}, "keybinding_paused": {"keydown_Return": "quit_game"}, } game.game_status = "paused" game.menu_screen = None game.game_end = (True, "level_clear") dummy.trigger("keydown_Return") self.assertEqual(calls, ["reset"]) if __name__ == "__main__": unittest.main()