diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index 39a33517..2355cd4f 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -21,9 +21,7 @@ along with this program. If not, see . import pytest -from time import time from shutil import copyfile -from pathlib import Path from zipfile import ZipFile from mocked import causeOSError @@ -31,8 +29,6 @@ from tools import C, cmpFiles, writeFile, buildTestProject, XML_IGNORE from novelwriter import CONFIG from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout -from novelwriter.common import formatTimeStamp -from novelwriter.constants import nwFiles from novelwriter.core.tree import NWTree from novelwriter.core.index import NWIndex from novelwriter.core.project import NWProject @@ -468,8 +464,7 @@ def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd): @pytest.mark.core def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd): - """Test other project class methods and functions. - """ + """Test other project class methods and functions.""" theProject = NWProject(mockGUI) buildTestProject(theProject, fncPath) @@ -578,46 +573,6 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd): assert theProject.setTreeOrder(oldOrder) assert theProject.tree.handles() == oldOrder - # Session stats - theProject.data.setInitCounts(50, 50) - theProject.data.setCurrCounts(100, 100) - - # No path for writing - with monkeypatch.context() as mp: - mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None) - assert theProject.session.appendSession(idleTime=0) is False - - # Block open - with monkeypatch.context() as mp: - mp.setattr("builtins.open", causeOSError) - assert theProject.session.appendSession(idleTime=0) is False - - # Session too short - theProject._session._start = time() - theProject.data.setInitCounts(50, 50) - theProject.data.setCurrCounts(50, 50) - assert theProject.session.appendSession(idleTime=0) is False - - # Write entry - statsFile = theProject.storage.getMetaFile(nwFiles.SESS_FILE) - assert isinstance(statsFile, Path) - if statsFile.exists(): - statsFile.unlink() - - theProject._session._start = 1600002000 - theProject.data._initCounts = [50, 50] - theProject.data._currCounts = [200, 100] - - with monkeypatch.context() as mp: - mp.setattr("novelwriter.core.project.time", lambda: 1600005600) - assert theProject.session.appendSession(idleTime=99) - - assert statsFile.read_text(encoding="utf-8") == ( - "# Offset 100\n" - "# Start Time End Time Novel Notes Idle\n" - "%s %s 200 100 99\n" - ) % (formatTimeStamp(1600002000), formatTimeStamp(1600005600)) - # END Test testCoreProject_Methods diff --git a/tests/test_core/test_core_spellcheck.py b/tests/test_core/test_core_spellcheck.py index 9e06c803..9f2e59de 100644 --- a/tests/test_core/test_core_spellcheck.py +++ b/tests/test_core/test_core_spellcheck.py @@ -21,33 +21,118 @@ along with this program. If not, see . import sys import pytest +import enchant +from pathlib import Path + +from tools import buildTestProject from mocked import causeOSError -from tools import readFile, writeFile -from novelwriter.core.spellcheck import FakeEnchant, NWSpellEnchant +from novelwriter.constants import nwFiles +from novelwriter.core.project import NWProject +from novelwriter.core.spellcheck import FakeEnchant, NWSpellEnchant, UserDictionary @pytest.mark.core -def testCoreSpell_FakeEnchant(monkeypatch): - """Test the FakeEnchant spell checker fallback. - """ +def testCoreSpell_UserDictionary(monkeypatch, mockGUI, fncPath): + """Test the UserDictionary class.""" + project = NWProject(mockGUI) + buildTestProject(project, fncPath) + + # Check that there is no file before we start + dictFile = project.storage.getMetaFile(nwFiles.DICT_FILE) + assert isinstance(dictFile, Path) + assert not dictFile.exists() + + # Add a couple of words + userDict = UserDictionary(project) + assert userDict.add("foo") is True + assert userDict.add("bar") is True + assert userDict.add("bar") is False # No duplicates + + # Check that we have them + assert "foo" in userDict + assert "bar" in userDict + + # Check the iterator + assert sorted(userDict) == ["bar", "foo"] + + # Save the file, but fail + assert userDict._path is None + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + userDict.save() + + # There should be no file, but the file path should now be cached + assert userDict._path == dictFile + assert not dictFile.exists() + + # Break the path check + userDict._path = None + with monkeypatch.context() as mp: + mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None) + userDict.save() + + # There should still be no file + assert not dictFile.exists() + + # Save proper + userDict.save() + assert dictFile.exists() + + # Clear the dictionary + userDict._words = set() + assert sorted(userDict) == [] + + # Load the file, but fail + userDict._path = None + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + userDict.load() + + # Path is now set, but no words + assert userDict._path == dictFile + assert sorted(userDict) == [] + + # Break the path check + userDict._path = None + with monkeypatch.context() as mp: + mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None) + userDict.load() + + # Path is now None, and no words + assert userDict._path is None + assert sorted(userDict) == [] + + # Load the words again, properly + userDict.load() + assert sorted(userDict) == ["bar", "foo"] + +# END Test testCoreSpell_UserDictionary + + +@pytest.mark.core +def testCoreSpell_FakeEnchant(monkeypatch, mockGUI, fncPath): + """Test the FakeEnchant spell checker fallback.""" + project = NWProject(mockGUI) + buildTestProject(project, fncPath) + # Make package import fail with monkeypatch.context() as mp: mp.setitem(sys.modules, "enchant", None) - spChk = NWSpellEnchant() - spChk.setLanguage("en_US", "") + spChk = NWSpellEnchant(project) + spChk.setLanguage("en_US") assert isinstance(spChk._dictObj, FakeEnchant) # Request a non-existent dictionary - spChk = NWSpellEnchant() - spChk.setLanguage("whatchamajig", "") + spChk = NWSpellEnchant(project) + spChk.setLanguage("whatchamajig") assert isinstance(spChk._dictObj, FakeEnchant) - # Request an emety language string + # Request an empty language string # See issue https://github.com/vkbo/novelWriter/issues/1096 - spChk = NWSpellEnchant() - spChk.setLanguage("", "") + spChk = NWSpellEnchant(project) + spChk.setLanguage("") assert isinstance(spChk._dictObj, FakeEnchant) # FakeEnchant should handle requests @@ -62,103 +147,53 @@ def testCoreSpell_FakeEnchant(monkeypatch): @pytest.mark.core -def testCoreSpell_Enchant(monkeypatch, fncPath): - """Test the pyenchant spell checker. - """ - wList = fncPath / "wordlist.txt" - writeFile(wList, "a_word\nb_word\nc_word\n") +def testCoreSpell_Enchant(monkeypatch, mockGUI, fncPath): + """Test the pyenchant spell checker.""" + project = NWProject(mockGUI) + buildTestProject(project, fncPath) # Break the enchant package, and check error handling with monkeypatch.context() as mp: mp.setitem(sys.modules, "enchant", None) - spChk = NWSpellEnchant() + spChk = NWSpellEnchant(project) + assert spChk.spellLanguage is None assert spChk.listDictionaries() == [] assert spChk.describeDict() == ("", "") - # Set the dict to None, and check dictionary call error handling - spChk = NWSpellEnchant() - spChk.theDict = None + spChk.setLanguage("en_US") + assert spChk.spellLanguage is None + + # Check that the FakeEnchant class is actually handling this + assert isinstance(spChk._dictObj, FakeEnchant) + assert spChk.checkWord("word") is True + assert spChk.suggestWords("word") == [] + assert spChk.addWord("word") is True + + # Set the dict to None, and check enchant error handling + spChk = NWSpellEnchant(project) + spChk._dictObj = None # type: ignore assert spChk.checkWord("word") is True assert spChk.suggestWords("word") == [] assert spChk.addWord("word") is False + assert spChk.addWord("\n\t ") is False + assert spChk.describeDict() == ("", "") # Load the proper enchant package (twice) - spChk = NWSpellEnchant() - spChk.setLanguage("en_US", wList) - spChk.setLanguage("en_US", wList) + spChk = NWSpellEnchant(project) + spChk.setLanguage("en_US") + spChk.setLanguage("en_US") + assert isinstance(spChk._dictObj, enchant.Dict) assert spChk.spellLanguage == "en_US" + assert spChk.listDictionaries() != [] + assert spChk.describeDict() != ("", "") - # Add a word to the user's dictionary - assert spChk._readProjectDictionary("stuff") is False + # Set to non-existent language + spChk.setLanguage("foo_bar") + + # Block the broker from figuring out the language with monkeypatch.context() as mp: - mp.setattr("builtins.open", causeOSError) - assert spChk._readProjectDictionary(wList) is False - - assert spChk._readProjectDictionary(None) is False - assert spChk._readProjectDictionary(wList) is True - assert spChk._userDictPath == wList - - # Cannot write to file - with monkeypatch.context() as mp: - mp.setattr("builtins.open", causeOSError) - assert spChk.addWord("d_word") is False - - assert readFile(wList) == "a_word\nb_word\nc_word\n" - assert spChk.addWord("d_word") is True - assert readFile(wList) == "a_word\nb_word\nc_word\nd_word\n" - assert spChk.addWord("d_word") is False - - # Check words - assert spChk.checkWord("a_word") is True - assert spChk.checkWord("b_word") is True - assert spChk.checkWord("c_word") is True - assert spChk.checkWord("d_word") is True - assert spChk.checkWord("e_word") is False - - spChk.addWord("d_word") - assert spChk.checkWord("d_word") is True - - wSuggest = spChk.suggestWords("wrod") - assert len(wSuggest) > 0 - assert "word" in wSuggest - - dList = spChk.listDictionaries() - assert len(dList) > 0 - - aTag, aName = spChk.describeDict() - assert aTag == "en_US" - assert aName != "" + mp.setattr("enchant.Broker.request_dict", lambda *a: None) + spChk.setLanguage("en_US") + assert isinstance(spChk._dictObj, FakeEnchant) # END Test testCoreSpell_Enchant - - -@pytest.mark.core -def testCoreSpell_SessionWords(fncPath): - """Test the handling of the custom word list in the spell checker. - New project sessions should not inherit the project word list from - other sessions, so this test checks that they don't bleed through. - """ - wList1 = fncPath / "wordlist1.txt" - wList2 = fncPath / "wordlist2.txt" - writeFile(wList1, "a_word\nb_word\nc_word\n") - writeFile(wList2, "d_word\ne_word\nf_word\n") - - spChk = NWSpellEnchant() - - spChk.setLanguage("en_US", wList1) - assert spChk.checkWord("a_word") is True - assert spChk.checkWord("b_word") is True - assert spChk.checkWord("c_word") is True - assert spChk.checkWord("d_word") is False - assert spChk.checkWord("e_word") is False - assert spChk.checkWord("f_word") is False - - spChk.setLanguage("en_US", wList2) - assert spChk.checkWord("a_word") is False - assert spChk.checkWord("b_word") is False - assert spChk.checkWord("c_word") is False - assert spChk.checkWord("d_word") is True - assert spChk.checkWord("e_word") is True - assert spChk.checkWord("f_word") is True - -# END Test testCoreSpell_SessionWords