Merge branch 'main' into multi_root
This commit is contained in:
@@ -20,6 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import random
|
||||
|
||||
from lxml import etree
|
||||
|
||||
@@ -31,9 +32,10 @@ from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_Setters(mockGUI):
|
||||
def testCoreItem_Setters(mockGUI, constData):
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
"""
|
||||
random.seed(42)
|
||||
theProject = NWProject(mockGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
@@ -42,6 +44,8 @@ def testCoreItem_Setters(mockGUI):
|
||||
assert theItem.itemName == "A Name"
|
||||
theItem.setName("\t A Name ")
|
||||
assert theItem.itemName == "A Name"
|
||||
theItem.setName("\t A\t\u2009\u202f\u2002\u2003\u2028\u2029Name ")
|
||||
assert theItem.itemName == "A Name"
|
||||
theItem.setName(123)
|
||||
assert theItem.itemName == ""
|
||||
|
||||
@@ -89,50 +93,32 @@ def testCoreItem_Setters(mockGUI):
|
||||
|
||||
# Importance
|
||||
theItem._class = nwItemClass.CHARACTER
|
||||
theItem.setImport("Nonsense")
|
||||
assert theItem.itemImport == "New"
|
||||
theItem.setImport("New")
|
||||
assert theItem.itemImport == "New"
|
||||
theItem.setImport("Minor")
|
||||
assert theItem.itemImport == "Minor"
|
||||
theItem.setImport("Major")
|
||||
assert theItem.itemImport == "Major"
|
||||
theItem.setImport("Main")
|
||||
assert theItem.itemImport == "Main"
|
||||
theItem.setImport("Word")
|
||||
assert theItem.itemImport == constData.importKeys[0] # Default
|
||||
for key in constData.importKeys:
|
||||
theItem.setImport(key)
|
||||
assert theItem.itemImport == key
|
||||
|
||||
# Status
|
||||
theItem._class = nwItemClass.NOVEL
|
||||
theItem.setStatus("Nonsense")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setStatus("New")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setStatus("Note")
|
||||
assert theItem.itemStatus == "Note"
|
||||
theItem.setStatus("Draft")
|
||||
assert theItem.itemStatus == "Draft"
|
||||
theItem.setStatus("Finished")
|
||||
assert theItem.itemStatus == "Finished"
|
||||
theItem.setStatus("Word")
|
||||
assert theItem.itemStatus == constData.statusKeys[0] # Default
|
||||
for key in constData.statusKeys:
|
||||
theItem.setStatus(key)
|
||||
assert theItem.itemStatus == key
|
||||
|
||||
# Status/Importance Wrapper
|
||||
theItem._class = nwItemClass.CHARACTER
|
||||
theItem.setImportStatus("New")
|
||||
assert theItem.itemImport == "New"
|
||||
theItem.setImportStatus("Minor")
|
||||
assert theItem.itemImport == "Minor"
|
||||
theItem.setImportStatus("Note")
|
||||
assert theItem.itemImport == "New"
|
||||
theItem.setImportStatus("Draft")
|
||||
assert theItem.itemImport == "New"
|
||||
for key in constData.importKeys:
|
||||
theItem.setImport(key)
|
||||
assert theItem.itemImport == key
|
||||
assert theItem.itemStatus == constData.statusKeys[3] # Should not change
|
||||
|
||||
theItem._class = nwItemClass.NOVEL
|
||||
theItem.setImportStatus("New")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setImportStatus("Minor")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setImportStatus("Note")
|
||||
assert theItem.itemStatus == "Note"
|
||||
theItem.setImportStatus("Draft")
|
||||
assert theItem.itemStatus == "Draft"
|
||||
for key in constData.statusKeys:
|
||||
theItem.setStatus(key)
|
||||
assert theItem.itemImport == constData.importKeys[3] # Should not change
|
||||
assert theItem.itemStatus == key
|
||||
|
||||
# Expanded
|
||||
theItem.setExpanded(8)
|
||||
@@ -366,9 +352,10 @@ def testCoreItem_LayoutSetter(mockGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
|
||||
"""Test packing and unpacking XML objects for the NWItem class.
|
||||
"""
|
||||
random.seed(42)
|
||||
theProject = NWProject(mockGUI)
|
||||
nwXML = etree.Element("novelWriterXML")
|
||||
|
||||
@@ -383,7 +370,7 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
theItem.setName("A Name")
|
||||
theItem.setClass("NOVEL")
|
||||
theItem.setType("FILE")
|
||||
theItem.setStatus("Main")
|
||||
theItem.setImport(constData.importKeys[3])
|
||||
theItem.setLayout("NOTE")
|
||||
theItem.setExported(False)
|
||||
theItem.setParaCount(3)
|
||||
@@ -398,15 +385,16 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
b'<content>'
|
||||
b'<item handle="0123456789abc" parent="0123456789abc" root="0123456789abc" order="1" '
|
||||
b'type="FILE" class="NOVEL" layout="NOTE"><meta charCount="7" wordCount="5" paraCount="3" '
|
||||
b'cursorPos="11"/><name status="New" import="None" exported="False">A Name</name></item>'
|
||||
b'cursorPos="11"/><name status="None" import="%s" exported="False">A Name</name></item>'
|
||||
b'</content>'
|
||||
)
|
||||
) % bytes(constData.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.itemOrder == 1
|
||||
assert theItem.isExported is False
|
||||
assert theItem.paraCount == 3
|
||||
@@ -416,6 +404,8 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
assert theItem.itemClass == nwItemClass.NOVEL
|
||||
assert theItem.itemType == nwItemType.FILE
|
||||
assert theItem.itemLayout == nwItemLayout.NOTE
|
||||
assert theItem.itemStatus == constData.statusKeys[0] # Was None, should now be default
|
||||
assert theItem.itemImport == constData.importKeys[3]
|
||||
|
||||
# Folder
|
||||
# ======
|
||||
@@ -428,7 +418,7 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
theItem.setName("A Name")
|
||||
theItem.setClass("NOVEL")
|
||||
theItem.setType("FOLDER")
|
||||
theItem.setStatus("Main")
|
||||
theItem.setStatus(constData.statusKeys[1])
|
||||
theItem.setLayout("NOTE")
|
||||
theItem.setExpanded(True)
|
||||
theItem.setExported(False)
|
||||
@@ -443,16 +433,17 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
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="New" '
|
||||
b'type="FOLDER" class="NOVEL"><meta expanded="True"/><name status="%s" '
|
||||
b'import="None">A Name</name></item>'
|
||||
b'</content>'
|
||||
)
|
||||
) % bytes(constData.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.isExported is True
|
||||
@@ -463,6 +454,8 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
assert theItem.itemClass == nwItemClass.NOVEL
|
||||
assert theItem.itemType == nwItemType.FOLDER
|
||||
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||
assert theItem.itemStatus == constData.statusKeys[1]
|
||||
assert theItem.itemImport == constData.importKeys[0] # Was None, should now be default
|
||||
|
||||
# Errors
|
||||
# ======
|
||||
|
||||
@@ -19,8 +19,9 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import pytest
|
||||
import random
|
||||
|
||||
from shutil import copyfile
|
||||
from zipfile import ZipFile
|
||||
@@ -44,6 +45,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI):
|
||||
testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||
|
||||
random.seed(42)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
@@ -112,6 +114,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI):
|
||||
"numScenes": 3,
|
||||
"chFolders": True,
|
||||
}
|
||||
random.seed(42)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
@@ -154,6 +157,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI):
|
||||
"numScenes": 6,
|
||||
"chFolders": True,
|
||||
}
|
||||
random.seed(42)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
@@ -262,6 +266,7 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
|
||||
testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
|
||||
|
||||
random.seed(42)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
@@ -299,6 +304,7 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI):
|
||||
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
|
||||
|
||||
random.seed(42)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
@@ -680,13 +686,127 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
def testCoreProject_StatusImport(mockGUI, fncDir, constData):
|
||||
"""Test the status and importance flag handling.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
random.seed(42)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.newProject({"projPath": fncDir}) is True
|
||||
|
||||
# Change Status
|
||||
# =============
|
||||
|
||||
theProject.projTree["44cb730c42048"].setStatus("Finished")
|
||||
theProject.projTree["71ee45a3c0db9"].setStatus("Draft")
|
||||
theProject.projTree["811786ad1ae74"].setStatus("Note")
|
||||
theProject.projTree["25fc0e7096fc6"].setStatus("Finished")
|
||||
|
||||
assert theProject.projTree["44cb730c42048"].itemStatus == constData.statusKeys[3]
|
||||
assert theProject.projTree["71ee45a3c0db9"].itemStatus == constData.statusKeys[2]
|
||||
assert theProject.projTree["811786ad1ae74"].itemStatus == constData.statusKeys[1]
|
||||
assert theProject.projTree["25fc0e7096fc6"].itemStatus == constData.statusKeys[3]
|
||||
|
||||
newList = [
|
||||
{"key": constData.statusKeys[0], "name": "New", "cols": (1, 1, 1)},
|
||||
{"key": constData.statusKeys[1], "name": "Draft", "cols": (2, 2, 2)}, # These are swapped
|
||||
{"key": constData.statusKeys[2], "name": "Note", "cols": (3, 3, 3)}, # These are swapped
|
||||
{"key": constData.statusKeys[3], "name": "Edited", "cols": (4, 4, 4)}, # Renamed
|
||||
{"key": None, "name": "Finished", "cols": (5, 5, 5)}, # New, reused name
|
||||
]
|
||||
assert theProject.setStatusColours(None, None) is False
|
||||
assert theProject.setStatusColours([], []) is False
|
||||
assert theProject.setStatusColours(newList, []) is True
|
||||
|
||||
assert theProject.statusItems.name(constData.statusKeys[0]) == "New"
|
||||
assert theProject.statusItems.name(constData.statusKeys[1]) == "Draft"
|
||||
assert theProject.statusItems.name(constData.statusKeys[2]) == "Note"
|
||||
assert theProject.statusItems.name(constData.statusKeys[3]) == "Edited"
|
||||
assert theProject.statusItems.cols(constData.statusKeys[0]) == (1, 1, 1)
|
||||
assert theProject.statusItems.cols(constData.statusKeys[1]) == (2, 2, 2)
|
||||
assert theProject.statusItems.cols(constData.statusKeys[2]) == (3, 3, 3)
|
||||
assert theProject.statusItems.cols(constData.statusKeys[3]) == (4, 4, 4)
|
||||
|
||||
# Check the new entry
|
||||
lastKey = theProject.statusItems.check("Finished")
|
||||
assert lastKey == "sbc8960"
|
||||
assert theProject.statusItems.name(lastKey) == "Finished"
|
||||
assert theProject.statusItems.cols(lastKey) == (5, 5, 5)
|
||||
|
||||
# Delete last entry
|
||||
assert theProject.setStatusColours([], [lastKey]) is True
|
||||
assert theProject.statusItems.name(lastKey) == "New"
|
||||
|
||||
# Change Importance
|
||||
# =================
|
||||
|
||||
fHandle = theProject.newFile("Jane Doe", nwItemClass.CHARACTER, "73475cb40a568")
|
||||
theProject.projTree[fHandle].setImport("Main")
|
||||
|
||||
assert theProject.projTree[fHandle].itemImport == constData.importKeys[3]
|
||||
newList = [
|
||||
{"key": constData.importKeys[0], "name": "New", "cols": (1, 1, 1)},
|
||||
{"key": constData.importKeys[1], "name": "Minor", "cols": (2, 2, 2)},
|
||||
{"key": constData.importKeys[2], "name": "Major", "cols": (3, 3, 3)},
|
||||
{"key": constData.importKeys[3], "name": "Min", "cols": (4, 4, 4)},
|
||||
{"key": None, "name": "Max", "cols": (5, 5, 5)},
|
||||
]
|
||||
assert theProject.setImportColours(None, None) is False
|
||||
assert theProject.setImportColours([], []) is False
|
||||
assert theProject.setImportColours(newList, []) is True
|
||||
|
||||
assert theProject.importItems.name(constData.importKeys[0]) == "New"
|
||||
assert theProject.importItems.name(constData.importKeys[1]) == "Minor"
|
||||
assert theProject.importItems.name(constData.importKeys[2]) == "Major"
|
||||
assert theProject.importItems.name(constData.importKeys[3]) == "Min"
|
||||
assert theProject.importItems.cols(constData.importKeys[0]) == (1, 1, 1)
|
||||
assert theProject.importItems.cols(constData.importKeys[1]) == (2, 2, 2)
|
||||
assert theProject.importItems.cols(constData.importKeys[2]) == (3, 3, 3)
|
||||
assert theProject.importItems.cols(constData.importKeys[3]) == (4, 4, 4)
|
||||
|
||||
# Check the new entry
|
||||
lastKey = theProject.importItems.check("Max")
|
||||
assert lastKey == "i1a3d1f"
|
||||
assert theProject.importItems.name(lastKey) == "Max"
|
||||
assert theProject.importItems.cols(lastKey) == (5, 5, 5)
|
||||
|
||||
# Delete last entry
|
||||
assert theProject.setImportColours([], [lastKey]) is True
|
||||
assert theProject.importItems.name(lastKey) == "New"
|
||||
|
||||
# Delete Status/Import
|
||||
# ====================
|
||||
|
||||
theProject.statusItems.resetCounts()
|
||||
for key in list(theProject.statusItems.keys()):
|
||||
assert theProject.statusItems.remove(key) is True
|
||||
|
||||
theProject.importItems.resetCounts()
|
||||
for key in list(theProject.importItems.keys()):
|
||||
assert theProject.importItems.remove(key) is True
|
||||
|
||||
assert len(theProject.statusItems) == 0
|
||||
assert len(theProject.importItems) == 0
|
||||
assert theProject.saveProject() is True
|
||||
assert theProject.closeProject() is True
|
||||
|
||||
# This should restore the default status/import labels
|
||||
random.seed(42)
|
||||
assert theProject.openProject(fncDir) is True
|
||||
assert theProject.saveProject() is True
|
||||
assert list(theProject.statusItems.keys()) == constData.statusKeys
|
||||
assert list(theProject.importItems.keys()) == constData.importKeys
|
||||
|
||||
# END Test testCoreProject_StatusImport
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir):
|
||||
"""Test other project class methods and functions.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
assert theProject.projPath == nwMinimal
|
||||
assert theProject.newProject({"projPath": fncDir}) is True
|
||||
|
||||
# Setting project path
|
||||
assert theProject.setProjectPath(None)
|
||||
@@ -697,16 +817,16 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
assert theProject.projPath == os.path.expanduser("~")
|
||||
|
||||
# Create a new folder and populate it
|
||||
projPath = os.path.join(nwMinimal, "mock1")
|
||||
projPath = os.path.join(fncDir, "mock1")
|
||||
assert theProject.setProjectPath(projPath, newProject=True)
|
||||
|
||||
# Make os.mkdir fail
|
||||
monkeypatch.setattr("os.mkdir", causeOSError)
|
||||
projPath = os.path.join(nwMinimal, "mock2")
|
||||
projPath = os.path.join(fncDir, "mock2")
|
||||
assert not theProject.setProjectPath(projPath, newProject=True)
|
||||
|
||||
# Set back
|
||||
assert theProject.setProjectPath(nwMinimal)
|
||||
assert theProject.setProjectPath(fncDir)
|
||||
|
||||
# Project Name
|
||||
assert theProject.setProjectName(" A Name ")
|
||||
@@ -744,9 +864,10 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
|
||||
# Trash folder
|
||||
# Should create on first call, and just returned on later calls
|
||||
assert theProject.projTree["73475cb40a568"] is None
|
||||
assert theProject.trashFolder() == "73475cb40a568"
|
||||
assert theProject.trashFolder() == "73475cb40a568"
|
||||
hTrash = "1a6562590ef19"
|
||||
assert theProject.projTree[hTrash] is None
|
||||
assert theProject.trashFolder() == hTrash
|
||||
assert theProject.trashFolder() == hTrash
|
||||
|
||||
# Project backup
|
||||
assert theProject.doBackup is True
|
||||
@@ -770,9 +891,10 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
|
||||
# Spell language
|
||||
theProject.projChanged = False
|
||||
assert theProject.setSpellLang(None)
|
||||
assert theProject.projSpell is None
|
||||
assert theProject.setSpellLang("None")
|
||||
assert theProject.setSpellLang(None) is False
|
||||
assert theProject.projSpell is None
|
||||
assert theProject.setSpellLang("None") is False # Should be interpreded as None
|
||||
assert theProject.projSpell is None
|
||||
assert theProject.setSpellLang("en_GB")
|
||||
assert theProject.projSpell == "en_GB"
|
||||
@@ -812,14 +934,14 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
|
||||
# Change project tree order
|
||||
oldOrder = [
|
||||
"a508bb932959c", "a35baf2e93843", "a6d311a93600a",
|
||||
"f5ab3e30151e1", "8c659a11cd429", "7695ce551d265",
|
||||
"afb3043c7b2b3", "9d5247ab588e0", "73475cb40a568",
|
||||
"73475cb40a568", "44cb730c42048", "71ee45a3c0db9",
|
||||
"811786ad1ae74", "25fc0e7096fc6", "31489056e0916",
|
||||
"98010bd9270f9", "0e17daca5f3e1", "1a6562590ef19",
|
||||
]
|
||||
newOrder = [
|
||||
"f5ab3e30151e1", "8c659a11cd429", "7695ce551d265",
|
||||
"a508bb932959c", "a35baf2e93843", "a6d311a93600a",
|
||||
"afb3043c7b2b3", "9d5247ab588e0",
|
||||
"811786ad1ae74", "25fc0e7096fc6", "31489056e0916",
|
||||
"73475cb40a568", "44cb730c42048", "71ee45a3c0db9",
|
||||
"98010bd9270f9", "0e17daca5f3e1",
|
||||
]
|
||||
assert theProject.projTree.handles() == oldOrder
|
||||
assert theProject.setTreeOrder(newOrder)
|
||||
@@ -827,56 +949,6 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
assert theProject.setTreeOrder(oldOrder)
|
||||
assert theProject.projTree.handles() == oldOrder
|
||||
|
||||
# Change status
|
||||
theProject.projTree["a35baf2e93843"].setStatus("Finished")
|
||||
theProject.projTree["a6d311a93600a"].setStatus("Draft")
|
||||
theProject.projTree["f5ab3e30151e1"].setStatus("Note")
|
||||
theProject.projTree["8c659a11cd429"].setStatus("Finished")
|
||||
newList = [
|
||||
("New", 1, 1, 1, "New"),
|
||||
("Draft", 2, 2, 2, "Note"), # These are swapped
|
||||
("Note", 3, 3, 3, "Draft"), # These are swapped
|
||||
("Edited", 4, 4, 4, "Finished"), # Renamed
|
||||
("Finished", 5, 5, 5, None), # New, with reused name
|
||||
]
|
||||
assert theProject.setStatusColours(newList)
|
||||
assert theProject.statusItems._theLabels == [
|
||||
"New", "Draft", "Note", "Edited", "Finished"
|
||||
]
|
||||
assert theProject.statusItems._theColours == [
|
||||
(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)
|
||||
]
|
||||
assert theProject.projTree["a35baf2e93843"].itemStatus == "Edited" # Renamed
|
||||
assert theProject.projTree["a6d311a93600a"].itemStatus == "Note" # Swapped
|
||||
assert theProject.projTree["f5ab3e30151e1"].itemStatus == "Draft" # Swapped
|
||||
assert theProject.projTree["8c659a11cd429"].itemStatus == "Edited" # Renamed
|
||||
|
||||
# Change importance
|
||||
fHandle = theProject.newFile("Jane Doe", nwItemClass.CHARACTER, "afb3043c7b2b3")
|
||||
theProject.projTree[fHandle].setImport("Main")
|
||||
newList = [
|
||||
("New", 1, 1, 1, "New"),
|
||||
("Minor", 2, 2, 2, "Minor"),
|
||||
("Major", 3, 3, 3, "Major"),
|
||||
("Min", 4, 4, 4, "Main"),
|
||||
("Max", 5, 5, 5, None),
|
||||
]
|
||||
assert theProject.setImportColours(newList)
|
||||
assert theProject.importItems._theLabels == [
|
||||
"New", "Minor", "Major", "Min", "Max"
|
||||
]
|
||||
assert theProject.importItems._theColours == [
|
||||
(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)
|
||||
]
|
||||
assert theProject.projTree[fHandle].itemImport == "Min"
|
||||
|
||||
# Check status counts
|
||||
assert theProject.statusItems._theCounts == [0, 0, 0, 0, 0]
|
||||
assert theProject.importItems._theCounts == [0, 0, 0, 0, 0]
|
||||
theProject.countStatus()
|
||||
assert theProject.statusItems._theCounts == [1, 1, 1, 2, 0]
|
||||
assert theProject.importItems._theCounts == [3, 0, 0, 1, 0]
|
||||
|
||||
# Session stats
|
||||
theProject.currWCount = 200
|
||||
theProject.lastWCount = 100
|
||||
@@ -890,7 +962,7 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
assert not theProject._appendSessionStats(idleTime=0)
|
||||
|
||||
# Write entry
|
||||
assert theProject.projMeta == os.path.join(nwMinimal, "meta")
|
||||
assert theProject.projMeta == os.path.join(fncDir, "meta")
|
||||
statsFile = os.path.join(theProject.projMeta, nwFiles.SESS_STATS)
|
||||
|
||||
theProject.projOpened = 1600002000
|
||||
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import random
|
||||
|
||||
from lxml import etree
|
||||
|
||||
@@ -29,103 +30,300 @@ from novelwriter.core.status import NWStatus
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Entries():
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
def testCoreStatus_Internal(constData):
|
||||
"""Test all the internal functions of the NWStatus class.
|
||||
"""
|
||||
theStatus = NWStatus()
|
||||
random.seed(42)
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
theImport = NWStatus(NWStatus.IMPORT)
|
||||
|
||||
# Add entries
|
||||
theStatus.addEntry("New", (100, 100, 100))
|
||||
theStatus.addEntry("Minor", (200, 50, 0))
|
||||
theStatus.addEntry("Major", (200, 150, 0))
|
||||
theStatus.addEntry("Main", (50, 200, 0))
|
||||
with pytest.raises(Exception):
|
||||
NWStatus(999)
|
||||
|
||||
assert theStatus._theLabels == ["New", "Minor", "Major", "Main"]
|
||||
assert theStatus._theColours == [(100, 100, 100), (200, 50, 0), (200, 150, 0), (50, 200, 0)]
|
||||
assert theStatus._theCounts == [0, 0, 0, 0]
|
||||
assert theStatus._theMap["New"] == 0
|
||||
assert theStatus._theMap["Minor"] == 1
|
||||
assert theStatus._theMap["Major"] == 2
|
||||
assert theStatus._theMap["Main"] == 3
|
||||
assert theStatus._theLength == 4
|
||||
# Generate Key
|
||||
# ============
|
||||
|
||||
# Lookups
|
||||
assert theStatus._getIndex(None) is None
|
||||
assert theStatus._getIndex("stuff") is None
|
||||
assert theStatus._getIndex("Main") == 3
|
||||
assert theStatus._newKey() == constData.statusKeys[0]
|
||||
assert theStatus._newKey() == constData.statusKeys[1]
|
||||
|
||||
# Checks
|
||||
assert theStatus.checkEntry(123) == "New"
|
||||
assert theStatus.checkEntry("Stuff") == "New"
|
||||
assert theStatus.checkEntry("New ") == "New"
|
||||
assert theStatus.checkEntry(" Main ") == "Main"
|
||||
# Key collision, should move to key 3
|
||||
theStatus.write(constData.statusKeys[2], "Crash", (0, 0, 0))
|
||||
assert theStatus._newKey() == constData.statusKeys[3]
|
||||
|
||||
# Icons
|
||||
assert isinstance(theStatus.getIcon("Stuff"), QIcon)
|
||||
assert isinstance(theStatus.getIcon("New"), QIcon)
|
||||
assert theImport._newKey() == constData.importKeys[0]
|
||||
assert theImport._newKey() == constData.importKeys[1]
|
||||
|
||||
# Set new list
|
||||
newList = [
|
||||
("New", 1, 1, 1, "New"),
|
||||
("Minor", 2, 2, 2, "Minor"),
|
||||
("Major", 3, 3, 3, "Major"),
|
||||
("Min", 4, 4, 4, "Main"),
|
||||
("Max", 5, 5, 5, None),
|
||||
]
|
||||
assert theStatus.setNewEntries(None) == {}
|
||||
assert theStatus.setNewEntries(newList) == {"Main": "Min"}
|
||||
# Key collision, should move to key 3
|
||||
theImport.write(constData.importKeys[2], "Crash", (0, 0, 0))
|
||||
assert theImport._newKey() == constData.importKeys[3]
|
||||
|
||||
assert theStatus._theLabels == ["New", "Minor", "Major", "Min", "Max"]
|
||||
assert theStatus._theColours == [(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)]
|
||||
assert theStatus._theCounts == [0, 0, 0, 0, 0]
|
||||
assert theStatus._theMap["New"] == 0
|
||||
assert theStatus._theMap["Minor"] == 1
|
||||
assert theStatus._theMap["Major"] == 2
|
||||
assert theStatus._theMap["Min"] == 3
|
||||
assert theStatus._theMap["Max"] == 4
|
||||
assert theStatus._theLength == 5
|
||||
# Check Key
|
||||
# =========
|
||||
|
||||
# Add counts
|
||||
countTo = [3, 5, 7, 9, 11]
|
||||
for i, n in enumerate(countTo):
|
||||
for _ in range(n):
|
||||
theStatus.countEntry(theStatus._theLabels[i])
|
||||
assert theStatus._theCounts == countTo
|
||||
assert theStatus._isKey(None) is False # Not a string
|
||||
assert theStatus._isKey("s00000") is False # Too short
|
||||
assert theStatus._isKey("s000000") is True # Correct length
|
||||
assert theStatus._isKey("s0000000") is False # Too long
|
||||
assert theStatus._isKey("i000000") is False # Wrong type
|
||||
assert theStatus._isKey("q000000") is False # Wrong type
|
||||
assert theStatus._isKey("s12345H") is False # Not a hex value
|
||||
assert theStatus._isKey("s12345F") is False # Not a lower case hex value
|
||||
assert theStatus._isKey("s12345f") is True # Valid hex value
|
||||
|
||||
assert theImport._isKey(None) is False # Not a string
|
||||
assert theImport._isKey("i00000") is False # Too short
|
||||
assert theImport._isKey("i000000") is True # Correct length
|
||||
assert theImport._isKey("i0000000") is False # Too long
|
||||
assert theImport._isKey("s000000") is False # Wrong type
|
||||
assert theImport._isKey("q000000") is False # Wrong type
|
||||
assert theImport._isKey("i12345H") is False # Not a hex value
|
||||
assert theImport._isKey("i12345F") is False # Not a lower case hex value
|
||||
assert theImport._isKey("i12345f") is True # Valid hex value
|
||||
|
||||
# END Test testCoreStatus_Internal
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Iterator(constData):
|
||||
"""Test the iterator functions 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))
|
||||
theStatus.write(None, "Draft", (200, 150, 0))
|
||||
theStatus.write(None, "Finished", (50, 200, 0))
|
||||
|
||||
# Direct access
|
||||
entry = theStatus[constData.statusKeys[0]]
|
||||
assert entry["cols"] == (100, 100, 100)
|
||||
assert entry["name"] == "New"
|
||||
assert entry["count"] == 0
|
||||
assert isinstance(entry["icon"], QIcon)
|
||||
|
||||
# Iterate
|
||||
for i, (sA, sB, sC, sD) in enumerate(theStatus):
|
||||
assert sA == theStatus._theLabels[i]
|
||||
assert sB == theStatus._theColours[i]
|
||||
assert sC == theStatus._theCounts[i]
|
||||
assert sD == theStatus._theIcons[i]
|
||||
entries = list(theStatus)
|
||||
assert len(entries) == 4
|
||||
assert len(theStatus) == 4
|
||||
|
||||
sA, sB, sC, sD = theStatus[9]
|
||||
assert sA is None
|
||||
assert sB is None
|
||||
assert sC is None
|
||||
assert isinstance(sD, QIcon)
|
||||
# Keys
|
||||
assert list(theStatus.keys()) == constData.statusKeys
|
||||
|
||||
# Items
|
||||
for index, (key, entry) in enumerate(theStatus.items()):
|
||||
assert key == constData.statusKeys[index]
|
||||
assert "cols" in entry
|
||||
assert "name" in entry
|
||||
assert "count" in entry
|
||||
assert "icon" in entry
|
||||
|
||||
# Valuse
|
||||
for entry in theStatus.values():
|
||||
assert "cols" in entry
|
||||
assert "name" in entry
|
||||
assert "count" in entry
|
||||
assert "icon" in entry
|
||||
|
||||
# END Test testCoreStatus_Iterator
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Entries(constData):
|
||||
"""Test all the simple setters for the NWStatus class.
|
||||
"""
|
||||
random.seed(42)
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
|
||||
# Write
|
||||
# =====
|
||||
|
||||
# Have a key
|
||||
theStatus.write(constData.statusKeys[0], "Entry 1", (200, 100, 50))
|
||||
assert theStatus[constData.statusKeys[0]]["name"] == "Entry 1"
|
||||
assert theStatus[constData.statusKeys[0]]["cols"] == (200, 100, 50)
|
||||
|
||||
# Don't have a key
|
||||
theStatus.write(None, "Entry 2", (210, 110, 60))
|
||||
assert theStatus[constData.statusKeys[1]]["name"] == "Entry 2"
|
||||
assert theStatus[constData.statusKeys[1]]["cols"] == (210, 110, 60)
|
||||
|
||||
# Wrong colour spec
|
||||
theStatus.write(None, "Entry 3", "what?")
|
||||
assert theStatus[constData.statusKeys[2]]["name"] == "Entry 3"
|
||||
assert theStatus[constData.statusKeys[2]]["cols"] == (100, 100, 100)
|
||||
|
||||
# Wrong colour count
|
||||
theStatus.write(None, "Entry 4", (10, 20))
|
||||
assert theStatus[constData.statusKeys[3]]["name"] == "Entry 4"
|
||||
assert theStatus[constData.statusKeys[3]]["cols"] == (100, 100, 100)
|
||||
|
||||
# Check reverse map
|
||||
assert theStatus._reverse == {
|
||||
"Entry 1": constData.statusKeys[0],
|
||||
"Entry 2": constData.statusKeys[1],
|
||||
"Entry 3": constData.statusKeys[2],
|
||||
"Entry 4": constData.statusKeys[3],
|
||||
}
|
||||
|
||||
# Check
|
||||
# =====
|
||||
|
||||
# Normal lookup
|
||||
for key in constData.statusKeys:
|
||||
assert theStatus.check(key) == key
|
||||
|
||||
# Reverse map lookup
|
||||
assert theStatus.check("Entry 1") == constData.statusKeys[0]
|
||||
assert theStatus.check("Entry 2") == constData.statusKeys[1]
|
||||
assert theStatus.check("Entry 3") == constData.statusKeys[2]
|
||||
assert theStatus.check("Entry 4") == constData.statusKeys[3]
|
||||
|
||||
# Non-existing name
|
||||
assert theStatus.check("Entry 5") == constData.statusKeys[0]
|
||||
|
||||
# Name Access
|
||||
# ===========
|
||||
|
||||
assert theStatus.name(constData.statusKeys[0]) == "Entry 1"
|
||||
assert theStatus.name(constData.statusKeys[1]) == "Entry 2"
|
||||
assert theStatus.name(constData.statusKeys[2]) == "Entry 3"
|
||||
assert theStatus.name(constData.statusKeys[3]) == "Entry 4"
|
||||
assert theStatus.name("blablabla") == "Entry 1"
|
||||
|
||||
# Colour Access
|
||||
# =============
|
||||
|
||||
assert theStatus.cols(constData.statusKeys[0]) == (200, 100, 50)
|
||||
assert theStatus.cols(constData.statusKeys[1]) == (210, 110, 60)
|
||||
assert theStatus.cols(constData.statusKeys[2]) == (100, 100, 100)
|
||||
assert theStatus.cols(constData.statusKeys[3]) == (100, 100, 100)
|
||||
assert theStatus.cols("blablabla") == (200, 100, 50)
|
||||
|
||||
# Icon Access
|
||||
# ===========
|
||||
|
||||
assert isinstance(theStatus.icon(constData.statusKeys[0]), QIcon)
|
||||
assert isinstance(theStatus.icon(constData.statusKeys[1]), QIcon)
|
||||
assert isinstance(theStatus.icon(constData.statusKeys[2]), QIcon)
|
||||
assert isinstance(theStatus.icon(constData.statusKeys[3]), QIcon)
|
||||
assert isinstance(theStatus.icon("blablabla"), QIcon)
|
||||
|
||||
# Increment and Count Access
|
||||
# ==========================
|
||||
|
||||
countTo = [3, 5, 7, 9]
|
||||
for i, n in enumerate(countTo):
|
||||
for _ in range(n):
|
||||
theStatus.increment(constData.statusKeys[i])
|
||||
|
||||
assert theStatus.count(constData.statusKeys[0]) == countTo[0]
|
||||
assert theStatus.count(constData.statusKeys[1]) == countTo[1]
|
||||
assert theStatus.count(constData.statusKeys[2]) == countTo[2]
|
||||
assert theStatus.count(constData.statusKeys[3]) == countTo[3]
|
||||
assert theStatus.count("blablabla") == countTo[0]
|
||||
|
||||
# Clear counts
|
||||
theStatus.resetCounts()
|
||||
assert theStatus._theCounts == [0, 0, 0, 0, 0]
|
||||
|
||||
assert theStatus.count(constData.statusKeys[0]) == 0
|
||||
assert theStatus.count(constData.statusKeys[1]) == 0
|
||||
assert theStatus.count(constData.statusKeys[2]) == 0
|
||||
assert theStatus.count(constData.statusKeys[3]) == 0
|
||||
|
||||
# Reorder
|
||||
# =======
|
||||
|
||||
cOrder = list(theStatus.keys())
|
||||
assert cOrder == constData.statusKeys
|
||||
|
||||
# Wrong length
|
||||
assert theStatus.reorder([]) is False
|
||||
|
||||
# No change
|
||||
assert theStatus.reorder(cOrder) is False
|
||||
|
||||
# Actual reaorder
|
||||
nOrder = [
|
||||
constData.statusKeys[0],
|
||||
constData.statusKeys[2],
|
||||
constData.statusKeys[1],
|
||||
constData.statusKeys[3],
|
||||
]
|
||||
assert theStatus.reorder(nOrder) is True
|
||||
assert list(theStatus.keys()) == nOrder
|
||||
|
||||
# Add an unknown key
|
||||
wOrder = nOrder.copy()
|
||||
wOrder[3] = theStatus._newKey()
|
||||
assert theStatus.reorder(wOrder) is False
|
||||
assert list(theStatus.keys()) == nOrder
|
||||
|
||||
# Put it back
|
||||
assert theStatus.reorder(cOrder) is True
|
||||
assert list(theStatus.keys()) == cOrder
|
||||
|
||||
# Default
|
||||
# =======
|
||||
|
||||
default = theStatus._default
|
||||
theStatus._default = None
|
||||
|
||||
assert theStatus.check("Entry 5") == ""
|
||||
assert theStatus.name("blablabla") == ""
|
||||
assert theStatus.cols("blablabla") == (100, 100, 100)
|
||||
assert theStatus.count("blablabla") == 0
|
||||
assert isinstance(theStatus.icon("blablabla"), QIcon)
|
||||
|
||||
theStatus._default = default
|
||||
|
||||
# Remove
|
||||
# ======
|
||||
|
||||
# Non-existing entry
|
||||
assert theStatus.remove("blablabla") is False
|
||||
|
||||
# Non-zero entry
|
||||
theStatus.increment(constData.statusKeys[3])
|
||||
assert theStatus.remove(constData.statusKeys[3]) is False
|
||||
|
||||
# Delete last entry
|
||||
theStatus.resetCounts()
|
||||
lastName = theStatus.name(constData.statusKeys[3])
|
||||
assert lastName == "Entry 4"
|
||||
assert theStatus.remove(constData.statusKeys[3]) is True
|
||||
assert theStatus.check(constData.statusKeys[3]) == theStatus._default
|
||||
assert theStatus.check(lastName) == theStatus._default
|
||||
|
||||
# Delete default entry, Entry 2 is new default
|
||||
firstName = theStatus.name(theStatus._default)
|
||||
assert firstName == "Entry 1"
|
||||
assert theStatus.remove(theStatus._default) is True
|
||||
assert theStatus.name(firstName) == "Entry 2"
|
||||
|
||||
# Remove remaining entries
|
||||
assert theStatus.remove(constData.statusKeys[1]) is True
|
||||
assert theStatus.remove(constData.statusKeys[2]) is True
|
||||
|
||||
assert len(theStatus) == 0
|
||||
assert theStatus._default is None
|
||||
|
||||
# END Test testCoreStatus_Entries
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_XMLPackUnpack():
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
def testCoreStatus_XMLPackUnpack(constData):
|
||||
"""Test all the XML pack/unpack of the NWStatus class.
|
||||
"""
|
||||
theStatus = NWStatus()
|
||||
theStatus.addEntry("New", (100, 100, 100))
|
||||
theStatus.addEntry("Minor", (200, 50, 0))
|
||||
theStatus.addEntry("Major", (200, 150, 0))
|
||||
theStatus.addEntry("Main", (50, 200, 0))
|
||||
random.seed(42)
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
theStatus.write(None, "New", (100, 100, 100))
|
||||
theStatus.write(None, "Note", (200, 50, 0))
|
||||
theStatus.write(None, "Draft", (200, 150, 0))
|
||||
theStatus.write(None, "Finished", (50, 200, 0))
|
||||
|
||||
countTo = [3, 5, 7, 9]
|
||||
for i, n in enumerate(countTo):
|
||||
for _ in range(n):
|
||||
theStatus.countEntry(theStatus._theLabels[i])
|
||||
theStatus.increment(constData.statusKeys[i])
|
||||
|
||||
nwXML = etree.Element("novelWriterXML")
|
||||
|
||||
@@ -134,23 +332,29 @@ def testCoreStatus_XMLPackUnpack():
|
||||
theStatus.packXML(xStatus)
|
||||
assert etree.tostring(xStatus, pretty_print=False, encoding="utf-8") == (
|
||||
b'<status>'
|
||||
b'<entry red="100" green="100" blue="100">New</entry>'
|
||||
b'<entry red="200" green="50" blue="0">Minor</entry>'
|
||||
b'<entry red="200" green="150" blue="0">Major</entry>'
|
||||
b'<entry red="50" green="200" blue="0">Main</entry>'
|
||||
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>'
|
||||
)
|
||||
|
||||
# Unpack
|
||||
theStatus = NWStatus()
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
assert theStatus.unpackXML(xStatus)
|
||||
assert theStatus._theLabels == ["New", "Minor", "Major", "Main"]
|
||||
assert theStatus._theColours == [(100, 100, 100), (200, 50, 0), (200, 150, 0), (50, 200, 0)]
|
||||
assert theStatus._theCounts == [0, 0, 0, 0]
|
||||
assert theStatus._theMap["New"] == 0
|
||||
assert theStatus._theMap["Minor"] == 1
|
||||
assert theStatus._theMap["Major"] == 2
|
||||
assert theStatus._theMap["Main"] == 3
|
||||
assert theStatus._theLength == 4
|
||||
assert len(theStatus._store) == 4
|
||||
assert list(theStatus._store.keys()) == constData.statusKeys
|
||||
assert theStatus._store[constData.statusKeys[0]]["name"] == "New"
|
||||
assert theStatus._store[constData.statusKeys[1]]["name"] == "Note"
|
||||
assert theStatus._store[constData.statusKeys[2]]["name"] == "Draft"
|
||||
assert theStatus._store[constData.statusKeys[3]]["name"] == "Finished"
|
||||
assert theStatus._store[constData.statusKeys[0]]["cols"] == (100, 100, 100)
|
||||
assert theStatus._store[constData.statusKeys[1]]["cols"] == (200, 50, 0)
|
||||
assert theStatus._store[constData.statusKeys[2]]["cols"] == (200, 150, 0)
|
||||
assert theStatus._store[constData.statusKeys[3]]["cols"] == (50, 200, 0)
|
||||
assert theStatus._store[constData.statusKeys[0]]["count"] == countTo[0]
|
||||
assert theStatus._store[constData.statusKeys[1]]["count"] == countTo[1]
|
||||
assert theStatus._store[constData.statusKeys[2]]["count"] == countTo[2]
|
||||
assert theStatus._store[constData.statusKeys[3]]["count"] == countTo[3]
|
||||
|
||||
# END Test testCoreStatus_XMLPackUnpack
|
||||
|
||||
Reference in New Issue
Block a user