Fix tests for core classes
This commit is contained in:
@@ -138,7 +138,7 @@ class NWDocument:
|
||||
contentPath = self.theProject.storage.contentPath
|
||||
if not isinstance(contentPath, Path):
|
||||
logger.error("No content path set")
|
||||
return None
|
||||
return False
|
||||
|
||||
docFile = self._docHandle+".nwd"
|
||||
logger.debug("Saving document: %s", docFile)
|
||||
@@ -195,7 +195,7 @@ class NWDocument:
|
||||
contentPath = self.theProject.storage.contentPath
|
||||
if not isinstance(contentPath, Path):
|
||||
logger.error("No content path set")
|
||||
return None
|
||||
return False
|
||||
|
||||
docPath = contentPath / f"{self._docHandle}.nwd"
|
||||
docTemp = docPath.with_suffix(".tmp")
|
||||
|
||||
@@ -820,8 +820,6 @@ class ItemIndex:
|
||||
elif tItem.itemRoot == rootHandle:
|
||||
for sTitle in self._items[tHandle].headings():
|
||||
yield tHandle, sTitle, self._items[tHandle][sTitle]
|
||||
else:
|
||||
continue
|
||||
|
||||
return
|
||||
|
||||
|
||||
+12
-18
@@ -25,7 +25,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import novelwriter
|
||||
@@ -82,8 +81,7 @@ class NWProject(QObject):
|
||||
self.lockedBy = None # Data on which computer has the project open
|
||||
|
||||
# Class Settings
|
||||
self.projDict = None # The spell check dictionary
|
||||
self.projFiles = [] # A list of all files in the content folder on load
|
||||
self.projFiles = [] # A list of all files in the content folder on load
|
||||
|
||||
# Internal Mapping
|
||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||
@@ -248,7 +246,6 @@ class NWProject(QObject):
|
||||
self._data = NWProjectData(self)
|
||||
|
||||
# Project Settings
|
||||
self.projDict = None
|
||||
self.projFiles = []
|
||||
|
||||
return
|
||||
@@ -265,8 +262,6 @@ class NWProject(QObject):
|
||||
|
||||
logger.info("Opening project: %s", projPath)
|
||||
|
||||
self.projDict = str(self._storage.getMetaFile(nwFiles.PROJ_DICT))
|
||||
|
||||
# Project Lock
|
||||
# ============
|
||||
|
||||
@@ -367,11 +362,12 @@ class NWProject(QObject):
|
||||
|
||||
# Check the project tree consistency
|
||||
for tItem in self._tree:
|
||||
tHandle = tItem.itemHandle
|
||||
logger.debug("Checking item '%s'", tHandle)
|
||||
if not self._tree.updateItemData(tHandle):
|
||||
logger.error("There was a problem item '%s', and it has been removed", tHandle)
|
||||
del self._tree[tHandle] # The file will be re-added as orphaned
|
||||
if tItem:
|
||||
tHandle = tItem.itemHandle
|
||||
logger.debug("Checking item '%s'", tHandle)
|
||||
if not self._tree.updateItemData(tHandle):
|
||||
logger.error("There was a problem the item, and it has been removed")
|
||||
del self._tree[tHandle] # The file will be re-added as orphaned
|
||||
|
||||
self._scanProjectFolder()
|
||||
self._index.loadIndex()
|
||||
@@ -694,20 +690,18 @@ class NWProject(QObject):
|
||||
def _loadProjectLocalisation(self):
|
||||
"""Load the language data for the current project language.
|
||||
"""
|
||||
if self._data.language is None:
|
||||
if self._data.language is None or self.mainConf.nwLangPath is None:
|
||||
self._langData = {}
|
||||
return False
|
||||
|
||||
langFile = os.path.join(
|
||||
self.mainConf.nwLangPath, "project_%s.json" % self._data.language
|
||||
)
|
||||
if not os.path.isfile(langFile):
|
||||
langFile = os.path.join(self.mainConf.nwLangPath, "project_en_GB.json")
|
||||
langFile = Path(self.mainConf.nwLangPath) / f"project_{self._data.language}.json"
|
||||
if not langFile.is_file():
|
||||
langFile = Path(self.mainConf.nwLangPath) / "project_en_GB.json"
|
||||
|
||||
try:
|
||||
with open(langFile, mode="r", encoding="utf-8") as inFile:
|
||||
self._langData = json.load(inFile)
|
||||
logger.debug("Loaded project language file: %s", os.path.basename(langFile))
|
||||
logger.debug("Loaded project language file: %s", langFile.name)
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to project language file")
|
||||
|
||||
@@ -23,10 +23,10 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from collections import namedtuple
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.error import logException
|
||||
|
||||
@@ -173,10 +173,10 @@ class NWSpellEnchant:
|
||||
self._projDict = set()
|
||||
self._projectDict = projectDict
|
||||
|
||||
if projectDict is None:
|
||||
if not isinstance(projectDict, Path):
|
||||
return False
|
||||
|
||||
if not os.path.isfile(projectDict):
|
||||
if not projectDict.exists():
|
||||
return False
|
||||
|
||||
try:
|
||||
|
||||
@@ -53,7 +53,7 @@ from PyQt5.QtWidgets import (
|
||||
from novelwriter.core import NWSpellEnchant, countWords
|
||||
from novelwriter.enum import nwAlert, nwDocAction, nwDocInsert, nwDocMode
|
||||
from novelwriter.common import transferCase
|
||||
from novelwriter.constants import nwConst, nwKeyWords, nwUnicode
|
||||
from novelwriter.constants import nwConst, nwFiles, nwKeyWords, nwUnicode
|
||||
from novelwriter.gui.dochighlight import GuiDocHighlighter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -689,7 +689,8 @@ class GuiDocEditor(QTextEdit):
|
||||
else:
|
||||
theLang = self.theProject.data.spellLang
|
||||
|
||||
self.spEnchant.setLanguage(theLang, self.theProject.projDict)
|
||||
projDict = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
|
||||
self.spEnchant.setLanguage(theLang, projDict)
|
||||
_, theProvider = self.spEnchant.describeDict()
|
||||
|
||||
self.spellDictionaryChanged.emit(str(theLang), str(theProvider))
|
||||
|
||||
@@ -19,7 +19,6 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from mock import causeOSError
|
||||
@@ -31,12 +30,12 @@ from novelwriter.core.document import NWDocument
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncPath, mockRnd):
|
||||
"""Test loading and saving a document with the NWDocument class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Read Document
|
||||
# =============
|
||||
@@ -51,6 +50,12 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
assert theDoc.readDocument() is None
|
||||
assert theDoc._currHash is None
|
||||
|
||||
# No content path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.contentPath", property(lambda *a: None))
|
||||
theDoc = NWDocument(theProject, C.hSceneDoc)
|
||||
assert theDoc.readDocument() is None
|
||||
|
||||
# Cause open() to fail while loading
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeOSError)
|
||||
@@ -72,17 +77,23 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
# Write Document
|
||||
# ==============
|
||||
|
||||
# Set handle and save again
|
||||
# No content path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.contentPath", property(lambda *a: None))
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.writeDocument("") is False
|
||||
|
||||
# Set handle and save
|
||||
theText = "### Test File\n\nText ...\n\n"
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.readDocument(xHandle) == ""
|
||||
assert theDoc.writeDocument(theText) is True
|
||||
|
||||
# Save again to ensure temp file and previous file is handled
|
||||
assert theDoc.writeDocument(theText)
|
||||
assert theDoc.writeDocument(theText) is True
|
||||
|
||||
# Check file content
|
||||
docPath = os.path.join(fncDir, "content", xHandle+".nwd")
|
||||
docPath = fncPath / "content" / f"{xHandle}.nwd"
|
||||
assert readFile(docPath) == (
|
||||
"%%~name: New File\n"
|
||||
f"%%~path: {C.hNovelRoot}/{xHandle}\n"
|
||||
@@ -128,10 +139,16 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
# Delete Document
|
||||
# ===============
|
||||
|
||||
# Delete the last document
|
||||
# Delete a non-existing document
|
||||
theDoc = NWDocument(theProject, "stuff")
|
||||
assert theDoc.deleteDocument() is False
|
||||
assert os.path.isfile(docPath)
|
||||
assert docPath.exists()
|
||||
|
||||
# No content path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.contentPath", property(lambda *a: None))
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.deleteDocument() is False
|
||||
|
||||
# Cause the delete to fail
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -143,26 +160,26 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
# Make the delete pass
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.deleteDocument() is True
|
||||
assert not os.path.isfile(docPath)
|
||||
assert not docPath.exists()
|
||||
|
||||
# END Test testCoreDocument_Load
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_Methods(mockGUI, fncDir, mockRnd):
|
||||
def testCoreDocument_Methods(mockGUI, fncPath, mockRnd):
|
||||
"""Test other methods of the NWDocument class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
theDoc = NWDocument(theProject, C.hSceneDoc)
|
||||
docPath = os.path.join(fncDir, "content", C.hSceneDoc+".nwd")
|
||||
docPath = fncPath / "content" / f"{C.hSceneDoc}.nwd"
|
||||
|
||||
assert theDoc.readDocument() == "### New Scene\n\n"
|
||||
|
||||
# Check location
|
||||
assert theDoc.getFileLocation() == docPath
|
||||
assert theDoc.getFileLocation() == str(docPath)
|
||||
|
||||
# Check the item
|
||||
assert theDoc.getCurrentItem() is not None
|
||||
|
||||
@@ -19,11 +19,11 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import pytest
|
||||
|
||||
from shutil import copyfile
|
||||
from pathlib import Path
|
||||
|
||||
from mock import causeException
|
||||
from tools import C, buildTestProject, cmpFiles, writeFile
|
||||
@@ -35,13 +35,13 @@ from novelwriter.core.project import NWProject
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, tstPaths):
|
||||
"""Test core functionality of scaning, saving, loading and checking
|
||||
the index cache file.
|
||||
"""
|
||||
projFile = os.path.join(nwLipsum, "meta", nwFiles.INDEX_FILE)
|
||||
testFile = os.path.join(outDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
projFile = Path(nwLipsum) / "meta" / nwFiles.INDEX_FILE
|
||||
testFile = tstPaths.outDir / "coreIndex_LoadSave_tagsIndex.json"
|
||||
compFile = tstPaths.refDir / "coreIndex_LoadSave_tagsIndex.json"
|
||||
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwLipsum)
|
||||
@@ -62,6 +62,11 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
|
||||
assert theIndex.reIndexHandle(None) is False
|
||||
|
||||
# No folder for saving
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None)
|
||||
assert theIndex.saveIndex() is False
|
||||
|
||||
# Make the save fail
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeException)
|
||||
@@ -86,6 +91,11 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
assert theIndex._tagsIndex._tags == {}
|
||||
assert theIndex._itemIndex._items == {}
|
||||
|
||||
# No folder for sloading
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None)
|
||||
assert theIndex.loadIndex() is False
|
||||
|
||||
# Make the load fail
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(json, "load", causeException)
|
||||
|
||||
@@ -19,8 +19,6 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import pytest
|
||||
|
||||
from time import time
|
||||
@@ -42,16 +40,16 @@ from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLR
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
def testCoreProject_NewRoot(fncPath, tstPaths, mockGUI, mockRnd):
|
||||
"""Check that new root folders can be added to the project.
|
||||
"""
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
|
||||
projFile = fncPath / "nwProject.nwx"
|
||||
testFile = tstPaths.outDir / "coreProject_NewRoot_nwProject.nwx"
|
||||
compFile = tstPaths.refDir / "coreProject_NewRoot_nwProject.nwx"
|
||||
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
assert theProject.newRoot(nwItemClass.NOVEL) == "0000000000010"
|
||||
assert theProject.newRoot(nwItemClass.PLOT) == "0000000000011"
|
||||
@@ -93,16 +91,16 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
def testCoreProject_NewFileFolder(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
|
||||
"""Check that new files can be added to the project.
|
||||
"""
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreProject_NewFileFolder_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewFileFolder_nwProject.nwx")
|
||||
projFile = fncPath / "nwProject.nwx"
|
||||
testFile = tstPaths.outDir / "coreProject_NewFileFolder_nwProject.nwx"
|
||||
compFile = tstPaths.refDir / "coreProject_NewFileFolder_nwProject.nwx"
|
||||
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Invalid call
|
||||
assert theProject.newFolder("New Folder", "1234567890abc") is None
|
||||
@@ -147,15 +145,15 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
||||
assert "0000000000011" in theProject.tree
|
||||
|
||||
# Delete new files and folders
|
||||
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000012.nwd"))
|
||||
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000011.nwd"))
|
||||
assert (fncPath / "content" / "0000000000012.nwd").exists()
|
||||
assert (fncPath / "content" / "0000000000011.nwd").exists()
|
||||
|
||||
assert theProject.removeItem("0000000000012") is True
|
||||
assert theProject.removeItem("0000000000011") is True
|
||||
assert theProject.removeItem("0000000000010") is True
|
||||
|
||||
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000012.nwd"))
|
||||
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000011.nwd"))
|
||||
assert not (fncPath / "content" / "0000000000012.nwd").exists()
|
||||
assert not (fncPath / "content" / "0000000000011.nwd").exists()
|
||||
|
||||
assert "0000000000010" not in theProject.tree
|
||||
assert "0000000000011" not in theProject.tree
|
||||
@@ -167,69 +165,66 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncDir, mockRnd):
|
||||
def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd):
|
||||
"""Test opening a project.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Rename the project file to check handling
|
||||
rName = os.path.join(fncDir, nwFiles.PROJ_FILE)
|
||||
wName = os.path.join(fncDir, nwFiles.PROJ_FILE+"_sdfghj")
|
||||
os.rename(rName, wName)
|
||||
assert theProject.openProject(fncDir) is False
|
||||
os.rename(wName, rName)
|
||||
|
||||
# Fail on folder structure check
|
||||
# Initialising the storage class fails
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("os.mkdir", causeOSError)
|
||||
shutil.rmtree(os.path.join(fncDir, "meta"))
|
||||
assert theProject.openProject(fncDir) is False
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.openProjectInPlace", lambda *a, **k: False)
|
||||
assert theProject.openProject(fncPath) is False
|
||||
|
||||
# Fail on lock file
|
||||
assert theProject._storage.writeLockFile()
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert theProject.openProject(fncPath) is False
|
||||
|
||||
# Fail to read lockfile (which still opens the project)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.readLockFile", lambda *a: ["ERROR"])
|
||||
caplog.clear()
|
||||
assert theProject.openProject(fncDir) is True
|
||||
assert theProject.openProject(fncPath) is True
|
||||
assert "Failed to check lock file" in caplog.text
|
||||
assert theProject.closeProject()
|
||||
assert theProject.closeProject()
|
||||
|
||||
# Force open with lockfile
|
||||
assert theProject._storage.writeLockFile()
|
||||
assert theProject.openProject(fncDir, overrideLock=True) is True
|
||||
assert theProject.openProject(fncPath, overrideLock=True) is True
|
||||
assert theProject.closeProject()
|
||||
|
||||
# Fail getting xml reader
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.getXmlReader", lambda *a: None)
|
||||
assert theProject.openProject(fncPath) is False
|
||||
|
||||
# Not a novelwriter XML file
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "read", lambda *a: False)
|
||||
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.NOT_NWX_FILE))
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert theProject.openProject(fncPath) is False
|
||||
assert "Project file does not appear" in mockGUI.lastAlert
|
||||
|
||||
# Unknown project file version
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "read", lambda *a: False)
|
||||
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.UNKNOWN_VERSION))
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert theProject.openProject(fncPath) is False
|
||||
assert "Unknown or unsupported novelWriter project file" in mockGUI.lastAlert
|
||||
|
||||
# Other parse error
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "read", lambda *a: False)
|
||||
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.CANNOT_PARSE))
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert theProject.openProject(fncPath) is False
|
||||
assert "Failed to parse project xml" in mockGUI.lastAlert
|
||||
|
||||
# Won't convert legacy file
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY))
|
||||
mockGUI.askResponse = False
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert theProject.openProject(fncPath) is False
|
||||
assert "The file format of your project is about to be" in mockGUI.lastQuestion[1]
|
||||
mockGUI.askResponse = True
|
||||
|
||||
@@ -237,17 +232,22 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncDir, mockRnd):
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: 0x99999999))
|
||||
mockGUI.askResponse = False
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert theProject.openProject(fncPath) is False
|
||||
assert "This project was saved by a newer version" in mockGUI.lastQuestion[1]
|
||||
mockGUI.askResponse = True
|
||||
|
||||
# Fail checking items should still pass
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.tree.NWTree.updateItemData", lambda *a: False)
|
||||
assert theProject.openProject(fncPath) is True
|
||||
|
||||
assert theProject.closeProject()
|
||||
|
||||
# END Test testCoreProject_Open
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncDir, refDir):
|
||||
def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncPath):
|
||||
"""Test saving a project.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
@@ -256,7 +256,12 @@ def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncDir, refDir):
|
||||
assert theProject.saveProject() is False
|
||||
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Fail getting xml writer
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.getXmlWriter", lambda *a: None)
|
||||
assert theProject.saveProject() is False
|
||||
|
||||
# Fail writing
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -272,11 +277,11 @@ def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncDir, refDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_AccessItems(mockGUI, fncDir, mockRnd):
|
||||
def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
|
||||
"""Test helper functions for the project folder.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Storage Objects
|
||||
assert isinstance(theProject.index, NWIndex)
|
||||
@@ -337,12 +342,12 @@ def testCoreProject_AccessItems(mockGUI, fncDir, mockRnd):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
||||
def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
|
||||
"""Test the status and importance flag handling.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
|
||||
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
|
||||
@@ -447,11 +452,11 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Methods(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
|
||||
"""Test other project class methods and functions.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Project Name
|
||||
theProject.data.setName(" A Name ")
|
||||
@@ -497,9 +502,12 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
|
||||
# Spell check
|
||||
theProject.setProjectChanged(False)
|
||||
theProject._projAltered = False
|
||||
theProject.data.setSpellCheck(True)
|
||||
theProject.data.setSpellCheck(False)
|
||||
assert theProject.projChanged
|
||||
assert theProject.projChanged is True
|
||||
assert theProject.projAltered is True
|
||||
assert theProject.projOpened > 0
|
||||
|
||||
# Spell language
|
||||
theProject.setProjectChanged(False)
|
||||
@@ -510,7 +518,7 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
assert theProject.data.spellLang is None
|
||||
theProject.data.setSpellLang("en_GB")
|
||||
assert theProject.data.spellLang == "en_GB"
|
||||
assert theProject.projChanged
|
||||
assert theProject.projChanged is True
|
||||
|
||||
# Project Language
|
||||
theProject.setProjectChanged(False)
|
||||
@@ -524,6 +532,20 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
assert theProject.localLookup(1) == "One"
|
||||
assert theProject.localLookup(10) == "Ten"
|
||||
|
||||
# Set invalid language
|
||||
theProject.data.setLanguage("foo")
|
||||
theProject._loadProjectLocalisation()
|
||||
assert theProject.localLookup(1) == "One"
|
||||
assert theProject.localLookup(10) == "Ten"
|
||||
|
||||
# Block reading language data
|
||||
theProject.data.setLanguage("en")
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeOSError)
|
||||
theProject._loadProjectLocalisation()
|
||||
assert theProject.localLookup(1) == "One"
|
||||
assert theProject.localLookup(10) == "Ten"
|
||||
|
||||
# Last edited
|
||||
theProject.setProjectChanged(False)
|
||||
theProject._data.setLastHandle("0123456789abc", "editor")
|
||||
@@ -624,7 +646,7 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
|
||||
assert theProject.closeProject() is True
|
||||
|
||||
# First Item with Meta Data
|
||||
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
|
||||
orphPath = Path(nwLipsum) / "content" / "636b6aa9b697b.nwd"
|
||||
writeFile(orphPath, (
|
||||
"%%~name:[Recovered] Mars\n"
|
||||
"%%~path:5eaea4e8cdee8/636b6aa9b697b\n"
|
||||
@@ -634,19 +656,19 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
|
||||
))
|
||||
|
||||
# Second Item without Meta Data
|
||||
orphPath = os.path.join(nwLipsum, "content", "736b6aa9b697b.nwd")
|
||||
orphPath = Path(nwLipsum) / "content" / "736b6aa9b697b.nwd"
|
||||
writeFile(orphPath, "\n")
|
||||
|
||||
# Invalid File Name
|
||||
tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.txt")
|
||||
tstPath = Path(nwLipsum) / "content" / "636b6aa9b697b.txt"
|
||||
writeFile(tstPath, "\n")
|
||||
|
||||
# Invalid File Name
|
||||
tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697bb.nwd")
|
||||
tstPath = Path(nwLipsum) / "content" / "636b6aa9b697bb.nwd"
|
||||
writeFile(tstPath, "\n")
|
||||
|
||||
# Invalid File Name
|
||||
tstPath = os.path.join(nwLipsum, "content", "abcdefghijklm.nwd")
|
||||
tstPath = Path(nwLipsum) / "content" / "abcdefghijklm.nwd"
|
||||
writeFile(tstPath, "\n")
|
||||
|
||||
assert theProject.openProject(nwLipsum)
|
||||
@@ -686,16 +708,21 @@ def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Backup(monkeypatch, mockGUI, fncDir, tmpDir):
|
||||
def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tmpPath):
|
||||
"""Test the automated backup feature of the project class. The test
|
||||
creates a backup of the Minimal test project, and then unzips the
|
||||
backupd file and checks that the project XML file is identical to
|
||||
the original file.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
# Test faulty settings
|
||||
# No Project
|
||||
assert theProject.backupProject(doNotify=False) is False
|
||||
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Invalid Settings
|
||||
# ================
|
||||
|
||||
# No project
|
||||
mockGUI.hasProject = False
|
||||
@@ -707,21 +734,14 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncDir, tmpDir):
|
||||
assert theProject.backupProject(doNotify=False) is False
|
||||
|
||||
# Missing project name
|
||||
theProject.mainConf.backupPath = tmpDir
|
||||
theProject.mainConf.backupPath = str(tmpPath)
|
||||
theProject.data.setName("")
|
||||
assert theProject.backupProject(doNotify=False) is False
|
||||
|
||||
# Non-existent folder
|
||||
theProject.mainConf.backupPath = os.path.join(tmpDir, "nonexistent")
|
||||
# Valid Settings
|
||||
# ==============
|
||||
theProject.mainConf.backupPath = str(tmpPath)
|
||||
theProject.data.setName("Test Minimal")
|
||||
assert theProject.backupProject(doNotify=False) is False
|
||||
|
||||
# Subfolder of project (causes infinite loop in zipping)
|
||||
theProject.mainConf.backupPath = os.path.join(fncDir, "subdir")
|
||||
assert theProject.backupProject(doNotify=False) is False
|
||||
|
||||
# Set a valid folder
|
||||
theProject.mainConf.backupPath = tmpDir
|
||||
|
||||
# Can't make folder
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -736,21 +756,21 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncDir, tmpDir):
|
||||
# Test correct settings
|
||||
assert theProject.backupProject(doNotify=True) is True
|
||||
|
||||
theFiles = os.listdir(os.path.join(tmpDir, "Test Minimal"))
|
||||
theFiles = list((tmpPath / "Test Minimal").iterdir())
|
||||
assert len(theFiles) == 1
|
||||
|
||||
theZip = theFiles[0]
|
||||
theZip = theFiles[0].name
|
||||
assert theZip[:12] == "Backup from "
|
||||
assert theZip[-4:] == ".zip"
|
||||
|
||||
# Extract the archive
|
||||
with ZipFile(os.path.join(tmpDir, "Test Minimal", theZip), "r") as inZip:
|
||||
inZip.extractall(os.path.join(tmpDir, "extract"))
|
||||
with ZipFile(tmpPath / "Test Minimal" / theZip, mode="r") as inZip:
|
||||
inZip.extractall(tmpPath / "extract")
|
||||
|
||||
# Check that the main project file was restored
|
||||
assert cmpFiles(
|
||||
os.path.join(fncDir, "nwProject.nwx"),
|
||||
os.path.join(tmpDir, "extract", "nwProject.nwx")
|
||||
fncPath / "nwProject.nwx",
|
||||
tmpPath / "extract" / "nwProject.nwx"
|
||||
)
|
||||
|
||||
# END Test testCoreProject_Backup
|
||||
|
||||
@@ -19,7 +19,6 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
@@ -63,10 +62,10 @@ def testCoreSpell_FakeEnchant(monkeypatch):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreSpell_Enchant(monkeypatch, fncDir):
|
||||
def testCoreSpell_Enchant(monkeypatch, fncPath):
|
||||
"""Test the pyenchant spell checker.
|
||||
"""
|
||||
wList = os.path.join(fncDir, "wordlist.txt")
|
||||
wList = fncPath / "wordlist.txt"
|
||||
writeFile(wList, "a_word\nb_word\nc_word\n")
|
||||
|
||||
# Break the enchant package, and check error handling
|
||||
@@ -134,13 +133,13 @@ def testCoreSpell_Enchant(monkeypatch, fncDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreSpell_SessionWords(fncDir):
|
||||
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 = os.path.join(fncDir, "wordlist1.txt")
|
||||
wList2 = os.path.join(fncDir, "wordlist2.txt")
|
||||
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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user