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
|
# Only announce numlock changes if an actual numlock key was pressed
|
||||||
# AND the LED state actually changed (some numpads send spurious NUMLOCK events)
|
# AND the LED state actually changed (some numpads send spurious NUMLOCK events)
|
||||||
current_input = self.env["input"]["curr_input"]
|
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:
|
# Check if this is a genuine numlock key press by verifying:
|
||||||
# 1. KEY_NUMLOCK is in the current input sequence
|
# 1. KEY_NUMLOCK is in the current input sequence
|
||||||
# 2. The LED state has actually changed
|
# 2. The LED state has actually changed
|
||||||
# 3. This isn't just a side effect from a KP_ key (which some buggy numpads do)
|
# 3. This isn't just a side effect from a KP_ key (which some buggy numpads do)
|
||||||
is_genuine_numlock = (
|
is_genuine_numlock = (
|
||||||
current_input and
|
relevant_input and
|
||||||
"KEY_NUMLOCK" in current_input and
|
"KEY_NUMLOCK" in relevant_input and
|
||||||
not any(key.startswith("KEY_KP") for key in current_input if isinstance(key, str))
|
not any(
|
||||||
|
key.startswith("KEY_KP")
|
||||||
|
for key in relevant_input
|
||||||
|
if isinstance(key, str)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_genuine_numlock:
|
if is_genuine_numlock:
|
||||||
|
|||||||
@@ -302,6 +302,35 @@ class driver(screenDriver):
|
|||||||
return
|
return
|
||||||
self.env["runtime"]["OutputManager"].interrupt_output()
|
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):
|
def get_session_information(self):
|
||||||
self.env["screen"]["autoIgnoreScreens"] = []
|
self.env["screen"]["autoIgnoreScreens"] = []
|
||||||
self.env["general"]["prev_user"] = getpass.getuser()
|
self.env["general"]["prev_user"] = getpass.getuser()
|
||||||
@@ -434,8 +463,7 @@ class driver(screenDriver):
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
self.interrupt_output_on_stdin_input(msg_bytes)
|
self.handle_stdin_input(msg_bytes, event_queue)
|
||||||
self.inject_text_to_screen(msg_bytes)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.env["runtime"][
|
self.env["runtime"][
|
||||||
"DebugManager"
|
"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
|
import pytest
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
from fenrirscreenreader.core.eventData import FenrirEventType
|
||||||
from fenrirscreenreader.screenDriver.ptyDriver import PTYConstants
|
from fenrirscreenreader.screenDriver.ptyDriver import PTYConstants
|
||||||
from fenrirscreenreader.screenDriver.ptyDriver import Terminal
|
from fenrirscreenreader.screenDriver.ptyDriver import Terminal
|
||||||
from fenrirscreenreader.screenDriver.ptyDriver import driver as PtyDriver
|
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")
|
pty_driver.interrupt_output_on_stdin_input(b"a")
|
||||||
|
|
||||||
output_manager.interrupt_output.assert_not_called()
|
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)
|
input_manager = Mock(convert_event_name=lambda key: key)
|
||||||
x11.env = {
|
x11.env = {
|
||||||
"input": {
|
"input": {
|
||||||
"fenrir_key": ["KEY_KP0", "KEY_CAPSLOCK"],
|
"fenrir_key": ["KEY_KP0", "KEY_META"],
|
||||||
"script_key": [],
|
"script_key": [],
|
||||||
},
|
},
|
||||||
"rawBindings": {
|
"rawBindings": {
|
||||||
"fenrir_combo": [1, ["KEY_FENRIR", "KEY_KP8"]],
|
"fenrir_combo": [1, ["KEY_FENRIR", "KEY_KP8"]],
|
||||||
"bare_keypad": [1, ["KEY_KP5"]],
|
"bare_keypad": [1, ["KEY_KP5"]],
|
||||||
"ctrl_keypad": [1, ["KEY_CTRL", "KEY_KP2"]],
|
"ctrl_keypad": [1, ["KEY_CTRL", "KEY_KP2"]],
|
||||||
|
"meta_combo": [1, ["KEY_FENRIR", "KEY_BACKSPACE"]],
|
||||||
},
|
},
|
||||||
"runtime": {"InputManager": input_manager},
|
"runtime": {"InputManager": input_manager},
|
||||||
}
|
}
|
||||||
|
|
||||||
grabs = x11.build_passive_grabs()
|
grabs = x11.build_passive_grabs()
|
||||||
|
|
||||||
assert ("KEY_KP0", 0) in grabs
|
assert ("KEY_KP0", 0, True) in grabs
|
||||||
assert ("KEY_CAPSLOCK", 0) in grabs
|
assert ("KEY_META", 0, True) in grabs
|
||||||
assert ("KEY_KP5", 0) in grabs
|
assert ("KEY_NUMLOCK", 0, True) in grabs
|
||||||
assert ("KEY_KP2", X.ControlMask) 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
|
@pytest.mark.unit
|
||||||
|
|||||||
Reference in New Issue
Block a user