# Unit tests for input_event_manager.py methods. # # Copyright 2025 Igalia, S.L. # Author: Joanmarie Diggs # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. # pylint: disable=wrong-import-position # pylint: disable=import-outside-toplevel # pylint: disable=too-many-public-methods # pylint: disable=too-many-statements # pylint: disable=protected-access # pylint: disable=too-many-arguments # pylint: disable=too-many-positional-arguments # pylint: disable=too-many-locals # pylint: disable=too-many-lines # pylint: disable=too-many-instance-attributes """Unit tests for input_event_manager.py methods.""" from __future__ import annotations from typing import TYPE_CHECKING from unittest.mock import call import pytest if TYPE_CHECKING: from unittest.mock import MagicMock from cthulhu_test_context import CthulhuTestContext @pytest.mark.unit class TestInputEventManager: """Test InputEventManager class methods.""" def _setup_dependencies(self, test_context: CthulhuTestContext) -> dict[str, MagicMock]: """Returns dependencies for input_event_manager module testing.""" additional_modules = ["cthulhu.ax_utilities", "gi.repository"] essential_modules = test_context.setup_shared_dependencies(additional_modules) debug_mock = essential_modules["cthulhu.debug"] debug_mock.LEVEL_INFO = 800 debug_mock.LEVEL_WARNING = 2 debug_mock.LEVEL_SEVERE = 3 debug_mock.debugLevel = 0 focus_manager_mock = essential_modules["cthulhu.focus_manager"] focus_mgr_instance = test_context.Mock() focus_mgr_instance.get_locus_of_focus = test_context.Mock() focus_mgr_instance.get_active_window = test_context.Mock() focus_mgr_instance.focus_and_window_are_unknown = test_context.Mock(return_value=False) focus_mgr_instance.clear_state = test_context.Mock() focus_manager_mock.get_manager = test_context.Mock(return_value=focus_mgr_instance) script_manager_mock = essential_modules["cthulhu.script_manager"] script_mgr_instance = test_context.Mock() script_instance = test_context.Mock() script_instance.app = test_context.Mock() script_instance.event_cache = {} script_instance.listeners = {} script_instance.is_activatable_event = test_context.Mock(return_value=True) script_instance.force_script_activation = test_context.Mock(return_value=False) script_instance.present_if_inactive = False script_mgr_instance.get_active_script = test_context.Mock(return_value=script_instance) script_mgr_instance.get_script = test_context.Mock(return_value=script_instance) script_mgr_instance.set_active_script = test_context.Mock() script_mgr_instance.get_default_script = test_context.Mock(return_value=script_instance) script_mgr_instance.reclaim_scripts = test_context.Mock() script_manager_mock.get_manager = test_context.Mock(return_value=script_mgr_instance) input_event_mock = essential_modules["cthulhu.input_event"] class MockKeyboardEvent: """Mock KeyboardEvent class for testing.""" def __init__(self, pressed=True, keycode=65, keysym=97, modifiers=0, text="a"): self.process = test_context.Mock() self.set_window = test_context.Mock() self.set_object = test_context.Mock() self.set_script = test_context.Mock() self.set_click_count = test_context.Mock() self.is_modifier_key = test_context.Mock(return_value=False) self.is_pressed_key = test_context.Mock(return_value=True) self.get_window = test_context.Mock() self.get_object = test_context.Mock() self.get_script = test_context.Mock() self.get_click_count = test_context.Mock(return_value=1) self.is_printable_key = test_context.Mock(return_value=True) self.as_single_line_string = test_context.Mock(return_value="KeyboardEvent") self.pressed = pressed self.keycode = keycode self.keysym = keysym self.modifiers = modifiers self.text = text self.id = 1 self.hw_code = keycode self.keyval_name = text self.time = 1000 self.button = None def __eq__(self, other): """Enable equality comparison for duplicate detection.""" if not isinstance(other, MockKeyboardEvent): return False return ( self.pressed == other.pressed and self.keycode == other.keycode and self.keysym == other.keysym and self.modifiers == other.modifiers and self.text == other.text ) def __hash__(self): return hash((self.pressed, self.keycode, self.keysym, self.modifiers, self.text)) class MockBrailleEvent: """Mock BrailleEvent class for testing.""" def __init__(self, *_args, **_kwargs): self.process = test_context.Mock(return_value=True) class MockMouseButtonEvent: """Mock MouseButtonEvent class for testing.""" def __init__(self, *_args, **_kwargs): self.set_click_count = test_context.Mock() self.get_click_count = test_context.Mock(return_value=1) self.button = "1" self.pressed = True self.time = 1000 input_event_mock.KeyboardEvent = MockKeyboardEvent input_event_mock.BrailleEvent = MockBrailleEvent input_event_mock.MouseButtonEvent = MockMouseButtonEvent ax_utilities_mock = essential_modules["cthulhu.ax_utilities"] ax_utilities_class = test_context.Mock() ax_utilities_class.can_be_active_window = test_context.Mock(return_value=True) ax_utilities_class.find_active_window = test_context.Mock(return_value=None) ax_utilities_class.is_single_line = test_context.Mock(return_value=False) ax_utilities_class.is_widget_controlled_by_line_navigation = test_context.Mock( return_value=False, ) ax_utilities_class.has_matching_shortcut = test_context.Mock(return_value=False) ax_utilities_class.is_table_header = test_context.Mock(return_value=False) ax_utilities_class.is_terminal = test_context.Mock(return_value=False) ax_utilities_mock.AXUtilities = ax_utilities_class gi_repo_mock = essential_modules["gi.repository"] atspi_mock = test_context.Mock() atspi_mock.get_version = test_context.Mock(return_value=(2, 56, 0)) atspi_mock.Device = test_context.Mock() atspi_mock.Device.new_full = test_context.Mock() atspi_mock.Device.new = test_context.Mock() atspi_mock.Device.grab_keyboard = test_context.Mock() atspi_mock.Device.ungrab_keyboard = test_context.Mock() atspi_mock.KeyDefinition = test_context.Mock() atspi_mock.ModifierType = test_context.Mock() atspi_mock.ModifierType.CONTROL = 2 atspi_mock.ModifierType.SHIFT = 0 atspi_mock.ModifierType.ALT = 3 gi_repo_mock.Atspi = atspi_mock essential_modules["focus_manager_instance"] = focus_mgr_instance essential_modules["script_manager_instance"] = script_mgr_instance essential_modules["script_instance"] = script_instance essential_modules["ax_utilities_class"] = ax_utilities_class essential_modules["atspi"] = atspi_mock return essential_modules def _setup_input_event_manager(self, test_context) -> tuple: """Helper method to set up InputEventManager with dependencies.""" essential_modules: dict[str, MagicMock] = self._setup_dependencies(test_context) test_context.patch("cthulhu.input_event_manager.debug", new=essential_modules["cthulhu.debug"]) test_context.patch( "cthulhu.input_event_manager.focus_manager", new=essential_modules["cthulhu.focus_manager"], ) test_context.patch( "cthulhu.input_event_manager.script_manager", new=essential_modules["cthulhu.script_manager"], ) test_context.patch( "cthulhu.input_event_manager.input_event", new=essential_modules["cthulhu.input_event"], ) test_context.patch( "cthulhu.input_event_manager.AXUtilities", new=essential_modules["ax_utilities_class"], ) test_context.patch("cthulhu.input_event_manager.Atspi", new=essential_modules["atspi"]) ax_device_mgr_mock = test_context.Mock() essential_modules["ax_device_manager"] = ax_device_mgr_mock test_context.patch("cthulhu.input_event_manager.ax_device_manager", new=ax_device_mgr_mock) from cthulhu.input_event_manager import InputEventManager return InputEventManager(), essential_modules def test_init(self, test_context: CthulhuTestContext) -> None: """Test InputEventManager.__init__.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) assert input_event_manager._last_input_event is None assert input_event_manager._last_non_modifier_key_event is None assert input_event_manager._device is None assert not input_event_manager._mapped_keycodes assert not input_event_manager._mapped_keysyms assert not input_event_manager._grabbed_bindings def test_start_key_watcher(self, test_context) -> None: """Test InputEventManager.start_key_watcher.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_device = test_context.Mock() ax_device_mgr = essential_modules["ax_device_manager"] ax_device_mgr.get_manager.return_value.get_device.return_value = mock_device input_event_manager.start_key_watcher() ax_device_mgr.get_manager.return_value.get_device.assert_called_once() mock_device.add_key_watcher.assert_called_once_with( input_event_manager.process_keyboard_event, ) assert input_event_manager._device is mock_device def test_stop_key_watcher(self, test_context: CthulhuTestContext) -> None: """Test InputEventManager.stop_key_watcher.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._device = test_context.Mock() input_event_manager.stop_key_watcher() assert input_event_manager._device is None essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "disabled", "scenario": "disabled", "is_enabled": False, "is_bound": True, "has_grabs": False, "has_device": False, "expected_result": [], "expects_debug_call": False, }, { "id": "unbound", "scenario": "unbound", "is_enabled": True, "is_bound": False, "has_grabs": False, "has_device": False, "expected_result": [], "expects_debug_call": False, }, { "id": "has_grabs", "scenario": "has_grabs", "is_enabled": True, "is_bound": True, "has_grabs": True, "has_device": False, "existing_grab_ids": [333, 444], "expected_result": [333, 444], "expects_debug_call": True, }, { "id": "no_device", "scenario": "no_device", "is_enabled": True, "is_bound": True, "has_grabs": False, "has_device": False, "expected_result": [], "expects_debug_call": True, }, { "id": "success", "scenario": "success", "is_enabled": True, "is_bound": True, "has_grabs": False, "has_device": True, "expected_result": [111, 222], "expects_debug_call": False, }, ], ids=lambda case: case["id"], ) def test_add_grabs_for_keybinding_scenarios( self, test_context: CthulhuTestContext, case: dict, ) -> None: """Test InputEventManager.add_grabs_for_keybinding with various scenarios.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_binding = test_context.Mock() mock_binding.is_enabled.return_value = case["is_enabled"] mock_binding.is_bound.return_value = case["is_bound"] mock_binding.has_grabs.return_value = case["has_grabs"] mock_binding.get_grab_ids.return_value = case.get("existing_grab_ids", []) if case["has_device"]: mock_device = test_context.Mock() mock_device.add_key_grab.side_effect = [111, 222] input_event_manager._device = mock_device mock_kd1 = test_context.Mock() mock_kd2 = test_context.Mock() mock_binding.key_definitions.return_value = [mock_kd1, mock_kd2] result = input_event_manager.add_grabs_for_keybinding(mock_binding, ["Insert", "KP_Insert"]) if case["scenario"] == "success": assert result == case["expected_result"] assert input_event_manager._grabbed_bindings[111] == mock_binding assert input_event_manager._grabbed_bindings[222] == mock_binding mock_device.add_key_grab.assert_has_calls([call(mock_kd1, None), call(mock_kd2, None)]) else: assert result == case["expected_result"] if case["expects_debug_call"]: essential_modules["cthulhu.debug"].print_tokens.assert_called() def test_add_grabs_for_keybinding_allows_default_cthulhu_modifiers( self, test_context: CthulhuTestContext, ) -> None: """Plugin bindings can be grabbed before a script supplies modifier keys.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) mock_device = test_context.Mock() mock_device.add_key_grab.return_value = 123 input_event_manager._device = mock_device mock_kd = test_context.Mock() mock_binding = test_context.Mock() mock_binding.has_grabs.return_value = False mock_binding.key_definitions.return_value = [mock_kd] result = input_event_manager.add_grabs_for_keybinding(mock_binding) assert result == [123] mock_binding.key_definitions.assert_called_once_with(None) mock_device.add_key_grab.assert_called_once_with(mock_kd, None) @pytest.mark.parametrize( "case", [ { "id": "no_device", "scenario": "no_device", "has_device": False, "grab_ids": [], "has_grabbed_bindings": False, "expects_debug_tokens": True, "expects_debug_message": False, "expects_device_calls": False, }, { "id": "no_grabs", "scenario": "no_grabs", "has_device": True, "grab_ids": [], "has_grabbed_bindings": False, "expects_debug_tokens": False, "expects_debug_message": False, "expects_device_calls": False, }, { "id": "success", "scenario": "success", "has_device": True, "grab_ids": [111, 222], "has_grabbed_bindings": True, "expects_debug_tokens": False, "expects_debug_message": False, "expects_device_calls": True, }, { "id": "missing_grab", "scenario": "missing_grab", "has_device": True, "grab_ids": [999], "has_grabbed_bindings": False, "expects_debug_tokens": False, "expects_debug_message": True, "expects_device_calls": True, }, ], ids=lambda case: case["id"], ) def test_remove_grabs_for_keybinding_scenarios( self, test_context: CthulhuTestContext, case: dict, ) -> None: """Test InputEventManager.remove_grabs_for_keybinding with various scenarios.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) if case["has_device"]: mock_device = test_context.Mock() input_event_manager._device = mock_device if case["has_grabbed_bindings"]: input_event_manager._grabbed_bindings = { 111: test_context.Mock(), 222: test_context.Mock(), } mock_binding = test_context.Mock() mock_binding.get_grab_ids.return_value = case["grab_ids"] input_event_manager.remove_grabs_for_keybinding(mock_binding) if case["expects_debug_tokens"]: essential_modules["cthulhu.debug"].print_tokens.assert_called() if case["expects_debug_message"]: essential_modules["cthulhu.debug"].print_message.assert_called() if case["expects_device_calls"]: if case["scenario"] == "success": mock_device.remove_key_grab.assert_has_calls([call(111), call(222)]) assert 111 not in input_event_manager._grabbed_bindings assert 222 not in input_event_manager._grabbed_bindings elif case["scenario"] == "missing_grab": mock_device.remove_key_grab.assert_called_once_with(999) @pytest.mark.parametrize( "case", [ { "id": "keysym_no_device", "has_device": False, "input_value": 0x61, "expected_result": 0, "device_method": None, "device_return_value": None, "mapped_collection": None, }, { "id": "keysym_success", "has_device": True, "input_value": 0x61, "expected_result": 16, "device_method": "map_keysym_modifier", "device_return_value": 16, "mapped_collection": "_mapped_keysyms", }, ], ids=lambda case: case["id"], ) def test_map_keysym_to_modifier_scenarios( self, test_context: CthulhuTestContext, case: dict, ) -> None: """Test InputEventManager.map_keysym_to_modifier with various scenarios.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) if case["has_device"]: mock_device = test_context.Mock() if case["device_method"]: getattr(mock_device, case["device_method"]).return_value = case[ "device_return_value" ] input_event_manager._device = mock_device result = input_event_manager.map_keysym_to_modifier(case["input_value"]) assert result == case["expected_result"] if case["has_device"] and case["device_method"] and case["mapped_collection"]: getattr(mock_device, case["device_method"]).assert_called_once_with(case["input_value"]) collection = getattr(input_event_manager, case["mapped_collection"]) assert case["input_value"] in collection elif not case["has_device"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "no_device", "has_device": False, "expects_debug_call": True, "expects_unmap_calls": False, }, { "id": "success", "has_device": True, "expects_debug_call": False, "expects_unmap_calls": True, }, ], ids=lambda case: case["id"], ) def test_unmap_all_modifiers_scenarios(self, test_context, case: dict) -> None: """Test InputEventManager.unmap_all_modifiers scenarios.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._mapped_keycodes = [42, 43] input_event_manager._mapped_keysyms = [0x61, 0x62] if case["has_device"]: mock_device = test_context.Mock() input_event_manager._device = mock_device input_event_manager.unmap_all_modifiers() if case["expects_debug_call"]: essential_modules["cthulhu.debug"].print_message.assert_called() if case["expects_unmap_calls"]: mock_device.unmap_modifier.assert_has_calls([call(42), call(43)]) mock_device.unmap_keysym_modifier.assert_has_calls([call(0x61), call(0x62)]) assert not input_event_manager._mapped_keycodes assert not input_event_manager._mapped_keysyms @pytest.mark.parametrize( "case", [ { "id": "add_no_device", "operation": "add", "scenario": "no_device", "has_device": False, "grab_id": None, "expected_result": -1, "expects_debug_call": True, }, { "id": "add_success", "operation": "add", "scenario": "success", "has_device": True, "grab_id": 789, "expected_result": 789, "expects_debug_call": False, }, { "id": "remove_no_device", "operation": "remove", "scenario": "no_device", "has_device": False, "grab_id": 789, "expected_result": None, "expects_debug_call": True, }, { "id": "remove_success", "operation": "remove", "scenario": "success", "has_device": True, "grab_id": 789, "expected_result": None, "expects_debug_call": True, }, ], ids=lambda case: case["id"], ) def test_grab_for_modifier_scenarios(self, test_context, case: dict) -> None: """Test InputEventManager add/remove_grab_for_modifier scenarios.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) if case["has_device"]: mock_device = test_context.Mock() if case["operation"] == "add": mock_device.add_key_grab.return_value = case["grab_id"] input_event_manager._device = mock_device if case["operation"] == "add": result = input_event_manager.add_grab_for_modifier("Shift", 0xFFE1, 50) if case["expected_result"] is not None: assert result == case["expected_result"] if case["has_device"] and case["scenario"] == "success": mock_device.add_key_grab.assert_called_once() else: input_event_manager.remove_grab_for_modifier("Shift", case["grab_id"]) if case["has_device"] and case["scenario"] == "success": mock_device.remove_key_grab.assert_called_once_with(case["grab_id"]) if case["expects_debug_call"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "grab_without_reason", "operation": "grab", "reason": None, "expected_reason_text": None, }, { "id": "grab_with_reason", "operation": "grab", "reason": "learn mode", "expected_reason_text": "learn mode", }, { "id": "ungrab_without_reason", "operation": "ungrab", "reason": None, "expected_reason_text": None, }, { "id": "ungrab_with_reason", "operation": "ungrab", "reason": "exiting learn mode", "expected_reason_text": "exiting learn mode", }, ], ids=lambda case: case["id"], ) def test_keyboard_grab_scenarios(self, test_context: CthulhuTestContext, case: dict) -> None: """Test InputEventManager keyboard grab/ungrab operations with and without reasons.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_device = test_context.Mock() input_event_manager._device = mock_device if case["operation"] == "grab": if case["reason"]: input_event_manager.grab_keyboard(case["reason"]) else: input_event_manager.grab_keyboard() essential_modules["atspi"].Device.grab_keyboard.assert_called_once_with(mock_device) else: if case["reason"]: input_event_manager.ungrab_keyboard(case["reason"]) else: input_event_manager.ungrab_keyboard() essential_modules["atspi"].Device.ungrab_keyboard.assert_called_once_with(mock_device) if case["expected_reason_text"]: debug_calls = essential_modules["cthulhu.debug"].print_message.call_args_list assert any(case["expected_reason_text"] in str(call) for call in debug_calls) def test_process_braille_event(self, test_context: CthulhuTestContext) -> None: """Test InputEventManager.process_braille_event.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_event = test_context.Mock() result = input_event_manager.process_braille_event(mock_event) assert result is True assert isinstance( input_event_manager._last_input_event, essential_modules["cthulhu.input_event"].BrailleEvent, ) assert input_event_manager._last_non_modifier_key_event is None def test_process_mouse_button_event(self, test_context: CthulhuTestContext) -> None: """Test InputEventManager.process_mouse_button_event.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_event = test_context.Mock() input_event_manager._determine_mouse_event_click_count = test_context.Mock(return_value=2) input_event_manager.process_mouse_button_event(mock_event) assert isinstance( input_event_manager._last_input_event, essential_modules["cthulhu.input_event"].MouseButtonEvent, ) input_event_manager._last_input_event.set_click_count.assert_called_once_with(2) @pytest.mark.parametrize( "case", [ { "id": "key_press_active_window", "pressed": True, "keycode": 65, "keysym": 97, "modifiers": 0, "text": "a", "window_can_be_active": True, "new_window_found": None, }, { "id": "key_press_inactive_window_with_alternative", "pressed": True, "keycode": 65, "keysym": 97, "modifiers": 0, "text": "a", "window_can_be_active": False, "new_window_found": "mock_window", }, { "id": "key_press_inactive_window_no_alternative", "pressed": True, "keycode": 65, "keysym": 97, "modifiers": 0, "text": "a", "window_can_be_active": False, "new_window_found": None, }, { "id": "key_release", "pressed": False, "keycode": 65, "keysym": 97, "modifiers": 0, "text": "a", "window_can_be_active": True, "new_window_found": None, }, ], ids=lambda case: case["id"], ) def test_process_keyboard_event(self, test_context, case: dict) -> None: """Test InputEventManager.process_keyboard_event.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) new_window_found = case["new_window_found"] if new_window_found == "mock_window": new_window_found = test_context.Mock() mock_device = test_context.Mock() mock_window = test_context.Mock() mock_focus = test_context.Mock() mock_script = test_context.Mock() mock_focus_manager = essential_modules["cthulhu.focus_manager"] mock_focus_manager.get_active_window.return_value = mock_window mock_focus_manager.get_locus_of_focus.return_value = mock_focus mock_script_manager = essential_modules["cthulhu.script_manager"] mock_script_manager.get_active_script.return_value = mock_script essential_modules["ax_utilities_class"].can_be_active_window.return_value = case[ "window_can_be_active" ] essential_modules["ax_utilities_class"].find_active_window.return_value = new_window_found input_event_manager._determine_keyboard_event_click_count = test_context.Mock( return_value=1, ) result = input_event_manager.process_keyboard_event( mock_device, case["pressed"], case["keycode"], case["keysym"], case["modifiers"], case["text"], ) assert result is True assert isinstance( input_event_manager._last_input_event, essential_modules["cthulhu.input_event"].KeyboardEvent, ) input_event_manager._last_input_event.process.assert_called_once() def test_process_keyboard_event_duplicate(self, test_context: CthulhuTestContext) -> None: """Test InputEventManager.process_keyboard_event with duplicate event.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_device = test_context.Mock() keyboard_event_instance = essential_modules["cthulhu.input_event"].KeyboardEvent( True, 65, 97, 0, "a", ) input_event_manager._last_input_event = keyboard_event_instance result = input_event_manager.process_keyboard_event(mock_device, True, 65, 97, 0, "a") assert result is False essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "keyboard_true", "method_name": "last_event_was_keyboard", "event_class_name": "KeyboardEvent", "expected_result": True, }, { "id": "keyboard_false", "method_name": "last_event_was_keyboard", "event_class_name": "MouseButtonEvent", "expected_result": False, }, { "id": "mouse_button_true", "method_name": "last_event_was_mouse_button", "event_class_name": "MouseButtonEvent", "expected_result": True, }, { "id": "mouse_button_false", "method_name": "last_event_was_mouse_button", "event_class_name": "KeyboardEvent", "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_basic_event_types( self, test_context: CthulhuTestContext, case: dict, ) -> None: """Test InputEventManager basic event type detection methods.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) if case["event_class_name"] == "KeyboardEvent": event_class = essential_modules["cthulhu.input_event"].KeyboardEvent mock_event = event_class() elif case["event_class_name"] == "MouseButtonEvent": event_class = essential_modules["cthulhu.input_event"].MouseButtonEvent mock_event = event_class() else: mock_event = test_context.Mock() input_event_manager._last_input_event = mock_event method = getattr(input_event_manager, case["method_name"]) result = method() assert result is case["expected_result"] @pytest.mark.parametrize( "case", [ { "id": "event1_none", "event1": None, "event2": "mock_event", "event1_pressed": None, "event2_pressed": None, "same_id": None, "same_hw_code": None, "same_keyval": None, "event1_modifier": None, "same_modifiers": None, "expected_result": False, }, { "id": "event2_none", "event1": "mock_event", "event2": None, "event1_pressed": None, "event2_pressed": None, "same_id": None, "same_hw_code": None, "same_keyval": None, "event1_modifier": None, "same_modifiers": None, "expected_result": False, }, { "id": "release_for_press", "event1": "mock_event1", "event2": "mock_event2", "event1_pressed": False, "event2_pressed": True, "same_id": True, "same_hw_code": True, "same_keyval": True, "event1_modifier": False, "same_modifiers": True, "expected_result": True, }, { "id": "both_pressed", "event1": "mock_event1", "event2": "mock_event2", "event1_pressed": True, "event2_pressed": True, "same_id": True, "same_hw_code": True, "same_keyval": True, "event1_modifier": False, "same_modifiers": True, "expected_result": False, }, { "id": "both_released", "event1": "mock_event1", "event2": "mock_event2", "event1_pressed": False, "event2_pressed": False, "same_id": True, "same_hw_code": True, "same_keyval": True, "event1_modifier": False, "same_modifiers": True, "expected_result": False, }, { "id": "different_id", "event1": "mock_event1", "event2": "mock_event2", "event1_pressed": False, "event2_pressed": True, "same_id": False, "same_hw_code": True, "same_keyval": True, "event1_modifier": False, "same_modifiers": True, "expected_result": False, }, { "id": "modifier_key_ignore_modifiers", "event1": "mock_event1", "event2": "mock_event2", "event1_pressed": False, "event2_pressed": True, "same_id": True, "same_hw_code": True, "same_keyval": True, "event1_modifier": True, "same_modifiers": True, "expected_result": True, }, { "id": "modifier_key_different_modifiers", "event1": "mock_event1", "event2": "mock_event2", "event1_pressed": False, "event2_pressed": True, "same_id": True, "same_hw_code": True, "same_keyval": True, "event1_modifier": True, "same_modifiers": False, "expected_result": True, }, { "id": "non_modifier_different_modifiers", "event1": "mock_event1", "event2": "mock_event2", "event1_pressed": False, "event2_pressed": True, "same_id": True, "same_hw_code": True, "same_keyval": True, "event1_modifier": False, "same_modifiers": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_is_release_for( self, test_context, case: dict, ) -> None: """Test InputEventManager.is_release_for.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) event1 = case["event1"] event2 = case["event2"] if event1 in ("mock_event", "mock_event1"): event1 = test_context.Mock() if event2 in ("mock_event", "mock_event2"): event2 = test_context.Mock() if event1 is not None and event2 is not None: keyboard_event_class = essential_modules["cthulhu.input_event"].KeyboardEvent event1 = keyboard_event_class() event2 = keyboard_event_class() event1.is_pressed_key.return_value = case["event1_pressed"] event2.is_pressed_key.return_value = case["event2_pressed"] event1.id = "test_id" if case["same_id"] else "other_id" event2.id = "test_id" event1.hw_code = 42 if case["same_hw_code"] else 43 event2.hw_code = 42 event1.keyval_name = "a" if case["same_keyval"] else "b" event2.keyval_name = "a" event1.is_modifier_key.return_value = case["event1_modifier"] event1.modifiers = 4 if case["same_modifiers"] else 8 event2.modifiers = 4 event1.as_single_line_string.return_value = "event1" event2.as_single_line_string.return_value = "event2" result = input_event_manager.is_release_for(event1, event2) assert result == case["expected_result"] def test_last_event_equals_or_is_release_for_event_no_last_event( self, test_context: CthulhuTestContext, ) -> None: """Test InputEventManager.last_event_equals_or_is_release_for_event with no last event.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) mock_event = test_context.Mock() result = input_event_manager.last_event_equals_or_is_release_for_event(mock_event) assert result is False def test_last_event_equals_or_is_release_for_event_equal( self, test_context: CthulhuTestContext, ) -> None: """Test InputEventManager.last_event_equals_or_is_release_for_event with equal events.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) mock_event = test_context.Mock() input_event_manager._last_non_modifier_key_event = mock_event input_event_manager.last_event_was_keyboard = test_context.Mock(return_value=True) result = input_event_manager.last_event_equals_or_is_release_for_event(mock_event) assert result is True def test_last_event_equals_or_is_release_for_event_is_release( self, test_context: CthulhuTestContext, ) -> None: """Test InputEventManager.last_event_equals_or_is_release_for_event with release event.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) mock_event = test_context.Mock() mock_last_event = test_context.Mock() input_event_manager._last_non_modifier_key_event = mock_last_event input_event_manager.last_event_was_keyboard = test_context.Mock(return_value=True) input_event_manager.is_release_for = test_context.Mock(return_value=True) result = input_event_manager.last_event_equals_or_is_release_for_event(mock_event) assert result is True input_event_manager.is_release_for.assert_called_once_with(mock_last_event, mock_event) @pytest.mark.parametrize( "case", [ { "id": "no_last_non_modifier", "has_last_non_modifier": False, "last_event_keyboard": True, "expected_key": "", "expected_modifiers": 0, }, { "id": "last_not_keyboard", "has_last_non_modifier": True, "last_event_keyboard": False, "expected_key": "", "expected_modifiers": 0, }, { "id": "has_both", "has_last_non_modifier": True, "last_event_keyboard": True, "expected_key": "a", "expected_modifiers": 4, }, ], ids=lambda case: case["id"], ) def test_last_key_and_modifiers( self, test_context, case: dict, ) -> None: """Test InputEventManager._last_key_and_modifiers.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) if case["has_last_non_modifier"]: mock_last_non_modifier = test_context.Mock() mock_last_non_modifier.keyval_name = "a" input_event_manager._last_non_modifier_key_event = mock_last_non_modifier else: input_event_manager._last_non_modifier_key_event = None if case["last_event_keyboard"]: mock_last_event = test_context.Mock() mock_last_event.modifiers = 4 input_event_manager._last_input_event = mock_last_event input_event_manager.last_event_was_keyboard = test_context.Mock( return_value=case["last_event_keyboard"], ) result = input_event_manager._last_key_and_modifiers() assert result == (case["expected_key"], case["expected_modifiers"]) @pytest.mark.parametrize( "case", [ { "id": "command_true", "method_name": "last_event_was_command", "key_modifiers": ("a", 1 << 2), "expected_result": True, "expects_debug": True, }, { "id": "command_false", "method_name": "last_event_was_command", "key_modifiers": ("a", 0), "expected_result": False, "expects_debug": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_command( self, test_context: CthulhuTestContext, case: dict, ) -> None: """Test InputEventManager command detection methods.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=case["key_modifiers"], ) method = getattr(input_event_manager, case["method_name"]) result = method() assert result is case["expected_result"] if case["expects_debug"]: essential_modules["cthulhu.debug"].print_message.assert_called() else: essential_modules["cthulhu.debug"].print_message.assert_not_called() @pytest.mark.parametrize( "case", [ { "id": "empty_key_string", "key_string": "", "has_shortcut": False, "expected_result": False, }, { "id": "matching_shortcut", "key_string": "f", "has_shortcut": True, "expected_result": True, }, { "id": "no_matching_shortcut", "key_string": "f", "has_shortcut": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_shortcut_for( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_shortcut_for.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_obj = test_context.Mock() input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], 0), ) ax_utilities_class = essential_modules["cthulhu.ax_utilities"].AXUtilities ax_utilities_class.has_matching_shortcut.return_value = case["has_shortcut"] result = input_event_manager.last_event_was_shortcut_for(mock_obj) assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_tokens.assert_called() @pytest.mark.parametrize( "case", [ { "id": "printable_key_true", "is_keyboard": True, "is_printable": True, "expected_result": True, "expects_debug": True, }, { "id": "not_keyboard", "is_keyboard": False, "is_printable": None, "expected_result": False, "expects_debug": False, }, { "id": "not_printable", "is_keyboard": True, "is_printable": False, "expected_result": False, "expects_debug": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_printable_key( self, test_context: CthulhuTestContext, case: dict, ) -> None: """Test InputEventManager.last_event_was_printable_key with various scenarios.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager.last_event_was_keyboard = test_context.Mock( return_value=case["is_keyboard"], ) if case["is_keyboard"] and case["is_printable"] is not None: mock_last_event = test_context.Mock() mock_last_event.is_printable_key.return_value = case["is_printable"] input_event_manager._last_input_event = mock_last_event result = input_event_manager.last_event_was_printable_key() assert result is case["expected_result"] if case["expects_debug"]: essential_modules["cthulhu.debug"].print_message.assert_called() else: essential_modules["cthulhu.debug"].print_message.assert_not_called() def test_last_event_was_caret_navigation(self, test_context: CthulhuTestContext) -> None: """Test InputEventManager.last_event_was_caret_navigation.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager.last_event_was_character_navigation = test_context.Mock( return_value=False, ) input_event_manager.last_event_was_word_navigation = test_context.Mock(return_value=True) input_event_manager.last_event_was_line_navigation = test_context.Mock(return_value=False) input_event_manager.last_event_was_line_boundary_navigation = test_context.Mock( return_value=False, ) input_event_manager.last_event_was_file_boundary_navigation = test_context.Mock( return_value=False, ) input_event_manager.last_event_was_page_navigation = test_context.Mock(return_value=False) result = input_event_manager.last_event_was_caret_navigation() assert result is True @pytest.mark.parametrize( "case", [ { "id": "left_with_shift", "key_string": "Left", "modifiers": 1 << 0, "expected_result": True, }, { "id": "right_with_shift", "key_string": "Right", "modifiers": 1 << 0, "expected_result": True, }, { "id": "up_with_shift", "key_string": "Up", "modifiers": 1 << 0, "expected_result": True, }, { "id": "down_with_shift", "key_string": "Down", "modifiers": 1 << 0, "expected_result": True, }, { "id": "left_without_shift", "key_string": "Left", "modifiers": 0, "expected_result": False, }, { "id": "non_arrow_with_shift", "key_string": "a", "modifiers": 1 << 0, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_caret_selection( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_caret_selection.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_caret_selection() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ {"id": "up_without_shift", "key_string": "Up", "modifiers": 0, "expected_result": True}, { "id": "left_without_shift", "key_string": "Left", "modifiers": 0, "expected_result": True, }, { "id": "down_without_shift", "key_string": "Down", "modifiers": 0, "expected_result": False, }, { "id": "right_without_shift", "key_string": "Right", "modifiers": 0, "expected_result": False, }, { "id": "up_with_shift", "key_string": "Up", "modifiers": 1 << 0, "expected_result": False, }, {"id": "non_arrow", "key_string": "a", "modifiers": 0, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_backward_caret_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_backward_caret_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_backward_caret_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "down_without_shift", "key_string": "Down", "modifiers": 0, "expected_result": True, }, { "id": "right_without_shift", "key_string": "Right", "modifiers": 0, "expected_result": True, }, { "id": "up_without_shift", "key_string": "Up", "modifiers": 0, "expected_result": False, }, { "id": "left_without_shift", "key_string": "Left", "modifiers": 0, "expected_result": False, }, { "id": "down_with_shift", "key_string": "Down", "modifiers": 1 << 0, "expected_result": False, }, {"id": "non_arrow", "key_string": "a", "modifiers": 0, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_forward_caret_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_forward_caret_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_forward_caret_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "down_with_shift", "key_string": "Down", "modifiers": 1 << 0, "expected_result": True, }, { "id": "right_with_shift", "key_string": "Right", "modifiers": 1 << 0, "expected_result": True, }, { "id": "up_with_shift", "key_string": "Up", "modifiers": 1 << 0, "expected_result": False, }, { "id": "left_with_shift", "key_string": "Left", "modifiers": 1 << 0, "expected_result": False, }, { "id": "down_without_shift", "key_string": "Down", "modifiers": 0, "expected_result": False, }, {"id": "non_arrow", "key_string": "a", "modifiers": 1 << 0, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_forward_caret_selection( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_forward_caret_selection.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_forward_caret_selection() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "left_no_modifiers", "key_string": "Left", "modifiers": 0, "expected_result": True, }, { "id": "right_no_modifiers", "key_string": "Right", "modifiers": 0, "expected_result": True, }, { "id": "left_with_ctrl", "key_string": "Left", "modifiers": 1 << 2, "expected_result": False, }, { "id": "right_with_alt", "key_string": "Right", "modifiers": 1 << 3, "expected_result": False, }, { "id": "non_horizontal_arrow", "key_string": "Up", "modifiers": 0, "expected_result": False, }, {"id": "non_arrow", "key_string": "a", "modifiers": 0, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_character_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_character_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_character_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "left_with_ctrl", "key_string": "Left", "modifiers": 1 << 2, "expected_result": True, }, { "id": "right_with_ctrl", "key_string": "Right", "modifiers": 1 << 2, "expected_result": True, }, {"id": "left_no_ctrl", "key_string": "Left", "modifiers": 0, "expected_result": False}, { "id": "right_no_ctrl", "key_string": "Right", "modifiers": 0, "expected_result": False, }, { "id": "non_horizontal_arrow", "key_string": "Up", "modifiers": 1 << 2, "expected_result": False, }, {"id": "non_arrow", "key_string": "a", "modifiers": 1 << 2, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_word_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_word_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_word_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "left_with_ctrl", "key_string": "Left", "modifiers": 1 << 2, "expected_result": True, }, { "id": "right_with_ctrl", "key_string": "Right", "modifiers": 1 << 2, "expected_result": False, }, {"id": "left_no_ctrl", "key_string": "Left", "modifiers": 0, "expected_result": False}, {"id": "non_arrow", "key_string": "a", "modifiers": 1 << 2, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_previous_word_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_previous_word_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_previous_word_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "right_with_ctrl", "key_string": "Right", "modifiers": 1 << 2, "expected_result": True, }, { "id": "left_with_ctrl", "key_string": "Left", "modifiers": 1 << 2, "expected_result": False, }, { "id": "right_no_ctrl", "key_string": "Right", "modifiers": 0, "expected_result": False, }, {"id": "non_arrow", "key_string": "a", "modifiers": 1 << 2, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_next_word_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_next_word_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_next_word_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "up_multiline_not_controlled", "key_string": "Up", "modifiers": 0, "is_single_line": False, "is_widget_controlled": False, "expected_result": True, }, { "id": "down_multiline_not_controlled", "key_string": "Down", "modifiers": 0, "is_single_line": False, "is_widget_controlled": False, "expected_result": True, }, { "id": "up_with_ctrl", "key_string": "Up", "modifiers": 1 << 2, "is_single_line": False, "is_widget_controlled": False, "expected_result": False, }, { "id": "up_single_line", "key_string": "Up", "modifiers": 0, "is_single_line": True, "is_widget_controlled": False, "expected_result": False, }, { "id": "up_widget_controlled", "key_string": "Up", "modifiers": 0, "is_single_line": False, "is_widget_controlled": True, "expected_result": False, }, { "id": "non_vertical_arrow", "key_string": "Left", "modifiers": 0, "is_single_line": False, "is_widget_controlled": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_line_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_line_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) mock_focus = test_context.Mock() essential_modules["cthulhu.focus_manager"].get_locus_of_focus.return_value = mock_focus essential_modules["ax_utilities_class"].is_single_line.return_value = case["is_single_line"] essential_modules[ "ax_utilities_class" ].is_widget_controlled_by_line_navigation.return_value = case["is_widget_controlled"] result = input_event_manager.last_event_was_line_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "up_with_ctrl_no_shift", "key_string": "Up", "modifiers": (1 << 2), "expected_result": True, }, { "id": "down_with_ctrl_no_shift", "key_string": "Down", "modifiers": (1 << 2), "expected_result": True, }, { "id": "up_with_ctrl_and_shift", "key_string": "Up", "modifiers": (1 << 2) | (1 << 0), "expected_result": False, }, {"id": "up_no_ctrl", "key_string": "Up", "modifiers": 0, "expected_result": False}, { "id": "non_vertical_arrow", "key_string": "Left", "modifiers": 1 << 2, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_paragraph_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_paragraph_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_paragraph_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ {"id": "home_no_ctrl", "key_string": "Home", "modifiers": 0, "expected_result": True}, {"id": "end_no_ctrl", "key_string": "End", "modifiers": 0, "expected_result": True}, { "id": "home_with_ctrl", "key_string": "Home", "modifiers": 1 << 2, "expected_result": False, }, { "id": "end_with_ctrl", "key_string": "End", "modifiers": 1 << 2, "expected_result": False, }, { "id": "non_boundary_key", "key_string": "Left", "modifiers": 0, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_line_boundary_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_line_boundary_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_line_boundary_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "home_with_ctrl", "key_string": "Home", "modifiers": 1 << 2, "expected_result": True, }, { "id": "end_with_ctrl", "key_string": "End", "modifiers": 1 << 2, "expected_result": True, }, {"id": "home_no_ctrl", "key_string": "Home", "modifiers": 0, "expected_result": False}, {"id": "end_no_ctrl", "key_string": "End", "modifiers": 0, "expected_result": False}, { "id": "non_boundary_key", "key_string": "Left", "modifiers": 1 << 2, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_file_boundary_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_file_boundary_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_file_boundary_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "page_up_multiline_not_controlled", "key_string": "Page_Up", "modifiers": 0, "is_single_line": False, "is_widget_controlled": False, "expected_result": True, }, { "id": "page_down_multiline_not_controlled", "key_string": "Page_Down", "modifiers": 0, "is_single_line": False, "is_widget_controlled": False, "expected_result": True, }, { "id": "page_up_with_ctrl", "key_string": "Page_Up", "modifiers": 1 << 2, "is_single_line": False, "is_widget_controlled": False, "expected_result": False, }, { "id": "page_up_single_line", "key_string": "Page_Up", "modifiers": 0, "is_single_line": True, "is_widget_controlled": False, "expected_result": False, }, { "id": "page_up_widget_controlled", "key_string": "Page_Up", "modifiers": 0, "is_single_line": False, "is_widget_controlled": True, "expected_result": False, }, { "id": "non_page_key", "key_string": "Left", "modifiers": 0, "is_single_line": False, "is_widget_controlled": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_page_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_page_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) mock_focus = test_context.Mock() essential_modules["cthulhu.focus_manager"].get_locus_of_focus.return_value = mock_focus essential_modules["ax_utilities_class"].is_single_line.return_value = case["is_single_line"] essential_modules[ "ax_utilities_class" ].is_widget_controlled_by_line_navigation.return_value = case["is_widget_controlled"] result = input_event_manager.last_event_was_page_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "numeric_with_alt", "key_string": "1", "modifiers": 1 << 3, "expected_result": True, }, { "id": "numeric_2_with_alt", "key_string": "2", "modifiers": 1 << 3, "expected_result": True, }, { "id": "page_up_with_ctrl", "key_string": "Page_Up", "modifiers": 1 << 2, "expected_result": True, }, { "id": "page_down_with_ctrl", "key_string": "Page_Down", "modifiers": 1 << 2, "expected_result": True, }, {"id": "numeric_no_alt", "key_string": "1", "modifiers": 0, "expected_result": False}, { "id": "page_up_no_ctrl", "key_string": "Page_Up", "modifiers": 0, "expected_result": False, }, { "id": "non_numeric_non_page", "key_string": "a", "modifiers": 1 << 3, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_page_switch( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_page_switch.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_page_switch() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "tab_no_modifiers", "key_string": "Tab", "modifiers": 0, "expected_result": True, }, { "id": "shift_tab_no_modifiers", "key_string": "ISO_Left_Tab", "modifiers": 0, "expected_result": True, }, { "id": "tab_with_ctrl", "key_string": "Tab", "modifiers": 1 << 2, "expected_result": False, }, { "id": "tab_with_alt", "key_string": "Tab", "modifiers": 1 << 3, "expected_result": False, }, {"id": "non_tab_key", "key_string": "a", "modifiers": 0, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_tab_navigation( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_tab_navigation.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_tab_navigation() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "not_table_header", "is_table_header": False, "is_mouse_button": True, "is_primary_click": True, "is_keyboard": False, "is_return_or_space": False, "expected_result": False, }, { "id": "table_header_mouse_primary_click", "is_table_header": True, "is_mouse_button": True, "is_primary_click": True, "is_keyboard": False, "is_return_or_space": False, "expected_result": True, }, { "id": "table_header_mouse_not_primary", "is_table_header": True, "is_mouse_button": True, "is_primary_click": False, "is_keyboard": False, "is_return_or_space": False, "expected_result": False, }, { "id": "table_header_keyboard_return_space", "is_table_header": True, "is_mouse_button": False, "is_primary_click": False, "is_keyboard": True, "is_return_or_space": True, "expected_result": True, }, { "id": "table_header_keyboard_not_return_space", "is_table_header": True, "is_mouse_button": False, "is_primary_click": False, "is_keyboard": True, "is_return_or_space": False, "expected_result": False, }, { "id": "table_header_neither_mouse_nor_keyboard", "is_table_header": True, "is_mouse_button": False, "is_primary_click": False, "is_keyboard": False, "is_return_or_space": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_table_sort( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_table_sort.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_focus = test_context.Mock() essential_modules["cthulhu.focus_manager"].get_locus_of_focus.return_value = mock_focus essential_modules["ax_utilities_class"].is_table_header.return_value = case[ "is_table_header" ] input_event_manager.last_event_was_mouse_button = test_context.Mock( return_value=case["is_mouse_button"], ) input_event_manager.last_event_was_primary_click = test_context.Mock( return_value=case["is_primary_click"], ) input_event_manager.last_event_was_keyboard = test_context.Mock( return_value=case["is_keyboard"], ) input_event_manager.last_event_was_return_or_space = test_context.Mock( return_value=case["is_return_or_space"], ) result = input_event_manager.last_event_was_table_sort() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "left_no_modifiers", "key_string": "Left", "modifiers": 0, "expected_result": True, }, { "id": "right_no_modifiers", "key_string": "Right", "modifiers": 0, "expected_result": True, }, {"id": "up_no_modifiers", "key_string": "Up", "modifiers": 0, "expected_result": True}, { "id": "down_no_modifiers", "key_string": "Down", "modifiers": 0, "expected_result": True, }, { "id": "left_with_ctrl", "key_string": "Left", "modifiers": 1 << 2, "expected_result": False, }, { "id": "left_with_shift", "key_string": "Left", "modifiers": 1 << 0, "expected_result": False, }, { "id": "left_with_alt", "key_string": "Left", "modifiers": 1 << 3, "expected_result": False, }, { "id": "left_with_cthulhu_modifier", "key_string": "Left", "modifiers": 1 << 8, "expected_result": False, }, {"id": "non_arrow_key", "key_string": "a", "modifiers": 0, "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_unmodified_arrow( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_unmodified_arrow.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], case["modifiers"]), ) result = input_event_manager.last_event_was_unmodified_arrow() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ { "id": "has_alt_modifier", "modifiers": 1 << 3, "expected_result": 8, }, # Returns bitwise result, not boolean {"id": "no_alt_modifier", "modifiers": 0, "expected_result": 0}, { "id": "alt_and_ctrl_modifiers", "modifiers": (1 << 3) | (1 << 2), "expected_result": 8, }, # Alt bit is still 8 ], ids=lambda case: case["id"], ) def test_last_event_was_alt_modified(self, test_context, case: dict) -> None: """Test InputEventManager.last_event_was_alt_modified.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=("a", case["modifiers"]), ) result = input_event_manager.last_event_was_alt_modified() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ {"id": "backspace_key", "key_string": "BackSpace", "expected_result": True}, {"id": "delete_key", "key_string": "Delete", "expected_result": False}, {"id": "regular_key", "key_string": "a", "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_backspace(self, test_context, case: dict) -> None: """Test InputEventManager.last_event_was_backspace.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], 0), ) result = input_event_manager.last_event_was_backspace() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ {"id": "down_key", "key_string": "Down", "expected_result": True}, {"id": "up_key", "key_string": "Up", "expected_result": False}, {"id": "regular_key", "key_string": "a", "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_down(self, test_context, case: dict) -> None: """Test InputEventManager.last_event_was_down.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], 0), ) result = input_event_manager.last_event_was_down() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ {"id": "f1_key", "key_string": "F1", "expected_result": True}, {"id": "f2_key", "key_string": "F2", "expected_result": False}, {"id": "regular_key", "key_string": "a", "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_f1(self, test_context, case: dict) -> None: """Test InputEventManager.last_event_was_f1.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], 0), ) result = input_event_manager.last_event_was_f1() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ {"id": "left_key", "key_string": "Left", "expected_result": True}, {"id": "right_key", "key_string": "Right", "expected_result": False}, {"id": "regular_key", "key_string": "a", "expected_result": False}, ], ids=lambda case: case["id"], ) def test_last_event_was_left(self, test_context, case: dict) -> None: """Test InputEventManager.last_event_was_left.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], 0), ) result = input_event_manager.last_event_was_left() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ { "id": "left_or_right_left_key", "method_name": "last_event_was_left_or_right", "key_string": "Left", "expected_result": True, }, { "id": "left_or_right_right_key", "method_name": "last_event_was_left_or_right", "key_string": "Right", "expected_result": True, }, { "id": "left_or_right_up_key", "method_name": "last_event_was_left_or_right", "key_string": "Up", "expected_result": False, }, { "id": "page_up_or_down_page_up", "method_name": "last_event_was_page_up_or_page_down", "key_string": "Page_Up", "expected_result": True, }, { "id": "page_up_or_down_page_down", "method_name": "last_event_was_page_up_or_page_down", "key_string": "Page_Down", "expected_result": True, }, { "id": "page_up_or_down_up_key", "method_name": "last_event_was_page_up_or_page_down", "key_string": "Up", "expected_result": False, }, { "id": "right_right_key", "method_name": "last_event_was_right", "key_string": "Right", "expected_result": True, }, { "id": "right_left_key", "method_name": "last_event_was_right", "key_string": "Left", "expected_result": False, }, { "id": "return_return_key", "method_name": "last_event_was_return", "key_string": "Return", "expected_result": True, }, { "id": "return_enter_key", "method_name": "last_event_was_return", "key_string": "Enter", "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_single_key_detection(self, test_context, case: dict) -> None: """Test InputEventManager single key detection methods.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], 0), ) method = getattr(input_event_manager, case["method_name"]) result = method() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ { "id": "return_or_space_return", "method_name": "last_event_was_return_or_space", "key_string": "Return", "expected_result": True, }, { "id": "return_or_space_space_key", "method_name": "last_event_was_return_or_space", "key_string": "space", "expected_result": True, }, { "id": "return_or_space_space_char", "method_name": "last_event_was_return_or_space", "key_string": " ", "expected_result": True, }, { "id": "return_or_space_enter", "method_name": "last_event_was_return_or_space", "key_string": "Enter", "expected_result": False, }, { "id": "return_tab_space_return", "method_name": "last_event_was_return_tab_or_space", "key_string": "Return", "expected_result": True, }, { "id": "return_tab_space_tab", "method_name": "last_event_was_return_tab_or_space", "key_string": "Tab", "expected_result": True, }, { "id": "return_tab_space_space_key", "method_name": "last_event_was_return_tab_or_space", "key_string": "space", "expected_result": True, }, { "id": "return_tab_space_space_char", "method_name": "last_event_was_return_tab_or_space", "key_string": " ", "expected_result": True, }, { "id": "return_tab_space_enter", "method_name": "last_event_was_return_tab_or_space", "key_string": "Enter", "expected_result": False, }, { "id": "space_space_character", "method_name": "last_event_was_space", "key_string": " ", "expected_result": True, }, { "id": "space_space_key", "method_name": "last_event_was_space", "key_string": "space", "expected_result": True, }, { "id": "space_return_key", "method_name": "last_event_was_space", "key_string": "Return", "expected_result": False, }, { "id": "tab_tab_key", "method_name": "last_event_was_tab", "key_string": "Tab", "expected_result": True, }, { "id": "tab_shift_tab_key", "method_name": "last_event_was_tab", "key_string": "ISO_Left_Tab", "expected_result": False, }, { "id": "up_up_key", "method_name": "last_event_was_up", "key_string": "Up", "expected_result": True, }, { "id": "up_down_key", "method_name": "last_event_was_up", "key_string": "Down", "expected_result": False, }, { "id": "up_or_down_up_key", "method_name": "last_event_was_up_or_down", "key_string": "Up", "expected_result": True, }, { "id": "up_or_down_down_key", "method_name": "last_event_was_up_or_down", "key_string": "Down", "expected_result": True, }, { "id": "up_or_down_left_key", "method_name": "last_event_was_up_or_down", "key_string": "Left", "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_composite_key_detection(self, test_context, case: dict) -> None: """Test InputEventManager composite key detection methods.""" input_event_manager, _essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["key_string"], 0), ) method = getattr(input_event_manager, case["method_name"]) result = method() assert result == case["expected_result"] @pytest.mark.parametrize( "case", [ { "id": "delete_delete_key", "method_name": "last_event_was_delete", "keynames": ["Delete"], "modifiers": 0, "expected_result": True, }, { "id": "delete_keypad_delete", "method_name": "last_event_was_delete", "keynames": ["KP_Delete"], "modifiers": 0, "expected_result": True, }, { "id": "delete_ctrl_d", "method_name": "last_event_was_delete", "keynames": ["d"], "modifiers": 1 << 2, "expected_result": True, }, { "id": "delete_d_without_ctrl", "method_name": "last_event_was_delete", "keynames": ["d"], "modifiers": 0, "expected_result": False, }, { "id": "delete_unknown_key", "method_name": "last_event_was_delete", "keynames": ["x"], "modifiers": 0, "expected_result": False, }, { "id": "cut_ctrl_x", "method_name": "last_event_was_cut", "keynames": ["x"], "modifiers": (1 << 2), "expected_result": True, }, { "id": "cut_ctrl_shift_x", "method_name": "last_event_was_cut", "keynames": ["x"], "modifiers": (1 << 2) | (1 << 0), "expected_result": False, }, { "id": "cut_x_without_ctrl", "method_name": "last_event_was_cut", "keynames": ["x"], "modifiers": 0, "expected_result": False, }, { "id": "cut_ctrl_y", "method_name": "last_event_was_cut", "keynames": ["y"], "modifiers": 1 << 2, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_simple_editing_actions( self, test_context, case: dict, ) -> None: """Test InputEventManager simple editing action methods.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["keynames"][0] if case["keynames"] else "", case["modifiers"]), ) method = getattr(input_event_manager, case["method_name"]) result = method() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "copy_ctrl_c_non_terminal", "method_name": "last_event_was_copy", "keynames": ["c"], "modifiers": (1 << 2), "is_terminal": False, "expected_result": True, }, { "id": "copy_ctrl_shift_c_terminal", "method_name": "last_event_was_copy", "keynames": ["c"], "modifiers": (1 << 2) | (1 << 0), "is_terminal": True, "expected_result": True, }, { "id": "copy_ctrl_shift_c_non_terminal", "method_name": "last_event_was_copy", "keynames": ["c"], "modifiers": (1 << 2) | (1 << 0), "is_terminal": False, "expected_result": False, }, { "id": "copy_ctrl_c_terminal", "method_name": "last_event_was_copy", "keynames": ["c"], "modifiers": (1 << 2), "is_terminal": True, "expected_result": False, }, { "id": "copy_c_without_ctrl", "method_name": "last_event_was_copy", "keynames": ["c"], "modifiers": 0, "is_terminal": False, "expected_result": False, }, { "id": "paste_ctrl_v_non_terminal", "method_name": "last_event_was_paste", "keynames": ["v"], "modifiers": (1 << 2), "is_terminal": False, "expected_result": True, }, { "id": "paste_ctrl_shift_v_terminal", "method_name": "last_event_was_paste", "keynames": ["v"], "modifiers": (1 << 2) | (1 << 0), "is_terminal": True, "expected_result": True, }, { "id": "paste_ctrl_shift_v_non_terminal", "method_name": "last_event_was_paste", "keynames": ["v"], "modifiers": (1 << 2) | (1 << 0), "is_terminal": False, "expected_result": False, }, { "id": "paste_ctrl_v_terminal", "method_name": "last_event_was_paste", "keynames": ["v"], "modifiers": (1 << 2), "is_terminal": True, "expected_result": False, }, { "id": "paste_v_without_ctrl", "method_name": "last_event_was_paste", "keynames": ["v"], "modifiers": 0, "is_terminal": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_terminal_aware_editing_actions( self, test_context, case: dict, ) -> None: """Test InputEventManager terminal-aware editing action methods.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_object = test_context.Mock() mock_last_event = test_context.Mock() mock_last_event.get_object.return_value = mock_object input_event_manager._last_input_event = mock_last_event input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["keynames"][0] if case["keynames"] else "", case["modifiers"]), ) essential_modules["ax_utilities_class"].is_terminal.return_value = case["is_terminal"] method = getattr(input_event_manager, case["method_name"]) result = method() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "undo_ctrl_z", "method_name": "last_event_was_undo", "keynames": ["z"], "modifiers": (1 << 2), "expected_result": True, }, { "id": "undo_ctrl_shift_z", "method_name": "last_event_was_undo", "keynames": ["z"], "modifiers": (1 << 2) | (1 << 0), "expected_result": False, }, { "id": "undo_z_without_ctrl", "method_name": "last_event_was_undo", "keynames": ["z"], "modifiers": 0, "expected_result": False, }, { "id": "undo_ctrl_y", "method_name": "last_event_was_undo", "keynames": ["y"], "modifiers": 1 << 2, "expected_result": False, }, { "id": "redo_ctrl_shift_z", "method_name": "last_event_was_redo", "keynames": ["z"], "modifiers": (1 << 2) | (1 << 0), "expected_result": True, }, { "id": "redo_ctrl_y_libreoffice", "method_name": "last_event_was_redo", "keynames": ["y"], "modifiers": (1 << 2), "expected_result": True, }, { "id": "redo_ctrl_z", "method_name": "last_event_was_redo", "keynames": ["z"], "modifiers": (1 << 2), "expected_result": False, }, { "id": "redo_ctrl_shift_y", "method_name": "last_event_was_redo", "keynames": ["y"], "modifiers": (1 << 2) | (1 << 0), "expected_result": False, }, { "id": "redo_z_without_ctrl", "method_name": "last_event_was_redo", "keynames": ["z"], "modifiers": 0, "expected_result": False, }, { "id": "redo_unknown_key", "method_name": "last_event_was_redo", "keynames": ["x"], "modifiers": 1 << 2, "expected_result": False, }, { "id": "select_all_ctrl_a", "method_name": "last_event_was_select_all", "keynames": ["a"], "modifiers": (1 << 2), "expected_result": True, }, { "id": "select_all_ctrl_shift_a", "method_name": "last_event_was_select_all", "keynames": ["a"], "modifiers": (1 << 2) | (1 << 0), "expected_result": False, }, { "id": "select_all_a_without_ctrl", "method_name": "last_event_was_select_all", "keynames": ["a"], "modifiers": 0, "expected_result": False, }, { "id": "select_all_unknown_key", "method_name": "last_event_was_select_all", "keynames": ["x"], "modifiers": 1 << 2, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_undo_redo_select_editing_actions( self, test_context, case: dict, ) -> None: """Test InputEventManager undo/redo/select editing action methods.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager._last_key_and_modifiers = test_context.Mock( return_value=(case["keynames"][0] if case["keynames"] else "", case["modifiers"]), ) method = getattr(input_event_manager, case["method_name"]) result = method() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "primary_click_primary_click", "method_name": "last_event_was_primary_click", "is_mouse_button": True, "button": "1", "pressed": True, "expected_result": True, }, { "id": "primary_click_primary_release", "method_name": "last_event_was_primary_click", "is_mouse_button": True, "button": "1", "pressed": False, "expected_result": False, }, { "id": "primary_click_middle_click", "method_name": "last_event_was_primary_click", "is_mouse_button": True, "button": "2", "pressed": True, "expected_result": False, }, { "id": "primary_click_not_mouse_button", "method_name": "last_event_was_primary_click", "is_mouse_button": False, "button": "1", "pressed": True, "expected_result": False, }, { "id": "primary_release_primary_release", "method_name": "last_event_was_primary_release", "is_mouse_button": True, "button": "1", "pressed": False, "expected_result": True, }, { "id": "primary_release_primary_click", "method_name": "last_event_was_primary_release", "is_mouse_button": True, "button": "1", "pressed": True, "expected_result": False, }, { "id": "primary_release_middle_release", "method_name": "last_event_was_primary_release", "is_mouse_button": True, "button": "2", "pressed": False, "expected_result": False, }, { "id": "primary_release_not_mouse_button", "method_name": "last_event_was_primary_release", "is_mouse_button": False, "button": "1", "pressed": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_primary_mouse_button_actions( self, test_context, case: dict, ) -> None: """Test InputEventManager primary mouse button action methods.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_last_event = test_context.Mock() mock_last_event.button = case["button"] mock_last_event.pressed = case["pressed"] input_event_manager._last_input_event = mock_last_event input_event_manager.last_event_was_mouse_button = test_context.Mock( return_value=case["is_mouse_button"], ) method = getattr(input_event_manager, case["method_name"]) result = method() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "primary_button", "is_mouse_button": True, "button": "1", "expected_result": True, }, { "id": "middle_button", "is_mouse_button": True, "button": "2", "expected_result": False, }, { "id": "secondary_button", "is_mouse_button": True, "button": "3", "expected_result": False, }, { "id": "not_mouse_button", "is_mouse_button": False, "button": "1", "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_last_event_was_primary_click_or_release( self, test_context, case: dict, ) -> None: """Test InputEventManager.last_event_was_primary_click_or_release.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_last_event = test_context.Mock() mock_last_event.button = case["button"] input_event_manager._last_input_event = mock_last_event input_event_manager.last_event_was_mouse_button = test_context.Mock( return_value=case["is_mouse_button"], ) result = input_event_manager.last_event_was_primary_click_or_release() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() @pytest.mark.parametrize( "case", [ { "id": "middle_click_middle_click", "method_name": "last_event_was_middle_click", "is_mouse_button": True, "button": "2", "pressed": True, "expected_result": True, }, { "id": "middle_click_middle_release", "method_name": "last_event_was_middle_click", "is_mouse_button": True, "button": "2", "pressed": False, "expected_result": False, }, { "id": "middle_click_primary_click", "method_name": "last_event_was_middle_click", "is_mouse_button": True, "button": "1", "pressed": True, "expected_result": False, }, { "id": "middle_click_not_mouse_button", "method_name": "last_event_was_middle_click", "is_mouse_button": False, "button": "2", "pressed": True, "expected_result": False, }, { "id": "middle_release_middle_release", "method_name": "last_event_was_middle_release", "is_mouse_button": True, "button": "2", "pressed": False, "expected_result": True, }, { "id": "middle_release_middle_click", "method_name": "last_event_was_middle_release", "is_mouse_button": True, "button": "2", "pressed": True, "expected_result": False, }, { "id": "middle_release_primary_release", "method_name": "last_event_was_middle_release", "is_mouse_button": True, "button": "1", "pressed": False, "expected_result": False, }, { "id": "middle_release_not_mouse_button", "method_name": "last_event_was_middle_release", "is_mouse_button": False, "button": "2", "pressed": False, "expected_result": False, }, { "id": "secondary_click_secondary_click", "method_name": "last_event_was_secondary_click", "is_mouse_button": True, "button": "3", "pressed": True, "expected_result": True, }, { "id": "secondary_click_secondary_release", "method_name": "last_event_was_secondary_click", "is_mouse_button": True, "button": "3", "pressed": False, "expected_result": False, }, { "id": "secondary_click_primary_click", "method_name": "last_event_was_secondary_click", "is_mouse_button": True, "button": "1", "pressed": True, "expected_result": False, }, { "id": "secondary_click_not_mouse_button", "method_name": "last_event_was_secondary_click", "is_mouse_button": False, "button": "3", "pressed": True, "expected_result": False, }, { "id": "secondary_release_secondary_release", "method_name": "last_event_was_secondary_release", "is_mouse_button": True, "button": "3", "pressed": False, "expected_result": True, }, { "id": "secondary_release_secondary_click", "method_name": "last_event_was_secondary_release", "is_mouse_button": True, "button": "3", "pressed": True, "expected_result": False, }, { "id": "secondary_release_primary_release", "method_name": "last_event_was_secondary_release", "is_mouse_button": True, "button": "1", "pressed": False, "expected_result": False, }, { "id": "secondary_release_not_mouse_button", "method_name": "last_event_was_secondary_release", "is_mouse_button": False, "button": "3", "pressed": False, "expected_result": False, }, ], ids=lambda case: case["id"], ) def test_middle_secondary_mouse_button_actions( self, test_context, case: dict, ) -> None: """Test InputEventManager middle and secondary mouse button action methods.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) mock_last_event = test_context.Mock() mock_last_event.button = case["button"] mock_last_event.pressed = case["pressed"] input_event_manager._last_input_event = mock_last_event input_event_manager.last_event_was_mouse_button = test_context.Mock( return_value=case["is_mouse_button"], ) method = getattr(input_event_manager, case["method_name"]) result = method() assert result == case["expected_result"] if case["expected_result"]: essential_modules["cthulhu.debug"].print_message.assert_called() def test_get_manager(self, test_context: CthulhuTestContext) -> None: """Test get_manager function returns singleton.""" self._setup_input_event_manager(test_context) from cthulhu.input_event_manager import get_manager manager1 = get_manager() manager2 = get_manager() assert manager1 is manager2 def test_pause_key_watcher_with_debug_message(self, test_context: CthulhuTestContext) -> None: """Test InputEventManager.pause_key_watcher logs debug message.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) input_event_manager.pause_key_watcher(True, "Testing pause functionality") essential_modules["cthulhu.debug"].print_message.assert_called() assert input_event_manager._paused is True input_event_manager.pause_key_watcher(False, "Testing unpause functionality") assert input_event_manager._paused is False def test_determine_click_count_no_multi_click_bindings( self, test_context: CthulhuTestContext, ) -> None: """Test click count stays at 1 when no multi-click bindings exist for the key.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) # Mock command_manager to return False for has_multi_click_bindings command_manager_instance = test_context.Mock() command_manager_instance.has_multi_click_bindings = test_context.Mock(return_value=False) test_context.patch( "cthulhu.command_manager.get_manager", return_value=command_manager_instance, ) # Create first event (press) first_event = essential_modules["cthulhu.input_event"].KeyboardEvent( pressed=True, keycode=79, keysym=65429, modifiers=0, text="KP_Home", ) first_event.keyval_name = "KP_Home" first_event.time = 1000.0 first_event.is_pressed_key = test_context.Mock(return_value=True) first_event.is_modifier_key = test_context.Mock(return_value=False) first_event.get_click_count = test_context.Mock(return_value=1) first_event.get_object = test_context.Mock(return_value=None) # Set first event as last event input_event_manager._last_input_event = first_event input_event_manager._last_non_modifier_key_event = first_event # Create release event release_event = essential_modules["cthulhu.input_event"].KeyboardEvent( pressed=False, keycode=79, keysym=65429, modifiers=0, text="KP_Home", ) release_event.keyval_name = "KP_Home" release_event.time = 1000.1 release_event.is_pressed_key = test_context.Mock(return_value=False) release_event.is_modifier_key = test_context.Mock(return_value=False) release_event.get_click_count = test_context.Mock(return_value=1) release_event.get_object = test_context.Mock(return_value=None) # Set release as last event input_event_manager._last_input_event = release_event input_event_manager._last_non_modifier_key_event = release_event # Create second press event within doubleClickTimeout second_event = essential_modules["cthulhu.input_event"].KeyboardEvent( pressed=True, keycode=79, keysym=65429, modifiers=0, text="KP_Home", ) second_event.keyval_name = "KP_Home" second_event.id = 65429 # keyval for has_multi_click_bindings call second_event.hw_code = 79 # keycode second_event.modifiers = 0 second_event.time = 1000.3 # Within 0.5s timeout second_event.is_pressed_key = test_context.Mock(return_value=True) second_event.is_modifier_key = test_context.Mock(return_value=False) second_event.is_cthulhu_modifier = test_context.Mock(return_value=False) second_event.get_object = test_context.Mock(return_value=None) # Determine click count - should stay at 1 since no multi-click bindings result = input_event_manager._determine_keyboard_event_click_count(second_event) assert result == 1 command_manager_instance.has_multi_click_bindings.assert_called_with(65429, 79, 0) def test_determine_click_count_with_multi_click_bindings( self, test_context: CthulhuTestContext, ) -> None: """Test click count increments when multi-click bindings exist for the key.""" input_event_manager, essential_modules = self._setup_input_event_manager(test_context) # Mock command_manager to return True for has_multi_click_bindings command_manager_instance = test_context.Mock() command_manager_instance.has_multi_click_bindings = test_context.Mock(return_value=True) test_context.patch( "cthulhu.command_manager.get_manager", return_value=command_manager_instance, ) # Create first event (press) first_event = essential_modules["cthulhu.input_event"].KeyboardEvent( pressed=True, keycode=80, keysym=65431, modifiers=0, text="KP_Up", ) first_event.keyval_name = "KP_Up" first_event.time = 1000.0 first_event.is_pressed_key = test_context.Mock(return_value=True) first_event.is_modifier_key = test_context.Mock(return_value=False) first_event.get_click_count = test_context.Mock(return_value=1) first_event.get_object = test_context.Mock(return_value=None) # Set first event as last event input_event_manager._last_input_event = first_event input_event_manager._last_non_modifier_key_event = first_event # Create release event release_event = essential_modules["cthulhu.input_event"].KeyboardEvent( pressed=False, keycode=80, keysym=65431, modifiers=0, text="KP_Up", ) release_event.keyval_name = "KP_Up" release_event.time = 1000.1 release_event.is_pressed_key = test_context.Mock(return_value=False) release_event.is_modifier_key = test_context.Mock(return_value=False) release_event.get_click_count = test_context.Mock(return_value=1) release_event.get_object = test_context.Mock(return_value=None) # Set release as last event input_event_manager._last_input_event = release_event input_event_manager._last_non_modifier_key_event = release_event # Create second press event within doubleClickTimeout second_event = essential_modules["cthulhu.input_event"].KeyboardEvent( pressed=True, keycode=80, keysym=65431, modifiers=0, text="KP_Up", ) second_event.keyval_name = "KP_Up" second_event.id = 65431 # keyval for has_multi_click_bindings call second_event.hw_code = 80 # keycode second_event.modifiers = 0 second_event.time = 1000.3 # Within 0.5s timeout second_event.is_pressed_key = test_context.Mock(return_value=True) second_event.is_modifier_key = test_context.Mock(return_value=False) second_event.is_cthulhu_modifier = test_context.Mock(return_value=False) second_event.get_object = test_context.Mock(return_value=None) # Determine click count - should increment to 2 since multi-click bindings exist result = input_event_manager._determine_keyboard_event_click_count(second_event) assert result == 2 command_manager_instance.has_multi_click_bindings.assert_called_with(65431, 80, 0)