Improve socket handling for -x spawned fenrir instances.
This commit is contained in:
@@ -216,6 +216,68 @@ class TestRemoteDataFormat:
|
||||
assert result["success"] is False
|
||||
assert "Unknown command format" in result["message"]
|
||||
|
||||
def test_list_instances_top_level_command(self, mock_environment):
|
||||
"""Test listing registered Fenrir instances."""
|
||||
self.manager.initialize(mock_environment)
|
||||
|
||||
with patch(
|
||||
"fenrirscreenreader.core.remoteManager.remoteInstanceRegistry.list_instances",
|
||||
return_value=[
|
||||
{
|
||||
"pid": 123,
|
||||
"ppid": 100,
|
||||
"screen_driver": "ptyDriver",
|
||||
"keyboard_driver": "x11Driver",
|
||||
"main_socket": True,
|
||||
"x11_window_id": "0x123",
|
||||
"socket_files": [
|
||||
"/tmp/fenrirscreenreader-123.sock",
|
||||
"/tmp/fenrirscreenreader-deamon.sock",
|
||||
],
|
||||
}
|
||||
],
|
||||
):
|
||||
result = self.manager.handle_remote_incomming_with_response("ls")
|
||||
|
||||
assert result["success"] is True
|
||||
assert "pid=123" in result["message"]
|
||||
assert "x11_window_id=0x123" in result["message"]
|
||||
assert "/tmp/fenrirscreenreader-123.sock" in result["message"]
|
||||
|
||||
def test_remote_incoming_suppresses_command_claimed_by_other_instance(
|
||||
self, mock_environment, tmp_path
|
||||
):
|
||||
"""Test untargeted duplicate remote commands only run in one process."""
|
||||
self.manager.initialize(mock_environment)
|
||||
lock_path = tmp_path / "remote-command.lock"
|
||||
lock_path.write_text("999999 1\n")
|
||||
|
||||
with patch.object(
|
||||
self.manager,
|
||||
"_get_remote_command_lock_path",
|
||||
return_value=str(lock_path),
|
||||
):
|
||||
self.manager.handle_remote_incomming("command say duplicated")
|
||||
|
||||
mock_environment["runtime"]["OutputManager"].speak_text.assert_not_called()
|
||||
|
||||
def test_remote_incoming_allows_same_instance_repeat(
|
||||
self, mock_environment, tmp_path
|
||||
):
|
||||
"""Test repeated direct commands to one instance are not suppressed."""
|
||||
self.manager.initialize(mock_environment)
|
||||
lock_path = tmp_path / "remote-command.lock"
|
||||
|
||||
with patch.object(
|
||||
self.manager,
|
||||
"_get_remote_command_lock_path",
|
||||
return_value=str(lock_path),
|
||||
):
|
||||
self.manager.handle_remote_incomming("command say repeat")
|
||||
self.manager.handle_remote_incomming("command say repeat")
|
||||
|
||||
assert mock_environment["runtime"]["OutputManager"].speak_text.call_count == 2
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.remote
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import pytest
|
||||
|
||||
from fenrirscreenreader.screenDriver.ptyDriver import Terminal
|
||||
|
||||
|
||||
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"]
|
||||
@@ -136,11 +136,14 @@ class TestDriverValidation:
|
||||
"""Keyboard driver should only accept whitelisted values."""
|
||||
# Valid drivers
|
||||
self.manager._validate_setting_value("keyboard", "driver", "evdevDriver")
|
||||
self.manager._validate_setting_value("keyboard", "driver", "ptyDriver")
|
||||
self.manager._validate_setting_value("keyboard", "driver", "x11Driver")
|
||||
self.manager._validate_setting_value("keyboard", "driver", "atspiDriver")
|
||||
self.manager._validate_setting_value("keyboard", "driver", "dummyDriver")
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid input driver"):
|
||||
self.manager._validate_setting_value("keyboard", "driver", "ptyDriver")
|
||||
with pytest.raises(ValueError, match="Invalid input driver"):
|
||||
self.manager._validate_setting_value("keyboard", "driver", "atspiDriver")
|
||||
|
||||
# Invalid driver
|
||||
with pytest.raises(ValueError, match="Invalid input driver"):
|
||||
self.manager._validate_setting_value("keyboard", "driver", "badDriver")
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Unit tests for automatic time announcement behavior.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
|
||||
def _load_time_module():
|
||||
module_path = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "src"
|
||||
/ "fenrirscreenreader"
|
||||
/ "commands"
|
||||
/ "onHeartBeat"
|
||||
/ "76000-time.py"
|
||||
)
|
||||
spec = importlib.util.spec_from_file_location("fenrir_time_command", module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def _create_environment(output_manager):
|
||||
now = datetime.datetime.now()
|
||||
settings_manager = Mock()
|
||||
settings_manager.get_setting.side_effect = lambda section, setting: {
|
||||
("time", "on_minutes"): str(now.minute).zfill(2),
|
||||
("general", "date_format"): "%A, %B %d, %Y",
|
||||
("general", "time_format"): "%I:%M%P",
|
||||
}[(section, setting)]
|
||||
settings_manager.get_setting_as_int.side_effect = lambda section, setting: {
|
||||
("time", "delay_sec"): 0,
|
||||
}[(section, setting)]
|
||||
settings_manager.get_setting_as_bool.side_effect = lambda section, setting: {
|
||||
("time", "enabled"): True,
|
||||
("time", "present_date"): True,
|
||||
("time", "present_time"): True,
|
||||
("time", "interrupt"): False,
|
||||
("time", "announce"): True,
|
||||
}[(section, setting)]
|
||||
|
||||
return {
|
||||
"runtime": {
|
||||
"OutputManager": output_manager,
|
||||
"SettingsManager": settings_manager,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_time_announcement_lock_suppresses_second_instance(tmp_path):
|
||||
time_module = _load_time_module()
|
||||
lock_path = tmp_path / "time-announcement.lock"
|
||||
first_output = Mock(
|
||||
interrupt_output=Mock(),
|
||||
play_sound_icon=Mock(),
|
||||
present_text=Mock(),
|
||||
)
|
||||
second_output = Mock(
|
||||
interrupt_output=Mock(),
|
||||
play_sound_icon=Mock(),
|
||||
present_text=Mock(),
|
||||
)
|
||||
first_command = time_module.command()
|
||||
second_command = time_module.command()
|
||||
|
||||
first_command.initialize(_create_environment(first_output))
|
||||
second_command.initialize(_create_environment(second_output))
|
||||
first_command._get_announcement_lock_path = lambda: str(lock_path)
|
||||
second_command._get_announcement_lock_path = lambda: str(lock_path)
|
||||
first_command.last_time -= datetime.timedelta(hours=1)
|
||||
second_command.last_time -= datetime.timedelta(hours=1)
|
||||
|
||||
first_command.run()
|
||||
second_command.run()
|
||||
|
||||
first_output.play_sound_icon.assert_called_once_with("announce")
|
||||
first_output.present_text.assert_called()
|
||||
second_output.interrupt_output.assert_not_called()
|
||||
second_output.play_sound_icon.assert_not_called()
|
||||
second_output.present_text.assert_not_called()
|
||||
@@ -30,14 +30,17 @@ def test_x11_mode_runs_in_foreground():
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_x11_mode_rejects_other_emulated_modes():
|
||||
def test_removed_emulated_pty_flag_is_rejected():
|
||||
fenrir = load_fenrir_entrypoint()
|
||||
args = fenrir.create_argument_parser().parse_args(["-x", "-e"])
|
||||
with pytest.raises(SystemExit):
|
||||
fenrir.create_argument_parser().parse_args(["-e"])
|
||||
|
||||
is_valid, error = fenrir.validate_arguments(args)
|
||||
|
||||
assert is_valid is False
|
||||
assert "--x11" in error
|
||||
@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
|
||||
@@ -50,6 +53,19 @@ def test_x11_cli_accepts_window_id():
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user