From 9b5b1a3ceff5b66e5b94fcb95bde51d5ab301544 Mon Sep 17 00:00:00 2001 From: Jatin K Malik Date: Sun, 5 Apr 2026 18:23:37 -0700 Subject: [PATCH] test(tray): cover degraded-startup and guard branches (#386) --- tests/test_main_args_deps.py | 20 ++++++++++++++++--- tests/test_tray_indicator.py | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/tests/test_main_args_deps.py b/tests/test_main_args_deps.py index 3b502f3..4a500ca 100644 --- a/tests/test_main_args_deps.py +++ b/tests/test_main_args_deps.py @@ -150,6 +150,19 @@ class TestCheckDependencies(unittest.TestCase): result = check_dependencies() assert result is False + def test_check_dependencies_logs_gnome_extension_guidance(self): + from vocalinux.main import check_dependencies + + with patch("vocalinux.main.logger") as mock_logger: + with patch("builtins.__import__", side_effect=ImportError("No module")): + result = check_dependencies() + + assert result is False + mock_logger.error.assert_any_call( + " NOTE: On GNOME Shell (default on Debian), you also need:" + ) + mock_logger.error.assert_any_call(" sudo apt install gnome-shell-extension-appindicator") + class TestCheckDisplayAvailable(unittest.TestCase): """Tests for check_display_available() function.""" @@ -439,15 +452,15 @@ class TestMainFunction(unittest.TestCase): "general": {"first_run": False}, } + mock_speech_manager_ctor = MagicMock(side_effect=Exception("stop")) + with patch.dict( sys.modules, { "vocalinux.single_instance": mock_single_instance, "vocalinux.common_types": MagicMock(), "vocalinux.speech_recognition": MagicMock( - recognition_manager=MagicMock( - SpeechRecognitionManager=MagicMock(side_effect=Exception("stop")) - ) + recognition_manager=MagicMock(SpeechRecognitionManager=mock_speech_manager_ctor) ), "vocalinux.text_injection.text_injector": MagicMock(), "vocalinux.ui.tray_indicator": MagicMock(), @@ -467,6 +480,7 @@ class TestMainFunction(unittest.TestCase): "No StatusNotifierWatcher found on D-Bus session bus." ) mock_logger.warning.assert_any_call("The system tray icon may not appear.") + mock_speech_manager_ctor.assert_called_once() @patch("vocalinux.main.logging") @patch("vocalinux.main.check_dependencies") diff --git a/tests/test_tray_indicator.py b/tests/test_tray_indicator.py index 7c89a95..5afeb3a 100644 --- a/tests/test_tray_indicator.py +++ b/tests/test_tray_indicator.py @@ -510,3 +510,40 @@ class TestTrayIndicator(unittest.TestCase): result = self.tray_indicator._init_indicator() self.assertEqual(result, False) mock_error_dialog.assert_called_once_with("boom") + + def test_init_indicator_creation_failure_without_prior_init_state(self): + from vocalinux.ui.tray_indicator import TrayIndicator + + tray = TrayIndicator.__new__(TrayIndicator) + tray.icon_paths = {} + tray._show_appindicator_error_dialog = MagicMock(return_value=False) + + with patch("vocalinux.ui.tray_indicator.os.path.exists", return_value=False): + with patch( + "vocalinux.ui.tray_indicator.AppIndicator3.Indicator.new_with_path", + side_effect=Exception("fresh-boom"), + ): + with patch( + "vocalinux.ui.tray_indicator.GLib.idle_add", + side_effect=lambda func, *args: func(*args), + ): + result = TrayIndicator._init_indicator(tray) + + self.assertEqual(result, False) + tray._show_appindicator_error_dialog.assert_called_once_with("fresh-boom") + self.assertFalse(hasattr(tray, "indicator")) + + def test_update_ui_returns_false_when_indicator_missing(self): + from vocalinux.common_types import RecognitionState + + if hasattr(self.tray_indicator, "indicator"): + delattr(self.tray_indicator, "indicator") + + result = self.tray_indicator._update_ui(RecognitionState.IDLE) + self.assertEqual(result, False) + + def test_set_menu_item_enabled_noop_when_menu_missing(self): + if hasattr(self.tray_indicator, "menu"): + delattr(self.tray_indicator, "menu") + + self.tray_indicator._set_menu_item_enabled("Start Voice Typing", True)