Tests updated. Attempt to fix remaining problems with forward keypress and numlock reporting.
This commit is contained in:
@@ -28,15 +28,21 @@ class command:
|
||||
# Only announce numlock changes if an actual numlock key was pressed
|
||||
# AND the LED state actually changed (some numpads send spurious NUMLOCK events)
|
||||
current_input = self.env["input"]["curr_input"]
|
||||
previous_input = self.env["input"]["prev_input"]
|
||||
relevant_input = current_input or previous_input
|
||||
|
||||
# Check if this is a genuine numlock key press by verifying:
|
||||
# 1. KEY_NUMLOCK is in the current input sequence
|
||||
# 2. The LED state has actually changed
|
||||
# 3. This isn't just a side effect from a KP_ key (which some buggy numpads do)
|
||||
is_genuine_numlock = (
|
||||
current_input and
|
||||
"KEY_NUMLOCK" in current_input and
|
||||
not any(key.startswith("KEY_KP") for key in current_input if isinstance(key, str))
|
||||
relevant_input and
|
||||
"KEY_NUMLOCK" in relevant_input and
|
||||
not any(
|
||||
key.startswith("KEY_KP")
|
||||
for key in relevant_input
|
||||
if isinstance(key, str)
|
||||
)
|
||||
)
|
||||
|
||||
if is_genuine_numlock:
|
||||
|
||||
@@ -302,6 +302,35 @@ class driver(screenDriver):
|
||||
return
|
||||
self.env["runtime"]["OutputManager"].interrupt_output()
|
||||
|
||||
def handle_stdin_input(self, msg_bytes, event_queue):
|
||||
if self.synthesize_backspace_shortcut(msg_bytes, event_queue):
|
||||
return
|
||||
self.interrupt_output_on_stdin_input(msg_bytes)
|
||||
self.inject_text_to_screen(msg_bytes)
|
||||
|
||||
def synthesize_backspace_shortcut(self, msg_bytes, event_queue):
|
||||
if msg_bytes not in [b"\x7f", b"\x08"]:
|
||||
return False
|
||||
if "KEY_FENRIR" not in self.env["input"]["curr_input"]:
|
||||
return False
|
||||
|
||||
event_time = time.time()
|
||||
for event_state in [1, 0]:
|
||||
event_queue.put(
|
||||
{
|
||||
"Type": FenrirEventType.keyboard_input,
|
||||
"data": {
|
||||
"event_name": "KEY_BACKSPACE",
|
||||
"event_value": 0,
|
||||
"event_sec": int(event_time),
|
||||
"event_usec": int((event_time % 1) * 1000000),
|
||||
"event_state": event_state,
|
||||
"event_type": 0,
|
||||
},
|
||||
}
|
||||
)
|
||||
return True
|
||||
|
||||
def get_session_information(self):
|
||||
self.env["screen"]["autoIgnoreScreens"] = []
|
||||
self.env["general"]["prev_user"] = getpass.getuser()
|
||||
@@ -434,8 +463,7 @@ class driver(screenDriver):
|
||||
)
|
||||
break
|
||||
try:
|
||||
self.interrupt_output_on_stdin_input(msg_bytes)
|
||||
self.inject_text_to_screen(msg_bytes)
|
||||
self.handle_stdin_input(msg_bytes, event_queue)
|
||||
except Exception as e:
|
||||
self.env["runtime"][
|
||||
"DebugManager"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def load_numlock_command():
|
||||
command_path = (
|
||||
Path(__file__).parents[2]
|
||||
/ "src"
|
||||
/ "fenrirscreenreader"
|
||||
/ "commands"
|
||||
/ "onKeyInput"
|
||||
/ "80500-numlock.py"
|
||||
)
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"numlock_command", command_path
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module.command
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_numlock_off_can_be_announced_from_release_event():
|
||||
output_manager = Mock()
|
||||
command = load_numlock_command()()
|
||||
command.initialize(
|
||||
{
|
||||
"input": {
|
||||
"old_num_lock": True,
|
||||
"new_num_lock": False,
|
||||
"curr_input": [],
|
||||
"prev_input": ["KEY_NUMLOCK"],
|
||||
},
|
||||
"runtime": {"OutputManager": output_manager},
|
||||
}
|
||||
)
|
||||
|
||||
command.run()
|
||||
|
||||
output_manager.present_text.assert_called_once()
|
||||
assert output_manager.present_text.call_args.args[0] == "Numlock off"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_numlock_command_ignores_non_numlock_release():
|
||||
output_manager = Mock()
|
||||
command = load_numlock_command()()
|
||||
command.initialize(
|
||||
{
|
||||
"input": {
|
||||
"old_num_lock": True,
|
||||
"new_num_lock": False,
|
||||
"curr_input": [],
|
||||
"prev_input": ["KEY_KP1"],
|
||||
},
|
||||
"runtime": {"OutputManager": output_manager},
|
||||
}
|
||||
)
|
||||
|
||||
command.run()
|
||||
|
||||
output_manager.present_text.assert_not_called()
|
||||
@@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from fenrirscreenreader.core.eventData import FenrirEventType
|
||||
from fenrirscreenreader.screenDriver.ptyDriver import PTYConstants
|
||||
from fenrirscreenreader.screenDriver.ptyDriver import Terminal
|
||||
from fenrirscreenreader.screenDriver.ptyDriver import driver as PtyDriver
|
||||
@@ -109,3 +110,37 @@ def test_pty_stdin_input_leaves_filtered_interrupts_to_key_events():
|
||||
pty_driver.interrupt_output_on_stdin_input(b"a")
|
||||
|
||||
output_manager.interrupt_output.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pty_backspace_with_fenrir_key_synthesizes_shortcut_events():
|
||||
pty_driver = PtyDriver()
|
||||
event_queue = Mock()
|
||||
pty_driver.env = {
|
||||
"input": {"curr_input": ["KEY_FENRIR"]},
|
||||
}
|
||||
|
||||
handled = pty_driver.synthesize_backspace_shortcut(b"\x7f", event_queue)
|
||||
|
||||
assert handled is True
|
||||
assert event_queue.put.call_count == 2
|
||||
first_event = event_queue.put.call_args_list[0].args[0]
|
||||
second_event = event_queue.put.call_args_list[1].args[0]
|
||||
assert first_event["Type"] == FenrirEventType.keyboard_input
|
||||
assert first_event["data"]["event_name"] == "KEY_BACKSPACE"
|
||||
assert first_event["data"]["event_state"] == 1
|
||||
assert second_event["data"]["event_state"] == 0
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_pty_plain_backspace_is_not_synthesized():
|
||||
pty_driver = PtyDriver()
|
||||
event_queue = Mock()
|
||||
pty_driver.env = {
|
||||
"input": {"curr_input": []},
|
||||
}
|
||||
|
||||
handled = pty_driver.synthesize_backspace_shortcut(b"\x7f", event_queue)
|
||||
|
||||
assert handled is False
|
||||
event_queue.put.assert_not_called()
|
||||
|
||||
@@ -167,23 +167,50 @@ def test_x11_build_passive_grabs_for_fenrir_keys_and_shortcuts():
|
||||
input_manager = Mock(convert_event_name=lambda key: key)
|
||||
x11.env = {
|
||||
"input": {
|
||||
"fenrir_key": ["KEY_KP0", "KEY_CAPSLOCK"],
|
||||
"fenrir_key": ["KEY_KP0", "KEY_META"],
|
||||
"script_key": [],
|
||||
},
|
||||
"rawBindings": {
|
||||
"fenrir_combo": [1, ["KEY_FENRIR", "KEY_KP8"]],
|
||||
"bare_keypad": [1, ["KEY_KP5"]],
|
||||
"ctrl_keypad": [1, ["KEY_CTRL", "KEY_KP2"]],
|
||||
"meta_combo": [1, ["KEY_FENRIR", "KEY_BACKSPACE"]],
|
||||
},
|
||||
"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
|
||||
assert ("KEY_KP0", 0, True) in grabs
|
||||
assert ("KEY_META", 0, True) in grabs
|
||||
assert ("KEY_NUMLOCK", 0, True) in grabs
|
||||
assert ("KEY_KP5", 0, False) in grabs
|
||||
assert ("KEY_KP2", X.ControlMask, False) in grabs
|
||||
assert ("KEY_BACKSPACE", X.Mod4Mask, True) in grabs
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_x11_optional_modifier_masks_can_exclude_numlock():
|
||||
x11 = X11Driver()
|
||||
x11.num_lock_mask = X.Mod2Mask
|
||||
|
||||
masks = x11.optional_modifier_masks(0, include_num_lock=False)
|
||||
|
||||
assert X.Mod2Mask not in masks
|
||||
assert X.Mod2Mask | X.LockMask not in masks
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_x11_get_led_state_reads_lock_modifiers_from_pointer_mask():
|
||||
x11 = X11Driver()
|
||||
x11.num_lock_mask = X.Mod2Mask
|
||||
pointer = Mock(mask=X.Mod2Mask | X.LockMask)
|
||||
x11.root = Mock()
|
||||
x11.root.query_pointer.return_value = pointer
|
||||
|
||||
assert x11.get_led_state(0) is True
|
||||
assert x11.get_led_state(1) is True
|
||||
assert x11.get_led_state(2) is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
Reference in New Issue
Block a user