"""Test suite for DaemonControl system tray functionality. Tests cover: - SystemTrayApp initialization - Icon loading - Menu creation - Status updates - Basic functionality (without actual Qt event loop) """ import sys from pathlib import Path import pytest # Add parent directory to path sys.path.insert(0, str(Path(__file__).parent.parent)) # Check if PyQt6 is available try: from PyQt6.QtWidgets import QApplication PYQT6_AVAILABLE = True except ImportError: PYQT6_AVAILABLE = False @pytest.fixture(scope='module') def qapp(): """Create QApplication for tests.""" if not PYQT6_AVAILABLE: pytest.skip("PyQt6 not available") app = QApplication.instance() if app is None: app = QApplication(sys.argv) yield app @pytest.mark.skipif(not PYQT6_AVAILABLE, reason="PyQt6 not installed") class TestSystemTrayApp: """Tests for SystemTrayApp class.""" def test_system_tray_creation(self, qapp): """Test SystemTrayApp can be instantiated.""" from gui import SystemTrayApp tray = SystemTrayApp() assert tray is not None assert tray.tray_icon is not None assert tray.daemon_process is None assert tray.daemon_running is False def test_icons_loaded(self, qapp): """Test that icons are loaded correctly.""" from gui import SystemTrayApp tray = SystemTrayApp() # Check icons are loaded assert tray.icon_active is not None assert tray.icon_inactive is not None assert tray.icon_error is not None # Check icons are not null assert not tray.icon_active.isNull() assert not tray.icon_inactive.isNull() assert not tray.icon_error.isNull() def test_initial_state(self, qapp): """Test initial state is stopped.""" from gui import SystemTrayApp tray = SystemTrayApp() assert tray.daemon_running is False assert tray.daemon_process is None assert "Stopped" in tray.status_action.text() def test_menu_creation(self, qapp): """Test context menu has all required actions.""" from gui import SystemTrayApp tray = SystemTrayApp() menu = tray.tray_icon.contextMenu() actions = menu.actions() # Should have at least 8 items (including separators) assert len(actions) >= 8 # Get action texts (excluding separators) action_texts = [a.text() for a in actions if not a.isSeparator()] # Check key actions exist assert any('Start Daemon' in text for text in action_texts) assert any('Stop Daemon' in text for text in action_texts) assert any('Reload' in text for text in action_texts) assert any('Logs' in text for text in action_texts) assert any('Exit' in text for text in action_texts) def test_status_update_running(self, qapp): """Test _update_status changes UI correctly for running state.""" from gui import SystemTrayApp tray = SystemTrayApp() # Update to running state tray._update_status('running') # Check UI state assert 'Running' in tray.status_action.text() assert not tray.start_action.isEnabled() assert tray.stop_action.isEnabled() assert tray.reload_action.isEnabled() assert "Running" in tray.tray_icon.toolTip() def test_status_update_stopped(self, qapp): """Test _update_status changes UI correctly for stopped state.""" from gui import SystemTrayApp tray = SystemTrayApp() # Update to stopped state tray._update_status('stopped') # Check UI state assert 'Stopped' in tray.status_action.text() assert tray.start_action.isEnabled() assert not tray.stop_action.isEnabled() assert not tray.reload_action.isEnabled() assert "Stopped" in tray.tray_icon.toolTip() def test_status_update_error(self, qapp): """Test _update_status changes UI correctly for error state.""" from gui import SystemTrayApp tray = SystemTrayApp() # Update to error state tray._update_status('error') # Check UI state assert 'Error' in tray.status_action.text() assert tray.start_action.isEnabled() assert not tray.stop_action.isEnabled() assert not tray.reload_action.isEnabled() assert "Error" in tray.tray_icon.toolTip() def test_status_signal_emitted(self, qapp): """Test status_changed signal is emitted.""" from gui import SystemTrayApp tray = SystemTrayApp() # Track signal emissions signal_received = [] def on_status_changed(status): signal_received.append(status) tray.status_changed.connect(on_status_changed) # Update status tray._update_status('running') tray._update_status('stopped') # Check signal was emitted assert 'running' in signal_received assert 'stopped' in signal_received def test_timer_initialized(self, qapp): """Test status timer is initialized.""" from gui import SystemTrayApp tray = SystemTrayApp() assert tray.status_timer is not None assert tray.status_timer.isActive() assert tray.status_timer.interval() == 5000 # 5 seconds class TestIconFiles: """Tests for icon file existence.""" def test_icon_files_exist(self): """Test that icon files were created.""" resources_dir = Path(__file__).parent.parent / 'gui' / 'resources' icon_active = resources_dir / 'icon_active.png' icon_inactive = resources_dir / 'icon_inactive.png' icon_error = resources_dir / 'icon_error.png' assert icon_active.exists(), f"Missing: {icon_active}" assert icon_inactive.exists(), f"Missing: {icon_inactive}" assert icon_error.exists(), f"Missing: {icon_error}" def test_icon_files_not_empty(self): """Test that icon files have content.""" resources_dir = Path(__file__).parent.parent / 'gui' / 'resources' icons = [ resources_dir / 'icon_active.png', resources_dir / 'icon_inactive.png', resources_dir / 'icon_error.png' ] for icon_path in icons: if icon_path.exists(): size = icon_path.stat().st_size assert size > 0, f"Icon file is empty: {icon_path}" assert size > 100, f"Icon file too small: {icon_path} ({size} bytes)" if __name__ == '__main__': pytest.main([__file__, '-v'])