# Unit tests for pronunciation_dictionary_manager.py. # # Copyright 2026 Igalia, S.L. # Author: Joanmarie Diggs # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. # pylint: disable=wrong-import-position # pylint: disable=protected-access # pylint: disable=import-outside-toplevel """Unit tests for pronunciation_dictionary_manager.py.""" from __future__ import annotations from typing import TYPE_CHECKING import pytest if TYPE_CHECKING: from cthulhu_test_context import CthulhuTestContext @pytest.mark.unit class TestPronunciationDictionaryManager: """Test PronunciationDictionaryManager class.""" def _setup_manager(self, test_context: CthulhuTestContext): """Set up mocks for pronunciation_dictionary_manager dependencies.""" additional_modules = [ "cthulhu.guilabels", "cthulhu.messages", "cthulhu.preferences_grid_base", "cthulhu.script_manager", "cthulhu.speech_manager", ] essential_modules = test_context.setup_shared_dependencies(additional_modules) # Set up guilabels with required constants guilabels = essential_modules["cthulhu.guilabels"] guilabels.PRONUNCIATION = "Pronunciation" guilabels.PRONUNCIATION_DICTIONARY = "Pronunciation Dictionary" guilabels.PRONUNCIATION_DICTIONARY_INFO = "Add custom pronunciations." guilabels.DICTIONARY_NEW_ENTRY = "New Entry" guilabels.DICTIONARY_DELETE = "Delete" guilabels.DICTIONARY_EMPTY = "No entries" guilabels.DICTIONARY_ACTUAL_STRING = "Actual String" guilabels.DICTIONARY_REPLACEMENT_STRING = "Replacement String" guilabels.ADD_NEW_PRONUNCIATION = "Add New Pronunciation" guilabels.EDIT_PRONUNCIATION = "Edit Pronunciation" guilabels.DIALOG_CANCEL = "Cancel" guilabels.DIALOG_ADD = "Add" guilabels.DIALOG_EDIT = "Edit" # Set up messages messages = essential_modules["cthulhu.messages"] messages.PRONUNCIATION_DELETED = "Pronunciation %s deleted" # Set up speech_manager speech_manager_mock = essential_modules["cthulhu.speech_manager"] speech_instance = speech_manager_mock.get_manager.return_value speech_instance.get_use_pronunciation_dictionary.return_value = True speech_instance.set_use_pronunciation_dictionary.return_value = True # Import and return the module from cthulhu import pronunciation_dictionary_manager return pronunciation_dictionary_manager, essential_modules def test_get_manager_returns_singleton(self, test_context: CthulhuTestContext) -> None: """Test get_manager returns the same instance.""" module, _mocks = self._setup_manager(test_context) manager1 = module.get_manager() manager2 = module.get_manager() assert manager1 is manager2 def test_manager_initial_state(self, test_context: CthulhuTestContext) -> None: """Test manager initializes with empty dictionary.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() # Reset the dictionary to ensure clean state manager.set_dictionary({}) assert manager.get_dictionary() == {} def test_get_pronunciation_returns_word_when_not_found( self, test_context: CthulhuTestContext, ) -> None: """Test get_pronunciation returns the original word when not in dictionary.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) result = manager.get_pronunciation("hello") assert result == "hello" def test_get_pronunciation_returns_replacement_when_found( self, test_context: CthulhuTestContext, ) -> None: """Test get_pronunciation returns replacement when word is in dictionary.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) manager.set_pronunciation("hello", "hi there") result = manager.get_pronunciation("hello") assert result == "hi there" def test_get_pronunciation_is_case_insensitive(self, test_context: CthulhuTestContext) -> None: """Test get_pronunciation lookup is case insensitive.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) manager.set_pronunciation("HELLO", "hi there") assert manager.get_pronunciation("hello") == "hi there" assert manager.get_pronunciation("Hello") == "hi there" assert manager.get_pronunciation("HELLO") == "hi there" def test_set_pronunciation_stores_lowercase_key(self, test_context: CthulhuTestContext) -> None: """Test set_pronunciation stores key as lowercase.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) manager.set_pronunciation("MixedCase", "replacement") dictionary = manager.get_dictionary() assert "mixedcase" in dictionary assert "MixedCase" not in dictionary def test_set_dictionary_replaces_entire_dictionary(self, test_context: CthulhuTestContext) -> None: """Test set_dictionary replaces the entire dictionary.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_pronunciation("old", "old replacement") manager.set_dictionary({"new": "new replacement"}) dictionary = manager.get_dictionary() assert "old" not in dictionary assert dictionary.get("new") == "new replacement" def test_multiple_pronunciations(self, test_context: CthulhuTestContext) -> None: """Test manager handles multiple pronunciations correctly.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) manager.set_pronunciation("word1", "replacement1") manager.set_pronunciation("word2", "replacement2") manager.set_pronunciation("word3", "replacement3") assert manager.get_pronunciation("word1") == "replacement1" assert manager.get_pronunciation("word2") == "replacement2" assert manager.get_pronunciation("word3") == "replacement3" assert len(manager.get_dictionary()) == 3 def test_set_pronunciation_overwrites_existing(self, test_context: CthulhuTestContext) -> None: """Test set_pronunciation overwrites existing entry for same word.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) manager.set_pronunciation("word", "first replacement") manager.set_pronunciation("word", "second replacement") assert manager.get_pronunciation("word") == "second replacement" assert len(manager.get_dictionary()) == 1 @pytest.mark.unit class TestPronunciationDictionaryManagerIntegration: """Integration tests for pronunciation dictionary manager.""" def _setup_manager(self, test_context: CthulhuTestContext): """Set up mocks for pronunciation_dictionary_manager dependencies.""" additional_modules = [ "cthulhu.guilabels", "cthulhu.messages", "cthulhu.preferences_grid_base", "cthulhu.script_manager", "cthulhu.speech_manager", ] essential_modules = test_context.setup_shared_dependencies(additional_modules) # Set up guilabels with required constants guilabels = essential_modules["cthulhu.guilabels"] guilabels.PRONUNCIATION = "Pronunciation" guilabels.PRONUNCIATION_DICTIONARY = "Pronunciation Dictionary" guilabels.PRONUNCIATION_DICTIONARY_INFO = "Add custom pronunciations." guilabels.DICTIONARY_NEW_ENTRY = "New Entry" guilabels.DICTIONARY_DELETE = "Delete" guilabels.DICTIONARY_EMPTY = "No entries" guilabels.DICTIONARY_ACTUAL_STRING = "Actual String" guilabels.DICTIONARY_REPLACEMENT_STRING = "Replacement String" guilabels.ADD_NEW_PRONUNCIATION = "Add New Pronunciation" guilabels.EDIT_PRONUNCIATION = "Edit Pronunciation" guilabels.DIALOG_CANCEL = "Cancel" guilabels.DIALOG_ADD = "Add" guilabels.DIALOG_EDIT = "Edit" # Set up messages messages = essential_modules["cthulhu.messages"] messages.PRONUNCIATION_DELETED = "Pronunciation %s deleted" # Set up speech_manager speech_manager_mock = essential_modules["cthulhu.speech_manager"] speech_instance = speech_manager_mock.get_manager.return_value speech_instance.get_use_pronunciation_dictionary.return_value = True speech_instance.set_use_pronunciation_dictionary.return_value = True # Import and return the module from cthulhu import pronunciation_dictionary_manager return pronunciation_dictionary_manager, essential_modules def test_pronunciation_application_to_text(self, test_context: CthulhuTestContext) -> None: """Test that pronunciation dictionary can be used to process text.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) manager.set_pronunciation("github", "git hub") manager.set_pronunciation("cli", "command line interface") # Simulate applying pronunciations to a sentence words = ["Check", " ", "out", " ", "the", " ", "GitHub", " ", "CLI"] result = "".join(map(manager.get_pronunciation, words)) # GitHub becomes "git hub" because lookup is case insensitive assert "git hub" in result # CLI becomes "command line interface" assert "command line interface" in result def test_empty_dictionary_returns_original_words(self, test_context: CthulhuTestContext) -> None: """Test that empty dictionary returns all original words unchanged.""" module, _mocks = self._setup_manager(test_context) manager = module.get_manager() manager.set_dictionary({}) words = ["Hello", " ", "world", "!"] result = "".join(map(manager.get_pronunciation, words)) assert result == "Hello world!"