Tests added, see the documentation in the tests directory for details. Improved the socket code.
This commit is contained in:
342
tests/integration/test_remote_control.py
Normal file
342
tests/integration/test_remote_control.py
Normal file
@@ -0,0 +1,342 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user