232 lines
6.8 KiB
Python
232 lines
6.8 KiB
Python
import threading
|
|
import time
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
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
|
|
|
|
|
|
class DummyProcessInput:
|
|
def __init__(self):
|
|
self.data = []
|
|
|
|
def write(self, data):
|
|
self.data.append(data)
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_csi_sequences_with_intermediate_characters_do_not_render_final_byte():
|
|
terminal = Terminal(10, 3, DummyProcessInput())
|
|
|
|
terminal.feed(b"\x1b[2026$p\x1b[2048$pX")
|
|
screen = terminal.get_screen_content()
|
|
|
|
assert screen["text"].splitlines()[0] == "X "
|
|
assert "p" not in screen["text"]
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_private_sgr_sequence_from_fullscreen_apps_does_not_crash():
|
|
terminal = Terminal(10, 3, DummyProcessInput())
|
|
|
|
terminal.feed(b"\x1b[?25;7mX")
|
|
screen = terminal.get_screen_content()
|
|
|
|
assert screen["text"].splitlines()[0] == "X "
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_optional_float_setting_uses_default_when_missing():
|
|
settings_manager = type(
|
|
"SettingsManager",
|
|
(),
|
|
{"get_setting": lambda self, section, setting: ""},
|
|
)()
|
|
pty_driver = PtyDriver()
|
|
|
|
assert (
|
|
pty_driver._get_optional_float_setting(
|
|
settings_manager,
|
|
"screen",
|
|
"ptyOutputTimeout",
|
|
PTYConstants.OUTPUT_READ_TIMEOUT,
|
|
)
|
|
== PTYConstants.OUTPUT_READ_TIMEOUT
|
|
)
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_pty_stdin_input_interrupts_output_when_all_keys_interrupt_enabled():
|
|
pty_driver = PtyDriver()
|
|
settings_manager = Mock()
|
|
settings_manager.get_setting_as_bool.return_value = True
|
|
settings_manager.get_setting.return_value = ""
|
|
output_manager = Mock()
|
|
pty_driver.env = {
|
|
"runtime": {
|
|
"SettingsManager": settings_manager,
|
|
"OutputManager": output_manager,
|
|
}
|
|
}
|
|
|
|
pty_driver.interrupt_output_on_stdin_input(b"a")
|
|
pty_driver.stdin_interrupt_thread.join(timeout=1.0)
|
|
|
|
output_manager.interrupt_output.assert_called_once_with()
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_pty_stdin_input_interrupt_does_not_block_input_injection():
|
|
pty_driver = PtyDriver()
|
|
settings_manager = Mock()
|
|
settings_manager.get_setting_as_bool.return_value = True
|
|
settings_manager.get_setting.return_value = ""
|
|
interrupt_started = threading.Event()
|
|
release_interrupt = threading.Event()
|
|
|
|
def slow_interrupt():
|
|
interrupt_started.set()
|
|
release_interrupt.wait(timeout=1.0)
|
|
|
|
output_manager = Mock(interrupt_output=Mock(side_effect=slow_interrupt))
|
|
pty_driver.env = {
|
|
"input": {"curr_input": []},
|
|
"runtime": {
|
|
"SettingsManager": settings_manager,
|
|
"OutputManager": output_manager,
|
|
"DebugManager": Mock(write_debug_out=Mock()),
|
|
},
|
|
}
|
|
pty_driver.inject_text_to_screen = Mock()
|
|
|
|
start_time = time.monotonic()
|
|
pty_driver.handle_stdin_input(b"a", Mock())
|
|
elapsed = time.monotonic() - start_time
|
|
|
|
try:
|
|
assert interrupt_started.wait(timeout=0.2)
|
|
assert elapsed < 0.2
|
|
pty_driver.inject_text_to_screen.assert_called_once_with(b"a")
|
|
finally:
|
|
release_interrupt.set()
|
|
pty_driver.stdin_interrupt_thread.join(timeout=1.0)
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_pty_raw_tab_records_recent_tab_keypress():
|
|
pty_driver = PtyDriver()
|
|
settings_manager = Mock()
|
|
settings_manager.get_setting_as_bool.return_value = False
|
|
input_manager = Mock()
|
|
pty_driver.env = {
|
|
"input": {"curr_input": []},
|
|
"runtime": {
|
|
"DebugManager": Mock(write_debug_out=Mock()),
|
|
"InputManager": input_manager,
|
|
"SettingsManager": settings_manager,
|
|
},
|
|
}
|
|
pty_driver.inject_text_to_screen = Mock()
|
|
|
|
pty_driver.handle_stdin_input(b"\t", Mock())
|
|
|
|
input_manager.record_unmanaged_keypress.assert_called_once_with("KEY_TAB")
|
|
pty_driver.inject_text_to_screen.assert_called_once_with(b"\t")
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_pty_plain_stdin_does_not_record_tab_keypress():
|
|
pty_driver = PtyDriver()
|
|
settings_manager = Mock()
|
|
settings_manager.get_setting_as_bool.return_value = False
|
|
input_manager = Mock()
|
|
pty_driver.env = {
|
|
"input": {"curr_input": []},
|
|
"runtime": {
|
|
"DebugManager": Mock(write_debug_out=Mock()),
|
|
"InputManager": input_manager,
|
|
"SettingsManager": settings_manager,
|
|
},
|
|
}
|
|
pty_driver.inject_text_to_screen = Mock()
|
|
|
|
pty_driver.handle_stdin_input(b"a", Mock())
|
|
|
|
input_manager.record_unmanaged_keypress.assert_not_called()
|
|
pty_driver.inject_text_to_screen.assert_called_once_with(b"a")
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_pty_stdin_input_honors_interrupt_disabled():
|
|
pty_driver = PtyDriver()
|
|
settings_manager = Mock()
|
|
settings_manager.get_setting_as_bool.return_value = False
|
|
output_manager = Mock()
|
|
pty_driver.env = {
|
|
"runtime": {
|
|
"SettingsManager": settings_manager,
|
|
"OutputManager": output_manager,
|
|
}
|
|
}
|
|
|
|
pty_driver.interrupt_output_on_stdin_input(b"a")
|
|
|
|
output_manager.interrupt_output.assert_not_called()
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_pty_stdin_input_leaves_filtered_interrupts_to_key_events():
|
|
pty_driver = PtyDriver()
|
|
settings_manager = Mock()
|
|
settings_manager.get_setting_as_bool.return_value = True
|
|
settings_manager.get_setting.return_value = "KEY_ENTER"
|
|
output_manager = Mock()
|
|
pty_driver.env = {
|
|
"runtime": {
|
|
"SettingsManager": settings_manager,
|
|
"OutputManager": output_manager,
|
|
}
|
|
}
|
|
|
|
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()
|