import sys import tempfile import unittest import xml.etree.ElementTree as ET from pathlib import Path from unittest import mock sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) from cthulhu import cthulhu from cthulhu import settings from cthulhu import cthulhu_gui_prefs from cthulhu import settings_manager class SoundSettingsPersistenceTests(unittest.TestCase): def test_progress_bar_beep_interval_is_user_customizable(self): self.assertIn("progressBarBeepInterval", settings.userCustomizableSettings) def test_activate_writes_progress_bar_beep_interval_default(self): with tempfile.TemporaryDirectory() as tempDir: manager = settings_manager.SettingsManager(mock.Mock()) manager.activate(prefsDir=tempDir) settingsText = Path(tempDir, "user-settings.toml").read_text(encoding="utf-8") self.assertIn("progressBarBeepInterval = 0", settingsText) class SoundPreferencesBuilderTests(unittest.TestCase): def test_ui_definition_exposes_sounds_tab_controls(self): uiPath = Path(__file__).resolve().parents[1] / "src" / "cthulhu" / "cthulhu-setup.ui" root = ET.fromstring(uiPath.read_text(encoding="utf-8")) objectIds = { element.attrib["id"] for element in root.iter() if element.tag == "object" and "id" in element.attrib } self.assertIn("soundsGrid", objectIds) self.assertIn("soundsTabLabel", objectIds) self.assertIn("enableSoundCheckButton", objectIds) self.assertIn("soundVolumeScale", objectIds) self.assertIn("progressBarBeepIntervalSpinButton", objectIds) def test_notebook_tab_positions_keep_sounds_page_and_label_aligned(self): uiPath = Path(__file__).resolve().parents[1] / "src" / "cthulhu" / "cthulhu-setup.ui" root = ET.fromstring(uiPath.read_text(encoding="utf-8")) notebook = next( element for element in root.iter() if element.tag == "object" and element.attrib.get("id") == "notebook" ) positions = {} for child in notebook.findall("child"): obj = child.find("object") if obj is None or "id" not in obj.attrib: continue position = None packing = child.find("packing") if packing is not None: for prop in packing.findall("property"): if prop.attrib.get("name") == "position": position = int(prop.text) break positions[obj.attrib["id"]] = position self.assertEqual(positions["generalGrid"], 0) self.assertEqual(positions["generalTabLabel"], 0) self.assertEqual(positions["soundsGrid"], 1) self.assertEqual(positions["soundsTabLabel"], 1) self.assertEqual(positions["voiceGrid"], 2) self.assertEqual(positions["voiceLabel"], 2) self.assertEqual(positions["textAttributesGrid"], 8) self.assertEqual(positions["textAttributesTabLabel"], 8) class SoundPreferencesControllerTests(unittest.TestCase): def test_init_sound_state_loads_sound_volume_and_beep_interval(self): gui = cthulhu_gui_prefs.CthulhuSetupGUI.__new__(cthulhu_gui_prefs.CthulhuSetupGUI) gui.prefsDict = { "enableSound": False, "soundSink": settings.SOUND_SINK_PULSE, "soundTheme": "default", "roleSoundPresentation": settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY, "soundVolume": 0.65, "progressBarBeepInterval": 0, "beepProgressBarUpdates": True, "playSoundForRole": True, "playSoundForState": True, "playSoundForPositionInSet": False, "playSoundForValue": False, } widgets = { "soundSinkCombo": mock.Mock(), "soundThemeCombo": mock.Mock(), "roleSoundPresentationCombo": mock.Mock(), "soundVolumeScale": mock.Mock(), "progressBarBeepIntervalSpinButton": mock.Mock(), "enableSoundCheckButton": mock.Mock(), "playSoundForRoleCheckButton": mock.Mock(), "playSoundForStateCheckButton": mock.Mock(), "playSoundForPositionInSetCheckButton": mock.Mock(), "playSoundForValueCheckButton": mock.Mock(), "beepProgressBarUpdatesCheckButton": mock.Mock(), } gui.get_widget = widgets.__getitem__ fakeThemeManager = mock.Mock() fakeThemeManager.getAvailableThemes.return_value = ["default"] fakeSettingsManager = mock.Mock() fakeSettingsManager.getSetting.return_value = settings.SOUND_SINK_AUTO fakeApp = mock.Mock(settingsManager=fakeSettingsManager) with ( mock.patch.object(cthulhu_gui_prefs.cthulhu, "cthulhuApp", fakeApp), mock.patch.object( cthulhu_gui_prefs.sound_theme_manager, "getManager", return_value=fakeThemeManager, ), ): gui._initSoundThemeState() widgets["enableSoundCheckButton"].set_active.assert_called_once_with(False) widgets["soundVolumeScale"].set_value.assert_called_once_with(0.65) widgets["playSoundForRoleCheckButton"].set_active.assert_called_once_with(True) widgets["playSoundForStateCheckButton"].set_active.assert_called_once_with(True) widgets["playSoundForPositionInSetCheckButton"].set_active.assert_called_once_with(False) widgets["playSoundForValueCheckButton"].set_active.assert_called_once_with(False) widgets["beepProgressBarUpdatesCheckButton"].set_active.assert_called_once_with(True) widgets["progressBarBeepIntervalSpinButton"].set_value.assert_called_once_with(0) def test_sound_value_handlers_store_current_values(self): gui = cthulhu_gui_prefs.CthulhuSetupGUI.__new__(cthulhu_gui_prefs.CthulhuSetupGUI) gui.prefsDict = {} volumeWidget = mock.Mock() volumeWidget.get_value.return_value = 0.7 beepWidget = mock.Mock() beepWidget.get_value_as_int.return_value = 0 gui.soundVolumeValueChanged(volumeWidget) gui.progressBarBeepIntervalValueChanged(beepWidget) self.assertEqual(gui.prefsDict["soundVolume"], 0.7) self.assertEqual(gui.prefsDict["progressBarBeepInterval"], 0) def test_load_profile_reinitializes_sound_state(self): gui = cthulhu_gui_prefs.CthulhuSetupGUI.__new__(cthulhu_gui_prefs.CthulhuSetupGUI) gui.saveBasicSettings = mock.Mock() gui._initGUIState = mock.Mock() gui._initSoundThemeState = mock.Mock() gui._initSpeechState = mock.Mock() gui._initEchoSpeechState = mock.Mock() gui._populateKeyBindings = mock.Mock() gui._CthulhuSetupGUI__initProfileCombo = mock.Mock() gui.prefsDict = {} fakeSettingsManager = mock.Mock() fakeSettingsManager.getGeneralSettings.return_value = {"soundVolume": 0.5} fakeApp = mock.Mock(settingsManager=fakeSettingsManager) with ( mock.patch.object(cthulhu_gui_prefs.cthulhu, "cthulhuApp", fakeApp), mock.patch.object(cthulhu_gui_prefs.cthulhu, "loadUserSettings"), mock.patch.object(cthulhu_gui_prefs.braille, "checkBrailleSetting"), ): gui.loadProfile(["Default", "default"]) gui._initSoundThemeState.assert_called_once_with() if __name__ == "__main__": unittest.main()