Files
fenrir/tests/integration/test_remote_control.py

343 lines
12 KiB
Python

"""
Integration tests for remote control functionality.
Tests the remote control system including Unix socket and TCP communication,
command parsing, and settings management.
"""
import pytest
import socket
import time
from unittest.mock import Mock, patch
from fenrirscreenreader.core.remoteManager import RemoteManager
@pytest.mark.integration
@pytest.mark.remote
class TestRemoteCommandParsing:
"""Test remote control command parsing and execution."""
def setup_method(self):
"""Create RemoteManager instance for each test."""
self.manager = RemoteManager()
def test_say_command_parsing(self, mock_environment):
"""Test parsing of 'command say' messages."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response(
"say Hello World"
)
assert result["success"] is True
assert "Speaking" in result["message"]
mock_environment["runtime"]["OutputManager"].speak_text.assert_called_once_with(
"Hello World"
)
def test_interrupt_command(self, mock_environment):
"""Test speech interruption command."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response("interrupt")
assert result["success"] is True
mock_environment["runtime"][
"OutputManager"
].interrupt_output.assert_called_once()
def test_setting_change_parsing(self, mock_environment):
"""Test parsing of 'setting set' commands."""
self.manager.initialize(mock_environment)
# Mock parse_setting_args to verify it gets called
with patch.object(
mock_environment["runtime"]["SettingsManager"], "parse_setting_args"
) as mock_parse:
result = self.manager.handle_settings_change_with_response(
"set speech#rate=0.8"
)
assert result["success"] is True
mock_parse.assert_called_once_with("speech#rate=0.8")
def test_clipboard_command(self, mock_environment):
"""Test clipboard setting via remote control."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response(
"clipboard Test clipboard content"
)
assert result["success"] is True
mock_environment["runtime"][
"MemoryManager"
].add_value_to_first_index.assert_called_once_with(
"clipboardHistory", "Test clipboard content"
)
def test_quit_command(self, mock_environment):
"""Test Fenrir shutdown command."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response(
"quitapplication"
)
assert result["success"] is True
mock_environment["runtime"][
"EventManager"
].stop_main_event_loop.assert_called_once()
def test_unknown_command_rejection(self, mock_environment):
"""Test that unknown commands are rejected."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response(
"unknown_command"
)
assert result["success"] is False
assert "Unknown command" in result["message"]
@pytest.mark.integration
@pytest.mark.remote
class TestRemoteSettingsControl:
"""Test remote control of settings."""
def setup_method(self):
"""Create RemoteManager instance for each test."""
self.manager = RemoteManager()
def test_setting_reset(self, mock_environment):
"""Test resetting settings to defaults."""
self.manager.initialize(mock_environment)
with patch.object(
mock_environment["runtime"]["SettingsManager"], "reset_setting_arg_dict"
) as mock_reset:
result = self.manager.handle_settings_change_with_response("reset")
assert result["success"] is True
mock_reset.assert_called_once()
def test_setting_save(self, mock_environment):
"""Test saving settings to file."""
self.manager.initialize(mock_environment)
mock_environment["runtime"]["SettingsManager"].get_settings_file = Mock(
return_value="/tmp/test.conf"
)
with patch.object(
mock_environment["runtime"]["SettingsManager"], "save_settings"
) as mock_save:
result = self.manager.handle_settings_change_with_response("save")
assert result["success"] is True
mock_save.assert_called_once()
def test_settings_remote_disabled(self, mock_environment):
"""Test that settings commands are blocked when disabled."""
mock_environment["runtime"]["SettingsManager"].get_setting_as_bool = Mock(
return_value=False
)
self.manager.initialize(mock_environment)
result = self.manager.handle_settings_change_with_response(
"set speech#rate=0.5"
)
assert result["success"] is False
assert "disabled" in result["message"].lower()
@pytest.mark.integration
@pytest.mark.remote
class TestRemoteDataFormat:
"""Test remote control data format handling."""
def setup_method(self):
"""Create RemoteManager instance for each test."""
self.manager = RemoteManager()
def test_command_prefix_case_insensitive(self, mock_environment):
"""Test that command prefixes are case-insensitive."""
self.manager.initialize(mock_environment)
# All of these should work
result1 = self.manager.handle_remote_incomming_with_response(
"COMMAND say test"
)
result2 = self.manager.handle_remote_incomming_with_response(
"command say test"
)
result3 = self.manager.handle_remote_incomming_with_response(
"CoMmAnD say test"
)
assert all(r["success"] for r in [result1, result2, result3])
def test_setting_prefix_case_insensitive(self, mock_environment):
"""Test that setting prefixes are case-insensitive."""
self.manager.initialize(mock_environment)
with patch.object(
mock_environment["runtime"]["SettingsManager"], "parse_setting_args"
):
result1 = self.manager.handle_remote_incomming_with_response(
"SETTING set speech#rate=0.5"
)
result2 = self.manager.handle_remote_incomming_with_response(
"setting set speech#rate=0.5"
)
assert all(r["success"] for r in [result1, result2])
def test_empty_data_handling(self, mock_environment):
"""Test handling of empty remote data."""
self.manager.initialize(mock_environment)
result = self.manager.handle_remote_incomming_with_response("")
assert result["success"] is False
assert "No data" in result["message"]
def test_invalid_format_rejection(self, mock_environment):
"""Test rejection of invalid command format."""
self.manager.initialize(mock_environment)
result = self.manager.handle_remote_incomming_with_response(
"invalid format without prefix"
)
assert result["success"] is False
assert "Unknown command format" in result["message"]
@pytest.mark.integration
@pytest.mark.remote
class TestWindowDefinition:
"""Test window definition via remote control."""
def setup_method(self):
"""Create RemoteManager instance for each test."""
self.manager = RemoteManager()
def test_define_window_valid_coordinates(self, mock_environment):
"""Test defining a window with valid coordinates."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response(
"window 10 5 70 20"
)
assert result["success"] is True
mock_environment["runtime"][
"CursorManager"
].set_window_for_application.assert_called_once()
# Verify the coordinates were parsed correctly
call_args = mock_environment["runtime"][
"CursorManager"
].set_window_for_application.call_args
start, end = call_args[0]
assert start == {"x": 10, "y": 5}
assert end == {"x": 70, "y": 20}
def test_define_window_insufficient_coordinates(self, mock_environment):
"""Test that window definition with < 4 coordinates is ignored."""
self.manager.initialize(mock_environment)
# Should succeed but not call set_window_for_application
result = self.manager.handle_command_execution_with_response("window 10 20")
assert result["success"] is True
mock_environment["runtime"][
"CursorManager"
].set_window_for_application.assert_not_called()
def test_reset_window(self, mock_environment):
"""Test resetting window to full screen."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response("resetwindow")
assert result["success"] is True
mock_environment["runtime"][
"CursorManager"
].clear_window_for_application.assert_called_once()
@pytest.mark.integration
@pytest.mark.remote
class TestVMenuControl:
"""Test VMenu control via remote."""
def setup_method(self):
"""Create RemoteManager instance for each test."""
self.manager = RemoteManager()
def test_set_vmenu(self, mock_environment):
"""Test setting VMenu to specific menu."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response(
"vmenu /vim/file/save"
)
assert result["success"] is True
mock_environment["runtime"]["VmenuManager"].set_curr_menu.assert_called_once_with(
"/vim/file/save"
)
def test_reset_vmenu(self, mock_environment):
"""Test resetting VMenu to default."""
self.manager.initialize(mock_environment)
result = self.manager.handle_command_execution_with_response("resetvmenu")
assert result["success"] is True
mock_environment["runtime"]["VmenuManager"].set_curr_menu.assert_called_once_with()
@pytest.mark.integration
@pytest.mark.remote
@pytest.mark.slow
class TestRemoteControlThroughput:
"""Test remote control performance characteristics."""
def test_rapid_say_commands(self, mock_environment):
"""Test handling of rapid successive say commands."""
manager = RemoteManager()
manager.initialize(mock_environment)
# Send 100 rapid commands
for i in range(100):
result = manager.handle_command_execution_with_response(f"say test {i}")
assert result["success"] is True
# Verify all were queued
assert (
mock_environment["runtime"]["OutputManager"].speak_text.call_count == 100
)
def test_rapid_setting_changes(self, mock_environment):
"""Test handling of rapid setting changes."""
manager = RemoteManager()
manager.initialize(mock_environment)
# Rapidly change speech rate
with patch.object(
mock_environment["runtime"]["SettingsManager"], "parse_setting_args"
) as mock_parse:
for rate in [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
result = manager.handle_settings_change_with_response(
f"set speech#rate={rate}"
)
assert result["success"] is True
assert mock_parse.call_count == 6