import importlib.machinery import importlib.util from pathlib import Path from unittest.mock import Mock import pytest from fenrirscreenreader.inputDriver.x11Driver import X from fenrirscreenreader.inputDriver.x11Driver import XK from fenrirscreenreader.inputDriver.x11Driver import driver as X11Driver def load_fenrir_entrypoint(): fenrir_path = Path(__file__).parents[2] / "src" / "fenrir" loader = importlib.machinery.SourceFileLoader( "fenrir_entrypoint_x11", str(fenrir_path) ) spec = importlib.util.spec_from_loader(loader.name, loader) module = importlib.util.module_from_spec(spec) loader.exec_module(module) return module @pytest.mark.unit def test_x11_mode_runs_in_foreground(): fenrir = load_fenrir_entrypoint() args = fenrir.create_argument_parser().parse_args(["-x"]) assert fenrir.should_run_foreground(args) is True @pytest.mark.unit def test_removed_emulated_pty_flag_is_rejected(): fenrir = load_fenrir_entrypoint() with pytest.raises(SystemExit): fenrir.create_argument_parser().parse_args(["-e"]) @pytest.mark.unit def test_removed_emulated_evdev_flag_is_rejected(): fenrir = load_fenrir_entrypoint() with pytest.raises(SystemExit): fenrir.create_argument_parser().parse_args(["-E"]) @pytest.mark.unit def test_x11_cli_accepts_window_id(): fenrir = load_fenrir_entrypoint() args = fenrir.create_argument_parser().parse_args( ["-x", "--x11-window-id", "0x123"] ) assert args.x11_window_id == "0x123" @pytest.mark.unit def test_x11_window_id_requires_x11_mode(): fenrir = load_fenrir_entrypoint() args = fenrir.create_argument_parser().parse_args( ["--x11-window-id", "0x123"] ) is_valid, error = fenrir.validate_arguments(args) assert is_valid is False assert "--x11-window-id requires --x11" == error @pytest.mark.unit def test_x11_key_name_mapping_for_keypad_and_capslock(): x11 = X11Driver() assert x11.keysym_name_to_key_name("KP_0") == "KEY_KP0" assert x11.keysym_name_to_key_name("KP_Insert") == "KEY_KP0" assert x11.keysym_name_to_key_name("Caps_Lock") == "KEY_CAPSLOCK" assert x11.keysym_name_to_key_name("Super_L") == "KEY_LEFTMETA" assert x11.keysym_name_to_key_name("Multi_key") == "KEY_COMPOSE" assert x11.keysym_name_to_key_name("Next") == "KEY_PAGEDOWN" assert x11.keysym_name_to_key_name("Prior") == "KEY_PAGEUP" assert x11.keysym_name_to_key_name("a") == "KEY_A" assert x11.keysym_name_to_key_name("F10") == "KEY_F10" @pytest.mark.unit def test_x11_keycode_mapping_prefers_keypad_aliases(): x11 = X11Driver() x11.display = Mock() keysyms = { 0: XK.string_to_keysym("Up"), 1: XK.string_to_keysym("KP_8"), 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, } x11.display.keycode_to_keysym.side_effect = lambda keycode, column: keysyms[column] assert x11.keycode_to_key_name(80) == "KEY_KP8" @pytest.mark.unit def test_x11_keycode_mapping_preserves_plain_insert(): x11 = X11Driver() x11.display = Mock() keysyms = { 0: XK.string_to_keysym("Insert"), 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, } x11.display.keycode_to_keysym.side_effect = lambda keycode, column: keysyms[column] assert x11.keycode_to_key_name(118) == "KEY_INSERT" @pytest.mark.unit def test_x11_keycode_mapping_detects_keypad_insert(): x11 = X11Driver() x11.display = Mock() keysyms = { 0: XK.string_to_keysym("Insert"), 1: XK.string_to_keysym("KP_Insert"), 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, } x11.display.keycode_to_keysym.side_effect = lambda keycode, column: keysyms[column] assert x11.keycode_to_key_name(90) == "KEY_KP0" @pytest.mark.unit def test_x11_key_name_to_keysym_names_includes_numlock_aliases(): x11 = X11Driver() assert x11.key_name_to_keysym_names("KEY_KP0") == ["KP_0", "KP_Insert"] assert "KP_Delete" in x11.key_name_to_keysym_names("KEY_KPDOT") assert "Caps_Lock" in x11.key_name_to_keysym_names("KEY_CAPSLOCK") assert "Super_L" in x11.key_name_to_keysym_names("KEY_META") assert "Meta_R" in x11.key_name_to_keysym_names("KEY_META") assert x11.key_name_to_keysym_names("KEY_COMPOSE") == ["Multi_key"] assert "Next" in x11.key_name_to_keysym_names("KEY_PAGEDOWN") assert "Prior" in x11.key_name_to_keysym_names("KEY_PAGEUP") @pytest.mark.unit def test_x11_should_emit_unbound_mapped_keys_for_speech_interrupt(): x11 = X11Driver() x11.interesting_keys = set() assert x11.should_emit_key("KEY_ESC") is True assert x11.should_emit_key("KEY_ENTER") is True @pytest.mark.unit def test_x11_build_passive_grabs_for_fenrir_keys_and_shortcuts(): x11 = X11Driver() input_manager = Mock(convert_event_name=lambda key: key) x11.env = { "input": { "fenrir_key": ["KEY_KP0", "KEY_CAPSLOCK"], "script_key": [], }, "rawBindings": { "fenrir_combo": [1, ["KEY_FENRIR", "KEY_KP8"]], "bare_keypad": [1, ["KEY_KP5"]], "ctrl_keypad": [1, ["KEY_CTRL", "KEY_KP2"]], }, "runtime": {"InputManager": input_manager}, } grabs = x11.build_passive_grabs() assert ("KEY_KP0", 0) in grabs assert ("KEY_CAPSLOCK", 0) in grabs assert ("KEY_KP5", 0) in grabs assert ("KEY_KP2", X.ControlMask) in grabs @pytest.mark.unit def test_x11_parse_window_id_accepts_decimal_and_hex(): x11 = X11Driver() assert x11.parse_window_id("123") == 123 assert x11.parse_window_id("0x123") == 0x123 assert x11.parse_window_id("") is None @pytest.mark.unit def test_x11_write_event_buffer_clears_buffer(): x11 = X11Driver() x11._initialized = True x11.env = {"input": {"event_buffer": ["KEY_KP0"]}} x11.write_event_buffer() assert x11.env["input"]["event_buffer"] == [] @pytest.mark.unit def test_x11_find_num_lock_mask_uses_modifier_mapping(): x11 = X11Driver() x11.display = Mock() x11.display.keysym_to_keycode.return_value = 77 x11.display.get_modifier_mapping.return_value = [ [], [], [], [], [77], [], [], [], ] assert x11.find_num_lock_mask() == X.Mod2Mask @pytest.mark.unit def test_x11_handle_key_event_keeps_event_buffer_for_input_manager(): x11 = X11Driver() x11.active = True x11.interesting_keys = {"KEY_KP0"} x11.env = { "input": { "curr_input": [], "event_buffer": [], }, "runtime": { "InputManager": Mock(convert_event_name=lambda key: key), "DebugManager": Mock(), }, } event_queue = Mock() event = Mock(type=X.KeyPress, detail=90, state=0) x11.keycode_to_key_name = Mock(return_value="KEY_KP0") x11.handle_x_event(event, event_queue) assert x11.env["input"]["event_buffer"][0]["event_name"] == "KEY_KP0" event_queue.put.assert_called_once()