Update tests for storage class

This commit is contained in:
Veronica Berglyd Olsen
2023-12-01 17:28:56 +01:00
parent f2b35d798b
commit 3fe2ee3058
2 changed files with 178 additions and 94 deletions
+3 -6
View File
@@ -85,7 +85,6 @@ class NWStorage:
self._lockFilePath = None
self._openMode = self.MODE_INACTIVE
self._ready = False
self._exception = None
return
##
@@ -154,6 +153,7 @@ class NWStorage:
metaPath.mkdir(exist_ok=True)
contPath.mkdir(exist_ok=True)
except Exception as exc:
self._exception = exc
logger.error("Failed to create project folders", exc_info=exc)
self.clear()
return False
@@ -199,11 +199,8 @@ class NWStorage:
self._clearLockFile()
self._readLockFile()
if self._lockedBy and len(self._lockedBy) == 4:
if self._lockedBy[0] == "ERROR":
logger.warning("Failed to check lock file")
else:
logger.error("Project is locked, so not opening")
if self._lockedBy:
logger.error("Project is locked, so not opening")
return NWStorageOpen.LOCKED
else:
logger.debug("Project is not locked")
+175 -88
View File
@@ -26,13 +26,14 @@ import pytest
from pathlib import Path
from zipfile import ZipFile
from tools import C, buildTestProject, writeFile
from tools import C, buildTestProject
from mocked import causeOSError
from novelwriter import CONFIG
from novelwriter.constants import nwFiles
from novelwriter.core.project import NWProject
from novelwriter.core.storage import NWStorage, _LegacyStorage
from novelwriter.core.storage import NWStorage, NWStorageOpen, _LegacyStorage
from novelwriter.core.document import NWDocument
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
@@ -42,21 +43,19 @@ class MockProject:
@pytest.mark.core
def testCoreStorage_OpenProjectInPlace(mockGUI, fncPath, mockRnd):
"""Test opening a project in a folder."""
theProject = NWProject()
mockRnd.reset()
buildTestProject(theProject, fncPath)
theProject.closeProject()
def testCoreStorage_CreateNewProject(mockGUI, fncPath):
"""Test creating a project in a folder."""
project = NWProject()
# Create instance
storage = NWStorage(theProject)
storage = NWStorage(project)
# Check defaults
assert storage.storagePath is None
assert storage.runtimePath is None
assert storage.contentPath is None
assert storage._openMode == NWStorage.MODE_INACTIVE
assert storage._ready is False
# Check closed project return values
assert storage.isOpen() is False
@@ -66,52 +65,137 @@ def testCoreStorage_OpenProjectInPlace(mockGUI, fncPath, mockRnd):
assert storage.getMetaFile("file") is None
assert storage.scanContent() == []
# Open project as a new project should fail
assert storage.initProjectStorage(fncPath, newProject=True) is False
# Cannot prepare a non-empty folder
(fncPath / "foobar.txt").touch()
assert storage.createNewProject(fncPath) is False
# Opening as a non-new project is fine
assert storage.initProjectStorage(fncPath, newProject=False) is True
# Try creating in a non-existent subfolder instead
assert storage.createNewProject(fncPath / "project1") is True
assert (fncPath / "project1").is_dir()
assert (fncPath / "project1" / "meta").is_dir()
assert (fncPath / "project1" / "content").is_dir()
# Opening the project file is also fine
assert storage.initProjectStorage(fncPath / nwFiles.PROJ_FILE, newProject=False) is True
# However, the parent folder must exist
assert storage.createNewProject(fncPath / "foobar" / "project1") is False
assert isinstance(storage.exc, FileNotFoundError)
# Opening as a non-new project on a non-existing folder should fail
assert storage.initProjectStorage(fncPath / "foobar", newProject=False) is False
project.closeProject()
# Check settings
assert storage.storagePath == fncPath
assert storage.runtimePath == fncPath
assert storage.contentPath == fncPath / "content"
assert storage._openMode == NWStorage.MODE_INPLACE
# END Test testCoreStorage_CreateNewProject
# Open the project itself
theProject.openProject(fncPath)
storage = theProject.storage
# Get XML components
assert isinstance(storage.getXmlReader(), ProjectXMLReader)
assert isinstance(storage.getXmlWriter(), ProjectXMLWriter)
@pytest.mark.core
def testCoreStorage_InitProjectStorage(mockGUI, fncPath, mockRnd):
"""Test initialising a project in a folder."""
project = NWProject()
# Get content
assert sorted(storage.scanContent()) == [C.hTitlePage, C.hChapterDoc, C.hSceneDoc]
# Create instance
storage = NWStorage(project)
# Get document
assert storage.getDocument(C.hSceneDoc).readDocument() == "### New Scene\n\n"
# Check defaults
assert storage.storagePath is None
assert storage.runtimePath is None
assert storage.contentPath is None
assert storage._openMode == NWStorage.MODE_INACTIVE
assert storage._ready is False
# Get paths
assert storage.getMetaFile("stuff") == fncPath / "meta" / "stuff"
# Clean up
theProject.closeProject()
# Check closed project return values (again)
# Check closed project return values
assert storage.isOpen() is False
assert storage.getXmlReader() is None
assert storage.getXmlWriter() is None
assert bool(storage.getDocument(C.hSceneDoc)) is False
assert storage.getMetaFile("file") is None
assert storage.scanContent() == []
# END Test testCoreStorage_ProjectInPlace
# Create a new project
buildTestProject(project, fncPath)
# Init with the wrong file
assert storage.initProjectStorage(fncPath / "foobar.txt") == NWStorageOpen.UNKOWN
storage._clearLockFile()
storage.clear()
# Init with the user's home dir
assert storage.initProjectStorage(Path.home()) == NWStorageOpen.UNKOWN
storage._clearLockFile()
storage.clear()
# Init with the project folder is OK
assert storage.initProjectStorage(fncPath) == NWStorageOpen.READY
assert storage.runtimePath == fncPath
assert storage.storagePath == fncPath
assert storage.contentPath == fncPath / "content"
assert storage._openMode == NWStorage.MODE_INPLACE
storage._clearLockFile()
storage.clear()
# Init with the project main file is OK
assert storage.initProjectStorage(fncPath / nwFiles.PROJ_FILE) == NWStorageOpen.READY
assert storage.runtimePath == fncPath
assert storage.storagePath == fncPath
assert storage.contentPath == fncPath / "content"
assert storage._openMode == NWStorage.MODE_INPLACE
storage._clearLockFile()
storage.clear()
# Open twice, where second should fail due to lockfile
assert storage.initProjectStorage(fncPath) == NWStorageOpen.READY
assert storage.initProjectStorage(fncPath) == NWStorageOpen.LOCKED
assert isinstance(storage.lockStatus, list)
assert len(storage.lockStatus) == 4
# But open again with clear lock file flag set is OK
assert storage.initProjectStorage(fncPath, clearLock=True) == NWStorageOpen.READY
assert storage.lockStatus is None
# We should now have access to project resources
assert isinstance(storage.getXmlReader(), ProjectXMLReader)
assert isinstance(storage.getXmlWriter(), ProjectXMLWriter)
assert isinstance(storage.getDocument(C.hSceneDoc), NWDocument)
assert repr(storage.getDocument(C.hSceneDoc)) == f"<NWDocument handle={C.hSceneDoc}>"
project.closeProject()
# END Test testCoreStorage_InitProjectStorage
@pytest.mark.core
def testCoreStorage_InitProjectStorage_Invalid(mockGUI, fncPath):
"""Test initialising a project in an invalid folder."""
project = NWProject()
# Create instance
storage = NWStorage(project)
# Check defaults
assert storage.storagePath is None
assert storage.runtimePath is None
assert storage.contentPath is None
assert storage._openMode == NWStorage.MODE_INACTIVE
assert storage._ready is False
# Check closed project return values
assert storage.isOpen() is False
assert storage.getXmlReader() is None
assert storage.getXmlWriter() is None
assert bool(storage.getDocument(C.hSceneDoc)) is False
assert storage.getMetaFile("file") is None
assert storage.scanContent() == []
# Populate folder with invalid files
(fncPath / "meta").touch() # These are now files but should be folders
(fncPath / "content").touch() # These are now files but should be folders
# Try opening the folder, but there is no project file
assert storage.initProjectStorage(fncPath) == NWStorageOpen.NOT_FOUND
# Add the project file, and we should now fail on the folders
(fncPath / nwFiles.PROJ_FILE).touch()
assert storage.initProjectStorage(fncPath) == NWStorageOpen.FAILED
project.closeProject()
# END Test testCoreStorage_InitProjectStorage_Invalid
@pytest.mark.core
@@ -123,7 +207,7 @@ def testCoreStorage_LockFile(monkeypatch, fncPath):
assert storage.isOpen() is False
# Project not open, so cannot read/write lock file
assert storage._readLockFile() == ["ERROR"]
assert storage._readLockFile() is None
assert storage._writeLockFile() is False
assert storage._clearLockFile() is False
@@ -132,7 +216,8 @@ def testCoreStorage_LockFile(monkeypatch, fncPath):
storage._lockFilePath = lockFilePath
# Path is set, but there is no lockfile
assert storage._readLockFile() == []
storage._readLockFile()
assert storage.lockStatus is None
# Write lockfile fails
with monkeypatch.context() as mp:
@@ -148,20 +233,20 @@ def testCoreStorage_LockFile(monkeypatch, fncPath):
# Read lockfile fails
with monkeypatch.context() as mp:
mp.setattr("pathlib.Path.read_text", causeOSError)
assert storage._readLockFile() == ["ERROR"]
storage._readLockFile()
assert storage.lockStatus == ["ERROR", "ERROR", "ERROR", "ERROR"]
assert lockFilePath.exists()
# Successful read
assert storage._readLockFile() == [
CONFIG.hostName,
CONFIG.osType,
CONFIG.kernelVer,
"1000",
storage._readLockFile()
assert storage.lockStatus == [
CONFIG.hostName, CONFIG.osType, CONFIG.kernelVer, "1000",
]
# Write an invalid lockfile
writeFile(lockFilePath, "a;b;c")
assert storage._readLockFile() == ["ERROR"]
lockFilePath.write_text("a;b;c")
storage._readLockFile()
assert storage.lockStatus is None
# Fail to remove lockfile
with monkeypatch.context() as mp:
@@ -212,46 +297,46 @@ def testCoreStorage_ZipIt(monkeypatch, mockGUI, fncPath, tstPaths, mockRnd):
# END Test testCoreStorage_ZipIt
@pytest.mark.core
def testCoreStorage_PrepareStorage(monkeypatch, fncPath):
"""Test the project path preparation functions."""
storage = NWStorage(MockProject()) # type: ignore
assert storage.isOpen() is False
# @pytest.mark.core
# def testCoreStorage_PrepareStorage(monkeypatch, fncPath):
# """Test the project path preparation functions."""
# storage = NWStorage(MockProject()) # type: ignore
# assert storage.isOpen() is False
# No path set
assert storage._prepareStorage() is False
# # No path set
# assert storage._prepareStorage() is False
# Set path to home
storage._runtimePath = fncPath
with monkeypatch.context() as mp:
mp.setattr("pathlib.Path.home", lambda: fncPath)
assert storage._prepareStorage() is False
# # Set path to home
# storage._runtimePath = fncPath
# with monkeypatch.context() as mp:
# mp.setattr("pathlib.Path.home", lambda: fncPath)
# assert storage._prepareStorage() is False
# Fail on mkdir
storage._runtimePath = fncPath
with monkeypatch.context() as mp:
mp.setattr("pathlib.Path.mkdir", causeOSError)
assert storage._prepareStorage() is False
# # Fail on mkdir
# storage._runtimePath = fncPath
# with monkeypatch.context() as mp:
# mp.setattr("pathlib.Path.mkdir", causeOSError)
# assert storage._prepareStorage() is False
# Set up the folder
storage._runtimePath = fncPath
assert storage._prepareStorage(checkLegacy=False) is True
assert (fncPath / "content").exists()
assert (fncPath / "meta").exists()
assert not (fncPath / "cache").exists() # Removed in 2.1b1
# # Set up the folder
# storage._runtimePath = fncPath
# assert storage._prepareStorage(checkLegacy=False) is True
# assert (fncPath / "content").exists()
# assert (fncPath / "meta").exists()
# assert not (fncPath / "cache").exists() # Removed in 2.1b1
# Add a legacy folder
storage._runtimePath = fncPath
dataDir = fncPath / "data_0"
dataDir.mkdir()
assert storage._prepareStorage(checkLegacy=True) is True
assert not dataDir.exists()
# # Add a legacy folder
# storage._runtimePath = fncPath
# dataDir = fncPath / "data_0"
# dataDir.mkdir()
# assert storage._prepareStorage(checkLegacy=True) is True
# assert not dataDir.exists()
# We cannot add a new project here
storage._runtimePath = fncPath
assert storage._prepareStorage(checkLegacy=False, newProject=True) is False
# # We cannot add a new project here
# storage._runtimePath = fncPath
# assert storage._prepareStorage(checkLegacy=False, newProject=True) is False
# END Test testCoreStorage_PrepareStorage
# # END Test testCoreStorage_PrepareStorage
@pytest.mark.core
@@ -261,7 +346,8 @@ def testCoreStorage_LegacyDataFolder(monkeypatch, fncPath):
storage = NWStorage(project) # type: ignore
assert storage.isOpen() is False
storage._runtimePath = fncPath
assert storage._prepareStorage() is True
(fncPath / nwFiles.PROJ_FILE).touch()
storage.initProjectStorage(fncPath)
legacy = _LegacyStorage(project) # type: ignore
data = []
@@ -290,8 +376,8 @@ def testCoreStorage_LegacyDataFolder(monkeypatch, fncPath):
legacy.legacyDataFolder(fncPath, data[i])
# Files form 0 to 8 should now be in content
for c in "012345678":
assert (fncPath / "content" / f"{c}00000000000{c}.nwd").exists()
for i in range(9):
assert (fncPath / "content" / f"{i}00000000000{i}.nwd").exists()
# Folders 0 to 6 should be deleted
for i in range(7):
@@ -314,7 +400,7 @@ def testCoreStorage_LegacyDataFolder(monkeypatch, fncPath):
assert not (fncPath / "content" / "9000000000009.nwd").exists()
# Run the remaining through the prepare storage call
assert storage._prepareStorage(checkLegacy=True) is True
assert storage.initProjectStorage(fncPath, clearLock=True) == NWStorageOpen.READY
for c in "0123456789abcdef":
assert (fncPath / "content" / f"{c}00000000000{c}.nwd").exists()
@@ -328,7 +414,8 @@ def testCoreStorage_DeprecatedFiles(monkeypatch, fncPath):
storage = NWStorage(project) # type: ignore
assert storage.isOpen() is False
storage._runtimePath = fncPath
assert storage._prepareStorage() is True
(fncPath / nwFiles.PROJ_FILE).touch()
storage.initProjectStorage(fncPath)
legacy = _LegacyStorage(project) # type: ignore
# Files/Folders to be Deleted or Renamed