Merge branch 'dev' into item_layouts
This commit is contained in:
+3
-3
@@ -32,7 +32,7 @@ import logging
|
|||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
from PyQt5.QtWidgets import QApplication, QErrorMessage
|
from PyQt5.QtWidgets import QApplication, QErrorMessage
|
||||||
|
|
||||||
from nw.error import exceptionHandler
|
from nw.error import exceptionHandler, logException
|
||||||
from nw.config import Config
|
from nw.config import Config
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -268,9 +268,9 @@ def main(sysArgs=None):
|
|||||||
bundle = NSBundle.mainBundle()
|
bundle = NSBundle.mainBundle()
|
||||||
info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
|
info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
|
||||||
info["CFBundleName"] = "novelWriter"
|
info["CFBundleName"] = "novelWriter"
|
||||||
except ImportError as e:
|
except ImportError:
|
||||||
logger.error("Failed to set application name")
|
logger.error("Failed to set application name")
|
||||||
logger.error(str(e))
|
logException()
|
||||||
|
|
||||||
# Import GUI (after dependency checks), and launch
|
# Import GUI (after dependency checks), and launch
|
||||||
from nw.guimain import GuiMain
|
from nw.guimain import GuiMain
|
||||||
|
|||||||
+34
-4
@@ -30,7 +30,9 @@ from datetime import datetime
|
|||||||
|
|
||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import qApp
|
||||||
|
|
||||||
from nw.constants import nwConst, nwUnicode
|
from nw.constants import (
|
||||||
|
nwConst, nwUnicode, nwItemClass, nwItemType, nwItemLayout
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -103,11 +105,39 @@ def isHandle(theString):
|
|||||||
return False
|
return False
|
||||||
if len(theString) != 13:
|
if len(theString) != 13:
|
||||||
return False
|
return False
|
||||||
invalidChar = False
|
|
||||||
for c in theString:
|
for c in theString:
|
||||||
if c not in "0123456789abcdef":
|
if c not in "0123456789abcdef":
|
||||||
invalidChar = True
|
return False
|
||||||
return not invalidChar
|
return True
|
||||||
|
|
||||||
|
def isTitleTag(theString):
|
||||||
|
"""Check if a string is a valid title string.
|
||||||
|
"""
|
||||||
|
if not isinstance(theString, str):
|
||||||
|
return False
|
||||||
|
if len(theString) != 7:
|
||||||
|
return False
|
||||||
|
if not theString.startswith("T"):
|
||||||
|
return False
|
||||||
|
for c in theString[1:]:
|
||||||
|
if c not in "0123456789":
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def isItemClass(theString):
|
||||||
|
"""Check if an item is a calid nwItemClass identifier.
|
||||||
|
"""
|
||||||
|
return theString in nwItemClass.__members__
|
||||||
|
|
||||||
|
def isItemType(theString):
|
||||||
|
"""Check if an item is a calid nwItemType identifier.
|
||||||
|
"""
|
||||||
|
return theString in nwItemType.__members__
|
||||||
|
|
||||||
|
def isItemLayout(theString):
|
||||||
|
"""Check if an item is a calid nwItemLayout identifier.
|
||||||
|
"""
|
||||||
|
return theString in nwItemLayout.__members__
|
||||||
|
|
||||||
def hexToInt(value, default=0):
|
def hexToInt(value, default=0):
|
||||||
"""Convert a hex string to an integer.
|
"""Convert a hex string to an integer.
|
||||||
|
|||||||
+7
-6
@@ -38,6 +38,7 @@ from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
|
|||||||
|
|
||||||
from nw.constants import nwConst, nwFiles, nwUnicode
|
from nw.constants import nwConst, nwFiles, nwUnicode
|
||||||
from nw.common import splitVersionNumber, formatTimeStamp
|
from nw.common import splitVersionNumber, formatTimeStamp
|
||||||
|
from nw.error import logException
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -293,7 +294,7 @@ class Config:
|
|||||||
os.mkdir(self.confPath)
|
os.mkdir(self.confPath)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not create folder: %s" % self.confPath)
|
logger.error("Could not create folder: %s" % self.confPath)
|
||||||
logger.error(str(e))
|
logException()
|
||||||
self.hasError = True
|
self.hasError = True
|
||||||
self.errData.append("Could not create folder: %s" % self.confPath)
|
self.errData.append("Could not create folder: %s" % self.confPath)
|
||||||
self.errData.append(str(e))
|
self.errData.append(str(e))
|
||||||
@@ -316,7 +317,7 @@ class Config:
|
|||||||
os.mkdir(self.dataPath)
|
os.mkdir(self.dataPath)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not create folder: %s" % self.dataPath)
|
logger.error("Could not create folder: %s" % self.dataPath)
|
||||||
logger.error(str(e))
|
logException()
|
||||||
self.hasError = True
|
self.hasError = True
|
||||||
self.errData.append("Could not create folder: %s" % self.dataPath)
|
self.errData.append("Could not create folder: %s" % self.dataPath)
|
||||||
self.errData.append(str(e))
|
self.errData.append(str(e))
|
||||||
@@ -361,7 +362,7 @@ class Config:
|
|||||||
cnfParse.read_file(inFile)
|
cnfParse.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not load config file")
|
logger.error("Could not load config file")
|
||||||
logger.error(str(e))
|
logException()
|
||||||
self.hasError = True
|
self.hasError = True
|
||||||
self.errData.append("Could not load config file")
|
self.errData.append("Could not load config file")
|
||||||
self.errData.append(str(e))
|
self.errData.append(str(e))
|
||||||
@@ -702,7 +703,7 @@ class Config:
|
|||||||
self.confChanged = False
|
self.confChanged = False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not save config file")
|
logger.error("Could not save config file")
|
||||||
logger.error(str(e))
|
logException()
|
||||||
self.hasError = True
|
self.hasError = True
|
||||||
self.errData.append("Could not save config file")
|
self.errData.append("Could not save config file")
|
||||||
self.errData.append(str(e))
|
self.errData.append(str(e))
|
||||||
@@ -978,9 +979,9 @@ class Config:
|
|||||||
return self._unpackList(
|
return self._unpackList(
|
||||||
cnfParse.get(cnfSec, cnfName), cnfDefault, self.CNF_S_LST
|
cnfParse.get(cnfSec, cnfName), cnfDefault, self.CNF_S_LST
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError:
|
||||||
logger.error("Failed to load value from config file.")
|
logger.error("Failed to load value from config file.")
|
||||||
logger.error(str(e))
|
logException()
|
||||||
return cnfDefault
|
return cnfDefault
|
||||||
|
|
||||||
return cnfDefault
|
return cnfDefault
|
||||||
|
|||||||
+162
-30
@@ -36,11 +36,13 @@ from nw.constants import (
|
|||||||
)
|
)
|
||||||
from nw.core.document import NWDoc
|
from nw.core.document import NWDoc
|
||||||
from nw.core.tools import countWords
|
from nw.core.tools import countWords
|
||||||
|
from nw.common import isHandle, isTitleTag, isItemClass, isItemLayout
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class NWIndex():
|
class NWIndex():
|
||||||
|
|
||||||
|
H_VALID = ("H0", "H1", "H2", "H3", "H4")
|
||||||
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
|
H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
|
||||||
|
|
||||||
def __init__(self, theProject, theParent):
|
def __init__(self, theProject, theParent):
|
||||||
@@ -155,9 +157,14 @@ class NWIndex():
|
|||||||
try:
|
try:
|
||||||
with open(indexFile, mode="r", encoding="utf8") as inFile:
|
with open(indexFile, mode="r", encoding="utf8") as inFile:
|
||||||
theData = json.load(inFile)
|
theData = json.load(inFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to load index file")
|
logger.error("Failed to load index file")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
|
self.indexBroken = True
|
||||||
|
self.theParent.makeAlert(
|
||||||
|
"Could not load cached index file. Rebuilding index.",
|
||||||
|
nwAlert.WARN
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._tagIndex = theData.get("tagIndex", {})
|
self._tagIndex = theData.get("tagIndex", {})
|
||||||
@@ -193,9 +200,9 @@ class NWIndex():
|
|||||||
"textCounts" : self._textCounts,
|
"textCounts" : self._textCounts,
|
||||||
"firstTitle" : self._firstTitle,
|
"firstTitle" : self._firstTitle,
|
||||||
}, outFile, indent=2)
|
}, outFile, indent=2)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to save index file")
|
logger.error("Failed to save index file")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -205,43 +212,29 @@ class NWIndex():
|
|||||||
elements it should.
|
elements it should.
|
||||||
"""
|
"""
|
||||||
logger.debug("Checking index")
|
logger.debug("Checking index")
|
||||||
self.indexBroken = False
|
tStart = time()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for tTag in self._tagIndex:
|
self._checkTagIndex()
|
||||||
if len(self._tagIndex[tTag]) != 4:
|
self._checkRefIndex()
|
||||||
self.indexBroken = True
|
self._checkNovelNoteIndex("novelIndex")
|
||||||
|
self._checkNovelNoteIndex("noteIndex")
|
||||||
for tHandle in self._refIndex:
|
self._checkTextCounts()
|
||||||
for sTitle in self._refIndex[tHandle]:
|
self.indexBroken = False
|
||||||
for tEntry in self._refIndex[tHandle][sTitle]["tags"]:
|
|
||||||
if len(tEntry) != 3:
|
|
||||||
self.indexBroken = True
|
|
||||||
|
|
||||||
for tHandle in self._novelIndex:
|
|
||||||
for sLine in self._novelIndex[tHandle]:
|
|
||||||
if len(self._novelIndex[tHandle][sLine].keys()) != 8:
|
|
||||||
self.indexBroken = True
|
|
||||||
|
|
||||||
for tHandle in self._noteIndex:
|
|
||||||
for sLine in self._noteIndex[tHandle]:
|
|
||||||
if len(self._noteIndex[tHandle][sLine].keys()) != 8:
|
|
||||||
self.indexBroken = True
|
|
||||||
|
|
||||||
for tHandle in self._textCounts:
|
|
||||||
if len(self._textCounts[tHandle]) != 3:
|
|
||||||
self.indexBroken = True
|
|
||||||
|
|
||||||
for tHandle in self._firstTitle:
|
for tHandle in self._firstTitle:
|
||||||
if len(self._firstTitle[tHandle]) != 2:
|
if len(self._firstTitle[tHandle]) != 2:
|
||||||
self.indexBroken = True
|
self.indexBroken = True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Error while checking index")
|
logger.error("Error while checking index")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
self.indexBroken = True
|
self.indexBroken = True
|
||||||
|
|
||||||
|
tEnd = time()
|
||||||
|
logger.debug("Index check took %.3f ms" % ((tEnd - tStart)*1000))
|
||||||
logger.debug("Index check complete")
|
logger.debug("Index check complete")
|
||||||
|
|
||||||
if self.indexBroken:
|
if self.indexBroken:
|
||||||
self.clearIndex()
|
self.clearIndex()
|
||||||
self.theParent.makeAlert(
|
self.theParent.makeAlert(
|
||||||
@@ -765,4 +758,143 @@ class NWIndex():
|
|||||||
|
|
||||||
return theHandles
|
return theHandles
|
||||||
|
|
||||||
|
##
|
||||||
|
# Index Checkers
|
||||||
|
##
|
||||||
|
|
||||||
|
def _checkTagIndex(self):
|
||||||
|
"""Scan the tag index for errors.
|
||||||
|
Waring: This function raises exceptions.
|
||||||
|
"""
|
||||||
|
for tTag in self._tagIndex:
|
||||||
|
if not isinstance(tTag, str):
|
||||||
|
raise KeyError("tagIndex key is not a string")
|
||||||
|
|
||||||
|
tEntry = self._tagIndex[tTag]
|
||||||
|
if len(tEntry) != 4:
|
||||||
|
raise IndexError("tagIndex[a] expected 4 values")
|
||||||
|
if not isinstance(tEntry[0], int):
|
||||||
|
raise ValueError("tagIndex[a][0] is not an integer")
|
||||||
|
if not isHandle(tEntry[1]):
|
||||||
|
raise ValueError("tagIndex[a][1] is not a handle")
|
||||||
|
if not isItemClass(tEntry[2]):
|
||||||
|
raise ValueError("tagIndex[a][2] is not an nwItemClass")
|
||||||
|
if not isTitleTag(tEntry[3]):
|
||||||
|
raise ValueError("tagIndex[a][3] is not a title tag")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _checkRefIndex(self):
|
||||||
|
"""Scan the reference index for errors.
|
||||||
|
Waring: This function raises exceptions.
|
||||||
|
"""
|
||||||
|
for tHandle in self._refIndex:
|
||||||
|
if not isHandle(tHandle):
|
||||||
|
raise KeyError("refIndex key is not a handle")
|
||||||
|
|
||||||
|
hEntry = self._refIndex[tHandle]
|
||||||
|
for sTitle in hEntry:
|
||||||
|
if not isTitleTag(sTitle):
|
||||||
|
raise KeyError("refIndex[a] key is not a title tag")
|
||||||
|
|
||||||
|
sEntry = hEntry[sTitle]
|
||||||
|
if "tags" not in sEntry:
|
||||||
|
raise KeyError("refIndex[a][b] has no 'tag' key")
|
||||||
|
for tEntry in sEntry["tags"]:
|
||||||
|
if len(tEntry) != 3:
|
||||||
|
raise IndexError("refIndex[a][b][tags][i] expected 3 values")
|
||||||
|
if not isinstance(tEntry[0], int):
|
||||||
|
raise ValueError("refIndex[a][b][tags][i][0] is not an integer")
|
||||||
|
if not tEntry[1] in nwKeyWords.VALID_KEYS:
|
||||||
|
raise ValueError("refIndex[a][b][tags][i][1] is not a keyword")
|
||||||
|
if not isinstance(tEntry[2], str):
|
||||||
|
raise ValueError("refIndex[a][b][tags][i][2] is not a string")
|
||||||
|
|
||||||
|
if "updated" not in sEntry:
|
||||||
|
raise KeyError("refIndex[a][b] has no 'updated' key")
|
||||||
|
if not isinstance(sEntry["updated"], int):
|
||||||
|
raise ValueError("%refIndex[a][b][updated] is not an integer")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _checkNovelNoteIndex(self, idxName):
|
||||||
|
"""Scan the novel or note index for errors.
|
||||||
|
Waring: This function raises exceptions.
|
||||||
|
"""
|
||||||
|
if idxName == "novelIndex":
|
||||||
|
theIndex = self._novelIndex
|
||||||
|
elif idxName == "noteIndex":
|
||||||
|
theIndex = self._noteIndex
|
||||||
|
else:
|
||||||
|
raise IndexError("Unknown index %s" % idxName)
|
||||||
|
|
||||||
|
for tHandle in theIndex:
|
||||||
|
if not isHandle(tHandle):
|
||||||
|
raise KeyError("%s key is not a handle" % idxName)
|
||||||
|
|
||||||
|
hEntry = theIndex[tHandle]
|
||||||
|
for sTitle in theIndex[tHandle]:
|
||||||
|
if not isTitleTag(sTitle):
|
||||||
|
raise KeyError("%s[a] key is not a title tag" % idxName)
|
||||||
|
|
||||||
|
sEntry = hEntry[sTitle]
|
||||||
|
if len(sEntry) != 8:
|
||||||
|
raise IndexError("%s[a][b] expected 8 values" % idxName)
|
||||||
|
|
||||||
|
if "level" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'level' key" % idxName)
|
||||||
|
if "title" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'title' key" % idxName)
|
||||||
|
if "layout" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'layout' key" % idxName)
|
||||||
|
if "synopsis" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'synopsis' key" % idxName)
|
||||||
|
if "cCount" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'cCount' key" % idxName)
|
||||||
|
if "wCount" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'wCount' key" % idxName)
|
||||||
|
if "pCount" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'pCount' key" % idxName)
|
||||||
|
if "updated" not in sEntry:
|
||||||
|
raise KeyError("%s[a][b] has no 'updated' key" % idxName)
|
||||||
|
|
||||||
|
if not sEntry["level"] in self.H_VALID:
|
||||||
|
raise ValueError("%s[a][b][level] is not a header level" % idxName)
|
||||||
|
if not isinstance(sEntry["title"], str):
|
||||||
|
raise ValueError("%s[a][b][title] is not a string" % idxName)
|
||||||
|
if not isItemLayout(sEntry["layout"]):
|
||||||
|
raise ValueError("%s[a][b][layout] is not an nwItemLayout" % idxName)
|
||||||
|
if not isinstance(sEntry["synopsis"], str):
|
||||||
|
raise ValueError("%s[a][b][synopsis] is not a string" % idxName)
|
||||||
|
if not isinstance(sEntry["cCount"], int):
|
||||||
|
raise ValueError("%s[a][b][cCount] is not an integer" % idxName)
|
||||||
|
if not isinstance(sEntry["wCount"], int):
|
||||||
|
raise ValueError("%s[a][b][wCount] is not an integer" % idxName)
|
||||||
|
if not isinstance(sEntry["pCount"], int):
|
||||||
|
raise ValueError("%s[a][b][pCount] is not an integer" % idxName)
|
||||||
|
if not isinstance(sEntry["updated"], int):
|
||||||
|
raise ValueError("%s[a][b][updated] is not an integer" % idxName)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _checkTextCounts(self):
|
||||||
|
"""Scan the text counts index for errors.
|
||||||
|
Waring: This function raises exceptions.
|
||||||
|
"""
|
||||||
|
for tHandle in self._textCounts:
|
||||||
|
if not isHandle(tHandle):
|
||||||
|
raise KeyError("textCounts key is not a handle")
|
||||||
|
|
||||||
|
tEntry = self._textCounts[tHandle]
|
||||||
|
if len(tEntry) != 3:
|
||||||
|
raise IndexError("textCounts[a] expected 3 values")
|
||||||
|
if not isinstance(tEntry[0], int):
|
||||||
|
raise ValueError("textCounts[a][0] is not an integer")
|
||||||
|
if not isinstance(tEntry[1], int):
|
||||||
|
raise ValueError("textCounts[a][1] is not an integer")
|
||||||
|
if not isinstance(tEntry[2], int):
|
||||||
|
raise ValueError("textCounts[a][2] is not an integer")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
# END Class NWIndex
|
# END Class NWIndex
|
||||||
|
|||||||
+6
-4
@@ -28,8 +28,10 @@ import logging
|
|||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from nw.common import checkInt, isHandle
|
|
||||||
from nw.constants import nwItemType, nwItemClass, nwItemLayout
|
from nw.constants import nwItemType, nwItemClass, nwItemLayout
|
||||||
|
from nw.common import (
|
||||||
|
checkInt, isHandle, isItemClass, isItemLayout, isItemType
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -201,7 +203,7 @@ class NWItem():
|
|||||||
"""
|
"""
|
||||||
if isinstance(theType, nwItemType):
|
if isinstance(theType, nwItemType):
|
||||||
self.itemType = theType
|
self.itemType = theType
|
||||||
elif theType in nwItemType.__members__:
|
elif isItemType(theType):
|
||||||
self.itemType = nwItemType[theType]
|
self.itemType = nwItemType[theType]
|
||||||
else:
|
else:
|
||||||
logger.error("Unrecognised item type '%s'" % theType)
|
logger.error("Unrecognised item type '%s'" % theType)
|
||||||
@@ -214,7 +216,7 @@ class NWItem():
|
|||||||
"""
|
"""
|
||||||
if isinstance(theClass, nwItemClass):
|
if isinstance(theClass, nwItemClass):
|
||||||
self.itemClass = theClass
|
self.itemClass = theClass
|
||||||
elif theClass in nwItemClass.__members__:
|
elif isItemClass(theClass):
|
||||||
self.itemClass = nwItemClass[theClass]
|
self.itemClass = nwItemClass[theClass]
|
||||||
else:
|
else:
|
||||||
logger.error("Unrecognised item class '%s'" % theClass)
|
logger.error("Unrecognised item class '%s'" % theClass)
|
||||||
@@ -227,7 +229,7 @@ class NWItem():
|
|||||||
"""
|
"""
|
||||||
if isinstance(theLayout, nwItemLayout):
|
if isinstance(theLayout, nwItemLayout):
|
||||||
self.itemLayout = theLayout
|
self.itemLayout = theLayout
|
||||||
elif theLayout in nwItemLayout.__members__:
|
elif isItemLayout(theLayout):
|
||||||
self.itemLayout = nwItemLayout[theLayout]
|
self.itemLayout = nwItemLayout[theLayout]
|
||||||
else:
|
else:
|
||||||
logger.error("Unrecognised item layout '%s'" % theLayout)
|
logger.error("Unrecognised item layout '%s'" % theLayout)
|
||||||
|
|||||||
+5
-4
@@ -25,6 +25,7 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import nw
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -121,9 +122,9 @@ class OptionState():
|
|||||||
try:
|
try:
|
||||||
with open(stateFile, mode="r", encoding="utf8") as inFile:
|
with open(stateFile, mode="r", encoding="utf8") as inFile:
|
||||||
theState = json.load(inFile)
|
theState = json.load(inFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to load GUI options file")
|
logger.error("Failed to load GUI options file")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Filter out unused variables
|
# Filter out unused variables
|
||||||
@@ -148,9 +149,9 @@ class OptionState():
|
|||||||
try:
|
try:
|
||||||
with open(stateFile, mode="w+", encoding="utf8") as outFile:
|
with open(stateFile, mode="w+", encoding="utf8") as outFile:
|
||||||
json.dump(self.theState, outFile, indent=2)
|
json.dump(self.theState, outFile, indent=2)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to save GUI options file")
|
logger.error("Failed to save GUI options file")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
+23
-18
@@ -1219,9 +1219,9 @@ class NWProject():
|
|||||||
if len(theLines) != 4:
|
if len(theLines) != 4:
|
||||||
return ["ERROR"]
|
return ["ERROR"]
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to read project lockfile")
|
logger.error("Failed to read project lockfile")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return ["ERROR"]
|
return ["ERROR"]
|
||||||
|
|
||||||
return theLines
|
return theLines
|
||||||
@@ -1240,9 +1240,9 @@ class NWProject():
|
|||||||
outFile.write("%s\n" % self.mainConf.kernelVer)
|
outFile.write("%s\n" % self.mainConf.kernelVer)
|
||||||
outFile.write("%d\n" % time())
|
outFile.write("%d\n" % time())
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to write project lockfile")
|
logger.error("Failed to write project lockfile")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -1257,9 +1257,9 @@ class NWProject():
|
|||||||
if os.path.isfile(lockFile):
|
if os.path.isfile(lockFile):
|
||||||
try:
|
try:
|
||||||
os.unlink(lockFile)
|
os.unlink(lockFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to remove project lockfile")
|
logger.error("Failed to remove project lockfile")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -1415,9 +1415,9 @@ class NWProject():
|
|||||||
self.notesWCount,
|
self.notesWCount,
|
||||||
))
|
))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to write session stats file")
|
logger.error("Failed to write session stats file")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -1453,17 +1453,19 @@ class NWProject():
|
|||||||
os.rename(theFile, newPath)
|
os.rename(theFile, newPath)
|
||||||
logger.info("Moved file: %s" % theFile)
|
logger.info("Moved file: %s" % theFile)
|
||||||
logger.info("New location: %s" % newPath)
|
logger.info("New location: %s" % newPath)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(str(e))
|
|
||||||
errList.append("Could not move: %s" % theFile)
|
errList.append("Could not move: %s" % theFile)
|
||||||
|
logger.error("Could not move: %s" % theFile)
|
||||||
|
nw.logException()
|
||||||
|
|
||||||
elif len(dataItem) == 21 and dataItem.endswith("_main.bak"):
|
elif len(dataItem) == 21 and dataItem.endswith("_main.bak"):
|
||||||
try:
|
try:
|
||||||
os.unlink(theFile)
|
os.unlink(theFile)
|
||||||
logger.info("Deleted file: %s" % theFile)
|
logger.info("Deleted file: %s" % theFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(str(e))
|
|
||||||
errList.append("Could not delete: %s" % theFile)
|
errList.append("Could not delete: %s" % theFile)
|
||||||
|
logger.error("Could not delete: %s" % theFile)
|
||||||
|
nw.logException()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
theErr = self._moveUnknownItem(theData, dataItem)
|
theErr = self._moveUnknownItem(theData, dataItem)
|
||||||
@@ -1475,9 +1477,10 @@ class NWProject():
|
|||||||
try:
|
try:
|
||||||
os.rmdir(theData)
|
os.rmdir(theData)
|
||||||
logger.info("Removed folder: %s" % theFolder)
|
logger.info("Removed folder: %s" % theFolder)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(str(e))
|
|
||||||
errList.append("Failed to remove: %s" % theFolder)
|
errList.append("Failed to remove: %s" % theFolder)
|
||||||
|
logger.error("Failed to remove: %s" % theFolder)
|
||||||
|
nw.logException()
|
||||||
|
|
||||||
return errList
|
return errList
|
||||||
|
|
||||||
@@ -1495,8 +1498,9 @@ class NWProject():
|
|||||||
try:
|
try:
|
||||||
os.rename(theSrc, theDst)
|
os.rename(theSrc, theDst)
|
||||||
logger.info("Moved to junk: %s" % theSrc)
|
logger.info("Moved to junk: %s" % theSrc)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(str(e))
|
logger.error("Could not move item %s to junk." % theSrc)
|
||||||
|
nw.logException()
|
||||||
return "Could not move item %s to junk." % theSrc
|
return "Could not move item %s to junk." % theSrc
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
@@ -1529,8 +1533,9 @@ class NWProject():
|
|||||||
logger.info("Deleting: %s" % rmFile)
|
logger.info("Deleting: %s" % rmFile)
|
||||||
try:
|
try:
|
||||||
os.unlink(rmFile)
|
os.unlink(rmFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(str(e))
|
logger.error("Could not delete: %s" % rmFile)
|
||||||
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ class NWSpellCheck():
|
|||||||
with open(self.projectDict, mode="a+", encoding="utf-8") as outFile:
|
with open(self.projectDict, mode="a+", encoding="utf-8") as outFile:
|
||||||
outFile.write("%s\n" % newWord)
|
outFile.write("%s\n" % newWord)
|
||||||
self.projDict.append(newWord)
|
self.projDict.append(newWord)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to add word to project word list %s" % str(self.projectDict))
|
logger.error("Failed to add word to project word list %s" % str(self.projectDict))
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -123,9 +123,9 @@ class NWSpellCheck():
|
|||||||
if len(theLine) > 0 and theLine not in self.projDict:
|
if len(theLine) > 0 and theLine not in self.projDict:
|
||||||
self.projDict.append(theLine)
|
self.projDict.append(theLine)
|
||||||
logger.debug("Project word list contains %d words" % len(self.projDict))
|
logger.debug("Project word list contains %d words" % len(self.projDict))
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to load project word list")
|
logger.error("Failed to load project word list")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -201,9 +201,9 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
try:
|
try:
|
||||||
spTag = self.theDict.tag
|
spTag = self.theDict.tag
|
||||||
spName = self.theDict.provider.name
|
spName = self.theDict.provider.name
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to extract information about the dictionary")
|
logger.error("Failed to extract information about the dictionary")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
spTag = ""
|
spTag = ""
|
||||||
spName = ""
|
spName = ""
|
||||||
|
|
||||||
@@ -261,9 +261,9 @@ class NWSpellSimple(NWSpellCheck):
|
|||||||
logger.debug("Spell check word list for language %s loaded" % theLang)
|
logger.debug("Spell check word list for language %s loaded" % theLang)
|
||||||
logger.debug("Word list contains %d words" % len(self.WORDS))
|
logger.debug("Word list contains %d words" % len(self.WORDS))
|
||||||
self.spellLanguage = theLang
|
self.spellLanguage = theLang
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to load spell check word list for language %s" % theLang)
|
logger.error("Failed to load spell check word list for language %s" % theLang)
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
self.spellLanguage = None
|
self.spellLanguage = None
|
||||||
|
|
||||||
self._readProjectDictionary(projectDict)
|
self._readProjectDictionary(projectDict)
|
||||||
|
|||||||
+4
-2
@@ -24,6 +24,7 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import nw
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@@ -204,8 +205,9 @@ class NWTree():
|
|||||||
outFile.write("\n".join(tocList))
|
outFile.write("\n".join(tocList))
|
||||||
outFile.write("\n")
|
outFile.write("\n")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(str(e))
|
logger.error("Could not write ToC file")
|
||||||
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
+19
-4
@@ -24,12 +24,31 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QDialog, QGridLayout, QStyle, QPlainTextEdit, QLabel,
|
qApp, QDialog, QGridLayout, QStyle, QPlainTextEdit, QLabel,
|
||||||
QDialogButtonBox
|
QDialogButtonBox
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# =============================================================================================== #
|
||||||
|
# Utility Functions
|
||||||
|
# =============================================================================================== #
|
||||||
|
|
||||||
|
def logException():
|
||||||
|
"""Log the content of an exception message.
|
||||||
|
"""
|
||||||
|
exType, exValue, _ = sys.exc_info()
|
||||||
|
logger.error("%s: %s" % (exType.__name__, str(exValue).strip("'")))
|
||||||
|
|
||||||
|
# =============================================================================================== #
|
||||||
|
# Error Handler
|
||||||
|
# =============================================================================================== #
|
||||||
|
|
||||||
class NWErrorMessage(QDialog):
|
class NWErrorMessage(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@@ -72,7 +91,6 @@ class NWErrorMessage(QDialog):
|
|||||||
"""Generate a message and append session data, error info and
|
"""Generate a message and append session data, error info and
|
||||||
error traceback.
|
error traceback.
|
||||||
"""
|
"""
|
||||||
import sys
|
|
||||||
from traceback import format_tb
|
from traceback import format_tb
|
||||||
from nw import __issuesurl__, __version__
|
from nw import __issuesurl__, __version__
|
||||||
from PyQt5.Qt import PYQT_VERSION_STR
|
from PyQt5.Qt import PYQT_VERSION_STR
|
||||||
@@ -133,15 +151,12 @@ class NWErrorMessage(QDialog):
|
|||||||
|
|
||||||
# END Class NWErrorMessage
|
# END Class NWErrorMessage
|
||||||
|
|
||||||
|
|
||||||
def exceptionHandler(exType, exValue, exTrace):
|
def exceptionHandler(exType, exValue, exTrace):
|
||||||
"""Function to catch unhandled global exceptions.
|
"""Function to catch unhandled global exceptions.
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
from traceback import print_tb
|
from traceback import print_tb
|
||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import qApp
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
logger.critical("%s: %s" % (exType.__name__, str(exValue)))
|
logger.critical("%s: %s" % (exType.__name__, str(exValue)))
|
||||||
print_tb(exTrace)
|
print_tb(exTrace)
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -671,9 +671,9 @@ class GuiBuildNovel(QDialog):
|
|||||||
bldObj.doConvert()
|
bldObj.doConvert()
|
||||||
bldObj.doPostProcessing()
|
bldObj.doPostProcessing()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to generate html of document '%s'" % tItem.itemHandle)
|
logger.error("Failed to generate html of document '%s'" % tItem.itemHandle)
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
if isPreview:
|
if isPreview:
|
||||||
self.docView.setText((
|
self.docView.setText((
|
||||||
"Failed to generate preview. "
|
"Failed to generate preview. "
|
||||||
@@ -997,9 +997,9 @@ class GuiBuildNovel(QDialog):
|
|||||||
with open(buildCache, mode="r", encoding="utf8") as inFile:
|
with open(buildCache, mode="r", encoding="utf8") as inFile:
|
||||||
theJson = inFile.read()
|
theJson = inFile.read()
|
||||||
theData = json.loads(theJson)
|
theData = json.loads(theJson)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to load build cache")
|
logger.error("Failed to load build cache")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if "htmlText" in theData.keys():
|
if "htmlText" in theData.keys():
|
||||||
@@ -1026,9 +1026,9 @@ class GuiBuildNovel(QDialog):
|
|||||||
"htmlStyle" : self.htmlStyle,
|
"htmlStyle" : self.htmlStyle,
|
||||||
"buildTime" : self.buildTime,
|
"buildTime" : self.buildTime,
|
||||||
}, indent=2))
|
}, indent=2))
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to save build cache")
|
logger.error("Failed to save build cache")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
+2
-2
@@ -180,9 +180,9 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
aDoc.tokenizeText()
|
aDoc.tokenizeText()
|
||||||
aDoc.doConvert()
|
aDoc.doConvert()
|
||||||
aDoc.doPostProcessing()
|
aDoc.doPostProcessing()
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Failed to generate preview for document with handle '%s'" % tHandle)
|
logger.error("Failed to generate preview for document with handle '%s'" % tHandle)
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
self.setText("An error occurred while generating the preview.")
|
self.setText("An error occurred while generating the preview.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -265,9 +265,9 @@ class GuiTheme:
|
|||||||
if os.path.isfile(self.cssFile):
|
if os.path.isfile(self.cssFile):
|
||||||
with open(self.cssFile, mode="r", encoding="utf8") as inFile:
|
with open(self.cssFile, mode="r", encoding="utf8") as inFile:
|
||||||
cssData = inFile.read()
|
cssData = inFile.read()
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Could not load theme css file")
|
logger.error("Could not load theme css file")
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Config File
|
# Config File
|
||||||
@@ -275,9 +275,9 @@ class GuiTheme:
|
|||||||
try:
|
try:
|
||||||
with open(self.confFile, mode="r", encoding="utf8") as inFile:
|
with open(self.confFile, mode="r", encoding="utf8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Could not load theme settings from: %s" % self.confFile)
|
logger.error("Could not load theme settings from: %s" % self.confFile)
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
## Main
|
## Main
|
||||||
@@ -333,9 +333,9 @@ class GuiTheme:
|
|||||||
try:
|
try:
|
||||||
with open(self.syntaxFile, mode="r", encoding="utf8") as inFile:
|
with open(self.syntaxFile, mode="r", encoding="utf8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Could not load syntax colours from: %s" % self.syntaxFile)
|
logger.error("Could not load syntax colours from: %s" % self.syntaxFile)
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
## Main
|
## Main
|
||||||
@@ -637,9 +637,9 @@ class GuiIcons:
|
|||||||
try:
|
try:
|
||||||
with open(self.confFile, mode="r", encoding="utf8") as inFile:
|
with open(self.confFile, mode="r", encoding="utf8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error("Could not load icon theme settings from: %s" % self.confFile)
|
logger.error("Could not load icon theme settings from: %s" % self.confFile)
|
||||||
logger.error(str(e))
|
nw.logException()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
## Main
|
## Main
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ import pytest
|
|||||||
from nw.common import (
|
from nw.common import (
|
||||||
checkString, checkBool, checkInt, colRange, formatInt, transferCase,
|
checkString, checkBool, checkInt, colRange, formatInt, transferCase,
|
||||||
fuzzyTime, checkHandle, formatTimeStamp, formatTime, hexToInt,
|
fuzzyTime, checkHandle, formatTimeStamp, formatTime, hexToInt,
|
||||||
makeFileNameSafe
|
makeFileNameSafe, isHandle, isTitleTag, isItemClass, isItemType,
|
||||||
|
isItemLayout
|
||||||
)
|
)
|
||||||
from tools import cmpList
|
from tools import cmpList
|
||||||
|
|
||||||
@@ -88,6 +89,95 @@ def testBaseCommon_CheckHandle():
|
|||||||
|
|
||||||
# END Test testBaseCommon_CheckHandle
|
# END Test testBaseCommon_CheckHandle
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_IsHandle():
|
||||||
|
"""Test the isHandle function.
|
||||||
|
"""
|
||||||
|
assert isHandle("47666c91c7ccf")
|
||||||
|
|
||||||
|
assert not isHandle("47666C91C7CCF")
|
||||||
|
assert not isHandle("h7666c91c7ccf")
|
||||||
|
assert not isHandle("None")
|
||||||
|
assert not isHandle(None)
|
||||||
|
assert not isHandle("STUFF")
|
||||||
|
|
||||||
|
# END Test testBaseCommon_IsHandle
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_IsTitleTag():
|
||||||
|
"""Test the isItemClass function.
|
||||||
|
"""
|
||||||
|
assert isTitleTag("T123456")
|
||||||
|
|
||||||
|
assert not isTitleTag("t123456")
|
||||||
|
assert not isTitleTag("S123456")
|
||||||
|
assert not isTitleTag("T12345A")
|
||||||
|
assert not isTitleTag("T1234567")
|
||||||
|
|
||||||
|
assert not isTitleTag("None")
|
||||||
|
assert not isTitleTag(None)
|
||||||
|
assert not isTitleTag("STUFF")
|
||||||
|
|
||||||
|
# END Test testBaseCommon_IsTitleTag
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_IsItemClass():
|
||||||
|
"""Test the isItemClass function.
|
||||||
|
"""
|
||||||
|
assert isItemClass("NO_CLASS")
|
||||||
|
assert isItemClass("NOVEL")
|
||||||
|
assert isItemClass("PLOT")
|
||||||
|
assert isItemClass("CHARACTER")
|
||||||
|
assert isItemClass("WORLD")
|
||||||
|
assert isItemClass("TIMELINE")
|
||||||
|
assert isItemClass("OBJECT")
|
||||||
|
assert isItemClass("ENTITY")
|
||||||
|
assert isItemClass("CUSTOM")
|
||||||
|
assert isItemClass("ARCHIVE")
|
||||||
|
assert isItemClass("TRASH")
|
||||||
|
|
||||||
|
assert not isItemClass("None")
|
||||||
|
assert not isItemClass(None)
|
||||||
|
assert not isItemClass("STUFF")
|
||||||
|
|
||||||
|
# END Test testBaseCommon_IsItemClass
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_IsItemType():
|
||||||
|
"""Test the isItemType function.
|
||||||
|
"""
|
||||||
|
assert isItemType("NO_TYPE")
|
||||||
|
assert isItemType("ROOT")
|
||||||
|
assert isItemType("FOLDER")
|
||||||
|
assert isItemType("FILE")
|
||||||
|
assert isItemType("TRASH")
|
||||||
|
|
||||||
|
assert not isItemType("None")
|
||||||
|
assert not isItemType(None)
|
||||||
|
assert not isItemType("STUFF")
|
||||||
|
|
||||||
|
# END Test testBaseCommon_IsItemType
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_IsItemLayout():
|
||||||
|
"""Test the isItemLayout function.
|
||||||
|
"""
|
||||||
|
assert isItemLayout("NO_LAYOUT")
|
||||||
|
assert isItemLayout("TITLE")
|
||||||
|
assert isItemLayout("BOOK")
|
||||||
|
assert isItemLayout("PAGE")
|
||||||
|
assert isItemLayout("PARTITION")
|
||||||
|
assert isItemLayout("UNNUMBERED")
|
||||||
|
assert isItemLayout("CHAPTER")
|
||||||
|
assert isItemLayout("SCENE")
|
||||||
|
assert isItemLayout("NOTE")
|
||||||
|
|
||||||
|
assert not isItemLayout("None")
|
||||||
|
assert not isItemLayout(None)
|
||||||
|
assert not isItemLayout("STUFF")
|
||||||
|
|
||||||
|
# END Test testBaseCommon_IsItemLayout
|
||||||
|
|
||||||
@pytest.mark.base
|
@pytest.mark.base
|
||||||
def testBaseCommon_HexToInt():
|
def testBaseCommon_HexToInt():
|
||||||
"""Test the hexToInt function.
|
"""Test the hexToInt function.
|
||||||
|
|||||||
@@ -115,38 +115,7 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
|
|||||||
|
|
||||||
# Break the index and check that we notice
|
# Break the index and check that we notice
|
||||||
assert not theIndex.indexBroken
|
assert not theIndex.indexBroken
|
||||||
theIndex._tagIndex["Bod"].append("Stuff") # No longer len() == 4
|
theIndex._tagIndex["Bod"].append("Stuff")
|
||||||
theIndex.checkIndex()
|
|
||||||
assert theIndex.indexBroken
|
|
||||||
|
|
||||||
assert theIndex.loadIndex()
|
|
||||||
assert not theIndex.indexBroken
|
|
||||||
theIndex._refIndex["fb609cd8319dc"]["T000001"]["tags"].append("Stuff") # No longer len() == 3
|
|
||||||
theIndex.checkIndex()
|
|
||||||
assert theIndex.indexBroken
|
|
||||||
|
|
||||||
assert theIndex.loadIndex()
|
|
||||||
assert not theIndex.indexBroken
|
|
||||||
theIndex._novelIndex["7a992350f3eb6"]["T000001"]["Stuff"] = "" # No longer len(keys()) == 8
|
|
||||||
theIndex.checkIndex()
|
|
||||||
assert theIndex.indexBroken
|
|
||||||
|
|
||||||
assert theIndex.loadIndex()
|
|
||||||
assert not theIndex.indexBroken
|
|
||||||
theIndex._noteIndex["4c4f28287af27"]["T000001"]["Stuff"] = "" # No longer len(keys()) == 8
|
|
||||||
theIndex.checkIndex()
|
|
||||||
assert theIndex.indexBroken
|
|
||||||
|
|
||||||
assert theIndex.loadIndex()
|
|
||||||
assert not theIndex.indexBroken
|
|
||||||
theIndex._textCounts["7a992350f3eb6"].append("Stuff") # No longer len() == 3
|
|
||||||
theIndex.checkIndex()
|
|
||||||
assert theIndex.indexBroken
|
|
||||||
|
|
||||||
# Make the try/except trigger as well
|
|
||||||
assert theIndex.loadIndex()
|
|
||||||
assert not theIndex.indexBroken
|
|
||||||
theIndex._refIndex["fb609cd8319dc"]["T000001"] = {"tagssss": []} # Wrong key name
|
|
||||||
theIndex.checkIndex()
|
theIndex.checkIndex()
|
||||||
assert theIndex.indexBroken
|
assert theIndex.indexBroken
|
||||||
|
|
||||||
@@ -676,3 +645,503 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreIndex_ExtractData
|
# END Test testCoreIndex_ExtractData
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreIndex_CheckTagIndex(dummyGUI):
|
||||||
|
"""Test the tag index checker.
|
||||||
|
"""
|
||||||
|
theProject = NWProject(dummyGUI)
|
||||||
|
theIndex = NWIndex(theProject, dummyGUI)
|
||||||
|
|
||||||
|
# Valid Index
|
||||||
|
theIndex._tagIndex = {
|
||||||
|
"John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||||
|
"Jane": [3, "bb2c23b3c42cc", "CHARACTER", "T000001"],
|
||||||
|
}
|
||||||
|
assert theIndex._checkTagIndex() is None
|
||||||
|
|
||||||
|
# Wrong Key Type
|
||||||
|
theIndex._tagIndex = {
|
||||||
|
"John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||||
|
123456: [3, "bb2c23b3c42cc", "CHARACTER", "T000001"],
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkTagIndex()
|
||||||
|
|
||||||
|
# Wrong Length
|
||||||
|
theIndex._tagIndex = {
|
||||||
|
"John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||||
|
"Jane": [3, "bb2c23b3c42cc", "CHARACTER", "T000001", "Stuff"],
|
||||||
|
}
|
||||||
|
with pytest.raises(IndexError):
|
||||||
|
theIndex._checkTagIndex()
|
||||||
|
|
||||||
|
# Wrong Type of Entry 0
|
||||||
|
theIndex._tagIndex = {
|
||||||
|
"John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||||
|
"Jane": ["3", "bb2c23b3c42cc", "CHARACTER", "T000001"],
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkTagIndex()
|
||||||
|
|
||||||
|
# Wrong Type of Entry 1
|
||||||
|
theIndex._tagIndex = {
|
||||||
|
"John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||||
|
"Jane": [3, 0xbb2c23b3c42cc, "CHARACTER", "T000001"],
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkTagIndex()
|
||||||
|
|
||||||
|
# Wrong Type of Entry 2
|
||||||
|
theIndex._tagIndex = {
|
||||||
|
"John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||||
|
"Jane": [3, "bb2c23b3c42cc", "INVALID_CLASS", "T000001"],
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkTagIndex()
|
||||||
|
|
||||||
|
# Wrong Type of Entry 3
|
||||||
|
theIndex._tagIndex = {
|
||||||
|
"John": [3, "14298de4d9524", "CHARACTER", "T000001"],
|
||||||
|
"Jane": [3, "bb2c23b3c42cc", "CHARACTER", "INVALID"],
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkTagIndex()
|
||||||
|
|
||||||
|
# END Test testCoreIndex_CheckTagIndex
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreIndex_CheckRefIndex(dummyGUI):
|
||||||
|
"""Test the reference index checker.
|
||||||
|
"""
|
||||||
|
theProject = NWProject(dummyGUI)
|
||||||
|
theIndex = NWIndex(theProject, dummyGUI)
|
||||||
|
|
||||||
|
# Valid Index
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": [
|
||||||
|
[3, "@pov", "Jane"], [4, "@location", "Earth"]
|
||||||
|
], "updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert theIndex._checkRefIndex() is None
|
||||||
|
|
||||||
|
# Invalid Handle
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"Ha2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": [
|
||||||
|
[3, "@pov", "Jane"], [4, "@location", "Earth"]
|
||||||
|
], "updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Invalid Title
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"INVALID": {"tags": [
|
||||||
|
[3, "@pov", "Jane"], [4, "@location", "Earth"]
|
||||||
|
], "updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Missing 'tags'
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Wrong Length of 'tags'
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": [
|
||||||
|
[3, "@pov", "Jane"], [4, "@location", "Earth", "Stuff"]
|
||||||
|
], "updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(IndexError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Wrong Type of 'tags' Entry 0
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": [
|
||||||
|
[3, "@pov", "Jane"], ["4", "@location", "Earth"]
|
||||||
|
], "updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Wrong Type of 'tags' Entry 1
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": [
|
||||||
|
[3, "@pov", "Jane"], [4, "@stuff", "Earth"]
|
||||||
|
], "updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Wrong Type of 'tags' Entry 1
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": [
|
||||||
|
[3, "@pov", "Jane"], [4, "@location", 123456]
|
||||||
|
], "updated": 1611922868}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Missing 'updated'
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": []}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# Wrong Type of 'updated' Entry 1
|
||||||
|
theIndex._refIndex = {
|
||||||
|
"6a2d6d5f4f401": {
|
||||||
|
"T000000": {"tags": [], "updated": 1611922868},
|
||||||
|
"T000001": {"tags": [], "updated": "1611922868"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkRefIndex()
|
||||||
|
|
||||||
|
# END Test testCoreIndex_CheckRefIndex
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreIndex_CheckNovelNoteIndex(dummyGUI):
|
||||||
|
"""Test the novel and note index checkers.
|
||||||
|
"""
|
||||||
|
theProject = NWProject(dummyGUI)
|
||||||
|
theIndex = NWIndex(theProject, dummyGUI)
|
||||||
|
|
||||||
|
# Valid Index
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
theIndex._noteIndex = theIndex._novelIndex.copy()
|
||||||
|
assert theIndex._checkNovelNoteIndex("novelIndex") is None
|
||||||
|
assert theIndex._checkNovelNoteIndex("noteIndex") is None
|
||||||
|
with pytest.raises(IndexError):
|
||||||
|
theIndex._checkNovelNoteIndex("notAnIndex")
|
||||||
|
|
||||||
|
# Invalid Handle
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"H3b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Invalid Title
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"INVALID": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Length
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868, "stuff": None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(IndexError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing Keys
|
||||||
|
# ============
|
||||||
|
|
||||||
|
# Missing 'level'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"stuff": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing 'title'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "stuff": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing 'layout'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "stuff": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing 'synopsis'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "stuff": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing 'cCount'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"stuff": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing 'wCount'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "stuff": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing 'pCount'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "stuff": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Missing 'updated'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "stuff": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Types
|
||||||
|
# ===========
|
||||||
|
|
||||||
|
# Wrong Type for 'level'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "XX", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Type for 'title'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": 12345678, "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Type for 'layout'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "INVALID", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Type for 'synopsis'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": 123456,
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Type for 'cCount'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": "72", "wCount": 15, "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Type for 'wCount'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": "15", "pCount": 2, "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Type for 'pCount'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": "2", "updated": 1611922868
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# Wrong Type for 'updated'
|
||||||
|
theIndex._novelIndex = {
|
||||||
|
"53b69b83cdafc": {
|
||||||
|
"T000001": {
|
||||||
|
"level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text",
|
||||||
|
"cCount": 72, "wCount": 15, "pCount": 2, "updated": "1611922868"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkNovelNoteIndex("novelIndex")
|
||||||
|
|
||||||
|
# END Test testCoreIndex_CheckNovelNoteIndex
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreIndex_CheckTextCounts(dummyGUI):
|
||||||
|
"""Test the text counts checker.
|
||||||
|
"""
|
||||||
|
theProject = NWProject(dummyGUI)
|
||||||
|
theIndex = NWIndex(theProject, dummyGUI)
|
||||||
|
|
||||||
|
# Valid Index
|
||||||
|
theIndex._textCounts = {
|
||||||
|
"53b69b83cdafc": [72, 15, 2],
|
||||||
|
"974e400180a99": [210, 40, 2],
|
||||||
|
}
|
||||||
|
assert theIndex._checkTextCounts() is None
|
||||||
|
|
||||||
|
# Invalid Handle
|
||||||
|
theIndex._textCounts = {
|
||||||
|
"53b69b83cdafc": [72, 15, 2],
|
||||||
|
"h74e400180a99": [210, 40, 2],
|
||||||
|
}
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
theIndex._checkTextCounts()
|
||||||
|
|
||||||
|
# Wrong Length
|
||||||
|
theIndex._textCounts = {
|
||||||
|
"53b69b83cdafc": [72, 15, 2],
|
||||||
|
"974e400180a99": [210, 40, 2, 8],
|
||||||
|
}
|
||||||
|
with pytest.raises(IndexError):
|
||||||
|
theIndex._checkTextCounts()
|
||||||
|
|
||||||
|
# Type of Entry 0
|
||||||
|
theIndex._textCounts = {
|
||||||
|
"53b69b83cdafc": [72, 15, 2],
|
||||||
|
"974e400180a99": ["210", 40, 2],
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkTextCounts()
|
||||||
|
|
||||||
|
# Type of Entry 1
|
||||||
|
theIndex._textCounts = {
|
||||||
|
"53b69b83cdafc": [72, 15, 2],
|
||||||
|
"974e400180a99": [210, "40", 2],
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkTextCounts()
|
||||||
|
|
||||||
|
# Type of Entry 2
|
||||||
|
theIndex._textCounts = {
|
||||||
|
"53b69b83cdafc": [72, 15, 2],
|
||||||
|
"974e400180a99": [210, 40, "2"],
|
||||||
|
}
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
theIndex._checkTextCounts()
|
||||||
|
|
||||||
|
# END Test testCoreIndex_CheckTextCounts
|
||||||
|
|||||||
Reference in New Issue
Block a user