Add full test coverage of index class
This commit is contained in:
@@ -34,7 +34,7 @@ def trConst(tString):
|
|||||||
return QCoreApplication.translate("Constant", tString)
|
return QCoreApplication.translate("Constant", tString)
|
||||||
|
|
||||||
|
|
||||||
class nwConst():
|
class nwConst:
|
||||||
|
|
||||||
# Date and Time Formats
|
# Date and Time Formats
|
||||||
FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format
|
FMT_TSTAMP = "%Y-%m-%d %H:%M:%S" # Default format
|
||||||
@@ -48,7 +48,7 @@ class nwConst():
|
|||||||
# END Class nwConst
|
# END Class nwConst
|
||||||
|
|
||||||
|
|
||||||
class nwRegEx():
|
class nwRegEx:
|
||||||
|
|
||||||
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||||
FMT_EB = r"(?<![\w\\])([\*]{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
FMT_EB = r"(?<![\w\\])([\*]{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||||
@@ -57,7 +57,7 @@ class nwRegEx():
|
|||||||
# END Class nwRegEx
|
# END Class nwRegEx
|
||||||
|
|
||||||
|
|
||||||
class nwFiles():
|
class nwFiles:
|
||||||
|
|
||||||
PROJ_FILE = "nwProject.nwx"
|
PROJ_FILE = "nwProject.nwx"
|
||||||
PROJ_DICT = "wordlist.txt"
|
PROJ_DICT = "wordlist.txt"
|
||||||
@@ -107,7 +107,7 @@ class nwKeyWords:
|
|||||||
# END Class nwKeyWords
|
# END Class nwKeyWords
|
||||||
|
|
||||||
|
|
||||||
class nwLabels():
|
class nwLabels:
|
||||||
|
|
||||||
CLASS_NAME = {
|
CLASS_NAME = {
|
||||||
nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"),
|
nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"),
|
||||||
@@ -185,7 +185,7 @@ class nwLabels():
|
|||||||
# END Class nwLabels
|
# END Class nwLabels
|
||||||
|
|
||||||
|
|
||||||
class nwQuotes():
|
class nwQuotes:
|
||||||
"""Allowed quotation marks.
|
"""Allowed quotation marks.
|
||||||
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
||||||
"""
|
"""
|
||||||
|
|||||||
+29
-15
@@ -67,12 +67,10 @@ class NWIndex:
|
|||||||
|
|
||||||
self.theProject = theProject
|
self.theProject = theProject
|
||||||
|
|
||||||
# Internal
|
# Storage and State
|
||||||
self._indexBroken = False
|
|
||||||
|
|
||||||
# Indices
|
|
||||||
self._tagsIndex = TagsIndex()
|
self._tagsIndex = TagsIndex()
|
||||||
self._itemIndex = ItemIndex(theProject)
|
self._itemIndex = ItemIndex(theProject)
|
||||||
|
self._indexBroken = False
|
||||||
|
|
||||||
# TimeStamps
|
# TimeStamps
|
||||||
self._timeNovel = 0
|
self._timeNovel = 0
|
||||||
@@ -81,6 +79,9 @@ class NWIndex:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<NWIndex project='{self.theProject.projName}'>"
|
||||||
|
|
||||||
##
|
##
|
||||||
# Properties
|
# Properties
|
||||||
##
|
##
|
||||||
@@ -124,8 +125,7 @@ class NWIndex:
|
|||||||
|
|
||||||
logger.debug("Re-indexing item '%s'", tHandle)
|
logger.debug("Re-indexing item '%s'", tHandle)
|
||||||
theDoc = NWDoc(self.theProject, tHandle)
|
theDoc = NWDoc(self.theProject, tHandle)
|
||||||
theText = theDoc.readDocument()
|
self.scanText(tHandle, theDoc.readDocument() or "")
|
||||||
self.scanText(tHandle, theText if theText is not None else "")
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ class NWIndex:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Indexers
|
# Internal Indexer Helpers
|
||||||
##
|
##
|
||||||
|
|
||||||
def _indexTitle(self, tHandle, aLine, nTitle):
|
def _indexTitle(self, tHandle, aLine, nTitle):
|
||||||
@@ -613,11 +613,13 @@ class NWIndex:
|
|||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
# Indexer Objects
|
# The Tags Index Object
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
class TagsIndex:
|
class TagsIndex:
|
||||||
"""A wrapper class that holds the reverse lookup tags index.
|
"""A wrapper class that holds the reverse lookup tags index. This is
|
||||||
|
just a simple wrapper around a single dictionary to keep tighter
|
||||||
|
control of the keys.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -719,8 +721,16 @@ class TagsIndex:
|
|||||||
# END Class TagsIndex
|
# END Class TagsIndex
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================================== #
|
||||||
|
# The Item Index Objects
|
||||||
|
# =============================================================================================== #
|
||||||
|
|
||||||
class ItemIndex:
|
class ItemIndex:
|
||||||
"""A wrapper object holding the indexed items.
|
"""A wrapper object holding the indexed items. This is a warapper
|
||||||
|
class around a single storage dictionary with a set of utility
|
||||||
|
functions for setting and accessing the index data. Each indexed
|
||||||
|
item is stored in an IndexItem object, which again holds an
|
||||||
|
IndexHeading object for each header of the text.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
@@ -896,7 +906,8 @@ class IndexItem:
|
|||||||
"""This object represents the index data of a project item (NWItem).
|
"""This object represents the index data of a project item (NWItem).
|
||||||
It holds a record of all the headings in the text, and the meta data
|
It holds a record of all the headings in the text, and the meta data
|
||||||
associated with each heading. It also holds a pointer to the project
|
associated with each heading. It also holds a pointer to the project
|
||||||
item.
|
item. The main heading level of the item is also held here since it
|
||||||
|
must be reset each time the item is re-indexed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, tHandle, tItem):
|
def __init__(self, tHandle, tItem):
|
||||||
@@ -912,7 +923,7 @@ class IndexItem:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<IndexItem handle={self._handle}>"
|
return f"<IndexItem handle='{self._handle}'>"
|
||||||
|
|
||||||
##
|
##
|
||||||
# Properties
|
# Properties
|
||||||
@@ -1062,7 +1073,7 @@ class IndexHeading:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<IndexHeading key={self._key}>"
|
return f"<IndexHeading key='{self._key}'>"
|
||||||
|
|
||||||
##
|
##
|
||||||
# Properties
|
# Properties
|
||||||
@@ -1165,8 +1176,11 @@ class IndexHeading:
|
|||||||
|
|
||||||
def packReferences(self):
|
def packReferences(self):
|
||||||
"""Pack references into a dictionary for saving to cache.
|
"""Pack references into a dictionary for saving to cache.
|
||||||
|
Multiple types are packed into a sorted, comma separated string.
|
||||||
|
It is sorted to prevent creating unnecessary diffs as the order
|
||||||
|
of a set is not guaranteed.
|
||||||
"""
|
"""
|
||||||
return {key: ",".join(value) for key, value in self._refs.items()}
|
return {key: ",".join(sorted(list(value))) for key, value in self._refs.items()}
|
||||||
|
|
||||||
def unpackData(self, data):
|
def unpackData(self, data):
|
||||||
"""Unpack a heading entry from a dictionary.
|
"""Unpack a heading entry from a dictionary.
|
||||||
@@ -1189,7 +1203,7 @@ class IndexHeading:
|
|||||||
if not isinstance(tagKey, str):
|
if not isinstance(tagKey, str):
|
||||||
raise ValueError("itemIndex reference key must be a string")
|
raise ValueError("itemIndex reference key must be a string")
|
||||||
if not isinstance(refTypes, str):
|
if not isinstance(refTypes, str):
|
||||||
raise ValueError("itemIndex reference types must be a string")
|
raise ValueError("itemIndex reference type must be a string")
|
||||||
for refType in refTypes.split(","):
|
for refType in refTypes.split(","):
|
||||||
if refType in nwKeyWords.VALID_KEYS:
|
if refType in nwKeyWords.VALID_KEYS:
|
||||||
self.addReference(tagKey, refType)
|
self.addReference(tagKey, refType)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ from mock import causeException
|
|||||||
from tools import buildTestProject, cmpFiles, writeFile
|
from tools import buildTestProject, cmpFiles, writeFile
|
||||||
|
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.index import NWIndex, countWords
|
from novelwriter.core.index import NWIndex, countWords, TagsIndex
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||||
|
|
||||||
|
|
||||||
@@ -46,6 +46,8 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
|||||||
assert theProject.openProject(nwLipsum)
|
assert theProject.openProject(nwLipsum)
|
||||||
|
|
||||||
theIndex = NWIndex(theProject)
|
theIndex = NWIndex(theProject)
|
||||||
|
assert repr(theIndex) == "<NWIndex project='Lorem Ipsum'>"
|
||||||
|
|
||||||
notIndexable = {
|
notIndexable = {
|
||||||
"b3643d0f92e32": False, # Novel ROOT
|
"b3643d0f92e32": False, # Novel ROOT
|
||||||
"45e6b01ca35c1": False, # Chapter One FOLDER
|
"45e6b01ca35c1": False, # Chapter One FOLDER
|
||||||
@@ -761,6 +763,166 @@ def testCoreIndex_ExtractData(mockGUI, fncDir, mockRnd):
|
|||||||
# END Test testCoreIndex_ExtractData
|
# END Test testCoreIndex_ExtractData
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreIndex_TagsIndex():
|
||||||
|
"""Check the TagsIndex class.
|
||||||
|
"""
|
||||||
|
tagsIndex = TagsIndex()
|
||||||
|
assert tagsIndex._tags == {}
|
||||||
|
|
||||||
|
# Expected data
|
||||||
|
content = {
|
||||||
|
"Tag1": {
|
||||||
|
"handle": "0000000000001",
|
||||||
|
"heading": "T000001",
|
||||||
|
"class": nwItemClass.NOVEL.name,
|
||||||
|
},
|
||||||
|
"Tag2": {
|
||||||
|
"handle": "0000000000002",
|
||||||
|
"heading": "T000002",
|
||||||
|
"class": nwItemClass.CHARACTER.name,
|
||||||
|
},
|
||||||
|
"Tag3": {
|
||||||
|
"handle": "0000000000003",
|
||||||
|
"heading": "T000003",
|
||||||
|
"class": nwItemClass.PLOT.name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add data
|
||||||
|
tagsIndex.add("Tag1", "0000000000001", "T000001", nwItemClass.NOVEL)
|
||||||
|
tagsIndex.add("Tag2", "0000000000002", "T000002", nwItemClass.CHARACTER)
|
||||||
|
tagsIndex.add("Tag3", "0000000000003", "T000003", nwItemClass.PLOT)
|
||||||
|
assert tagsIndex._tags == content
|
||||||
|
|
||||||
|
# Get items
|
||||||
|
assert tagsIndex["Tag1"] == content["Tag1"]
|
||||||
|
assert tagsIndex["Tag2"] == content["Tag2"]
|
||||||
|
assert tagsIndex["Tag3"] == content["Tag3"]
|
||||||
|
assert tagsIndex["Tag4"] is None
|
||||||
|
|
||||||
|
# Contains
|
||||||
|
assert "Tag1" in tagsIndex
|
||||||
|
assert "Tag2" in tagsIndex
|
||||||
|
assert "Tag3" in tagsIndex
|
||||||
|
assert "Tag4" not in tagsIndex
|
||||||
|
|
||||||
|
# Read back handles
|
||||||
|
assert tagsIndex.tagHandle("Tag1") == "0000000000001"
|
||||||
|
assert tagsIndex.tagHandle("Tag2") == "0000000000002"
|
||||||
|
assert tagsIndex.tagHandle("Tag3") == "0000000000003"
|
||||||
|
assert tagsIndex.tagHandle("Tag4") is None
|
||||||
|
|
||||||
|
# Read back headings
|
||||||
|
assert tagsIndex.tagHeading("Tag1") == "T000001"
|
||||||
|
assert tagsIndex.tagHeading("Tag2") == "T000002"
|
||||||
|
assert tagsIndex.tagHeading("Tag3") == "T000003"
|
||||||
|
assert tagsIndex.tagHeading("Tag4") == "T000000"
|
||||||
|
|
||||||
|
# Read back classes
|
||||||
|
assert tagsIndex.tagClass("Tag1") == nwItemClass.NOVEL.name
|
||||||
|
assert tagsIndex.tagClass("Tag2") == nwItemClass.CHARACTER.name
|
||||||
|
assert tagsIndex.tagClass("Tag3") == nwItemClass.PLOT.name
|
||||||
|
assert tagsIndex.tagClass("Tag4") is None
|
||||||
|
|
||||||
|
# Pack Data
|
||||||
|
assert tagsIndex.packData() == content
|
||||||
|
|
||||||
|
# Delete the second key and a nomn-existant key
|
||||||
|
del tagsIndex["Tag2"]
|
||||||
|
del tagsIndex["Tag4"]
|
||||||
|
assert "Tag1" in tagsIndex
|
||||||
|
assert "Tag2" not in tagsIndex
|
||||||
|
assert "Tag3" in tagsIndex
|
||||||
|
assert "Tag4" not in tagsIndex
|
||||||
|
|
||||||
|
# Clear and reload
|
||||||
|
tagsIndex.clear()
|
||||||
|
assert tagsIndex._tags == {}
|
||||||
|
assert tagsIndex.packData() == {}
|
||||||
|
|
||||||
|
tagsIndex.unpackData(content)
|
||||||
|
assert tagsIndex._tags == content
|
||||||
|
assert tagsIndex.packData() == content
|
||||||
|
|
||||||
|
# Unpack Errors
|
||||||
|
# =============
|
||||||
|
tagsIndex.clear()
|
||||||
|
|
||||||
|
# Invalid data type
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
tagsIndex.unpackData([])
|
||||||
|
|
||||||
|
# Invalid key
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
tagsIndex.unpackData({
|
||||||
|
1234: {
|
||||||
|
"handle": "0000000000001",
|
||||||
|
"heading": "T000001",
|
||||||
|
"class": "NOVEL",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Missing handle
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
tagsIndex.unpackData({
|
||||||
|
"Tag1": {
|
||||||
|
"heading": "T000001",
|
||||||
|
"class": "NOVEL",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Missing heading
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
tagsIndex.unpackData({
|
||||||
|
"Tag1": {
|
||||||
|
"handle": "0000000000001",
|
||||||
|
"class": "NOVEL",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Missing class
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
tagsIndex.unpackData({
|
||||||
|
"Tag1": {
|
||||||
|
"handle": "0000000000001",
|
||||||
|
"heading": "T000001",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Invalid handle
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
tagsIndex.unpackData({
|
||||||
|
"Tag1": {
|
||||||
|
"handle": "blablabla",
|
||||||
|
"heading": "T000001",
|
||||||
|
"class": "NOVEL",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Invalid heading
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
tagsIndex.unpackData({
|
||||||
|
"Tag1": {
|
||||||
|
"handle": "0000000000001",
|
||||||
|
"heading": "blabla",
|
||||||
|
"class": "NOVEL",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Invalid class
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
tagsIndex.unpackData({
|
||||||
|
"Tag1": {
|
||||||
|
"handle": "0000000000001",
|
||||||
|
"heading": "T000001",
|
||||||
|
"class": "blabla",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# END Test testCoreIndex_TagsIndex
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_ItemIndex(mockGUI, fncDir, mockRnd):
|
def testCoreIndex_ItemIndex(mockGUI, fncDir, mockRnd):
|
||||||
"""Check the ItemIndex class.
|
"""Check the ItemIndex class.
|
||||||
@@ -780,26 +942,6 @@ def testCoreIndex_ItemIndex(mockGUI, fncDir, mockRnd):
|
|||||||
assert cHandle not in itemIndex
|
assert cHandle not in itemIndex
|
||||||
assert sHandle not in itemIndex
|
assert sHandle not in itemIndex
|
||||||
|
|
||||||
# Unpack Data
|
|
||||||
# ===========
|
|
||||||
|
|
||||||
# Data must be dictionary
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
itemIndex.unpackData("stuff")
|
|
||||||
|
|
||||||
# Keys must be valid handles
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
itemIndex.unpackData({"stuff": "more stuff"})
|
|
||||||
|
|
||||||
# Unknown keys should be skipped
|
|
||||||
itemIndex.unpackData({"0000000000000": {}})
|
|
||||||
assert itemIndex._items == {}
|
|
||||||
|
|
||||||
# Known keys can be added, even witout data
|
|
||||||
itemIndex.unpackData({nHandle: {}})
|
|
||||||
assert nHandle in itemIndex
|
|
||||||
itemIndex.clear()
|
|
||||||
|
|
||||||
# Add Items
|
# Add Items
|
||||||
# =========
|
# =========
|
||||||
assert cHandle not in itemIndex
|
assert cHandle not in itemIndex
|
||||||
@@ -820,7 +962,7 @@ def testCoreIndex_ItemIndex(mockGUI, fncDir, mockRnd):
|
|||||||
# Set the remainig data values
|
# Set the remainig data values
|
||||||
itemIndex.setHeadingCounts(cHandle, "T000001", 60, 10, 2)
|
itemIndex.setHeadingCounts(cHandle, "T000001", 60, 10, 2)
|
||||||
itemIndex.setHeadingSynopsis(cHandle, "T000001", "In the beginning ...")
|
itemIndex.setHeadingSynopsis(cHandle, "T000001", "In the beginning ...")
|
||||||
itemIndex.setHeadingTag(cHandle, "T000001", "One") # Although it isn't allowed to have a tag
|
itemIndex.setHeadingTag(cHandle, "T000001", "One")
|
||||||
itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane"], "@pov")
|
itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane"], "@pov")
|
||||||
itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane"], "@focus")
|
itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane"], "@focus")
|
||||||
itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane", "John"], "@char")
|
itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane", "John"], "@char")
|
||||||
@@ -842,6 +984,37 @@ def testCoreIndex_ItemIndex(mockGUI, fncDir, mockRnd):
|
|||||||
itemIndex.addItemHeading(nHandle, "T000001", "H1", "Novel")
|
itemIndex.addItemHeading(nHandle, "T000001", "H1", "Novel")
|
||||||
itemIndex.addItemHeading(sHandle, "T000001", "H3", "Scene One")
|
itemIndex.addItemHeading(sHandle, "T000001", "H3", "Scene One")
|
||||||
|
|
||||||
|
# Check Item and Heading Direct Access
|
||||||
|
# ====================================
|
||||||
|
|
||||||
|
# Check repr strings
|
||||||
|
assert repr(itemIndex[nHandle]) == f"<IndexItem handle='{nHandle}'>"
|
||||||
|
assert repr(itemIndex[nHandle]["T000001"]) == "<IndexHeading key='T000001'>"
|
||||||
|
|
||||||
|
# Check content of a single item
|
||||||
|
assert "T000001" in itemIndex[nHandle]
|
||||||
|
assert itemIndex[cHandle].allTags() == ["One"]
|
||||||
|
|
||||||
|
# Check the content of a single heading
|
||||||
|
assert itemIndex[cHandle]["T000001"].key == "T000001"
|
||||||
|
assert itemIndex[cHandle]["T000001"].level == "H2"
|
||||||
|
assert itemIndex[cHandle]["T000001"].title == "Chapter One"
|
||||||
|
assert itemIndex[cHandle]["T000001"].tag == "One"
|
||||||
|
assert itemIndex[cHandle]["T000001"].charCount == 60
|
||||||
|
assert itemIndex[cHandle]["T000001"].wordCount == 10
|
||||||
|
assert itemIndex[cHandle]["T000001"].paraCount == 2
|
||||||
|
assert itemIndex[cHandle]["T000001"].synopsis == "In the beginning ..."
|
||||||
|
assert "Jane" in itemIndex[cHandle]["T000001"].references
|
||||||
|
assert "John" in itemIndex[cHandle]["T000001"].references
|
||||||
|
|
||||||
|
# Check heading level setter
|
||||||
|
itemIndex[cHandle]["T000001"].setLevel("H3") # Change it
|
||||||
|
assert itemIndex[cHandle]["T000001"].level == "H3"
|
||||||
|
itemIndex[cHandle]["T000001"].setLevel("H2") # Set it back
|
||||||
|
assert itemIndex[cHandle]["T000001"].level == "H2"
|
||||||
|
itemIndex[cHandle]["T000001"].setLevel("H5") # Invalid level
|
||||||
|
assert itemIndex[cHandle]["T000001"].level == "H2"
|
||||||
|
|
||||||
# Data Extraction
|
# Data Extraction
|
||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
@@ -908,6 +1081,82 @@ def testCoreIndex_ItemIndex(mockGUI, fncDir, mockRnd):
|
|||||||
del itemIndex[uHandle]
|
del itemIndex[uHandle]
|
||||||
assert uHandle not in itemIndex
|
assert uHandle not in itemIndex
|
||||||
|
|
||||||
|
# Unpack Error Handling
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
# Pack/unpack should restore state
|
||||||
|
content = itemIndex.packData()
|
||||||
|
itemIndex.clear()
|
||||||
|
itemIndex.unpackData(content)
|
||||||
|
assert itemIndex.packData() == content
|
||||||
|
itemIndex.clear()
|
||||||
|
|
||||||
|
# Data must be dictionary
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
itemIndex.unpackData("stuff")
|
||||||
|
|
||||||
|
# Keys must be valid handles
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
itemIndex.unpackData({"stuff": "more stuff"})
|
||||||
|
|
||||||
|
# Unknown keys should be skipped
|
||||||
|
itemIndex.unpackData({"0000000000000": {}})
|
||||||
|
assert itemIndex._items == {}
|
||||||
|
|
||||||
|
# Known keys can be added, even witout data
|
||||||
|
itemIndex.unpackData({nHandle: {}})
|
||||||
|
assert nHandle in itemIndex
|
||||||
|
|
||||||
|
# Title tags must be valid
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
itemIndex.unpackData({cHandle: {"headings": {"TTTTTTT": {}}}})
|
||||||
|
|
||||||
|
# Reference without a heading should be rejected
|
||||||
|
itemIndex.unpackData({
|
||||||
|
cHandle: {
|
||||||
|
"headings": {"T000001": {}},
|
||||||
|
"references": {"T000001": {}, "T000002": {}},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
assert "T000001" in itemIndex[cHandle]
|
||||||
|
assert "T000002" not in itemIndex[cHandle]
|
||||||
|
itemIndex.clear()
|
||||||
|
|
||||||
|
# Tag keys must be strings
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
itemIndex.unpackData({
|
||||||
|
cHandle: {
|
||||||
|
"headings": {"T000001": {}},
|
||||||
|
"references": {"T000001": {1234: "@pov"}},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Type must be strings
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
itemIndex.unpackData({
|
||||||
|
cHandle: {
|
||||||
|
"headings": {"T000001": {}},
|
||||||
|
"references": {"T000001": {"John": []}},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# Types must be valid
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
itemIndex.unpackData({
|
||||||
|
cHandle: {
|
||||||
|
"headings": {"T000001": {}},
|
||||||
|
"references": {"T000001": {"John": "@pov,@char,@stuff"}},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# This should pass
|
||||||
|
itemIndex.unpackData({
|
||||||
|
cHandle: {
|
||||||
|
"headings": {"T000001": {}},
|
||||||
|
"references": {"T000001": {"John": "@pov,@char"}},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
# END Test testCoreIndex_ItemIndex
|
# END Test testCoreIndex_ItemIndex
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user