343 lines
12 KiB
Python
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
|