Tests added, see the documentation in the tests directory for details. Improved the socket code.

This commit is contained in:
Storm Dragon
2025-12-03 02:51:49 -05:00
parent 2092a3e257
commit bf0d134187
12 changed files with 1622 additions and 30 deletions

225
tests/conftest.py Normal file
View File

@@ -0,0 +1,225 @@
"""
Shared pytest fixtures for Fenrir tests.
This file contains fixtures and configuration used across all test modules.
"""
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, Mock
import pytest
# Add src directory to Python path for imports
fenrir_root = Path(__file__).parent.parent
sys.path.insert(0, str(fenrir_root / "src"))
@pytest.fixture
def mock_environment():
"""Create a minimal mock environment for testing.
Returns a mock environment dict with required runtime managers mocked.
This allows testing components without initializing the full Fenrir stack.
"""
env = {
"runtime": {
"DebugManager": Mock(write_debug_out=Mock()),
"OutputManager": Mock(
present_text=Mock(),
speak_text=Mock(),
interrupt_output=Mock(),
),
"SettingsManager": Mock(
get_setting=Mock(return_value="default"),
get_setting_as_int=Mock(return_value=0),
get_setting_as_float=Mock(return_value=0.0),
get_setting_as_bool=Mock(return_value=True),
),
"InputManager": Mock(
sendKeys=Mock(),
handle_device_grab=Mock(),
),
"ScreenManager": Mock(update_screen_ignored=Mock()),
"EventManager": Mock(stop_main_event_loop=Mock()),
"MemoryManager": Mock(
add_value_to_first_index=Mock(),
get_index_list_element=Mock(return_value="test clipboard"),
is_index_list_empty=Mock(return_value=False),
),
"VmenuManager": Mock(
set_curr_menu=Mock(),
),
"CursorManager": Mock(
set_window_for_application=Mock(),
clear_window_for_application=Mock(),
),
},
"settings": Mock(),
"general": {
"curr_user": "testuser",
},
}
return env
@pytest.fixture
def temp_config_file(tmp_path):
"""Create a temporary configuration file for testing.
Returns path to a valid test configuration file.
"""
config_path = tmp_path / "test_settings.conf"
config_content = """[sound]
enabled=True
driver=gstreamerDriver
theme=default
volume=0.7
[speech]
enabled=True
driver=speechdDriver
rate=0.5
pitch=0.5
volume=1.0
autoReadIncoming=True
[screen]
driver=vcsaDriver
encoding=auto
screenUpdateDelay=0.05
[keyboard]
driver=evdevDriver
device=ALL
grabDevices=True
keyboardLayout=desktop
[general]
debugLevel=2
debugMode=File
[remote]
enable=True
driver=unixDriver
port=22447
enableSettingsRemote=True
enableCommandRemote=True
"""
config_path.write_text(config_content)
return config_path
@pytest.fixture
def temp_socket_path(tmp_path):
"""Create a temporary Unix socket path for testing.
Returns path that can be used for Unix socket testing.
"""
return tmp_path / "test_fenrir.sock"
@pytest.fixture
def temp_clipboard_file(tmp_path):
"""Create a temporary clipboard file for testing.
Returns path to a temporary file for clipboard operations.
"""
clipboard_path = tmp_path / "fenrirClipboard"
clipboard_path.write_text("")
return clipboard_path
@pytest.fixture
def sample_screen_data():
"""Return sample screen data for testing screen-related functionality.
Returns dict with screen dimensions and content.
"""
return {
"columns": 80,
"lines": 24,
"delta": "Hello World",
"cursor": {"x": 0, "y": 0},
"content": "Sample screen content\nSecond line\nThird line",
}
@pytest.fixture
def sample_remote_commands():
"""Return sample remote control commands for testing.
Returns list of valid remote commands.
"""
return [
"command say Hello World",
"command interrupt",
"setting set speech#rate=0.8",
"setting set speech#pitch=0.6",
"setting set sound#volume=0.5",
"setting reset",
]
@pytest.fixture
def invalid_remote_commands():
"""Return invalid remote control commands for testing validation.
Returns list of commands that should be rejected.
"""
return [
"setting set speech#rate=999", # Out of range
"setting set speech#rate=-1", # Negative value
"setting set speech#pitch=10", # Out of range
"setting set speech#volume=-0.5", # Negative volume
"setting set invalid#setting=value", # Invalid section
"command unknown_command", # Unknown command
]
# Pytest hooks for test session customization
def pytest_configure(config):
"""Configure pytest with custom settings."""
# Add custom markers
config.addinivalue_line(
"markers", "unit: Unit tests (fast, no mocking required)"
)
config.addinivalue_line(
"markers", "integration: Integration tests (require mocking)"
)
config.addinivalue_line(
"markers", "driver: Driver tests (require root access)"
)
config.addinivalue_line(
"markers", "slow: Tests that take more than 1 second"
)
def pytest_collection_modifyitems(config, items):
"""Modify test collection to skip driver tests unless explicitly run.
Driver tests require root access and hardware, so skip by default.
Run with: pytest --run-driver-tests
"""
skip_driver = pytest.mark.skip(
reason="Driver tests require root access (use --run-driver-tests)"
)
run_driver_tests = config.getoption("--run-driver-tests", default=False)
for item in items:
if "driver" in item.keywords and not run_driver_tests:
item.add_marker(skip_driver)
def pytest_addoption(parser):
"""Add custom command line options."""
parser.addoption(
"--run-driver-tests",
action="store_true",
default=False,
help="Run driver tests that require root access",
)