Test Structure Reorganization and CI Improvements (#5)
* Add GitHub Actions CI/CD workflows and status badges * Fix CI workflow dependency installation * Fix CI workflow: remove pip caching and add explicit requirements.txt * Fix CI workflow: add system dependencies for PyGObject and pycairo * Refactor code for improved readability and consistency - Cleaned up whitespace and formatting in audio_feedback.py, config_manager.py, keyboard_shortcuts.py, tray_indicator.py, run_basic_tests.py, and test_basic.py. - Enhanced the structure of the code by aligning comments and ensuring consistent indentation. - Updated function definitions and calls to maintain a uniform style across the codebase. - Improved logging messages for better clarity during debugging. - Ensured all files end with a newline for better compatibility with various tools. * Refactor imports for consistency and clarity across multiple modules * Fix test imports by setting PYTHONPATH and adding conftest.py * Update CI workflow to use Python 3.12 and ensure proper import path in tests * Fix CI: Add GObject Introspection development dependencies * Update CI: Use virtual environment like local setup * Fix CI * Update README and setup.py: Change Python version to 3.8 and correct author email * Restructure tests for better organization and maintainability * Fix formatting * Update CI workflow to remove redundant test step * Fix CI tests by properly mocking VOSK model loading * Update CI and Tests * Simplify
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Configuration file for pytest.
|
||||
This file makes sure that the 'src' module can be imported in tests.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add the parent directory to sys.path so that 'src' can be imported
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
# This will help pytest discover all test files correctly
|
||||
pytest_plugins = []
|
||||
@@ -1,229 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic test script for Vocalinux.
|
||||
|
||||
This script performs basic tests of individual components
|
||||
without requiring complex dependencies like GTK or VOSK.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import tempfile
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
print("Starting basic tests for Vocalinux")
|
||||
|
||||
# Add project root to Python path
|
||||
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
# Test TextInjector class with mocked dependencies
|
||||
class TextInjectorTests(unittest.TestCase):
|
||||
"""Basic tests for the TextInjector class."""
|
||||
|
||||
@patch('shutil.which')
|
||||
@patch('os.environ.get')
|
||||
def test_environment_detection_x11(self, mock_environ_get, mock_which):
|
||||
"""Test environment detection for X11."""
|
||||
# Import here to avoid loading all dependencies
|
||||
from src.text_injection.text_injector import TextInjector, DesktopEnvironment
|
||||
|
||||
# Mock environment variables for X11
|
||||
mock_environ_get.return_value = "x11"
|
||||
mock_which.return_value = "/usr/bin/xdotool"
|
||||
|
||||
# Create injector with mocked environment
|
||||
injector = TextInjector()
|
||||
|
||||
# Check environment detection
|
||||
self.assertEqual(injector.environment, DesktopEnvironment.X11)
|
||||
print("✓ Environment detection for X11 works correctly")
|
||||
|
||||
@patch('shutil.which')
|
||||
@patch('os.environ.get')
|
||||
def test_environment_detection_wayland(self, mock_environ_get, mock_which):
|
||||
"""Test environment detection for Wayland."""
|
||||
# Import here to avoid loading all dependencies
|
||||
from src.text_injection.text_injector import TextInjector, DesktopEnvironment
|
||||
|
||||
# Mock environment variables for Wayland
|
||||
mock_environ_get.return_value = "wayland"
|
||||
mock_which.return_value = "/usr/bin/wtype"
|
||||
|
||||
# Create injector with mocked environment
|
||||
injector = TextInjector()
|
||||
|
||||
# Check environment detection
|
||||
self.assertEqual(injector.environment, DesktopEnvironment.WAYLAND)
|
||||
print("✓ Environment detection for Wayland works correctly")
|
||||
|
||||
# Test command processor with mocked dependencies
|
||||
class CommandProcessorTests(unittest.TestCase):
|
||||
"""Basic tests for the CommandProcessor class."""
|
||||
|
||||
def test_command_processing(self):
|
||||
"""Test basic command processing functionality."""
|
||||
# Import command processor
|
||||
from src.speech_recognition.command_processor import CommandProcessor
|
||||
|
||||
# Create processor
|
||||
processor = CommandProcessor()
|
||||
|
||||
# Test text commands (punctuation, etc.)
|
||||
text, actions = processor.process_text("add a period to the end")
|
||||
self.assertEqual(text, "add a . to the end")
|
||||
self.assertEqual(actions, [])
|
||||
print("✓ Basic text command processing works correctly")
|
||||
|
||||
# Test action commands
|
||||
text, actions = processor.process_text("delete that please")
|
||||
# Adjust expected text to match actual behavior
|
||||
self.assertEqual(text, "please")
|
||||
self.assertEqual(actions, ["delete_last"])
|
||||
print("✓ Action command processing works correctly")
|
||||
|
||||
# Test format commands
|
||||
text, actions = processor.process_text("capitalize hello")
|
||||
self.assertEqual(text, "Hello")
|
||||
self.assertEqual(actions, [])
|
||||
print("✓ Format command processing works correctly")
|
||||
|
||||
# Test config manager with a temporary test directory
|
||||
class ConfigManagerTests(unittest.TestCase):
|
||||
"""Basic tests for the ConfigManager class."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up a temporary config directory for testing."""
|
||||
# Create temp directory for configs
|
||||
self.temp_dir = tempfile.TemporaryDirectory()
|
||||
|
||||
# Patch the CONFIG_DIR constant
|
||||
self.patcher = patch('src.ui.config_manager.CONFIG_DIR', self.temp_dir.name)
|
||||
self.patcher.start()
|
||||
|
||||
# Patch the CONFIG_FILE constant
|
||||
config_file = os.path.join(self.temp_dir.name, "config.json")
|
||||
self.file_patcher = patch('src.ui.config_manager.CONFIG_FILE', config_file)
|
||||
self.file_patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after tests."""
|
||||
self.patcher.stop()
|
||||
self.file_patcher.stop()
|
||||
self.temp_dir.cleanup()
|
||||
|
||||
def test_config_defaults(self):
|
||||
"""Test that default configuration values are set correctly."""
|
||||
# Import config manager
|
||||
from src.ui.config_manager import ConfigManager, DEFAULT_CONFIG
|
||||
|
||||
# Create config manager
|
||||
config = ConfigManager()
|
||||
|
||||
# Test default values
|
||||
self.assertEqual(config.get("recognition", "engine"), "vosk")
|
||||
self.assertEqual(config.get("shortcuts", "toggle_recognition"), "alt+shift+v")
|
||||
self.assertEqual(config.get("ui", "show_notifications"), True)
|
||||
print("✓ Default configuration values are correct")
|
||||
|
||||
def test_set_and_get_config(self):
|
||||
"""Test setting and getting configuration values."""
|
||||
# Import config manager
|
||||
from src.ui.config_manager import ConfigManager
|
||||
|
||||
# Create config manager
|
||||
config = ConfigManager()
|
||||
|
||||
# Test setting and getting a value
|
||||
self.assertTrue(config.set("recognition", "engine", "whisper"))
|
||||
self.assertEqual(config.get("recognition", "engine"), "whisper")
|
||||
|
||||
# Test saving and reloading
|
||||
self.assertTrue(config.save_config())
|
||||
|
||||
# Create a new instance to load from file
|
||||
config2 = ConfigManager()
|
||||
self.assertEqual(config2.get("recognition", "engine"), "whisper")
|
||||
print("✓ Setting, saving, and loading configuration works correctly")
|
||||
|
||||
# Simplified test for keyboard shortcuts that doesn't require the pynput module directly
|
||||
class SimpleKeyboardShortcutTests(unittest.TestCase):
|
||||
"""Simplified tests for keyboard shortcut functionality without direct pynput imports."""
|
||||
|
||||
def test_shortcut_parsing(self):
|
||||
"""Test that keyboard shortcut parsing works correctly."""
|
||||
# Define a mock KeyboardShortcutManager class to avoid importing pynput
|
||||
class MockKeyboardShortcutManager:
|
||||
def __init__(self):
|
||||
self.shortcuts = {}
|
||||
self.current_keys = set()
|
||||
|
||||
def parse_shortcut(self, shortcut_str):
|
||||
"""Parse a shortcut string into a list of key identifiers."""
|
||||
return shortcut_str.lower().split('+')
|
||||
|
||||
def register_shortcut(self, name, shortcut, callback):
|
||||
"""Register a keyboard shortcut."""
|
||||
self.shortcuts[name] = {
|
||||
"keys": self.parse_shortcut(shortcut),
|
||||
"callback": callback
|
||||
}
|
||||
return True
|
||||
|
||||
# Create an instance of our mock manager
|
||||
shortcut_manager = MockKeyboardShortcutManager()
|
||||
|
||||
# Test shortcut parsing
|
||||
shortcut_manager.register_shortcut("test", "ctrl+shift+t", lambda: None)
|
||||
self.assertIn("test", shortcut_manager.shortcuts)
|
||||
self.assertEqual(shortcut_manager.shortcuts["test"]["keys"], ["ctrl", "shift", "t"])
|
||||
print("✓ Keyboard shortcut parsing works correctly")
|
||||
|
||||
# Test multiple shortcut registration
|
||||
callback_called = False
|
||||
def test_callback():
|
||||
nonlocal callback_called
|
||||
callback_called = True
|
||||
|
||||
shortcut_manager.register_shortcut("toggle_dictation", "alt+shift+v", test_callback)
|
||||
self.assertIn("toggle_dictation", shortcut_manager.shortcuts)
|
||||
self.assertEqual(shortcut_manager.shortcuts["toggle_dictation"]["keys"], ["alt", "shift", "v"])
|
||||
self.assertEqual(shortcut_manager.shortcuts["toggle_dictation"]["callback"], test_callback)
|
||||
print("✓ Multiple keyboard shortcuts can be registered")
|
||||
|
||||
# Mock test for the detection algorithm - without the dependency
|
||||
def test_shortcut_detection_logic(self):
|
||||
"""Test the logic for detecting keyboard shortcuts."""
|
||||
|
||||
# Define a simple shortcut detection algorithm
|
||||
def check_shortcut_triggered(current_keys, shortcut_keys):
|
||||
"""Check if the currently pressed keys match a shortcut."""
|
||||
return set(shortcut_keys).issubset(set(current_keys)) and len(current_keys) == len(shortcut_keys)
|
||||
|
||||
# Test detection with various key combinations
|
||||
# Case 1: All keys in the shortcut are pressed
|
||||
current_keys = ["alt", "shift", "v"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
self.assertTrue(check_shortcut_triggered(current_keys, shortcut_keys))
|
||||
|
||||
# Case 2: Some keys in the shortcut are pressed
|
||||
current_keys = ["alt", "v"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
self.assertFalse(check_shortcut_triggered(current_keys, shortcut_keys))
|
||||
|
||||
# Case 3: Extra keys are pressed
|
||||
current_keys = ["alt", "shift", "v", "ctrl"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
self.assertFalse(check_shortcut_triggered(current_keys, shortcut_keys))
|
||||
|
||||
# Case 4: Different shortcut is pressed
|
||||
current_keys = ["ctrl", "shift", "t"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
self.assertFalse(check_shortcut_triggered(current_keys, shortcut_keys))
|
||||
|
||||
print("✓ Keyboard shortcut detection logic works correctly")
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,77 +0,0 @@
|
||||
"""
|
||||
Basic tests for the Vocalinux application.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from src.speech_recognition.recognition_manager import SpeechRecognitionManager, RecognitionState
|
||||
from src.text_injection.text_injector import TextInjector, DesktopEnvironment
|
||||
|
||||
|
||||
class TestSpeechRecognition(unittest.TestCase):
|
||||
"""Test cases for the speech recognition manager."""
|
||||
|
||||
@patch('src.speech_recognition.recognition_manager.os.makedirs')
|
||||
def test_init_state(self, mock_makedirs):
|
||||
"""Test the initial state of the speech recognition manager."""
|
||||
manager = SpeechRecognitionManager(engine="vosk", model_size="small")
|
||||
self.assertEqual(manager.state, RecognitionState.IDLE)
|
||||
self.assertEqual(manager.engine, "vosk")
|
||||
self.assertEqual(manager.model_size, "small")
|
||||
|
||||
@patch('src.speech_recognition.recognition_manager.os.makedirs')
|
||||
def test_callbacks(self, mock_makedirs):
|
||||
"""Test callback registration and invocation."""
|
||||
manager = SpeechRecognitionManager(engine="vosk", model_size="small")
|
||||
|
||||
# Mock callbacks
|
||||
text_callback = MagicMock()
|
||||
state_callback = MagicMock()
|
||||
|
||||
# Register callbacks
|
||||
manager.register_text_callback(text_callback)
|
||||
manager.register_state_callback(state_callback)
|
||||
|
||||
# Verify callbacks were registered
|
||||
self.assertIn(text_callback, manager.text_callbacks)
|
||||
self.assertIn(state_callback, manager.state_callbacks)
|
||||
|
||||
# Test state update
|
||||
manager._update_state(RecognitionState.LISTENING)
|
||||
state_callback.assert_called_once_with(RecognitionState.LISTENING)
|
||||
|
||||
|
||||
class TestTextInjection(unittest.TestCase):
|
||||
"""Test cases for the text injector."""
|
||||
|
||||
@patch('src.text_injection.text_injector.shutil.which')
|
||||
@patch('src.text_injection.text_injector.TextInjector._detect_environment')
|
||||
def test_init_x11(self, mock_detect, mock_which):
|
||||
"""Test initialization in X11 environment."""
|
||||
mock_detect.return_value = DesktopEnvironment.X11
|
||||
mock_which.return_value = '/usr/bin/xdotool' # Simulate xdotool exists
|
||||
|
||||
injector = TextInjector()
|
||||
self.assertEqual(injector.environment, DesktopEnvironment.X11)
|
||||
|
||||
@patch('src.text_injection.text_injector.subprocess.run')
|
||||
@patch('src.text_injection.text_injector.shutil.which')
|
||||
@patch('src.text_injection.text_injector.TextInjector._detect_environment')
|
||||
def test_inject_text_x11(self, mock_detect, mock_which, mock_run):
|
||||
"""Test text injection in X11 environment."""
|
||||
mock_detect.return_value = DesktopEnvironment.X11
|
||||
mock_which.return_value = '/usr/bin/xdotool'
|
||||
|
||||
injector = TextInjector()
|
||||
injector.inject_text("Hello, world!")
|
||||
|
||||
mock_run.assert_called_once()
|
||||
args, kwargs = mock_run.call_args
|
||||
self.assertEqual(args[0][0], "xdotool")
|
||||
self.assertEqual(args[0][2], "--clearmodifiers")
|
||||
self.assertEqual(args[0][3], "Hello, world!")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Tests for the CommandProcessor component.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.speech_recognition.command_processor import CommandProcessor
|
||||
|
||||
|
||||
class TestCommandProcessor(unittest.TestCase):
|
||||
"""Test cases for the command processor."""
|
||||
|
||||
def test_command_processing(self):
|
||||
"""Test basic command processing functionality."""
|
||||
# Create processor
|
||||
processor = CommandProcessor()
|
||||
|
||||
# Test text commands (punctuation, etc.)
|
||||
text, actions = processor.process_text("add a period to the end")
|
||||
assert text == "add a . to the end"
|
||||
assert actions == []
|
||||
|
||||
# Test action commands
|
||||
text, actions = processor.process_text("delete that please")
|
||||
# Adjust expected text to match actual behavior
|
||||
assert text == "please"
|
||||
assert actions == ["delete_last"]
|
||||
|
||||
# Test format commands
|
||||
text, actions = processor.process_text("capitalize hello")
|
||||
assert text == "Hello"
|
||||
assert actions == []
|
||||
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
Tests for the ConfigManager component.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.ui.config_manager import ConfigManager
|
||||
|
||||
|
||||
class TestConfigManager(unittest.TestCase):
|
||||
"""Test cases for the config manager."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up a temporary config directory for testing."""
|
||||
# Create temp directory for configs
|
||||
self.temp_dir = tempfile.TemporaryDirectory()
|
||||
|
||||
# Patch the CONFIG_DIR constant
|
||||
self.patcher = patch("src.ui.config_manager.CONFIG_DIR", self.temp_dir.name)
|
||||
self.patcher.start()
|
||||
|
||||
# Patch the CONFIG_FILE constant
|
||||
config_file = os.path.join(self.temp_dir.name, "config.json")
|
||||
self.file_patcher = patch("src.ui.config_manager.CONFIG_FILE", config_file)
|
||||
self.file_patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after tests."""
|
||||
self.patcher.stop()
|
||||
self.file_patcher.stop()
|
||||
self.temp_dir.cleanup()
|
||||
|
||||
def test_config_defaults(self):
|
||||
"""Test that default configuration values are set correctly."""
|
||||
# Create config manager
|
||||
config = ConfigManager()
|
||||
|
||||
# Test default values
|
||||
assert config.get("recognition", "engine") == "vosk"
|
||||
assert config.get("shortcuts", "toggle_recognition") == "alt+shift+v"
|
||||
assert config.get("ui", "show_notifications") is True
|
||||
|
||||
def test_set_and_get_config(self):
|
||||
"""Test setting and getting configuration values."""
|
||||
# Create config manager
|
||||
config = ConfigManager()
|
||||
|
||||
# Test setting and getting a value
|
||||
assert config.set("recognition", "engine", "whisper") is True
|
||||
assert config.get("recognition", "engine") == "whisper"
|
||||
|
||||
# Test saving and reloading
|
||||
assert config.save_config() is True
|
||||
|
||||
# Create a new instance to load from file
|
||||
config2 = ConfigManager()
|
||||
assert config2.get("recognition", "engine") == "whisper"
|
||||
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
Tests for the keyboard shortcuts functionality.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class TestKeyboardShortcuts(unittest.TestCase):
|
||||
"""Test cases for keyboard shortcuts functionality."""
|
||||
|
||||
def test_shortcut_parsing(self):
|
||||
"""Test that keyboard shortcut parsing works correctly."""
|
||||
|
||||
# Define a mock KeyboardShortcutManager class to avoid importing pynput
|
||||
class MockKeyboardShortcutManager:
|
||||
def __init__(self):
|
||||
self.shortcuts = {}
|
||||
self.current_keys = set()
|
||||
|
||||
def parse_shortcut(self, shortcut_str):
|
||||
"""Parse a shortcut string into a list of key identifiers."""
|
||||
return shortcut_str.lower().split("+")
|
||||
|
||||
def register_shortcut(self, name, shortcut, callback):
|
||||
"""Register a keyboard shortcut."""
|
||||
self.shortcuts[name] = {
|
||||
"keys": self.parse_shortcut(shortcut),
|
||||
"callback": callback,
|
||||
}
|
||||
return True
|
||||
|
||||
# Create an instance of our mock manager
|
||||
shortcut_manager = MockKeyboardShortcutManager()
|
||||
|
||||
# Test shortcut parsing
|
||||
shortcut_manager.register_shortcut("test", "ctrl+shift+t", lambda: None)
|
||||
assert "test" in shortcut_manager.shortcuts
|
||||
assert shortcut_manager.shortcuts["test"]["keys"] == ["ctrl", "shift", "t"]
|
||||
|
||||
# Test multiple shortcut registration
|
||||
callback_called = False
|
||||
|
||||
def test_callback():
|
||||
nonlocal callback_called
|
||||
callback_called = True
|
||||
|
||||
shortcut_manager.register_shortcut(
|
||||
"toggle_dictation", "alt+shift+v", test_callback
|
||||
)
|
||||
assert "toggle_dictation" in shortcut_manager.shortcuts
|
||||
assert shortcut_manager.shortcuts["toggle_dictation"]["keys"] == [
|
||||
"alt",
|
||||
"shift",
|
||||
"v",
|
||||
]
|
||||
assert (
|
||||
shortcut_manager.shortcuts["toggle_dictation"]["callback"] == test_callback
|
||||
)
|
||||
|
||||
# Mock test for the detection algorithm - without the dependency
|
||||
def test_shortcut_detection_logic(self):
|
||||
"""Test the logic for detecting keyboard shortcuts."""
|
||||
|
||||
# Define a simple shortcut detection algorithm
|
||||
def check_shortcut_triggered(current_keys, shortcut_keys):
|
||||
"""Check if the currently pressed keys match a shortcut."""
|
||||
return set(shortcut_keys).issubset(set(current_keys)) and len(
|
||||
current_keys
|
||||
) == len(shortcut_keys)
|
||||
|
||||
# Test detection with various key combinations
|
||||
# Case 1: All keys in the shortcut are pressed
|
||||
current_keys = ["alt", "shift", "v"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
assert check_shortcut_triggered(current_keys, shortcut_keys) is True
|
||||
|
||||
# Case 2: Some keys in the shortcut are pressed
|
||||
current_keys = ["alt", "v"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
assert check_shortcut_triggered(current_keys, shortcut_keys) is False
|
||||
|
||||
# Case 3: Extra keys are pressed
|
||||
current_keys = ["alt", "shift", "v", "ctrl"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
assert check_shortcut_triggered(current_keys, shortcut_keys) is False
|
||||
|
||||
# Case 4: Different shortcut is pressed
|
||||
current_keys = ["ctrl", "shift", "t"]
|
||||
shortcut_keys = ["alt", "shift", "v"]
|
||||
assert check_shortcut_triggered(current_keys, shortcut_keys) is False
|
||||
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
Tests for the SpeechRecognitionManager component.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.speech_recognition.recognition_manager import (
|
||||
RecognitionState,
|
||||
SpeechRecognitionManager,
|
||||
)
|
||||
|
||||
|
||||
class TestSpeechRecognition(unittest.TestCase):
|
||||
"""Test cases for the speech recognition manager."""
|
||||
|
||||
@patch("src.speech_recognition.recognition_manager.os.path.exists")
|
||||
@patch("src.speech_recognition.recognition_manager.os.makedirs")
|
||||
@patch(
|
||||
"src.speech_recognition.recognition_manager.SpeechRecognitionManager._get_vosk_model_path"
|
||||
)
|
||||
def test_init_state(self, mock_get_model_path, mock_makedirs, mock_exists):
|
||||
"""Test the initial state of the speech recognition manager."""
|
||||
# Mock the file existence check and model path
|
||||
mock_exists.return_value = True
|
||||
mock_get_model_path.return_value = "/fake/path/to/model"
|
||||
|
||||
# We need to patch the import of vosk to avoid errors
|
||||
with patch.dict("sys.modules", {"vosk": MagicMock()}):
|
||||
manager = SpeechRecognitionManager(engine="vosk", model_size="small")
|
||||
assert manager.state == RecognitionState.IDLE
|
||||
assert manager.engine == "vosk"
|
||||
assert manager.model_size == "small"
|
||||
|
||||
@patch("src.speech_recognition.recognition_manager.os.path.exists")
|
||||
@patch("src.speech_recognition.recognition_manager.os.makedirs")
|
||||
@patch(
|
||||
"src.speech_recognition.recognition_manager.SpeechRecognitionManager._get_vosk_model_path"
|
||||
)
|
||||
def test_callbacks(self, mock_get_model_path, mock_makedirs, mock_exists):
|
||||
"""Test callback registration and invocation."""
|
||||
# Mock the file existence check and model path
|
||||
mock_exists.return_value = True
|
||||
mock_get_model_path.return_value = "/fake/path/to/model"
|
||||
|
||||
# We need to patch the import of vosk to avoid errors
|
||||
with patch.dict("sys.modules", {"vosk": MagicMock()}):
|
||||
manager = SpeechRecognitionManager(engine="vosk", model_size="small")
|
||||
|
||||
# Mock callbacks
|
||||
text_callback = MagicMock()
|
||||
state_callback = MagicMock()
|
||||
|
||||
# Register callbacks
|
||||
manager.register_text_callback(text_callback)
|
||||
manager.register_state_callback(state_callback)
|
||||
|
||||
# Verify callbacks were registered
|
||||
assert text_callback in manager.text_callbacks
|
||||
assert state_callback in manager.state_callbacks
|
||||
|
||||
# Test state update
|
||||
manager._update_state(RecognitionState.LISTENING)
|
||||
state_callback.assert_called_once_with(RecognitionState.LISTENING)
|
||||
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Tests for the TextInjector component.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.text_injection.text_injector import DesktopEnvironment, TextInjector
|
||||
|
||||
|
||||
class TestTextInjector(unittest.TestCase):
|
||||
"""Test cases for the text injector."""
|
||||
|
||||
@patch("src.text_injection.text_injector.shutil.which")
|
||||
@patch("src.text_injection.text_injector.TextInjector._detect_environment")
|
||||
def test_init_x11(self, mock_detect, mock_which):
|
||||
"""Test initialization in X11 environment."""
|
||||
mock_detect.return_value = DesktopEnvironment.X11
|
||||
mock_which.return_value = "/usr/bin/xdotool" # Simulate xdotool exists
|
||||
|
||||
injector = TextInjector()
|
||||
assert injector.environment == DesktopEnvironment.X11
|
||||
|
||||
@patch("src.text_injection.text_injector.subprocess.run")
|
||||
@patch("src.text_injection.text_injector.shutil.which")
|
||||
@patch("src.text_injection.text_injector.TextInjector._detect_environment")
|
||||
def test_inject_text_x11(self, mock_detect, mock_which, mock_run):
|
||||
"""Test text injection in X11 environment."""
|
||||
mock_detect.return_value = DesktopEnvironment.X11
|
||||
mock_which.return_value = "/usr/bin/xdotool"
|
||||
|
||||
injector = TextInjector()
|
||||
injector.inject_text("Hello, world!")
|
||||
|
||||
# The TextInjector makes two subprocess calls:
|
||||
# 1. To type the text
|
||||
# 2. To press Escape to exit insertion mode
|
||||
assert mock_run.call_count == 2
|
||||
first_call_args = mock_run.call_args_list[0][0][0]
|
||||
second_call_args = mock_run.call_args_list[1][0][0]
|
||||
|
||||
# Verify first call types the text
|
||||
assert first_call_args[0] == "xdotool"
|
||||
assert first_call_args[2] == "--clearmodifiers"
|
||||
assert first_call_args[3] == "Hello, world!"
|
||||
|
||||
# Verify second call presses Escape
|
||||
assert second_call_args[0] == "xdotool"
|
||||
assert second_call_args[2] == "--clearmodifiers"
|
||||
assert second_call_args[3] == "Escape"
|
||||
|
||||
@patch("shutil.which")
|
||||
@patch("os.environ.get")
|
||||
def test_environment_detection_x11(self, mock_environ_get, mock_which):
|
||||
"""Test environment detection for X11."""
|
||||
# Mock environment variables for X11
|
||||
mock_environ_get.return_value = "x11"
|
||||
mock_which.return_value = "/usr/bin/xdotool"
|
||||
|
||||
# Create injector with mocked environment
|
||||
injector = TextInjector()
|
||||
|
||||
# Check environment detection
|
||||
assert injector.environment == DesktopEnvironment.X11
|
||||
|
||||
@patch("shutil.which")
|
||||
@patch("os.environ.get")
|
||||
def test_environment_detection_wayland(self, mock_environ_get, mock_which):
|
||||
"""Test environment detection for Wayland."""
|
||||
# Mock environment variables for Wayland
|
||||
mock_environ_get.return_value = "wayland"
|
||||
mock_which.return_value = "/usr/bin/wtype"
|
||||
|
||||
# Create injector with mocked environment
|
||||
injector = TextInjector()
|
||||
|
||||
# Check environment detection
|
||||
assert injector.environment == DesktopEnvironment.WAYLAND
|
||||
Reference in New Issue
Block a user