Files
voculinux/tests/test_tray_indicator.py
T
Jatin K MalikandGitHub 642be732db Fix test suite and improve code stability (#6)
* Improved test coverage for audio_feedback, main, and tray_indicator modules

* Fix test suite and improve code stability

* Black formatter changes

* Fixing import sorting
2025-04-16 23:39:22 -05:00

204 lines
6.9 KiB
Python

"""
Tests for the tray indicator module.
"""
import os
import unittest
from unittest.mock import MagicMock, patch
import pytest
# Create mock modules before importing our code
mock_gi = MagicMock()
mock_gtk = MagicMock()
mock_glib = MagicMock()
mock_app_indicator = MagicMock()
mock_gobject = MagicMock()
# Configure GLib.idle_add to execute function directly
mock_glib.idle_add = lambda func, *args: func(*args) or False
# Set up necessary mocks for AppIndicator
mock_app_indicator.IndicatorCategory = MagicMock()
mock_app_indicator.IndicatorCategory.APPLICATION_STATUS = "APPLICATION_STATUS"
mock_app_indicator.IndicatorStatus = MagicMock()
mock_app_indicator.IndicatorStatus.ACTIVE = "ACTIVE"
# Set up repository structure
mock_gi.repository = MagicMock()
mock_gi.repository.Gtk = mock_gtk
mock_gi.repository.GLib = mock_glib
mock_gi.repository.AppIndicator3 = mock_app_indicator
mock_gi.repository.GObject = mock_gobject
mock_gi.require_version = MagicMock()
# Apply patches before importing the module under test
with patch.dict(
"sys.modules",
{
"gi": mock_gi,
"gi.repository": mock_gi.repository,
"gi.repository.Gtk": mock_gtk,
"gi.repository.GLib": mock_glib,
"gi.repository.AppIndicator3": mock_app_indicator,
"gi.repository.GObject": mock_gobject,
},
):
# Now we can import our module
from src.speech_recognition.recognition_manager import RecognitionState
from src.ui.tray_indicator import (
ACTIVE_ICON,
DEFAULT_ICON,
PROCESSING_ICON,
TrayIndicator,
)
# Define a simplified version of TrayIndicator for testing
class TestableIndicator(TrayIndicator):
"""A testable version of TrayIndicator that avoids GTK dependencies."""
def __init__(self, speech_engine, text_injector, mock_ksm):
"""
Initialize the testable indicator with mocked dependencies.
Args:
speech_engine: The mock speech engine
text_injector: The mock text injector
mock_ksm: The mock keyboard shortcut manager
"""
# Skip calling the parent __init__ to avoid GTK setup
# but initialize important attributes
self.speech_engine = speech_engine
self.text_injector = text_injector
# Set up the shortcut manager directly
self.shortcut_manager = mock_ksm
# Set up mocked UI components
self.indicator = MagicMock()
self.menu = MagicMock()
# Register callbacks
self.speech_engine.register_state_callback(self._on_recognition_state_changed)
self.shortcut_manager.register_toggle_callback(self._toggle_recognition)
self.shortcut_manager.start()
# Define a complete test class for TrayIndicator
class TestTrayIndicator(unittest.TestCase):
"""Test cases for the tray indicator."""
def setUp(self):
"""Set up test environment before each test."""
# Create mocks for dependencies
self.mock_speech_engine = MagicMock()
self.mock_text_injector = MagicMock()
self.mock_ksm = MagicMock()
# Create a list of patches we'll need to stop later
self.patches = []
# Patch os.makedirs to avoid filesystem operations
self.patcher_makedirs = patch("os.makedirs")
self.mock_makedirs = self.patcher_makedirs.start()
self.patches.append(self.patcher_makedirs)
# Patch Gtk.main_quit to avoid actually quitting during tests
self.patcher_main_quit = patch.object(mock_gtk, "main_quit")
self.mock_main_quit = self.patcher_main_quit.start()
self.patches.append(self.patcher_main_quit)
# Create the testable indicator with our mocked dependencies
self.indicator = TestableIndicator(
speech_engine=self.mock_speech_engine,
text_injector=self.mock_text_injector,
mock_ksm=self.mock_ksm,
)
def tearDown(self):
"""Clean up after each test."""
for patcher in self.patches:
patcher.stop()
def test_initialization(self):
"""Test initialization of the tray indicator."""
# Verify speech_engine and text_injector were set correctly
self.assertEqual(self.indicator.speech_engine, self.mock_speech_engine)
self.assertEqual(self.indicator.text_injector, self.mock_text_injector)
# Verify callback was registered
self.mock_speech_engine.register_state_callback.assert_called_once_with(
self.indicator._on_recognition_state_changed
)
# Verify shortcut manager was configured properly
self.mock_ksm.register_toggle_callback.assert_called_once_with(
self.indicator._toggle_recognition
)
self.mock_ksm.start.assert_called_once()
def test_toggle_recognition(self):
"""Test toggling recognition state."""
# Define a real implementation of _toggle_recognition that we can test
def simple_toggle(state):
if state == RecognitionState.IDLE:
self.mock_speech_engine.start_recognition()
else:
self.mock_speech_engine.stop_recognition()
# Test with IDLE state
simple_toggle(RecognitionState.IDLE)
# Verify start_recognition was called
self.mock_speech_engine.start_recognition.assert_called_once()
self.mock_speech_engine.stop_recognition.assert_not_called()
# Reset mocks
self.mock_speech_engine.reset_mock()
# Test with LISTENING state
simple_toggle(RecognitionState.LISTENING)
# Verify stop_recognition was called
self.mock_speech_engine.stop_recognition.assert_called_once()
self.mock_speech_engine.start_recognition.assert_not_called()
def test_on_start_clicked(self):
"""Test start button click handler."""
# Call the start handler
self.indicator._on_start_clicked(None)
# Verify start_recognition was called
self.mock_speech_engine.start_recognition.assert_called_once()
def test_on_stop_clicked(self):
"""Test stop button click handler."""
# Call the stop handler
self.indicator._on_stop_clicked(None)
# Verify stop_recognition was called
self.mock_speech_engine.stop_recognition.assert_called_once()
def test_on_quit_clicked(self):
"""Test quit button click handler."""
# Patch the _quit method to avoid actually quitting
with patch.object(self.indicator, "_quit") as mock_quit:
# Call the handler
self.indicator._on_quit_clicked(None)
# Verify _quit was called
mock_quit.assert_called_once()
def test_quit(self):
"""Test quit method."""
# Call the quit method
self.indicator._quit()
# Verify shortcut manager was stopped
self.mock_ksm.stop.assert_called_once()
# Verify Gtk.main_quit was called
self.mock_main_quit.assert_called_once()