681 lines
25 KiB
Python
681 lines
25 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""novelWriter Project Class Tester
|
|
"""
|
|
|
|
import pytest
|
|
import os
|
|
|
|
from shutil import copyfile
|
|
from zipfile import ZipFile
|
|
|
|
from nwtools import cmpFiles
|
|
|
|
from nw.core.project import NWProject
|
|
from nw.core.document import NWDoc
|
|
from nw.core.spellcheck import NWSpellEnchant, NWSpellSimple
|
|
from nw.constants import nwItemClass, nwItemType, nwItemLayout, nwFiles
|
|
|
|
@pytest.mark.project
|
|
def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy):
|
|
"""Test that a basic project can be created, and opened and saved.
|
|
"""
|
|
projFile = os.path.join(nwFuncTemp, "nwProject.nwx")
|
|
testFile = os.path.join(nwTempProj, "1_nwProject.nwx")
|
|
refFile = os.path.join(nwRef, "proj", "1_nwProject.nwx")
|
|
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
|
|
# Setting no data should fail
|
|
assert not theProject.newProject({})
|
|
|
|
# Try again with a proper path
|
|
assert theProject.newProject({"projPath": nwFuncTemp})
|
|
assert theProject.setProjectPath(nwFuncTemp)
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
|
|
# Creating the project once more should fail
|
|
assert not theProject.newProject({"projPath": nwFuncTemp})
|
|
|
|
# Check the new project
|
|
copyfile(projFile, testFile)
|
|
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
|
|
|
|
# Open again
|
|
assert theProject.openProject(projFile)
|
|
|
|
# Save and close
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
copyfile(projFile, testFile)
|
|
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
|
|
assert not theProject.projChanged
|
|
|
|
# Open a second time
|
|
assert theProject.openProject(projFile)
|
|
assert not theProject.openProject(projFile)
|
|
assert theProject.openProject(projFile, overrideLock=True)
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
copyfile(projFile, testFile)
|
|
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
|
|
|
|
@pytest.mark.project
|
|
def testProjectNewRoot(nwFuncTemp, nwTempProj, nwRef, nwDummy):
|
|
"""Check that new root folders can be added to the project.
|
|
"""
|
|
projFile = os.path.join(nwFuncTemp, "nwProject.nwx")
|
|
testFile = os.path.join(nwTempProj, "2_nwProject.nwx")
|
|
refFile = os.path.join(nwRef, "proj", "2_nwProject.nwx")
|
|
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
|
|
assert theProject.newProject({"projPath": nwFuncTemp})
|
|
assert theProject.setProjectPath(nwFuncTemp)
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
assert theProject.openProject(projFile)
|
|
|
|
assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), type(None))
|
|
assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), type(None))
|
|
assert isinstance(theProject.newRoot("Character", nwItemClass.CHARACTER), type(None))
|
|
assert isinstance(theProject.newRoot("World", nwItemClass.WORLD), type(None))
|
|
assert isinstance(theProject.newRoot("Timeline", nwItemClass.TIMELINE), str)
|
|
assert isinstance(theProject.newRoot("Object", nwItemClass.OBJECT), str)
|
|
assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str)
|
|
assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str)
|
|
|
|
assert theProject.projChanged
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
|
|
copyfile(projFile, testFile)
|
|
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
|
|
assert not theProject.projChanged
|
|
|
|
@pytest.mark.project
|
|
def testProjectNewFile(nwFuncTemp, nwTempProj, nwRef, nwDummy):
|
|
"""Check that new files can be added to the project.
|
|
"""
|
|
projFile = os.path.join(nwFuncTemp, "nwProject.nwx")
|
|
testFile = os.path.join(nwTempProj, "3_nwProject.nwx")
|
|
refFile = os.path.join(nwRef, "proj", "3_nwProject.nwx")
|
|
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
|
|
assert theProject.newProject({"projPath": nwFuncTemp})
|
|
assert theProject.setProjectPath(nwFuncTemp)
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
assert theProject.openProject(projFile)
|
|
|
|
assert isinstance(theProject.newFile("Hello", nwItemClass.NOVEL, "31489056e0916"), str)
|
|
assert isinstance(theProject.newFile("Jane", nwItemClass.CHARACTER, "71ee45a3c0db9"), str)
|
|
assert theProject.projChanged
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
|
|
copyfile(projFile, testFile)
|
|
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
|
|
assert not theProject.projChanged
|
|
|
|
@pytest.mark.project
|
|
def testProjectNewCustomA(nwFuncTemp, nwTempProj, nwRef, nwDummy):
|
|
"""Create a new project from a project wizard dictionary.
|
|
Custom type with chapters and scenes.
|
|
"""
|
|
projFile = os.path.join(nwFuncTemp, "nwProject.nwx")
|
|
testFile = os.path.join(nwTempProj, "4_nwProject.nwx")
|
|
refFile = os.path.join(nwRef, "proj", "4_nwProject.nwx")
|
|
|
|
projData = {
|
|
"projName": "Test Custom",
|
|
"projTitle": "Test Novel",
|
|
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
"projPath": nwFuncTemp,
|
|
"popSample": False,
|
|
"popMinimal": False,
|
|
"popCustom": True,
|
|
"addRoots": [
|
|
nwItemClass.PLOT,
|
|
nwItemClass.CHARACTER,
|
|
nwItemClass.WORLD,
|
|
nwItemClass.TIMELINE,
|
|
nwItemClass.OBJECT,
|
|
nwItemClass.ENTITY,
|
|
],
|
|
"numChapters": 3,
|
|
"numScenes": 3,
|
|
"chFolders": True,
|
|
}
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
|
|
assert theProject.newProject(projData)
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
|
|
copyfile(projFile, testFile)
|
|
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
|
|
|
|
@pytest.mark.project
|
|
def testProjectNewCustomB(nwFuncTemp, nwTempProj, nwRef, nwDummy):
|
|
"""Create a new project from a project wizard dictionary.
|
|
Custom type without chapters, but with scenes.
|
|
"""
|
|
projFile = os.path.join(nwFuncTemp, "nwProject.nwx")
|
|
testFile = os.path.join(nwTempProj, "5_nwProject.nwx")
|
|
refFile = os.path.join(nwRef, "proj", "5_nwProject.nwx")
|
|
|
|
projData = {
|
|
"projName": "Test Custom",
|
|
"projTitle": "Test Novel",
|
|
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
"projPath": nwFuncTemp,
|
|
"popSample": False,
|
|
"popMinimal": False,
|
|
"popCustom": True,
|
|
"addRoots": [
|
|
nwItemClass.PLOT,
|
|
nwItemClass.CHARACTER,
|
|
nwItemClass.WORLD,
|
|
nwItemClass.TIMELINE,
|
|
nwItemClass.OBJECT,
|
|
nwItemClass.ENTITY,
|
|
],
|
|
"numChapters": 0,
|
|
"numScenes": 6,
|
|
"chFolders": True,
|
|
}
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
|
|
assert theProject.newProject(projData)
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
|
|
copyfile(projFile, testFile)
|
|
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
|
|
|
|
@pytest.mark.project
|
|
def testProjectNewSampleA(nwFuncTemp, nwConf, nwDummy, nwTemp):
|
|
"""Check that we can create a new project can be created from the
|
|
provided sample project via a zip file.
|
|
"""
|
|
projData = {
|
|
"projName": "Test Sample",
|
|
"projTitle": "Test Novel",
|
|
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
"projPath": nwFuncTemp,
|
|
"popSample": True,
|
|
"popMinimal": False,
|
|
"popCustom": False,
|
|
}
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
theProject.mainConf = nwConf
|
|
|
|
# Sample set, but no path
|
|
assert not theProject.newProject({"popSample": True})
|
|
|
|
# Force the lookup path for assets to our temp folder
|
|
srcSample = os.path.abspath(os.path.join(nwConf.appRoot, "sample"))
|
|
dstSample = os.path.join(nwTemp, "sample.zip")
|
|
nwConf.assetPath = nwTemp
|
|
|
|
# Create and open a defective zip file
|
|
with open(dstSample, mode="w+") as outFile:
|
|
outFile.write("foo")
|
|
|
|
assert not theProject.newProject(projData)
|
|
os.unlink(dstSample)
|
|
|
|
# Create a real zip file, and unpack it
|
|
with ZipFile(dstSample, "w") as zipObj:
|
|
zipObj.write(os.path.join(srcSample, "nwProject.nwx"), "nwProject.nwx")
|
|
for docFile in os.listdir(os.path.join(srcSample, "content")):
|
|
srcDoc = os.path.join(srcSample, "content", docFile)
|
|
zipObj.write(srcDoc, "content/"+docFile)
|
|
|
|
assert theProject.newProject(projData)
|
|
assert theProject.openProject(nwFuncTemp)
|
|
assert theProject.projName == "Sample Project"
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
os.unlink(dstSample)
|
|
|
|
@pytest.mark.project
|
|
def testProjectNewSampleB(monkeypatch, nwFuncTemp, nwConf, nwDummy, nwTemp):
|
|
"""Check that we can create a new project can be created from the
|
|
provided sample project folder.
|
|
"""
|
|
projData = {
|
|
"projName": "Test Sample",
|
|
"projTitle": "Test Novel",
|
|
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
"projPath": nwFuncTemp,
|
|
"popSample": True,
|
|
"popMinimal": False,
|
|
"popCustom": False,
|
|
}
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
theProject.mainConf = nwConf
|
|
|
|
# Make sure we do not pick up the nw/assets/sample.zip file
|
|
nwConf.assetPath = nwTemp
|
|
|
|
# Set a fake project file name
|
|
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nothing.nwx")
|
|
assert not theProject.newProject(projData)
|
|
|
|
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx")
|
|
assert theProject.newProject(projData)
|
|
assert theProject.openProject(nwFuncTemp)
|
|
assert theProject.projName == "Sample Project"
|
|
assert theProject.saveProject()
|
|
assert theProject.closeProject()
|
|
|
|
# Misdirect the appRoot path so neither is possible
|
|
nwConf.appRoot = nwTemp
|
|
assert not theProject.newProject(projData)
|
|
|
|
@pytest.mark.project
|
|
def testProjectMethods(monkeypatch, nwMinimal, nwDummy):
|
|
"""Test other project class methods and functions.
|
|
"""
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
assert theProject.openProject(nwMinimal)
|
|
assert theProject.projPath == nwMinimal
|
|
|
|
# Setting project path
|
|
assert theProject.setProjectPath(None)
|
|
assert theProject.projPath is None
|
|
assert theProject.setProjectPath("")
|
|
assert theProject.projPath is None
|
|
assert theProject.setProjectPath("~")
|
|
assert theProject.projPath == os.path.expanduser("~")
|
|
|
|
# Create a new folder and populate it
|
|
projPath = os.path.join(nwMinimal, "dummy1")
|
|
assert theProject.setProjectPath(projPath, newProject=True)
|
|
|
|
# Make os.mkdir fail
|
|
def altMkdir(*args):
|
|
raise Exception("Oops!")
|
|
|
|
monkeypatch.setattr("os.mkdir", altMkdir)
|
|
projPath = os.path.join(nwMinimal, "dummy2")
|
|
assert not theProject.setProjectPath(projPath, newProject=True)
|
|
|
|
# Project Name
|
|
assert theProject.setProjectName(" A Name ")
|
|
assert theProject.projName == "A Name"
|
|
|
|
# Project Title
|
|
assert theProject.setBookTitle(" A Title ")
|
|
assert theProject.bookTitle == "A Title"
|
|
|
|
# Project Authors
|
|
assert not theProject.setBookAuthors([])
|
|
assert theProject.setBookAuthors(" Jane Doe \n John Doh \n ")
|
|
assert theProject.bookAuthors == ["Jane Doe", "John Doh"]
|
|
|
|
@pytest.mark.project
|
|
def testDocMeta(nwDummy, nwLipsum):
|
|
"""Check that the document meta data string is parsed correctly.
|
|
"""
|
|
theProject = NWProject(nwDummy)
|
|
theProject.projTree.setSeed(42)
|
|
assert theProject.openProject(nwLipsum)
|
|
|
|
aDoc = NWDoc(theProject, nwDummy)
|
|
assert aDoc.openDocument("47666c91c7ccf")
|
|
theMeta, thePath, theClass, theLayout = aDoc.getMeta()
|
|
|
|
assert theMeta == "Scene Five"
|
|
assert len(thePath) == 3
|
|
assert thePath[0] == "47666c91c7ccf"
|
|
assert thePath[1] == "6bd935d2490cd"
|
|
assert thePath[2] == "b3643d0f92e32"
|
|
assert theClass == nwItemClass.NOVEL
|
|
assert theLayout == nwItemLayout.SCENE
|
|
|
|
aDoc._docMeta = "too_short"
|
|
theMeta, thePath, theClass, theLayout = aDoc.getMeta()
|
|
assert theMeta == ""
|
|
assert thePath == []
|
|
assert theClass is None
|
|
assert theLayout is None
|
|
|
|
@pytest.mark.project
|
|
def testSpellEnchant(nwTemp, nwConf):
|
|
wList = os.path.join(nwTemp, "wordlist.txt")
|
|
with open(wList, mode="w") as wFile:
|
|
wFile.write("a_word\nb_word\nc_word\n")
|
|
|
|
spChk = NWSpellEnchant()
|
|
spChk.mainConf = nwConf
|
|
spChk.setLanguage("en", wList)
|
|
|
|
assert spChk.checkWord("a_word")
|
|
assert spChk.checkWord("b_word")
|
|
assert spChk.checkWord("c_word")
|
|
assert not spChk.checkWord("d_word")
|
|
|
|
spChk.addWord("d_word")
|
|
assert spChk.checkWord("d_word")
|
|
|
|
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"
|
|
assert aName != ""
|
|
|
|
@pytest.mark.project
|
|
def testSpellSimple(nwTemp, nwConf):
|
|
wList = os.path.join(nwTemp, "wordlist.txt")
|
|
with open(wList, mode="w") as wFile:
|
|
wFile.write("a_word\nb_word\nc_word\n")
|
|
|
|
spChk = NWSpellSimple()
|
|
spChk.mainConf = nwConf
|
|
spChk.setLanguage("en", wList)
|
|
|
|
assert spChk.checkWord("a_word")
|
|
assert spChk.checkWord("b_word")
|
|
assert spChk.checkWord("c_word")
|
|
assert not spChk.checkWord("d_word")
|
|
|
|
spChk.addWord("d_word")
|
|
assert spChk.checkWord("d_word")
|
|
|
|
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"
|
|
assert aName == "internal"
|
|
|
|
@pytest.mark.project
|
|
def testProjectOptions(nwDummy, nwLipsum):
|
|
"""Test the class that holds all the GUI state user options that are
|
|
tied to the current open project. Non-project related GUI options
|
|
are handled by the Config class.
|
|
"""
|
|
theProject = NWProject(nwDummy)
|
|
assert theProject.projMeta is None
|
|
|
|
theOpts = theProject.optState
|
|
assert not theOpts.loadSettings()
|
|
assert not theOpts.saveSettings()
|
|
|
|
# No Settings
|
|
assert theProject.openProject(nwLipsum)
|
|
assert theOpts.loadSettings()
|
|
assert theOpts.saveSettings()
|
|
assert str(theOpts.theState) == r"{}"
|
|
|
|
# Read Invalid Settings and Filter
|
|
stateFile = os.path.join(theProject.projMeta, nwFiles.OPTS_FILE)
|
|
with open(stateFile, mode="w", encoding="utf8") as outFile:
|
|
outFile.write(
|
|
r'{"GuiProjectSettings": {"winWidth": 100, "winHeight": 50}, "NoGroup": {"NoName": 0}}'
|
|
)
|
|
assert theOpts.loadSettings()
|
|
assert str(theOpts.theState) == r"{'GuiProjectSettings': {'winWidth': 100, 'winHeight': 50}}"
|
|
|
|
# Set New Settings
|
|
assert not theOpts.setValue("NoGroup", "NoName", None)
|
|
assert not theOpts.setValue("GuiProjectSettings", "NoName", None)
|
|
assert theOpts.setValue("GuiProjectSettings", "winWidth", 200)
|
|
assert theOpts.setValue("GuiProjectSettings", "winHeight", 80)
|
|
assert str(theOpts.theState) == r"{'GuiProjectSettings': {'winWidth': 200, 'winHeight': 80}}"
|
|
|
|
# Check Read/Write Types
|
|
|
|
## String
|
|
assert theOpts.setValue("GuiWritingStats", "winWidth", "123")
|
|
assert isinstance(theOpts.getString("GuiWritingStats", "winWidth", "456"), str)
|
|
assert theOpts.getString("GuiWritingStats", "NoName", "456") == "456"
|
|
|
|
## Int
|
|
assert theOpts.setValue("GuiWritingStats", "winWidth", "123")
|
|
assert isinstance(theOpts.getInt("GuiWritingStats", "winWidth", 456), int)
|
|
assert theOpts.getInt("GuiWritingStats", "NoName", 456) == 456
|
|
assert theOpts.setValue("GuiWritingStats", "winWidth", "True")
|
|
assert theOpts.getInt("GuiWritingStats", "NoName", 456) == 456
|
|
|
|
## Float
|
|
assert theOpts.setValue("GuiWritingStats", "winWidth", "123")
|
|
assert isinstance(theOpts.getFloat("GuiWritingStats", "winWidth", 456.0), float)
|
|
assert theOpts.getFloat("GuiWritingStats", "NoName", 456.0) == 456.0
|
|
assert theOpts.setValue("GuiWritingStats", "winWidth", "True")
|
|
assert theOpts.getFloat("GuiWritingStats", "winWidth", 456.0) == 456.0
|
|
|
|
## Bool
|
|
assert theOpts.setValue("GuiWritingStats", "winWidth", True)
|
|
assert isinstance(theOpts.getBool("GuiWritingStats", "winWidth", False), bool)
|
|
assert theOpts.getFloat("GuiWritingStats", "NoName", False) is False
|
|
assert theOpts.setValue("GuiWritingStats", "winWidth", "True")
|
|
assert theOpts.getFloat("GuiWritingStats", "winWidth", False) is False
|
|
|
|
@pytest.mark.project
|
|
def testProjectOrphanedFiles(nwDummy, nwLipsum):
|
|
"""Check that files in the content folder that are not tracked in
|
|
the project XML file are handled correctly by the orphaned files
|
|
function. It should also restore as much meta data as possible from
|
|
the meta line at the top of the document file.
|
|
"""
|
|
theProject = NWProject(nwDummy)
|
|
assert theProject.openProject(nwLipsum)
|
|
assert theProject.projTree["636b6aa9b697b"] is None
|
|
assert theProject.closeProject()
|
|
|
|
# First Item with Meta Data
|
|
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
|
|
with open(orphPath, mode="w", encoding="utf8") as outFile:
|
|
outFile.write(r"%%~ 5eaea4e8cdee8:15c4492bd5107:WORLD:NOTE:Mars")
|
|
outFile.write("\n")
|
|
|
|
# Second Item without Meta Data
|
|
orphPath = os.path.join(nwLipsum, "content", "736b6aa9b697b.nwd")
|
|
with open(orphPath, mode="w", encoding="utf8") as outFile:
|
|
outFile.write("\n")
|
|
|
|
# Invalid File Name
|
|
dummyPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.txt")
|
|
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
|
outFile.write("\n")
|
|
|
|
# Invalid File Name
|
|
dummyPath = os.path.join(nwLipsum, "content", "636b6aa9b697bb.nwd")
|
|
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
|
outFile.write("\n")
|
|
|
|
# Invalid File Name
|
|
dummyPath = os.path.join(nwLipsum, "content", "abcdefghijklm.nwd")
|
|
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
|
outFile.write("\n")
|
|
|
|
assert theProject.openProject(nwLipsum)
|
|
assert theProject.projPath is not None
|
|
assert theProject.projTree["636b6aa9b697bb"] is None
|
|
assert theProject.projTree["abcdefghijklm"] is None
|
|
|
|
# First Item with Meta Data
|
|
oItem = theProject.projTree["636b6aa9b697b"]
|
|
assert oItem is not None
|
|
assert oItem.itemName == "Mars"
|
|
assert oItem.itemHandle == "636b6aa9b697b"
|
|
assert oItem.itemParent is None
|
|
assert oItem.itemClass == nwItemClass.WORLD
|
|
assert oItem.itemType == nwItemType.FILE
|
|
assert oItem.itemLayout == nwItemLayout.NOTE
|
|
|
|
# Second Item without Meta Data
|
|
oItem = theProject.projTree["736b6aa9b697b"]
|
|
assert oItem is not None
|
|
assert oItem.itemName == "Orphaned File 1"
|
|
assert oItem.itemHandle == "736b6aa9b697b"
|
|
assert oItem.itemParent is None
|
|
assert oItem.itemClass == nwItemClass.NO_CLASS
|
|
assert oItem.itemType == nwItemType.FILE
|
|
assert oItem.itemLayout == nwItemLayout.NO_LAYOUT
|
|
|
|
assert theProject.saveProject(nwLipsum)
|
|
assert theProject.closeProject()
|
|
|
|
@pytest.mark.project
|
|
def testProjectOldFormat(nwDummy, nwOldProj):
|
|
"""Test that a project folder structure of version 1.0 can be
|
|
converted to the latest folder structure. Version 1.0 split the
|
|
documents into 'data_0' ... 'data_f' folders, which are now all
|
|
contained in a single 'content' folder.
|
|
"""
|
|
theProject = NWProject(nwDummy)
|
|
theProject.mainConf.showGUI = False
|
|
|
|
# Create dummy files for known legacy files
|
|
deleteFiles = [
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.0"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.1"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.2"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.3"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.4"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.5"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.6"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.7"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.8"),
|
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.9"),
|
|
os.path.join(nwOldProj, "meta", "mainOptions.json"),
|
|
os.path.join(nwOldProj, "meta", "exportOptions.json"),
|
|
os.path.join(nwOldProj, "meta", "outlineOptions.json"),
|
|
os.path.join(nwOldProj, "meta", "timelineOptions.json"),
|
|
os.path.join(nwOldProj, "meta", "docMergeOptions.json"),
|
|
os.path.join(nwOldProj, "meta", "sessionLogOptions.json"),
|
|
]
|
|
|
|
# Add some files that shouldn't be there
|
|
deleteFiles.append(os.path.join(nwOldProj, "data_f", "whatnow.nwd"))
|
|
deleteFiles.append(os.path.join(nwOldProj, "data_f", "whatnow.txt"))
|
|
|
|
# Add some folders that shouldn't be there
|
|
os.mkdir(os.path.join(nwOldProj, "stuff"))
|
|
os.mkdir(os.path.join(nwOldProj, "data_1", "stuff"))
|
|
|
|
# Create dummy files
|
|
os.mkdir(os.path.join(nwOldProj, "cache"))
|
|
for aFile in deleteFiles:
|
|
with open(aFile, mode="w+", encoding="utf8") as outFile:
|
|
outFile.write("Hi")
|
|
for aFile in deleteFiles:
|
|
assert os.path.isfile(aFile)
|
|
|
|
# Open project and check that files that are not supposed to be
|
|
# there have been removed
|
|
assert theProject.openProject(nwOldProj)
|
|
for aFile in deleteFiles:
|
|
assert not os.path.isfile(aFile)
|
|
|
|
assert not os.path.isdir(os.path.join(nwOldProj, "data_1", "stuff"))
|
|
assert not os.path.isdir(os.path.join(nwOldProj, "data_1"))
|
|
assert not os.path.isdir(os.path.join(nwOldProj, "data_7"))
|
|
assert not os.path.isdir(os.path.join(nwOldProj, "data_8"))
|
|
assert not os.path.isdir(os.path.join(nwOldProj, "data_9"))
|
|
assert not os.path.isdir(os.path.join(nwOldProj, "data_a"))
|
|
assert not os.path.isdir(os.path.join(nwOldProj, "data_f"))
|
|
|
|
# Check stuff that has been moved
|
|
assert os.path.isdir(os.path.join(nwOldProj, "junk"))
|
|
assert os.path.isdir(os.path.join(nwOldProj, "junk", "stuff"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "junk", "whatnow.nwd"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "junk", "whatnow.txt"))
|
|
|
|
# Check that files we want to keep are in the right place
|
|
assert os.path.isdir(os.path.join(nwOldProj, "cache"))
|
|
assert os.path.isdir(os.path.join(nwOldProj, "content"))
|
|
assert os.path.isdir(os.path.join(nwOldProj, "meta"))
|
|
|
|
assert os.path.isfile(os.path.join(nwOldProj, "content", "f528d831f5b24.nwd"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "content", "88124a4292d8b.nwd"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "content", "91239bf2f8b69.nwd"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "content", "19752e7f9d8af.nwd"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "content", "a764d5acf5a21.nwd"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "content", "9058ae29f0dfd.nwd"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "content", "7ff63b8afc4cd.nwd"))
|
|
|
|
assert os.path.isfile(os.path.join(nwOldProj, "meta", "tagsIndex.json"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "meta", "sessionInfo.log"))
|
|
|
|
# Close the project
|
|
theProject.closeProject()
|
|
|
|
# Check that new files have been created
|
|
assert os.path.isfile(os.path.join(nwOldProj, "meta", "guiOptions.json"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "meta", "sessionStats.log"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "ToC.json"))
|
|
assert os.path.isfile(os.path.join(nwOldProj, "ToC.txt"))
|
|
|
|
@pytest.mark.project
|
|
def testProjectBackup(nwDummy, nwMinimal, nwTemp):
|
|
"""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(nwDummy)
|
|
assert theProject.openProject(nwMinimal)
|
|
|
|
# Test faulty settings
|
|
# Invalid path
|
|
theProject.mainConf.backupPath = None
|
|
assert not theProject.zipIt(doNotify=False)
|
|
|
|
# Missing project name
|
|
theProject.mainConf.backupPath = nwTemp
|
|
theProject.projName = ""
|
|
assert not theProject.zipIt(doNotify=False)
|
|
|
|
# Non-existent folder
|
|
theProject.mainConf.backupPath = os.path.join(nwTemp, "nonexistent")
|
|
theProject.projName = "Test Minimal"
|
|
assert not theProject.zipIt(doNotify=False)
|
|
|
|
# Same folder as project (causes infinite loop in zipping)
|
|
theProject.mainConf.backupPath = nwMinimal
|
|
assert not theProject.zipIt(doNotify=False)
|
|
|
|
# Test correct settings
|
|
theProject.mainConf.backupPath = nwTemp
|
|
assert theProject.zipIt(doNotify=False)
|
|
|
|
theFiles = os.listdir(os.path.join(nwTemp, "Test Minimal"))
|
|
assert len(theFiles) == 1
|
|
|
|
theZip = theFiles[0]
|
|
assert theZip[:12] == "Backup from "
|
|
assert theZip[-4:] == ".zip"
|
|
|
|
# Extract the archive
|
|
with ZipFile(os.path.join(nwTemp, "Test Minimal", theZip), "r") as inZip:
|
|
inZip.extractall(os.path.join(nwTemp, "extract"))
|
|
|
|
# Check that the main project file was restored
|
|
assert cmpFiles(
|
|
os.path.join(nwMinimal, "nwProject.nwx"), os.path.join(nwTemp, "extract", "nwProject.nwx")
|
|
)
|