diff --git a/novelwriter/core/document.py b/novelwriter/core/document.py index 7fe7f8e5..ab9b8ac9 100644 --- a/novelwriter/core/document.py +++ b/novelwriter/core/document.py @@ -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") diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index f07c0ee0..249fd19d 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -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 diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index d117f109..65d292c8 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -25,7 +25,6 @@ along with this program. If not, see . 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") diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index 4a63bf39..30396d0d 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -23,10 +23,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -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: diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 3e53b897..39c5edb3 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -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)) diff --git a/tests/test_core/test_core_document.py b/tests/test_core/test_core_document.py index ba74afbd..50dd28bf 100644 --- a/tests/test_core/test_core_document.py +++ b/tests/test_core/test_core_document.py @@ -19,7 +19,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -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 diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 678f00e5..d08577d7 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -19,11 +19,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -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) diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index e2e6aad2..377273da 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -19,8 +19,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -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 diff --git a/tests/test_core/test_core_spellcheck.py b/tests/test_core/test_core_spellcheck.py index 66dd33b2..b3b00bf4 100644 --- a/tests/test_core/test_core_spellcheck.py +++ b/tests/test_core/test_core_spellcheck.py @@ -19,7 +19,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -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")