Fix test coverage of spell check classes
This commit is contained in:
@@ -21,9 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from time import time
|
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from pathlib import Path
|
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from mocked import causeOSError
|
from mocked import causeOSError
|
||||||
@@ -31,8 +29,6 @@ from tools import C, cmpFiles, writeFile, buildTestProject, XML_IGNORE
|
|||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
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.tree import NWTree
|
||||||
from novelwriter.core.index import NWIndex
|
from novelwriter.core.index import NWIndex
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
@@ -468,8 +464,7 @@ def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
|
|||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
|
def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
|
||||||
"""Test other project class methods and functions.
|
"""Test other project class methods and functions."""
|
||||||
"""
|
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
buildTestProject(theProject, fncPath)
|
buildTestProject(theProject, fncPath)
|
||||||
|
|
||||||
@@ -578,46 +573,6 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
|
|||||||
assert theProject.setTreeOrder(oldOrder)
|
assert theProject.setTreeOrder(oldOrder)
|
||||||
assert theProject.tree.handles() == 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
|
# END Test testCoreProject_Methods
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,33 +21,118 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
|
import enchant
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from tools import buildTestProject
|
||||||
from mocked import causeOSError
|
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
|
@pytest.mark.core
|
||||||
def testCoreSpell_FakeEnchant(monkeypatch):
|
def testCoreSpell_UserDictionary(monkeypatch, mockGUI, fncPath):
|
||||||
"""Test the FakeEnchant spell checker fallback.
|
"""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
|
# Make package import fail
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setitem(sys.modules, "enchant", None)
|
mp.setitem(sys.modules, "enchant", None)
|
||||||
spChk = NWSpellEnchant()
|
spChk = NWSpellEnchant(project)
|
||||||
spChk.setLanguage("en_US", "")
|
spChk.setLanguage("en_US")
|
||||||
assert isinstance(spChk._dictObj, FakeEnchant)
|
assert isinstance(spChk._dictObj, FakeEnchant)
|
||||||
|
|
||||||
# Request a non-existent dictionary
|
# Request a non-existent dictionary
|
||||||
spChk = NWSpellEnchant()
|
spChk = NWSpellEnchant(project)
|
||||||
spChk.setLanguage("whatchamajig", "")
|
spChk.setLanguage("whatchamajig")
|
||||||
assert isinstance(spChk._dictObj, FakeEnchant)
|
assert isinstance(spChk._dictObj, FakeEnchant)
|
||||||
|
|
||||||
# Request an emety language string
|
# Request an empty language string
|
||||||
# See issue https://github.com/vkbo/novelWriter/issues/1096
|
# See issue https://github.com/vkbo/novelWriter/issues/1096
|
||||||
spChk = NWSpellEnchant()
|
spChk = NWSpellEnchant(project)
|
||||||
spChk.setLanguage("", "")
|
spChk.setLanguage("")
|
||||||
assert isinstance(spChk._dictObj, FakeEnchant)
|
assert isinstance(spChk._dictObj, FakeEnchant)
|
||||||
|
|
||||||
# FakeEnchant should handle requests
|
# FakeEnchant should handle requests
|
||||||
@@ -62,103 +147,53 @@ def testCoreSpell_FakeEnchant(monkeypatch):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreSpell_Enchant(monkeypatch, fncPath):
|
def testCoreSpell_Enchant(monkeypatch, mockGUI, fncPath):
|
||||||
"""Test the pyenchant spell checker.
|
"""Test the pyenchant spell checker."""
|
||||||
"""
|
project = NWProject(mockGUI)
|
||||||
wList = fncPath / "wordlist.txt"
|
buildTestProject(project, fncPath)
|
||||||
writeFile(wList, "a_word\nb_word\nc_word\n")
|
|
||||||
|
|
||||||
# Break the enchant package, and check error handling
|
# Break the enchant package, and check error handling
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setitem(sys.modules, "enchant", None)
|
mp.setitem(sys.modules, "enchant", None)
|
||||||
spChk = NWSpellEnchant()
|
spChk = NWSpellEnchant(project)
|
||||||
|
assert spChk.spellLanguage is None
|
||||||
assert spChk.listDictionaries() == []
|
assert spChk.listDictionaries() == []
|
||||||
assert spChk.describeDict() == ("", "")
|
assert spChk.describeDict() == ("", "")
|
||||||
|
|
||||||
# Set the dict to None, and check dictionary call error handling
|
spChk.setLanguage("en_US")
|
||||||
spChk = NWSpellEnchant()
|
assert spChk.spellLanguage is None
|
||||||
spChk.theDict = 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.checkWord("word") is True
|
||||||
assert spChk.suggestWords("word") == []
|
assert spChk.suggestWords("word") == []
|
||||||
assert spChk.addWord("word") is False
|
assert spChk.addWord("word") is False
|
||||||
|
assert spChk.addWord("\n\t ") is False
|
||||||
|
assert spChk.describeDict() == ("", "")
|
||||||
|
|
||||||
# Load the proper enchant package (twice)
|
# Load the proper enchant package (twice)
|
||||||
spChk = NWSpellEnchant()
|
spChk = NWSpellEnchant(project)
|
||||||
spChk.setLanguage("en_US", wList)
|
spChk.setLanguage("en_US")
|
||||||
spChk.setLanguage("en_US", wList)
|
spChk.setLanguage("en_US")
|
||||||
|
assert isinstance(spChk._dictObj, enchant.Dict)
|
||||||
assert spChk.spellLanguage == "en_US"
|
assert spChk.spellLanguage == "en_US"
|
||||||
|
assert spChk.listDictionaries() != []
|
||||||
|
assert spChk.describeDict() != ("", "")
|
||||||
|
|
||||||
# Add a word to the user's dictionary
|
# Set to non-existent language
|
||||||
assert spChk._readProjectDictionary("stuff") is False
|
spChk.setLanguage("foo_bar")
|
||||||
|
|
||||||
|
# Block the broker from figuring out the language
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("builtins.open", causeOSError)
|
mp.setattr("enchant.Broker.request_dict", lambda *a: None)
|
||||||
assert spChk._readProjectDictionary(wList) is False
|
spChk.setLanguage("en_US")
|
||||||
|
assert isinstance(spChk._dictObj, FakeEnchant)
|
||||||
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 != ""
|
|
||||||
|
|
||||||
# END Test testCoreSpell_Enchant
|
# 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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user