Fix tests broken by xml rewrite

This commit is contained in:
Veronica Berglyd Olsen
2022-11-01 18:31:03 +01:00
parent 5b7f2b2b55
commit 494b94430c
4 changed files with 355 additions and 482 deletions
+9 -7
View File
@@ -190,7 +190,7 @@ class NWItem:
if "handle" in data:
self.setHandle(data["handle"])
else:
logger.error("XML item entry does not have a handle")
logger.error("Item does not have a handle")
return False
self.setName(data.get("label", ""))
@@ -217,12 +217,14 @@ class NWItem:
self._parent = None # Root items cannot have a parent
if self._type != nwItemType.FILE:
self._heading = "H0" # Only files have headers
self._active = False # Can only be True for files
self._charCount = 0 # Only set for files
self._wordCount = 0 # Only set for files
self._paraCount = 0 # Only set for files
self._cursorPos = 0 # Only set for files
# Reset values that should only be set for files
self._layout = nwItemLayout.NO_LAYOUT
self._heading = "H0"
self._active = False
self._charCount = 0
self._wordCount = 0
self._paraCount = 0
self._cursorPos = 0
return True
+181 -241
View File
@@ -21,8 +21,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest
from lxml import etree
from PyQt5.QtGui import QIcon
from tools import C, buildTestProject
@@ -497,257 +495,199 @@ def testCoreItem_ClassDefaults(mockGUI):
@pytest.mark.core
@pytest.mark.skip
def testCoreItem_XMLPackUnpack(mockGUI, caplog, mockRnd):
"""Test packing and unpacking XML objects for the NWItem class.
def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
"""Test packing and unpacking entries for the NWItem class.
"""
theProject = NWProject(mockGUI)
nwXML = etree.Element("novelWriterXML")
theProject.data.itemStatus.write(None, "New", (100, 100, 100))
theProject.data.itemImport.write(None, "New", (100, 100, 100))
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
# Invalid
theItem = NWItem(theProject)
assert theItem.unpack({}) is False
# File
# ====
theItem = NWItem(theProject)
theItem.setHandle("0123456789abc")
theItem.setParent("0123456789abc")
theItem.setRoot("0123456789abc")
theItem.setOrder(1)
theItem.setName("A Name")
theItem.setClass("NOVEL")
theItem.setType("FILE")
theItem.setImport(importKeys[3])
theItem.setLayout("NOTE")
theItem.setActive(False)
theItem.setParaCount(3)
theItem.setWordCount(5)
theItem.setCharCount(7)
theItem.setCursorPos(11)
assert theItem.unpack({
"label": "A File",
"handle": "0000000000003",
"parent": "0000000000002",
"root": "0000000000001",
"order": 1,
"type": "FILE",
"class": "NOVEL",
"layout": "DOCUMENT",
"expanded": True,
"status": None,
"import": None,
"heading": "H1",
"charCount": 100,
"wordCount": 20,
"paraCount": 2,
"cursorPos": 50,
"active": False,
}) is True
# Pack
xContent = etree.SubElement(nwXML, "content")
theItem.packXML(xContent)
assert etree.tostring(xContent, pretty_print=False, encoding="utf-8") == (
b'<content>'
b'<item handle="0123456789abc" parent="0123456789abc" root="0123456789abc" order="1" '
b'type="FILE" class="NOVEL" layout="NOTE"><meta expanded="False" mainHeading="H0" '
b'charCount="7" wordCount="5" paraCount="3" cursorPos="11"/><name status="None" '
b'import="%s" active="False">A Name</name></item>'
b'</content>'
) % bytes(importKeys[3], encoding="utf8")
# Unpack
theItem = NWItem(theProject)
assert theItem.unpackXML(xContent[0])
assert theItem.itemHandle == "0123456789abc"
assert theItem.itemParent == "0123456789abc"
assert theItem.itemRoot == "0123456789abc"
assert theItem.itemName == "A File"
assert theItem.itemHandle == "0000000000003"
assert theItem.itemParent == "0000000000002"
assert theItem.itemRoot == "0000000000001"
assert theItem.itemOrder == 1
assert theItem.itemType == nwItemType.FILE
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemLayout == nwItemLayout.DOCUMENT
assert theItem.itemStatus == "s000000"
assert theItem.itemImport == "i000001"
assert theItem.isActive is False
assert theItem.paraCount == 3
assert theItem.wordCount == 5
assert theItem.charCount == 7
assert theItem.cursorPos == 11
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemType == nwItemType.FILE
assert theItem.itemLayout == nwItemLayout.NOTE
assert theItem.itemStatus == statusKeys[0] # Was None, should now be default
assert theItem.itemImport == importKeys[3]
# Folder
# ======
theItem = NWItem(theProject)
theItem.setHandle("0123456789abc")
theItem.setParent("0123456789abc")
theItem.setRoot("0123456789abc")
theItem.setOrder(1)
theItem.setName("A Name")
theItem.setClass("NOVEL")
theItem.setType("FOLDER")
theItem.setStatus(statusKeys[1])
theItem.setLayout("NOTE")
theItem.setExpanded(True)
theItem.setActive(False)
theItem.setParaCount(3)
theItem.setWordCount(5)
theItem.setCharCount(7)
theItem.setCursorPos(11)
# Pack
xContent = etree.SubElement(nwXML, "content")
theItem.packXML(xContent)
assert etree.tostring(xContent, pretty_print=False, encoding="utf-8") == (
b'<content>'
b'<item handle="0123456789abc" parent="0123456789abc" root="0123456789abc" order="1" '
b'type="FOLDER" class="NOVEL"><meta expanded="True"/><name status="%s" '
b'import="None">A Name</name></item>'
b'</content>'
) % bytes(statusKeys[1], encoding="utf8")
# Unpack
theItem = NWItem(theProject)
assert theItem.unpackXML(xContent[0])
assert theItem.itemHandle == "0123456789abc"
assert theItem.itemParent == "0123456789abc"
assert theItem.itemRoot == "0123456789abc"
assert theItem.itemOrder == 1
assert theItem.isExpanded is True
assert theItem.isActive is True
assert theItem.paraCount == 0
assert theItem.wordCount == 0
assert theItem.charCount == 0
assert theItem.cursorPos == 0
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemType == nwItemType.FOLDER
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
assert theItem.itemStatus == statusKeys[1]
assert theItem.itemImport == importKeys[0] # Was None, should now be default
# Errors
# ======
# Not an Item
mockXml = etree.SubElement(nwXML, "stuff")
assert theItem.unpackXML(mockXml) is False
# Item without Handle
mockXml = etree.SubElement(nwXML, "item", attrib={"stuff": "nah"})
assert theItem.unpackXML(mockXml) is False
# Item with Invalid SubElement is Accepted w/Error
mockXml = etree.SubElement(nwXML, "item", attrib={"handle": "0123456789abc"})
xParam = etree.SubElement(mockXml, "invalid")
xParam.text = "stuff"
caplog.clear()
assert theItem.unpackXML(mockXml) is True
assert "Unknown tag 'invalid'" in caplog.text
# Pack Valid Item
mockXml = etree.SubElement(nwXML, "group")
theItem._subPack(mockXml, "subGroup", {"one": "two"}, "value", False)
assert etree.tostring(mockXml, pretty_print=False, encoding="utf-8") == (
b"<group><subGroup one=\"two\">value</subGroup></group>"
)
# Pack Not Allowed None
mockXml = etree.SubElement(nwXML, "group")
assert theItem._subPack(mockXml, "subGroup", {}, None, False) is None
assert theItem._subPack(mockXml, "subGroup", {}, "None", False) is None
assert etree.tostring(mockXml, pretty_print=False, encoding="utf-8") == (
b"<group/>"
)
# END Test testCoreItem_XMLPackUnpack
@pytest.mark.core
@pytest.mark.skip
def testCoreItem_ConvertFromFmt12(mockGUI):
"""Test the setter for all the nwItemLayout values for the NWItem
class using the class names that were present in file format 1.2.
"""
theProject = NWProject(mockGUI)
theItem = NWItem(theProject)
# Deprecated Layouts
theItem.setLayout("TITLE")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("PAGE")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("BOOK")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("PARTITION")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("UNNUMBERED")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("CHAPTER")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("SCENE")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("MUMBOJUMBO")
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
# END Test testCoreItem_ConvertFromFmt12
@pytest.mark.core
@pytest.mark.skip
def testCoreItem_ConvertFromFmt13(mockGUI):
"""Test packing and unpacking XML objects for the NWItem class from
format version 1.3
"""
theProject = NWProject(mockGUI)
# Make Version 1.3 XML
nwXML = etree.Element("novelWriterXML")
xContent = etree.SubElement(nwXML, "content")
# Folder
xPack = etree.SubElement(xContent, "item", attrib={
"handle": "a000000000001",
"order": "1",
"parent": "b000000000001",
})
NWItem._subPack(xPack, "name", text="Folder")
NWItem._subPack(xPack, "type", text="FOLDER")
NWItem._subPack(xPack, "class", text="NOVEL")
NWItem._subPack(xPack, "status", text="New")
NWItem._subPack(xPack, "expanded", text="True")
# Unpack Folder
theItem = NWItem(theProject)
theItem.unpackXML(xContent[0])
assert theItem.itemHandle == "a000000000001"
assert theItem.itemParent == "b000000000001"
assert theItem.itemOrder == 1
assert theItem.isExpanded is True
assert theItem.isActive is True
assert theItem.charCount == 0
assert theItem.wordCount == 0
assert theItem.paraCount == 0
assert theItem.cursorPos == 0
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemType == nwItemType.FOLDER
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
# File
xPack = etree.SubElement(xContent, "item", attrib={
"handle": "c000000000001",
"order": "2",
"parent": "a000000000001",
})
NWItem._subPack(xPack, "name", text="Scene")
NWItem._subPack(xPack, "type", text="FILE")
NWItem._subPack(xPack, "class", text="NOVEL")
NWItem._subPack(xPack, "status", text="New")
NWItem._subPack(xPack, "exported", text="True")
NWItem._subPack(xPack, "layout", text="DOCUMENT")
NWItem._subPack(xPack, "charCount", text="600")
NWItem._subPack(xPack, "wordCount", text="100")
NWItem._subPack(xPack, "paraCount", text="6")
NWItem._subPack(xPack, "cursorPos", text="50")
# Unpack File
theItem = NWItem(theProject)
theItem.unpackXML(xContent[1])
assert theItem.itemHandle == "c000000000001"
assert theItem.itemParent == "a000000000001"
assert theItem.itemOrder == 2
assert theItem.isExpanded is False
assert theItem.isActive is True
assert theItem.charCount == 600
assert theItem.wordCount == 100
assert theItem.paraCount == 6
assert theItem.mainHeading == "H1"
assert theItem.charCount == 100
assert theItem.wordCount == 20
assert theItem.paraCount == 2
assert theItem.cursorPos == 50
assert theItem.pack() == {
"name": "A File",
"itemAttr": {
"handle": "0000000000003",
"parent": "0000000000002",
"root": "0000000000001",
"order": "1",
"type": "FILE",
"class": "NOVEL",
"layout": "DOCUMENT",
},
"metaAttr": {
"expanded": "True",
"heading": "H1",
"charCount": "100",
"wordCount": "20",
"paraCount": "2",
"cursorPos": "50",
},
"nameAttr": {
"status": "s000000",
"import": "i000001",
"active": "False",
}
}
# Folder
theItem = NWItem(theProject)
assert theItem.unpack({
"label": "A Folder",
"handle": "0000000000003",
"parent": "0000000000002",
"root": "0000000000001",
"order": 1,
"type": "FOLDER",
"class": "NOVEL",
"layout": "DOCUMENT",
"expanded": True,
"status": "",
"import": "",
"heading": "H1",
"charCount": 100,
"wordCount": 20,
"paraCount": 2,
"cursorPos": 50,
"active": True,
}) is True
assert theItem.itemName == "A Folder"
assert theItem.itemHandle == "0000000000003"
assert theItem.itemParent == "0000000000002"
assert theItem.itemRoot == "0000000000001"
assert theItem.itemOrder == 1
assert theItem.itemType == nwItemType.FOLDER
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemType == nwItemType.FILE
assert theItem.itemLayout == nwItemLayout.DOCUMENT
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
assert theItem.itemStatus == "s000000"
assert theItem.itemImport == "i000001"
assert theItem.isActive is False
assert theItem.isExpanded is True
assert theItem.mainHeading == "H0"
assert theItem.charCount == 0
assert theItem.wordCount == 0
assert theItem.paraCount == 0
assert theItem.cursorPos == 0
# Deprecated Type
theItem.setType("TRASH")
assert theItem.pack() == {
"name": "A Folder",
"itemAttr": {
"handle": "0000000000003",
"parent": "0000000000002",
"root": "0000000000001",
"order": "1",
"type": "FOLDER",
"class": "NOVEL",
},
"metaAttr": {
"expanded": "True",
},
"nameAttr": {
"status": "s000000",
"import": "i000001",
}
}
# Root
theItem = NWItem(theProject)
assert theItem.unpack({
"label": "A Novel",
"handle": "0000000000003",
"parent": "0000000000002",
"root": "0000000000001",
"order": 1,
"type": "ROOT",
"class": "NOVEL",
"layout": "DOCUMENT",
"expanded": True,
"status": None,
"import": None,
"heading": "H1",
"charCount": 100,
"wordCount": 20,
"paraCount": 2,
"cursorPos": 50,
"active": True,
}) is True
assert theItem.itemName == "A Novel"
assert theItem.itemHandle == "0000000000003"
assert theItem.itemParent is None
assert theItem.itemRoot == "0000000000003"
assert theItem.itemOrder == 1
assert theItem.itemType == nwItemType.ROOT
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
assert theItem.itemStatus == "s000000"
assert theItem.itemImport == "i000001"
assert theItem.isActive is False
assert theItem.isExpanded is True
assert theItem.mainHeading == "H0"
assert theItem.charCount == 0
assert theItem.wordCount == 0
assert theItem.paraCount == 0
assert theItem.cursorPos == 0
# END Test testCoreItem_ConvertFromFmt13
assert theItem.pack() == {
"name": "A Novel",
"itemAttr": {
"handle": "0000000000003",
"parent": "None",
"root": "0000000000003",
"order": "1",
"type": "ROOT",
"class": "NOVEL",
},
"metaAttr": {
"expanded": "True",
},
"nameAttr": {
"status": "s000000",
"import": "i000001",
}
}
# END Test testCoreItem_PackUnpack
+113 -199
View File
@@ -20,13 +20,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import os
import shutil
import pytest
from shutil import copyfile
from zipfile import ZipFile
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE, C
from mock import causeOSError
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE, C
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
from novelwriter.common import formatTimeStamp
@@ -36,6 +37,7 @@ from novelwriter.core.index import NWIndex
from novelwriter.core.project import NWProject
from novelwriter.core.options import OptionState
from novelwriter.core.document import NWDoc
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
@pytest.mark.core
@@ -380,140 +382,90 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
@pytest.mark.core
@pytest.mark.skip
def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncDir, mockRnd):
"""Test opening a project.
"""
theProject = NWProject(mockGUI)
mockRnd.reset()
buildTestProject(theProject, fncDir)
# Rename the project file to check handling
rName = os.path.join(nwMinimal, nwFiles.PROJ_FILE)
wName = os.path.join(nwMinimal, nwFiles.PROJ_FILE+"_sdfghj")
rName = os.path.join(fncDir, nwFiles.PROJ_FILE)
wName = os.path.join(fncDir, nwFiles.PROJ_FILE+"_sdfghj")
os.rename(rName, wName)
assert theProject.openProject(nwMinimal) is False
assert theProject.openProject(fncDir) is False
os.rename(wName, rName)
# Fail on folder structure check
with monkeypatch.context() as mp:
mp.setattr("os.mkdir", causeOSError)
assert theProject.openProject(nwMinimal) is False
shutil.rmtree(os.path.join(fncDir, "meta"))
assert theProject.openProject(fncDir) is False
# Fail on lock file
theProject.setProjectPath(nwMinimal)
theProject.setProjectPath(fncDir)
assert theProject._writeLockFile()
assert theProject.openProject(nwMinimal) is False
assert theProject.openProject(fncDir) is False
# Fail to read lockfile (which still opens the project)
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert theProject.openProject(nwMinimal) is True
caplog.clear()
assert theProject.openProject(fncDir) is True
assert "Failed to check lock file" in caplog.text
assert theProject.closeProject()
# Force open with lockfile
theProject.setProjectPath(nwMinimal)
theProject.setProjectPath(fncDir)
assert theProject._writeLockFile()
assert theProject.openProject(nwMinimal, overrideLock=True) is True
assert theProject.openProject(fncDir, overrideLock=True) is True
assert theProject.closeProject()
# Make a junk XML file
oName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"orig")
bName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"bak")
os.rename(rName, oName)
writeFile(rName, "stuff")
assert theProject.openProject(nwMinimal) 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 "Project file does not appear" in mockGUI.lastAlert
# Also write a jun XML backup file
writeFile(bName, "stuff")
assert theProject.openProject(nwMinimal) is False
# 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 "Unknown or unsupported novelWriter project file" in mockGUI.lastAlert
# Wrong root item
writeFile(rName, "<not_novelWriterXML></not_novelWriterXML>\n")
assert theProject.openProject(nwMinimal) is False
# 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 "Failed to parse project xml" in mockGUI.lastAlert
# Wrong file version
writeFile(rName, (
"<?xml version='0.0' encoding='utf-8'?>\n"
"<novelWriterXML "
"appVersion=\"1.0\" "
"hexVersion=\"0x01000000\" "
"fileVersion=\"1.0\" "
"timeStamp=\"2020-01-01 00:00:00\">\n"
"</novelWriterXML>\n"
))
mockGUI.askResponse = False
assert theProject.openProject(nwMinimal) is False
mockGUI.undo()
# 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 "The file format of your project is about to be" in mockGUI.lastQuestion[1]
mockGUI.askResponse = True
# Future file version
writeFile(rName, (
"<?xml version='1.0' encoding='utf-8'?>\n"
"<novelWriterXML "
"appVersion=\"1.0\" "
"hexVersion=\"0x01000000\" "
"fileVersion=\"99.99\" "
"timeStamp=\"2020-01-01 00:00:00\">\n"
"</novelWriterXML>\n"
))
assert theProject.openProject(nwMinimal) is False
# Update file version
writeFile(rName, (
"<?xml version='1.0' encoding='utf-8'?>\n"
"<novelWriterXML "
"appVersion=\"1.0\" "
"hexVersion=\"0xffffffff\" "
"fileVersion=\"1.2\" "
"timeStamp=\"2020-01-01 00:00:00\">\n"
"</novelWriterXML>\n"
))
mockGUI.askResponse = False
assert theProject.openProject(nwMinimal) is False
assert mockGUI.lastQuestion[0] == "File Version"
mockGUI.undo()
# Larger hex version
writeFile(rName, (
"<?xml version='1.0' encoding='utf-8'?>\n"
"<novelWriterXML "
"appVersion=\"1.0\" "
"hexVersion=\"0xffffffff\" "
"fileVersion=\"%s\" "
"timeStamp=\"2020-01-01 00:00:00\">\n"
"</novelWriterXML>\n"
) % theProject.FILE_VERSION)
mockGUI.askResponse = False
assert theProject.openProject(nwMinimal) is False
assert mockGUI.lastQuestion[0] == "Version Conflict"
mockGUI.undo()
# Test skipping XML entries
writeFile(rName, (
"<?xml version='1.0' encoding='utf-8'?>\n"
"<novelWriterXML "
"appVersion=\"1.0\" "
"hexVersion=\"0x01000000\" "
"fileVersion=\"1.2\" "
"timeStamp=\"2020-01-01 00:00:00\">\n"
"<project><stuff/></project>\n"
"<settings><stuff/></settings>\n"
"</novelWriterXML>\n"
))
assert theProject.openProject(nwMinimal) is True
assert theProject.closeProject()
# Clean up XML files
os.unlink(rName)
os.unlink(bName)
os.rename(oName, rName)
# Won't convert legacy file
with monkeypatch.context() as mp:
mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: "0x99999999"))
mockGUI.askResponse = False
assert theProject.openProject(fncDir) is False
assert "This project was saved by a newer version" in mockGUI.lastQuestion[1]
mockGUI.askResponse = True
# Add some legacy stuff that cannot be removed
with monkeypatch.context() as mp:
mp.setattr(theProject, "_legacyDataFolder", causeOSError)
os.mkdir(os.path.join(nwMinimal, "data_0"))
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.nwd"), "stuff")
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.bak"), "stuff")
os.mkdir(os.path.join(fncDir, "data_0"))
writeFile(os.path.join(fncDir, "data_0", "123456789abc_main.nwd"), "stuff")
writeFile(os.path.join(fncDir, "data_0", "123456789abc_main.bak"), "stuff")
mockGUI.clear()
assert theProject.openProject(nwMinimal) is True
assert theProject.openProject(fncDir) is True
assert "There was an error updating the project." in mockGUI.lastAlert
assert theProject.closeProject()
@@ -522,57 +474,31 @@ def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
@pytest.mark.core
@pytest.mark.skip
def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncDir, refDir):
"""Test saving a project.
"""
theProject = NWProject(mockGUI)
testFile = os.path.join(nwMinimal, "nwProject.nwx")
backFile = os.path.join(nwMinimal, "nwProject.bak")
compFile = os.path.join(refDir, os.path.pardir, "minimal", "nwProject.nwx")
# Nothing to save
assert theProject.saveProject() is False
# Open test project
assert theProject.openProject(nwMinimal)
mockRnd.reset()
buildTestProject(theProject, fncDir)
# Fail on folder structure check
with monkeypatch.context() as mp:
mp.setattr("os.path.isdir", lambda *a: False)
mp.setattr("os.mkdir", causeOSError)
shutil.rmtree(os.path.join(fncDir, "meta"))
assert theProject.saveProject() is False
# Fail on open file
# Fail writing
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
mp.setattr(ProjectXMLWriter, "write", lambda *a: False)
assert theProject.saveProject() is False
# Fail on creating .bak file
with monkeypatch.context() as mp:
mp.setattr("os.replace", causeOSError)
assert theProject.saveProject() is False
assert os.path.isfile(backFile) is False
# Successful save
saveCount = theProject.data.saveCount
autoCount = theProject.data.autoCount
assert theProject.saveProject() is True
assert theProject.data.saveCount == saveCount + 1
assert theProject.data.autoCount == autoCount
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# Check that a second save creates a .bak file
assert os.path.isfile(backFile) is True
# Successful autosave
saveCount = theProject.data.saveCount
autoCount = theProject.data.autoCount
# Save with and without autosave
assert theProject.saveProject(autoSave=False) is True
assert theProject.saveProject(autoSave=True) is True
assert theProject.data.saveCount == saveCount
assert theProject.data.autoCount == autoCount + 1
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# Close test project
assert theProject.closeProject()
# END Test testCoreProject_Save
@@ -683,11 +609,11 @@ def testCoreProject_Helpers(monkeypatch, fncDir, mockGUI):
@pytest.mark.core
def testCoreProject_AccessItems(nwMinimal, mockGUI):
def testCoreProject_AccessItems(mockGUI, fncDir, mockRnd):
"""Test helper functions for the project folder.
"""
theProject = NWProject(mockGUI)
theProject.openProject(nwMinimal)
buildTestProject(theProject, fncDir)
# Storage Objects
assert isinstance(theProject.index, NWIndex)
@@ -696,34 +622,34 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
# Move Novel ROOT to after its files
oldOrder = [
"a508bb932959c", # ROOT: Novel
"a35baf2e93843", # FILE: Title Page
"a6d311a93600a", # FOLDER: New Chapter
"f5ab3e30151e1", # FILE: New Chapter
"8c659a11cd429", # FILE: New Scene
"7695ce551d265", # ROOT: Plot
"afb3043c7b2b3", # ROOT: Characters
"9d5247ab588e0", # ROOT: World
C.hNovelRoot,
C.hPlotRoot,
C.hCharRoot,
C.hWorldRoot,
C.hTitlePage,
C.hChapterDir,
C.hChapterDoc,
C.hSceneDoc,
]
newOrder = [
"a35baf2e93843", # FILE: Title Page
"f5ab3e30151e1", # FILE: New Chapter
"8c659a11cd429", # FILE: New Scene
"a6d311a93600a", # FOLDER: New Chapter
"a508bb932959c", # ROOT: Novel
"7695ce551d265", # ROOT: Plot
"afb3043c7b2b3", # ROOT: Characters
"9d5247ab588e0", # ROOT: World
C.hTitlePage,
C.hChapterDoc,
C.hSceneDoc,
C.hChapterDir,
C.hNovelRoot,
C.hPlotRoot,
C.hCharRoot,
C.hWorldRoot,
]
assert theProject.tree.handles() == oldOrder
assert theProject.setTreeOrder(newOrder)
assert theProject.tree.handles() == newOrder
# Add a non-existing item
theProject.tree._treeOrder.append("01234567789abc")
theProject.tree._treeOrder.append(C.hInvalid)
# Add an item with a non-existent parent
nHandle = theProject.newFile("Test File", "a6d311a93600a")
nHandle = theProject.newFile("Test File", C.hChapterDir)
theProject.tree[nHandle].setParent("cba9876543210")
assert theProject.tree[nHandle].itemParent == "cba9876543210"
@@ -732,15 +658,15 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
retOrder.append(tItem.itemHandle)
assert retOrder == [
"a508bb932959c", # ROOT: Novel
"7695ce551d265", # ROOT: Plot
"afb3043c7b2b3", # ROOT: Characters
"9d5247ab588e0", # ROOT: World
nHandle, # FILE: Test File
"a35baf2e93843", # FILE: Title Page
"a6d311a93600a", # FOLDER: New Chapter
"f5ab3e30151e1", # FILE: New Chapter
"8c659a11cd429", # FILE: New Scene
C.hNovelRoot,
C.hPlotRoot,
C.hCharRoot,
C.hWorldRoot,
nHandle,
C.hTitlePage,
C.hChapterDir,
C.hChapterDoc,
C.hSceneDoc,
]
assert theProject.tree[nHandle].itemParent is None
@@ -748,28 +674,28 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
@pytest.mark.core
@pytest.mark.skip
def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
"""Test the status and importance flag handling.
"""
theProject = NWProject(mockGUI)
mockRnd.reset()
buildTestProject(theProject, fncDir)
statusKeys = ["s000008", "s000009", "s00000a", "s00000b"]
importKeys = ["i00000c", "i00000d", "i00000e", "i00000f"]
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
# Change Status
# =============
theProject.tree["0000000000014"].setStatus(statusKeys[3])
theProject.tree["0000000000015"].setStatus(statusKeys[2])
theProject.tree["0000000000016"].setStatus(statusKeys[1])
theProject.tree["0000000000017"].setStatus(statusKeys[3])
theProject.tree[C.hNovelRoot].setStatus(statusKeys[3])
theProject.tree[C.hPlotRoot].setStatus(statusKeys[2])
theProject.tree[C.hCharRoot].setStatus(statusKeys[1])
theProject.tree[C.hWorldRoot].setStatus(statusKeys[3])
assert theProject.tree["0000000000014"].itemStatus == statusKeys[3]
assert theProject.tree["0000000000015"].itemStatus == statusKeys[2]
assert theProject.tree["0000000000016"].itemStatus == statusKeys[1]
assert theProject.tree["0000000000017"].itemStatus == statusKeys[3]
assert theProject.tree[C.hNovelRoot].itemStatus == statusKeys[3]
assert theProject.tree[C.hPlotRoot].itemStatus == statusKeys[2]
assert theProject.tree[C.hCharRoot].itemStatus == statusKeys[1]
assert theProject.tree[C.hWorldRoot].itemStatus == statusKeys[3]
newList = [
{"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)},
@@ -792,8 +718,8 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
assert theProject.data.itemStatus.cols(statusKeys[3]) == (4, 4, 4)
# Check the new entry
lastKey = theProject.data.itemStatus.check("s000018")
assert lastKey == "s000018"
lastKey = theProject.data.itemStatus.check("s000010")
assert lastKey == "s000010"
assert theProject.data.itemStatus.name(lastKey) == "Finished"
assert theProject.data.itemStatus.cols(lastKey) == (5, 5, 5)
@@ -804,7 +730,7 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
# Change Importance
# =================
fHandle = theProject.newFile("Jane Doe", "0000000000012")
fHandle = theProject.newFile("Jane Doe", C.hCharRoot)
theProject.tree[fHandle].setImport(importKeys[3])
assert theProject.tree[fHandle].itemImport == importKeys[3]
@@ -829,8 +755,8 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
assert theProject.data.itemImport.cols(importKeys[3]) == (4, 4, 4)
# Check the new entry
lastKey = theProject.data.itemImport.check("i00001a")
assert lastKey == "i00001a"
lastKey = theProject.data.itemImport.check("i000012")
assert lastKey == "i000012"
assert theProject.data.itemImport.name(lastKey) == "Max"
assert theProject.data.itemImport.cols(lastKey) == (5, 5, 5)
@@ -854,18 +780,6 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
assert theProject.saveProject() is True
assert theProject.closeProject() is True
# This should restore the default status/import labels
assert theProject.openProject(fncDir) is True
assert theProject.saveProject() is True
assert theProject.data.itemStatus.name("s000023") == "New"
assert theProject.data.itemStatus.name("s000024") == "Note"
assert theProject.data.itemStatus.name("s000025") == "Draft"
assert theProject.data.itemStatus.name("s000026") == "Finished"
assert theProject.data.itemImport.name("i000027") == "New"
assert theProject.data.itemImport.name("i000028") == "Minor"
assert theProject.data.itemImport.name("i000029") == "Major"
assert theProject.data.itemImport.name("i00002a") == "Main"
# END Test testCoreProject_StatusImport
@@ -1268,14 +1182,14 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
@pytest.mark.core
def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
def testCoreProject_Backup(monkeypatch, mockGUI, fncDir, tmpDir):
"""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)
assert theProject.openProject(nwMinimal)
buildTestProject(theProject, fncDir)
# Test faulty settings
@@ -1299,11 +1213,11 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
assert theProject.zipIt(doNotify=False) is False
# Same folder as project (causes infinite loop in zipping)
theProject.mainConf.backupPath = nwMinimal
theProject.mainConf.backupPath = fncDir
assert theProject.zipIt(doNotify=False) is False
# Subfolder of project (causes infinite loop in zipping)
theProject.mainConf.backupPath = os.path.join(nwMinimal, "subdir")
theProject.mainConf.backupPath = os.path.join(fncDir, "subdir")
assert theProject.zipIt(doNotify=False) is False
# Set a valid folder
@@ -1335,7 +1249,7 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
# Check that the main project file was restored
assert cmpFiles(
os.path.join(nwMinimal, "nwProject.nwx"),
os.path.join(fncDir, "nwProject.nwx"),
os.path.join(tmpDir, "extract", "nwProject.nwx")
)
+52 -35
View File
@@ -20,23 +20,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import pytest
import random
from lxml import etree
from tools import C
from PyQt5.QtGui import QIcon
from novelwriter.core.status import NWStatus
statusKeys = ["sa3b179", "s1c8031", "s06671a", "sbdd640"]
importKeys = ["i466852", "i3eb13b", "i392456", "i23b8c1"]
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
@pytest.mark.core
def testCoreStatus_Internal():
def testCoreStatus_Internal(mockRnd):
"""Test all the internal functions of the NWStatus class.
"""
random.seed(42)
theStatus = NWStatus(NWStatus.STATUS)
theImport = NWStatus(NWStatus.IMPORT)
@@ -87,10 +85,9 @@ def testCoreStatus_Internal():
@pytest.mark.core
def testCoreStatus_Iterator():
def testCoreStatus_Iterator(mockRnd):
"""Test the iterator functions of the NWStatus class.
"""
random.seed(42)
theStatus = NWStatus(NWStatus.STATUS)
theStatus.write(None, "New", (100, 100, 100))
@@ -132,10 +129,9 @@ def testCoreStatus_Iterator():
@pytest.mark.core
def testCoreStatus_Entries():
def testCoreStatus_Entries(mockRnd):
"""Test all the simple setters for the NWStatus class.
"""
random.seed(42)
theStatus = NWStatus(NWStatus.STATUS)
# Write
@@ -300,11 +296,9 @@ def testCoreStatus_Entries():
@pytest.mark.core
@pytest.mark.skip
def testCoreStatus_XMLPackUnpack():
"""Test all the XML pack/unpack of the NWStatus class.
def testCoreStatus_PackUnpack(mockRnd):
"""Test all the pack/unpack of the NWStatus class.
"""
random.seed(42)
theStatus = NWStatus(NWStatus.STATUS)
theStatus.write(None, "New", (100, 100, 100))
theStatus.write(None, "Note", (200, 50, 0))
@@ -316,36 +310,59 @@ def testCoreStatus_XMLPackUnpack():
for _ in range(n):
theStatus.increment(statusKeys[i])
nwXML = etree.Element("novelWriterXML")
# Pack
xStatus = etree.SubElement(nwXML, "status")
theStatus.packXML(xStatus)
assert etree.tostring(xStatus, pretty_print=False, encoding="utf-8") == (
b'<status>'
b'<entry key="sa3b179" count="3" red="100" green="100" blue="100">New</entry>'
b'<entry key="s1c8031" count="5" red="200" green="50" blue="0">Note</entry>'
b'<entry key="s06671a" count="7" red="200" green="150" blue="0">Draft</entry>'
b'<entry key="sbdd640" count="9" red="50" green="200" blue="0">Finished</entry>'
b'</status>'
)
assert list(theStatus.pack()) == [
("New", {
"key": statusKeys[0],
"count": "3",
"red": "100",
"green": "100",
"blue": "100"
}),
("Note", {
"key": statusKeys[1],
"count": "5",
"red": "200",
"green": "50",
"blue": "0"
}),
("Draft", {
"key": statusKeys[2],
"count": "7",
"red": "200",
"green": "150",
"blue": "0"
}),
("Finished", {
"key": statusKeys[3],
"count": "9",
"red": "50",
"green": "200",
"blue": "0"
}),
]
# Unpack
theStatus = NWStatus(NWStatus.STATUS)
assert theStatus.unpackXML(xStatus)
assert theStatus.unpack({
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},
statusKeys[3]: {"label": "New3", "colour": (250, 250, 250), "count": countTo[3]},
})
assert len(theStatus._store) == 4
assert list(theStatus._store.keys()) == statusKeys
assert theStatus._store[statusKeys[0]]["name"] == "New"
assert theStatus._store[statusKeys[1]]["name"] == "Note"
assert theStatus._store[statusKeys[2]]["name"] == "Draft"
assert theStatus._store[statusKeys[3]]["name"] == "Finished"
assert theStatus._store[statusKeys[0]]["name"] == "New0"
assert theStatus._store[statusKeys[1]]["name"] == "New1"
assert theStatus._store[statusKeys[2]]["name"] == "New2"
assert theStatus._store[statusKeys[3]]["name"] == "New3"
assert theStatus._store[statusKeys[0]]["cols"] == (100, 100, 100)
assert theStatus._store[statusKeys[1]]["cols"] == (200, 50, 0)
assert theStatus._store[statusKeys[2]]["cols"] == (200, 150, 0)
assert theStatus._store[statusKeys[3]]["cols"] == (50, 200, 0)
assert theStatus._store[statusKeys[1]]["cols"] == (150, 150, 150)
assert theStatus._store[statusKeys[2]]["cols"] == (200, 200, 200)
assert theStatus._store[statusKeys[3]]["cols"] == (250, 250, 250)
assert theStatus._store[statusKeys[0]]["count"] == countTo[0]
assert theStatus._store[statusKeys[1]]["count"] == countTo[1]
assert theStatus._store[statusKeys[2]]["count"] == countTo[2]
assert theStatus._store[statusKeys[3]]["count"] == countTo[3]
# END Test testCoreStatus_XMLPackUnpack
# END Test testCoreStatus_PackUnpack