+15
-14
@@ -246,19 +246,20 @@ def main(sysArgs=None):
|
|||||||
errorCode |= 32
|
errorCode |= 32
|
||||||
|
|
||||||
if errorData:
|
if errorData:
|
||||||
if not testMode:
|
errApp = QApplication([])
|
||||||
errApp = QApplication([])
|
errMsg = QErrorMessage()
|
||||||
errMsg = QErrorMessage()
|
errMsg.resize(500, 300)
|
||||||
errMsg.resize(500, 300)
|
errMsg.showMessage((
|
||||||
errMsg.showMessage((
|
"<h3>A critical error has been encountered</h3>"
|
||||||
"<h3>A critical error has been encountered</h3>"
|
"<p>novelWriter cannot start due to the following issues:<p>"
|
||||||
"<p>novelWriter cannot start due to the following issues:<p>"
|
"<p> - %s</p>"
|
||||||
"<p> - %s</p>"
|
"<p>Shutting down ...</p>"
|
||||||
"<p>Shutting down ...</p>"
|
) % (
|
||||||
) % (
|
"<br> - ".join(errorData)
|
||||||
"<br> - ".join(errorData)
|
))
|
||||||
))
|
for errMsg in errorData:
|
||||||
errApp.exec_()
|
logger.critical(errMsg)
|
||||||
|
errApp.exec_()
|
||||||
sys.exit(errorCode)
|
sys.exit(errorCode)
|
||||||
|
|
||||||
# Finish initialising config
|
# Finish initialising config
|
||||||
@@ -293,4 +294,4 @@ def main(sysArgs=None):
|
|||||||
nwGUI = GuiMain()
|
nwGUI = GuiMain()
|
||||||
sys.exit(nwApp.exec_())
|
sys.exit(nwApp.exec_())
|
||||||
|
|
||||||
return
|
# END Function main
|
||||||
|
|||||||
+51
-40
@@ -27,12 +27,12 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
|
import shutil
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
from shutil import which
|
|
||||||
|
|
||||||
from PyQt5.Qt import PYQT_VERSION_STR
|
from PyQt5.Qt import PYQT_VERSION_STR
|
||||||
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
|
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
|
||||||
@@ -44,10 +44,11 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
|
||||||
CNF_STR = 0
|
CNF_STR = 0
|
||||||
CNF_INT = 1
|
CNF_INT = 1
|
||||||
CNF_BOOL = 2
|
CNF_BOOL = 2
|
||||||
CNF_LIST = 3
|
CNF_S_LST = 3
|
||||||
|
CNF_I_LST = 4
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
@@ -211,12 +212,8 @@ class Config:
|
|||||||
self.osUnknown = True
|
self.osUnknown = True
|
||||||
|
|
||||||
# Other System Info
|
# Other System Info
|
||||||
if self.verQtValue >= 50600:
|
self.hostName = "Unknown"
|
||||||
self.hostName = QSysInfo.machineHostName()
|
self.kernelVer = "Unknown"
|
||||||
self.kernelVer = QSysInfo.kernelVersion()
|
|
||||||
else:
|
|
||||||
self.hostName = "Unknown"
|
|
||||||
self.kernelVer = "Unknown"
|
|
||||||
|
|
||||||
# Packages
|
# Packages
|
||||||
self.hasEnchant = False # The pyenchant package
|
self.hasEnchant = False # The pyenchant package
|
||||||
@@ -321,6 +318,11 @@ class Config:
|
|||||||
self.errData.append(str(e))
|
self.errData.append(str(e))
|
||||||
self.dataPath = None
|
self.dataPath = None
|
||||||
|
|
||||||
|
# Host and Kernel
|
||||||
|
if self.verQtValue >= 50600:
|
||||||
|
self.hostName = QSysInfo.machineHostName()
|
||||||
|
self.kernelVer = QSysInfo.kernelVersion()
|
||||||
|
|
||||||
# Load recent projects cache
|
# Load recent projects cache
|
||||||
self.loadRecentCache()
|
self.loadRecentCache()
|
||||||
|
|
||||||
@@ -388,25 +390,25 @@ class Config:
|
|||||||
## Sizes
|
## Sizes
|
||||||
cnfSec = "Sizes"
|
cnfSec = "Sizes"
|
||||||
self.winGeometry = self._parseLine(
|
self.winGeometry = self._parseLine(
|
||||||
cnfParse, cnfSec, "geometry", self.CNF_LIST, self.winGeometry
|
cnfParse, cnfSec, "geometry", self.CNF_I_LST, self.winGeometry
|
||||||
)
|
)
|
||||||
self.treeColWidth = self._parseLine(
|
self.treeColWidth = self._parseLine(
|
||||||
cnfParse, cnfSec, "treecols", self.CNF_LIST, self.treeColWidth
|
cnfParse, cnfSec, "treecols", self.CNF_I_LST, self.treeColWidth
|
||||||
)
|
)
|
||||||
self.projColWidth = self._parseLine(
|
self.projColWidth = self._parseLine(
|
||||||
cnfParse, cnfSec, "projcols", self.CNF_LIST, self.projColWidth
|
cnfParse, cnfSec, "projcols", self.CNF_I_LST, self.projColWidth
|
||||||
)
|
)
|
||||||
self.mainPanePos = self._parseLine(
|
self.mainPanePos = self._parseLine(
|
||||||
cnfParse, cnfSec, "mainpane", self.CNF_LIST, self.mainPanePos
|
cnfParse, cnfSec, "mainpane", self.CNF_I_LST, self.mainPanePos
|
||||||
)
|
)
|
||||||
self.docPanePos = self._parseLine(
|
self.docPanePos = self._parseLine(
|
||||||
cnfParse, cnfSec, "docpane", self.CNF_LIST, self.docPanePos
|
cnfParse, cnfSec, "docpane", self.CNF_I_LST, self.docPanePos
|
||||||
)
|
)
|
||||||
self.viewPanePos = self._parseLine(
|
self.viewPanePos = self._parseLine(
|
||||||
cnfParse, cnfSec, "viewpane", self.CNF_LIST, self.viewPanePos
|
cnfParse, cnfSec, "viewpane", self.CNF_I_LST, self.viewPanePos
|
||||||
)
|
)
|
||||||
self.outlnPanePos = self._parseLine(
|
self.outlnPanePos = self._parseLine(
|
||||||
cnfParse, cnfSec, "outlinepane", self.CNF_LIST, self.outlnPanePos
|
cnfParse, cnfSec, "outlinepane", self.CNF_I_LST, self.outlnPanePos
|
||||||
)
|
)
|
||||||
self.isFullScreen = self._parseLine(
|
self.isFullScreen = self._parseLine(
|
||||||
cnfParse, cnfSec, "fullscreen", self.CNF_BOOL, self.isFullScreen
|
cnfParse, cnfSec, "fullscreen", self.CNF_BOOL, self.isFullScreen
|
||||||
@@ -484,10 +486,10 @@ class Config:
|
|||||||
cnfParse, cnfSec, "autoscrollpos", self.CNF_INT, self.autoScrollPos
|
cnfParse, cnfSec, "autoscrollpos", self.CNF_INT, self.autoScrollPos
|
||||||
)
|
)
|
||||||
self.fmtSingleQuotes = self._parseLine(
|
self.fmtSingleQuotes = self._parseLine(
|
||||||
cnfParse, cnfSec, "fmtsinglequote", self.CNF_LIST, self.fmtSingleQuotes
|
cnfParse, cnfSec, "fmtsinglequote", self.CNF_S_LST, self.fmtSingleQuotes
|
||||||
)
|
)
|
||||||
self.fmtDoubleQuotes = self._parseLine(
|
self.fmtDoubleQuotes = self._parseLine(
|
||||||
cnfParse, cnfSec, "fmtdoublequote", self.CNF_LIST, self.fmtDoubleQuotes
|
cnfParse, cnfSec, "fmtdoublequote", self.CNF_S_LST, self.fmtDoubleQuotes
|
||||||
)
|
)
|
||||||
self.spellTool = self._parseLine(
|
self.spellTool = self._parseLine(
|
||||||
cnfParse, cnfSec, "spelltool", self.CNF_STR, self.spellTool
|
cnfParse, cnfSec, "spelltool", self.CNF_STR, self.spellTool
|
||||||
@@ -901,23 +903,28 @@ class Config:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _unpackList(self, inStr, listLen, listDefault, castTo=int):
|
|
||||||
"""Unpack a comma separated string of items into a list.
|
|
||||||
"""
|
|
||||||
inData = inStr.split(",")
|
|
||||||
outData = []
|
|
||||||
for i in range(listLen):
|
|
||||||
try:
|
|
||||||
outData.append(castTo(inData[i]))
|
|
||||||
except Exception:
|
|
||||||
outData.append(listDefault[i])
|
|
||||||
return outData
|
|
||||||
|
|
||||||
def _packList(self, inData):
|
def _packList(self, inData):
|
||||||
"""Pack a list of items into a comma separated string.
|
"""Pack a list of items into a comma-separated string.
|
||||||
"""
|
"""
|
||||||
return ", ".join([str(inVal) for inVal in inData])
|
return ", ".join([str(inVal) for inVal in inData])
|
||||||
|
|
||||||
|
def _unpackList(self, inStr, listDefault, cnfType):
|
||||||
|
"""Unpack a comma-separated string of items into a list.
|
||||||
|
"""
|
||||||
|
inData = inStr.split(",")
|
||||||
|
outData = listDefault.copy()
|
||||||
|
for i in range(min(len(inData), len(listDefault))):
|
||||||
|
try:
|
||||||
|
if cnfType == self.CNF_S_LST:
|
||||||
|
outData[i] = inData[i].strip()
|
||||||
|
elif cnfType == self.CNF_I_LST:
|
||||||
|
outData[i] = int(inData[i].strip())
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return outData
|
||||||
|
|
||||||
def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault):
|
def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault):
|
||||||
"""Parse a line and return the correct datatype.
|
"""Parse a line and return the correct datatype.
|
||||||
"""
|
"""
|
||||||
@@ -930,18 +937,24 @@ class Config:
|
|||||||
return cnfParse.getint(cnfSec, cnfName)
|
return cnfParse.getint(cnfSec, cnfName)
|
||||||
elif cnfType == self.CNF_BOOL:
|
elif cnfType == self.CNF_BOOL:
|
||||||
return cnfParse.getboolean(cnfSec, cnfName)
|
return cnfParse.getboolean(cnfSec, cnfName)
|
||||||
elif cnfType == self.CNF_LIST:
|
elif cnfType == self.CNF_I_LST:
|
||||||
return self._unpackList(
|
return self._unpackList(
|
||||||
cnfParse.get(cnfSec, cnfName), len(cnfDefault), cnfDefault
|
cnfParse.get(cnfSec, cnfName), cnfDefault, self.CNF_I_LST
|
||||||
|
)
|
||||||
|
elif cnfType == self.CNF_S_LST:
|
||||||
|
return self._unpackList(
|
||||||
|
cnfParse.get(cnfSec, cnfName), cnfDefault, self.CNF_S_LST
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error("Failed to load value from config file.")
|
logger.error("Failed to load value from config file.")
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
|
return cnfDefault
|
||||||
|
|
||||||
return cnfDefault
|
return cnfDefault
|
||||||
|
|
||||||
def _checkNone(self, checkVal):
|
def _checkNone(self, checkVal):
|
||||||
"""Convert a string to a none type.
|
"""Return a NoneType if the value correspomds to None, otherwise
|
||||||
|
return the value unchanged.
|
||||||
"""
|
"""
|
||||||
if checkVal is None:
|
if checkVal is None:
|
||||||
return None
|
return None
|
||||||
@@ -961,10 +974,8 @@ class Config:
|
|||||||
self.hasEnchant = False
|
self.hasEnchant = False
|
||||||
logger.debug("Checking package 'pyenchant': Missing")
|
logger.debug("Checking package 'pyenchant': Missing")
|
||||||
|
|
||||||
try:
|
assistPath = shutil.which("assistant")
|
||||||
self.hasAssistant = which("assistant")
|
self.hasAssistant = assistPath is not None
|
||||||
except Exception:
|
|
||||||
self.hasAssistant = False
|
|
||||||
if self.hasAssistant:
|
if self.hasAssistant:
|
||||||
logger.debug("Checking executable 'assistant': Ok")
|
logger.debug("Checking executable 'assistant': Ok")
|
||||||
else:
|
else:
|
||||||
|
|||||||
+3
-8
@@ -135,10 +135,9 @@ class NWErrorMessage(QDialog):
|
|||||||
# END Class NWErrorMessage
|
# END Class NWErrorMessage
|
||||||
|
|
||||||
|
|
||||||
def exceptionHandler(exType, exValue, exTrace, testMode=False):
|
def exceptionHandler(exType, exValue, exTrace):
|
||||||
"""Function to catch unhandled global exceptions.
|
"""Function to catch unhandled global exceptions.
|
||||||
"""
|
"""
|
||||||
import nw
|
|
||||||
import logging
|
import logging
|
||||||
from traceback import print_tb
|
from traceback import print_tb
|
||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import qApp
|
||||||
@@ -160,8 +159,7 @@ def exceptionHandler(exType, exValue, exTrace, testMode=False):
|
|||||||
|
|
||||||
errMsg = NWErrorMessage(nwGUI)
|
errMsg = NWErrorMessage(nwGUI)
|
||||||
errMsg.setMessage(exType, exValue, exTrace)
|
errMsg.setMessage(exType, exValue, exTrace)
|
||||||
if nw.CONFIG.showGUI:
|
errMsg.exec_()
|
||||||
errMsg.exec_()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Try a controlled shudown
|
# Try a controlled shudown
|
||||||
@@ -173,10 +171,7 @@ def exceptionHandler(exType, exValue, exTrace, testMode=False):
|
|||||||
logger.critical("Could not close the project before exiting")
|
logger.critical("Could not close the project before exiting")
|
||||||
logger.critical(str(e))
|
logger.critical(str(e))
|
||||||
|
|
||||||
if testMode:
|
qApp.exit(1)
|
||||||
return errMsg.msgBody.toPlainText()
|
|
||||||
else:
|
|
||||||
qApp.exit(1)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.critical(str(e))
|
logger.critical(str(e))
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[pytest]
|
[pytest]
|
||||||
markers =
|
markers =
|
||||||
error: Test various error handling scenarios
|
base: Base functionality tests
|
||||||
core: Core functionality tests
|
core: Core functionality tests
|
||||||
gui: Qt5 GUI tests
|
gui: Qt5 GUI tests
|
||||||
serial
|
serial
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ The commands for the respective test categories are listed below.
|
|||||||
|
|
||||||
| Type | Test Target | Source File(s) | Marker | Filter |
|
| Type | Test Target | Source File(s) | Marker | Filter |
|
||||||
| :--- | :----------------- | :-------------------- | :-------- | :-------------------- |
|
| :--- | :----------------- | :-------------------- | :-------- | :-------------------- |
|
||||||
|
| Unit | Main function | nw/\_\_init\_\_.py | `-m base` | `-k testBaseInit` |
|
||||||
|
| Unit | Common functions | nw/common.py | `-m base` | `-k testBaseCommon` |
|
||||||
|
| Unit | Config class | nw/config.py | `-m base` | `-k testBaseConfig` |
|
||||||
|
| Unit | Error handlers | nw/error.py | `-m base` | `-k testBaseError` |
|
||||||
| Unit | Core functions | nw/core/tools.py | `-m core` | `-k testCoreTools` |
|
| Unit | Core functions | nw/core/tools.py | `-m core` | `-k testCoreTools` |
|
||||||
| Unit | NWDoc class | nw/core/document.py | `-m core` | `-k testCoreDocument` |
|
| Unit | NWDoc class | nw/core/document.py | `-m core` | `-k testCoreDocument` |
|
||||||
| Unit | NWIndex class | nw/core/index.py | `-m core` | `-k testCoreIndex` |
|
| Unit | NWIndex class | nw/core/index.py | `-m core` | `-k testCoreIndex` |
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ def fncDir(tmpDir):
|
|||||||
def tmpConf(tmpDir):
|
def tmpConf(tmpDir):
|
||||||
"""Create a temporary novelWriter configuration object.
|
"""Create a temporary novelWriter configuration object.
|
||||||
"""
|
"""
|
||||||
|
confFile = os.path.join(tmpDir, "novelwriter.conf")
|
||||||
|
if os.path.isfile(confFile):
|
||||||
|
os.unlink(confFile)
|
||||||
theConf = Config()
|
theConf = Config()
|
||||||
theConf.initConfig(tmpDir, tmpDir)
|
theConf.initConfig(tmpDir, tmpDir)
|
||||||
theConf.setLastPath("")
|
theConf.setLastPath("")
|
||||||
|
|||||||
+13
-4
@@ -13,7 +13,7 @@ class DummyMain():
|
|||||||
self.hasProject = True
|
self.hasProject = True
|
||||||
self.theIndex = None
|
self.theIndex = None
|
||||||
self.theProject = None
|
self.theProject = None
|
||||||
self.statusBar = StatusBar()
|
self.statusBar = DummyStatusBar()
|
||||||
|
|
||||||
# Test Variables
|
# Test Variables
|
||||||
self.askResponse = True
|
self.askResponse = True
|
||||||
@@ -42,6 +42,12 @@ class DummyMain():
|
|||||||
def rebuildIndex(self):
|
def rebuildIndex(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def closeMain(self):
|
||||||
|
return "closeMain"
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
return "close"
|
||||||
|
|
||||||
# Test Functions
|
# Test Functions
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
@@ -52,9 +58,9 @@ class DummyMain():
|
|||||||
self.lastAlert = ""
|
self.lastAlert = ""
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class GuiMain
|
# END Class DummyMain
|
||||||
|
|
||||||
class StatusBar():
|
class DummyStatusBar():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
return
|
return
|
||||||
@@ -62,7 +68,7 @@ class StatusBar():
|
|||||||
def setStatus(self, theText):
|
def setStatus(self, theText):
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class StatusBar
|
# END Class DummyStatusBar
|
||||||
|
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
# Error Functions
|
# Error Functions
|
||||||
@@ -71,3 +77,6 @@ class StatusBar():
|
|||||||
|
|
||||||
def causeOSError(*args, **kwargs):
|
def causeOSError(*args, **kwargs):
|
||||||
raise OSError
|
raise OSError
|
||||||
|
|
||||||
|
def causeException(*args, **kwargs):
|
||||||
|
raise Exception
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""novelWriter Common Class Tester
|
"""novelWriter Common Functions Tester
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
@@ -11,8 +11,10 @@ from nw.common import (
|
|||||||
)
|
)
|
||||||
from tools import cmpList
|
from tools import cmpList
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.base
|
||||||
def testCheckString():
|
def testBaseCommon_CheckString():
|
||||||
|
"""Test the checkString function.
|
||||||
|
"""
|
||||||
assert checkString(None, "NotNone", True) is None
|
assert checkString(None, "NotNone", True) is None
|
||||||
assert checkString("None", "NotNone", True) is None
|
assert checkString("None", "NotNone", True) is None
|
||||||
assert checkString("None", "NotNone", False) == "None"
|
assert checkString("None", "NotNone", False) == "None"
|
||||||
@@ -21,8 +23,12 @@ def testCheckString():
|
|||||||
assert checkString(1.0, "NotNone", False) == "NotNone"
|
assert checkString(1.0, "NotNone", False) == "NotNone"
|
||||||
assert checkString(True, "NotNone", False) == "NotNone"
|
assert checkString(True, "NotNone", False) == "NotNone"
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_CheckString
|
||||||
def testCheckInt():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_CheckInt():
|
||||||
|
"""Test the checkInt function.
|
||||||
|
"""
|
||||||
assert checkInt(None, 3, True) is None
|
assert checkInt(None, 3, True) is None
|
||||||
assert checkInt("None", 3, True) is None
|
assert checkInt("None", 3, True) is None
|
||||||
assert checkInt(None, 3, False) == 3
|
assert checkInt(None, 3, False) == 3
|
||||||
@@ -30,8 +36,12 @@ def testCheckInt():
|
|||||||
assert checkInt(1.0, 3, False) == 1
|
assert checkInt(1.0, 3, False) == 1
|
||||||
assert checkInt(True, 3, False) == 1
|
assert checkInt(True, 3, False) == 1
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_CheckInt
|
||||||
def testCheckBool():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_CheckBool():
|
||||||
|
"""Test the checkBool function.
|
||||||
|
"""
|
||||||
assert checkBool(None, 3, True) is None
|
assert checkBool(None, 3, True) is None
|
||||||
assert checkBool("None", 3, True) is None
|
assert checkBool("None", 3, True) is None
|
||||||
assert checkBool("True", False, False)
|
assert checkBool("True", False, False)
|
||||||
@@ -44,8 +54,12 @@ def testCheckBool():
|
|||||||
assert checkBool(1.0, None, False) is None
|
assert checkBool(1.0, None, False) is None
|
||||||
assert checkBool(2.0, None, False) is None
|
assert checkBool(2.0, None, False) is None
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_CheckBool
|
||||||
def testCheckHandle():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_CheckHandle():
|
||||||
|
"""Test the checkHandle function.
|
||||||
|
"""
|
||||||
assert checkHandle("None", 1, True) is None
|
assert checkHandle("None", 1, True) is None
|
||||||
assert checkHandle("None", 1, False) == 1
|
assert checkHandle("None", 1, False) == 1
|
||||||
assert checkHandle(None, 1, True) is None
|
assert checkHandle(None, 1, True) is None
|
||||||
@@ -53,8 +67,12 @@ def testCheckHandle():
|
|||||||
assert checkHandle("47666c91c7ccf", None, False) == "47666c91c7ccf"
|
assert checkHandle("47666c91c7ccf", None, False) == "47666c91c7ccf"
|
||||||
assert checkHandle("h7666c91c7ccf", None, False) is None
|
assert checkHandle("h7666c91c7ccf", None, False) is None
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_CheckHandle
|
||||||
def testColRange():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_ColRange():
|
||||||
|
"""Test the colRange function.
|
||||||
|
"""
|
||||||
assert colRange([0, 0], [0, 0], 0) is None
|
assert colRange([0, 0], [0, 0], 0) is None
|
||||||
assert cmpList(
|
assert cmpList(
|
||||||
colRange([200, 50, 0], [50, 200, 0], 1),
|
colRange([200, 50, 0], [50, 200, 0], 1),
|
||||||
@@ -77,14 +95,22 @@ def testColRange():
|
|||||||
[[200, 50, 0], [162, 87, 0], [124, 124, 0], [86, 161, 0], [50, 200, 0]]
|
[[200, 50, 0], [162, 87, 0], [124, 124, 0], [86, 161, 0], [50, 200, 0]]
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_ColRange
|
||||||
def testFormatTimeStamp():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_FormatTimeStamp():
|
||||||
|
"""Test the formatTimeStamp function.
|
||||||
|
"""
|
||||||
tTime = time.mktime(time.gmtime(0))
|
tTime = time.mktime(time.gmtime(0))
|
||||||
assert formatTimeStamp(tTime, False) == "1970-01-01 00:00:00"
|
assert formatTimeStamp(tTime, False) == "1970-01-01 00:00:00"
|
||||||
assert formatTimeStamp(tTime, True) == "1970-01-01 00.00.00"
|
assert formatTimeStamp(tTime, True) == "1970-01-01 00.00.00"
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_FormatTimeStamp
|
||||||
def testFormatTime():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_FormatTime():
|
||||||
|
"""Test the formatTime function.
|
||||||
|
"""
|
||||||
assert formatTime("1") == "ERROR"
|
assert formatTime("1") == "ERROR"
|
||||||
assert formatTime(1.0) == "ERROR"
|
assert formatTime(1.0) == "ERROR"
|
||||||
assert formatTime(1) == "00:00:01"
|
assert formatTime(1) == "00:00:01"
|
||||||
@@ -101,8 +127,12 @@ def testFormatTime():
|
|||||||
assert formatTime(86400) == "1-00:00:00"
|
assert formatTime(86400) == "1-00:00:00"
|
||||||
assert formatTime(360000) == "4-04:00:00"
|
assert formatTime(360000) == "4-04:00:00"
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_FormatTime
|
||||||
def testFormatInt():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_FormatInt():
|
||||||
|
"""Test the formatInt function.
|
||||||
|
"""
|
||||||
assert formatInt(1000) == "1000"
|
assert formatInt(1000) == "1000"
|
||||||
assert formatInt(1234) == "1.23\u2009k"
|
assert formatInt(1234) == "1.23\u2009k"
|
||||||
assert formatInt(12345) == "12.3\u2009k"
|
assert formatInt(12345) == "12.3\u2009k"
|
||||||
@@ -112,8 +142,12 @@ def testFormatInt():
|
|||||||
assert formatInt(123456789) == "123\u2009M"
|
assert formatInt(123456789) == "123\u2009M"
|
||||||
assert formatInt(1234567890) == "1.23\u2009G"
|
assert formatInt(1234567890) == "1.23\u2009G"
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_FormatInt
|
||||||
def testTransferCase():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_TransferCase():
|
||||||
|
"""Test the transferCase function.
|
||||||
|
"""
|
||||||
assert transferCase(1, "TaRgEt") == "TaRgEt"
|
assert transferCase(1, "TaRgEt") == "TaRgEt"
|
||||||
assert transferCase("source", 1) == 1
|
assert transferCase("source", 1) == 1
|
||||||
assert transferCase("", "TaRgEt") == "TaRgEt"
|
assert transferCase("", "TaRgEt") == "TaRgEt"
|
||||||
@@ -122,8 +156,12 @@ def testTransferCase():
|
|||||||
assert transferCase("SOURCE", "target") == "TARGET"
|
assert transferCase("SOURCE", "target") == "TARGET"
|
||||||
assert transferCase("source", "TARGET") == "target"
|
assert transferCase("source", "TARGET") == "target"
|
||||||
|
|
||||||
@pytest.mark.core
|
# END Test testBaseCommon_TransferCase
|
||||||
def testFuzzyTime():
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseCommon_FuzzyTime():
|
||||||
|
"""Test the fuzzyTime function.
|
||||||
|
"""
|
||||||
assert fuzzyTime(-1) == "in the future"
|
assert fuzzyTime(-1) == "in the future"
|
||||||
assert fuzzyTime(0) == "just now"
|
assert fuzzyTime(0) == "just now"
|
||||||
assert fuzzyTime(29) == "just now"
|
assert fuzzyTime(29) == "just now"
|
||||||
@@ -152,3 +190,5 @@ def testFuzzyTime():
|
|||||||
assert fuzzyTime(29808000) == "a year ago"
|
assert fuzzyTime(29808000) == "a year ago"
|
||||||
assert fuzzyTime(47336399) == "a year ago"
|
assert fuzzyTime(47336399) == "a year ago"
|
||||||
assert fuzzyTime(47336400) == "2 years ago"
|
assert fuzzyTime(47336400) == "2 years ago"
|
||||||
|
|
||||||
|
# END Test testBaseCommon_FuzzyTime
|
||||||
@@ -0,0 +1,505 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""novelWriter Config Class Tester
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
from shutil import copyfile
|
||||||
|
|
||||||
|
from dummy import causeOSError
|
||||||
|
from tools import cmpFiles
|
||||||
|
|
||||||
|
from nw.config import Config
|
||||||
|
from nw.constants import nwConst, nwFiles
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseConfig_Constructor(monkeypatch):
|
||||||
|
"""Test config contructor.
|
||||||
|
"""
|
||||||
|
# Linux
|
||||||
|
monkeypatch.setattr("sys.platform", "linux")
|
||||||
|
tstConf = Config()
|
||||||
|
assert tstConf.osLinux is True
|
||||||
|
assert tstConf.osDarwin is False
|
||||||
|
assert tstConf.osWindows is False
|
||||||
|
assert tstConf.osUnknown is False
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
monkeypatch.setattr("sys.platform", "darwin")
|
||||||
|
tstConf = Config()
|
||||||
|
assert tstConf.osLinux is False
|
||||||
|
assert tstConf.osDarwin is True
|
||||||
|
assert tstConf.osWindows is False
|
||||||
|
assert tstConf.osUnknown is False
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
monkeypatch.setattr("sys.platform", "win32")
|
||||||
|
tstConf = Config()
|
||||||
|
assert tstConf.osLinux is False
|
||||||
|
assert tstConf.osDarwin is False
|
||||||
|
assert tstConf.osWindows is True
|
||||||
|
assert tstConf.osUnknown is False
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Cygwin
|
||||||
|
monkeypatch.setattr("sys.platform", "cygwin")
|
||||||
|
tstConf = Config()
|
||||||
|
assert tstConf.osLinux is False
|
||||||
|
assert tstConf.osDarwin is False
|
||||||
|
assert tstConf.osWindows is True
|
||||||
|
assert tstConf.osUnknown is False
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Other
|
||||||
|
monkeypatch.setattr("sys.platform", "some_ther_os")
|
||||||
|
tstConf = Config()
|
||||||
|
assert tstConf.osLinux is False
|
||||||
|
assert tstConf.osDarwin is False
|
||||||
|
assert tstConf.osWindows is False
|
||||||
|
assert tstConf.osUnknown is True
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# END Test testBaseConfig_Constructor
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir):
|
||||||
|
"""Test config intialisation.
|
||||||
|
"""
|
||||||
|
tstConf = Config()
|
||||||
|
|
||||||
|
confFile = os.path.join(tmpDir, "novelwriter.conf")
|
||||||
|
testFile = os.path.join(outDir, "baseConfig_novelwriter.conf")
|
||||||
|
compFile = os.path.join(refDir, "baseConfig_novelwriter.conf")
|
||||||
|
|
||||||
|
# Make sure we don't have any old conf file
|
||||||
|
if os.path.isfile(confFile):
|
||||||
|
os.unlink(confFile)
|
||||||
|
|
||||||
|
# Let the config class figure out the path
|
||||||
|
monkeypatch.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *args: fncDir)
|
||||||
|
tstConf.verQtValue = 50600
|
||||||
|
tstConf.initConfig()
|
||||||
|
assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle)
|
||||||
|
assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle)
|
||||||
|
assert not os.path.isfile(confFile)
|
||||||
|
tstConf.verQtValue = 50000
|
||||||
|
tstConf.initConfig()
|
||||||
|
assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle)
|
||||||
|
assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle)
|
||||||
|
assert not os.path.isfile(confFile)
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Fail to make folders
|
||||||
|
monkeypatch.setattr("os.mkdir", causeOSError)
|
||||||
|
|
||||||
|
tstConfDir = os.path.join(fncDir, "test_conf")
|
||||||
|
tstConf.initConfig(confPath=tstConfDir, dataPath=tmpDir)
|
||||||
|
assert tstConf.confPath is None
|
||||||
|
assert tstConf.dataPath == tmpDir
|
||||||
|
assert not os.path.isfile(confFile)
|
||||||
|
|
||||||
|
tstDataDir = os.path.join(fncDir, "test_data")
|
||||||
|
tstConf.initConfig(confPath=tmpDir, dataPath=tstDataDir)
|
||||||
|
assert tstConf.confPath == tmpDir
|
||||||
|
assert tstConf.dataPath is None
|
||||||
|
assert os.path.isfile(confFile)
|
||||||
|
os.unlink(confFile)
|
||||||
|
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Test load/save with no path
|
||||||
|
tstConf.confPath = None
|
||||||
|
assert not tstConf.loadConfig()
|
||||||
|
assert not tstConf.saveConfig()
|
||||||
|
|
||||||
|
# Run again and set the paths directly and correctly
|
||||||
|
# This should create a config file as well
|
||||||
|
monkeypatch.setattr("os.path.expanduser", lambda *args: "")
|
||||||
|
tstConf.spellTool = nwConst.SP_INTERNAL
|
||||||
|
tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
|
||||||
|
assert tstConf.confPath == tmpDir
|
||||||
|
assert tstConf.dataPath == tmpDir
|
||||||
|
assert os.path.isfile(confFile)
|
||||||
|
|
||||||
|
copyfile(confFile, testFile)
|
||||||
|
assert cmpFiles(testFile, compFile, [2, 9])
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Load and save with OSError
|
||||||
|
monkeypatch.setattr("builtins.open", causeOSError)
|
||||||
|
|
||||||
|
assert not tstConf.loadConfig()
|
||||||
|
assert tstConf.hasError is True
|
||||||
|
assert tstConf.errData != []
|
||||||
|
assert tstConf.getErrData().startswith("Could not")
|
||||||
|
assert tstConf.hasError is False
|
||||||
|
assert tstConf.errData == []
|
||||||
|
|
||||||
|
assert not tstConf.saveConfig()
|
||||||
|
assert tstConf.hasError is True
|
||||||
|
assert tstConf.errData != []
|
||||||
|
assert tstConf.getErrData().startswith("Could not")
|
||||||
|
assert tstConf.hasError is False
|
||||||
|
assert tstConf.errData == []
|
||||||
|
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
assert tstConf.loadConfig()
|
||||||
|
assert tstConf.saveConfig()
|
||||||
|
|
||||||
|
copyfile(confFile, testFile)
|
||||||
|
assert cmpFiles(testFile, compFile, [2, 9])
|
||||||
|
|
||||||
|
# END Test testBaseConfig_Init
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir):
|
||||||
|
"""Test recent cache file.
|
||||||
|
"""
|
||||||
|
# Check failing
|
||||||
|
tmpConf.dataPath = None
|
||||||
|
assert not tmpConf.loadRecentCache()
|
||||||
|
assert not tmpConf.saveRecentCache()
|
||||||
|
tmpConf.dataPath = tmpDir
|
||||||
|
|
||||||
|
# Add a couple of values
|
||||||
|
pathOne = os.path.join(fncDir, "projPathOne", nwFiles.PROJ_FILE)
|
||||||
|
pathTwo = os.path.join(fncDir, "projPathTwo", nwFiles.PROJ_FILE)
|
||||||
|
assert tmpConf.updateRecentCache(pathOne, "Proj One", 100, 1600002000)
|
||||||
|
assert tmpConf.updateRecentCache(pathTwo, "Proj Two", 200, 1600005600)
|
||||||
|
assert tmpConf.recentProj == {
|
||||||
|
pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
|
||||||
|
pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fail to Save
|
||||||
|
monkeypatch.setattr("builtins.open", causeOSError)
|
||||||
|
assert not tmpConf.saveRecentCache()
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Save Proper
|
||||||
|
cacheFile = os.path.join(tmpDir, nwFiles.RECENT_FILE)
|
||||||
|
assert tmpConf.saveRecentCache()
|
||||||
|
assert tmpConf.saveRecentCache()
|
||||||
|
assert os.path.isfile(cacheFile)
|
||||||
|
|
||||||
|
# Fail to Load
|
||||||
|
monkeypatch.setattr("builtins.open", causeOSError)
|
||||||
|
tmpConf.recentProj = {}
|
||||||
|
assert not tmpConf.loadRecentCache()
|
||||||
|
assert tmpConf.recentProj == {}
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Load Proper
|
||||||
|
tmpConf.recentProj = {}
|
||||||
|
assert tmpConf.loadRecentCache()
|
||||||
|
assert tmpConf.recentProj == {
|
||||||
|
pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
|
||||||
|
pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove Non-Existent Entry
|
||||||
|
assert not tmpConf.removeFromRecentCache("dummy")
|
||||||
|
assert tmpConf.recentProj == {
|
||||||
|
pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
|
||||||
|
pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove Second Entry
|
||||||
|
assert tmpConf.removeFromRecentCache(pathTwo)
|
||||||
|
assert tmpConf.recentProj == {
|
||||||
|
pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
|
||||||
|
}
|
||||||
|
|
||||||
|
# END Test testBaseConfig_RecentCache
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseConfig_SetPath(tmpConf, tmpDir):
|
||||||
|
"""Test path setters.
|
||||||
|
"""
|
||||||
|
# Conf Path
|
||||||
|
assert tmpConf.setConfPath(None)
|
||||||
|
assert not tmpConf.setConfPath(os.path.join("somewhere", "over", "the", "rainbow"))
|
||||||
|
assert tmpConf.setConfPath(os.path.join(tmpDir, "novelwriter.conf"))
|
||||||
|
assert tmpConf.confPath == tmpDir
|
||||||
|
assert tmpConf.confFile == "novelwriter.conf"
|
||||||
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
|
# Data Path
|
||||||
|
assert tmpConf.setDataPath(None)
|
||||||
|
assert not tmpConf.setDataPath(os.path.join("somewhere", "over", "the", "rainbow"))
|
||||||
|
assert tmpConf.setDataPath(tmpDir)
|
||||||
|
assert tmpConf.dataPath == tmpDir
|
||||||
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
|
# Last Path
|
||||||
|
assert tmpConf.setLastPath(None)
|
||||||
|
assert tmpConf.lastPath == ""
|
||||||
|
|
||||||
|
assert tmpConf.setLastPath(os.path.join(tmpDir, "file.tmp"))
|
||||||
|
assert tmpConf.lastPath == tmpDir
|
||||||
|
|
||||||
|
assert tmpConf.setLastPath("")
|
||||||
|
assert tmpConf.lastPath == ""
|
||||||
|
|
||||||
|
# END Test testBaseConfig_SetPath
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
|
||||||
|
"""Set various sizes and positions
|
||||||
|
"""
|
||||||
|
confFile = os.path.join(tmpDir, "novelwriter.conf")
|
||||||
|
testFile = os.path.join(outDir, "baseConfig_novelwriter.conf")
|
||||||
|
compFile = os.path.join(refDir, "baseConfig_novelwriter.conf")
|
||||||
|
|
||||||
|
# GUI Scaling
|
||||||
|
# ===========
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.pxInt(10) == 10
|
||||||
|
assert tmpConf.pxInt(13) == 13
|
||||||
|
assert tmpConf.rpxInt(10) == 10
|
||||||
|
assert tmpConf.rpxInt(13) == 13
|
||||||
|
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.pxInt(10) == 20
|
||||||
|
assert tmpConf.pxInt(13) == 26
|
||||||
|
assert tmpConf.rpxInt(10) == 5
|
||||||
|
assert tmpConf.rpxInt(13) == 6
|
||||||
|
|
||||||
|
# Setter + Getter Combos
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
# Window Size
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setWinSize(1205, 655)
|
||||||
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.setWinSize(70, 70)
|
||||||
|
assert tmpConf.getWinSize() == [70, 70]
|
||||||
|
assert tmpConf.winGeometry == [35, 35]
|
||||||
|
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setWinSize(70, 70)
|
||||||
|
assert tmpConf.getWinSize() == [70, 70]
|
||||||
|
assert tmpConf.winGeometry == [70, 70]
|
||||||
|
|
||||||
|
assert tmpConf.setWinSize(1200, 650)
|
||||||
|
|
||||||
|
# Project Tree Columns
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.setTreeColWidths([10, 20, 25])
|
||||||
|
assert tmpConf.getTreeColWidths() == [10, 20, 24]
|
||||||
|
assert tmpConf.treeColWidth == [5, 10, 12]
|
||||||
|
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setTreeColWidths([10, 20, 25])
|
||||||
|
assert tmpConf.getTreeColWidths() == [10, 20, 25]
|
||||||
|
assert tmpConf.treeColWidth == [10, 20, 25]
|
||||||
|
|
||||||
|
assert tmpConf.setTreeColWidths([200, 50, 30])
|
||||||
|
|
||||||
|
# Project Settings Tree Columns
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.setProjColWidths([10, 20, 30])
|
||||||
|
assert tmpConf.getProjColWidths() == [10, 20, 30]
|
||||||
|
assert tmpConf.projColWidth == [5, 10, 15]
|
||||||
|
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setProjColWidths([10, 20, 30])
|
||||||
|
assert tmpConf.getProjColWidths() == [10, 20, 30]
|
||||||
|
assert tmpConf.projColWidth == [10, 20, 30]
|
||||||
|
|
||||||
|
assert tmpConf.setProjColWidths([200, 60, 140])
|
||||||
|
|
||||||
|
# Main Pane Splitter
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.setMainPanePos([200, 700])
|
||||||
|
assert tmpConf.getMainPanePos() == [200, 700]
|
||||||
|
assert tmpConf.mainPanePos == [100, 350]
|
||||||
|
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setMainPanePos([200, 700])
|
||||||
|
assert tmpConf.getMainPanePos() == [200, 700]
|
||||||
|
assert tmpConf.mainPanePos == [200, 700]
|
||||||
|
|
||||||
|
assert tmpConf.setMainPanePos([300, 800])
|
||||||
|
|
||||||
|
# Doc Pane Splitter
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.setDocPanePos([300, 300])
|
||||||
|
assert tmpConf.getDocPanePos() == [300, 300]
|
||||||
|
assert tmpConf.docPanePos == [150, 150]
|
||||||
|
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setDocPanePos([300, 300])
|
||||||
|
assert tmpConf.getDocPanePos() == [300, 300]
|
||||||
|
assert tmpConf.docPanePos == [300, 300]
|
||||||
|
|
||||||
|
assert tmpConf.setDocPanePos([400, 400])
|
||||||
|
|
||||||
|
# View Pane Splitter
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.setViewPanePos([400, 250])
|
||||||
|
assert tmpConf.getViewPanePos() == [400, 250]
|
||||||
|
assert tmpConf.viewPanePos == [200, 125]
|
||||||
|
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setViewPanePos([400, 250])
|
||||||
|
assert tmpConf.getViewPanePos() == [400, 250]
|
||||||
|
assert tmpConf.viewPanePos == [400, 250]
|
||||||
|
|
||||||
|
assert tmpConf.setViewPanePos([500, 150])
|
||||||
|
|
||||||
|
# Outline Pane Splitter
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.setOutlinePanePos([400, 250])
|
||||||
|
assert tmpConf.getOutlinePanePos() == [400, 250]
|
||||||
|
assert tmpConf.outlnPanePos == [200, 125]
|
||||||
|
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.setOutlinePanePos([400, 250])
|
||||||
|
assert tmpConf.getOutlinePanePos() == [400, 250]
|
||||||
|
assert tmpConf.outlnPanePos == [400, 250]
|
||||||
|
|
||||||
|
assert tmpConf.setOutlinePanePos([500, 150])
|
||||||
|
|
||||||
|
# Getters Only
|
||||||
|
# ============
|
||||||
|
tmpConf.guiScale = 1.0
|
||||||
|
assert tmpConf.getTextWidth() == 600
|
||||||
|
assert tmpConf.getTextMargin() == 40
|
||||||
|
assert tmpConf.getTabWidth() == 40
|
||||||
|
assert tmpConf.getFocusWidth() == 800
|
||||||
|
|
||||||
|
tmpConf.guiScale = 2.0
|
||||||
|
assert tmpConf.getTextWidth() == 1200
|
||||||
|
assert tmpConf.getTextMargin() == 80
|
||||||
|
assert tmpConf.getTabWidth() == 80
|
||||||
|
assert tmpConf.getFocusWidth() == 1600
|
||||||
|
|
||||||
|
# Flag Setters
|
||||||
|
# ============
|
||||||
|
assert not tmpConf.setShowRefPanel(False)
|
||||||
|
assert not tmpConf.showRefPanel
|
||||||
|
assert tmpConf.setShowRefPanel(True)
|
||||||
|
|
||||||
|
assert not tmpConf.setViewComments(False)
|
||||||
|
assert not tmpConf.viewComments
|
||||||
|
assert tmpConf.setViewComments(True)
|
||||||
|
|
||||||
|
assert not tmpConf.setViewSynopsis(False)
|
||||||
|
assert not tmpConf.viewSynopsis
|
||||||
|
assert tmpConf.setViewSynopsis(True)
|
||||||
|
|
||||||
|
# Check Final File
|
||||||
|
# ================
|
||||||
|
|
||||||
|
assert tmpConf.confChanged
|
||||||
|
assert tmpConf.saveConfig()
|
||||||
|
assert not tmpConf.confChanged
|
||||||
|
|
||||||
|
copyfile(confFile, testFile)
|
||||||
|
assert cmpFiles(testFile, compFile, [2, 9])
|
||||||
|
|
||||||
|
# END Test testBaseConfig_SettersGetters
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseConfig_Internal(monkeypatch, tmpConf):
|
||||||
|
"""Check internal functions.
|
||||||
|
"""
|
||||||
|
# Function _packList
|
||||||
|
assert tmpConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False"
|
||||||
|
|
||||||
|
# Function _unpackList
|
||||||
|
assert tmpConf._unpackList("1, 2, 3", [0, 0, 0], tmpConf.CNF_I_LST) == [1, 2, 3]
|
||||||
|
assert tmpConf._unpackList("1, 2 ", [0, 0, 0], tmpConf.CNF_I_LST) == [1, 2, 0]
|
||||||
|
assert tmpConf._unpackList("A, B, C", [0, 0, 0], tmpConf.CNF_I_LST) == [0, 0, 0]
|
||||||
|
assert tmpConf._unpackList("1, 2, 3", ["X", "Y", "Z"], tmpConf.CNF_S_LST) == ["1", "2", "3"]
|
||||||
|
assert tmpConf._unpackList("A, B ", ["X", "Y", "Z"], tmpConf.CNF_S_LST) == ["A", "B", "Z"]
|
||||||
|
assert tmpConf._unpackList("A, B, C", ["X", "Y", "Z"], tmpConf.CNF_S_LST) == ["A", "B", "C"]
|
||||||
|
assert tmpConf._unpackList("A, B, C", ["X", "Y", "Z"], tmpConf.CNF_STR) == ["X", "Y", "Z"]
|
||||||
|
|
||||||
|
# Function _parseLine
|
||||||
|
cnfParse = configparser.ConfigParser()
|
||||||
|
cnfParse.read_string(
|
||||||
|
"[Main]\n"
|
||||||
|
"val_string = dummy\n"
|
||||||
|
"val_int = 123\n"
|
||||||
|
"val_bool = True\n"
|
||||||
|
"val_list_string = A, B, C\n"
|
||||||
|
"val_list_int = 1, 2, 3\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "val_string", tmpConf.CNF_STR, "default"
|
||||||
|
) == "dummy"
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "nope", tmpConf.CNF_STR, "default"
|
||||||
|
) == "default"
|
||||||
|
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "val_int", tmpConf.CNF_INT, "0"
|
||||||
|
) == 123
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "nope", tmpConf.CNF_INT, 0
|
||||||
|
) == 0
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "val_string", tmpConf.CNF_INT, 0
|
||||||
|
) == 0
|
||||||
|
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "val_bool", tmpConf.CNF_BOOL, False
|
||||||
|
) is True
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "nope", tmpConf.CNF_BOOL, False
|
||||||
|
) is False
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "val_string", tmpConf.CNF_BOOL, False
|
||||||
|
) is False
|
||||||
|
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "val_list_string", tmpConf.CNF_S_LST, ["W", "X", "Y", "Z"]
|
||||||
|
) == ["A", "B", "C", "Z"]
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "nope", tmpConf.CNF_S_LST, ["W", "X", "Y", "Z"]
|
||||||
|
) == ["W", "X", "Y", "Z"]
|
||||||
|
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "val_list_int", tmpConf.CNF_I_LST, [6, 7, 8, 9]
|
||||||
|
) == [1, 2, 3, 9]
|
||||||
|
assert tmpConf._parseLine(
|
||||||
|
cnfParse, "Main", "nope", tmpConf.CNF_S_LST, [6, 7, 8, 9]
|
||||||
|
) == [6, 7, 8, 9]
|
||||||
|
|
||||||
|
# Function _checkNone
|
||||||
|
assert tmpConf._checkNone(None) is None
|
||||||
|
assert tmpConf._checkNone("None") is None
|
||||||
|
assert tmpConf._checkNone("stuff") == "stuff"
|
||||||
|
|
||||||
|
# Function _checkOptionalPackages
|
||||||
|
# (Assumes enchant package exists ans is importable)
|
||||||
|
tmpConf._checkOptionalPackages()
|
||||||
|
assert tmpConf.hasEnchant is True
|
||||||
|
|
||||||
|
monkeypatch.setitem(sys.modules, "enchant", None)
|
||||||
|
tmpConf._checkOptionalPackages()
|
||||||
|
assert tmpConf.hasEnchant is False
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
monkeypatch.setattr("shutil.which", lambda *args: "dummy")
|
||||||
|
tmpConf._checkOptionalPackages()
|
||||||
|
assert tmpConf.hasAssistant is True
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
monkeypatch.setattr("shutil.which", lambda *args: None)
|
||||||
|
tmpConf._checkOptionalPackages()
|
||||||
|
assert tmpConf.hasAssistant is False
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# END Test testBaseConfig_Internal
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""novelWriter Error Tester
|
||||||
|
"""
|
||||||
|
|
||||||
|
import nw
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import qApp
|
||||||
|
|
||||||
|
from dummy import causeException
|
||||||
|
|
||||||
|
from nw.error import NWErrorMessage, exceptionHandler
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseError_Dialog(qtbot, monkeypatch, fncDir, tmpDir):
|
||||||
|
"""Test the error dialog.
|
||||||
|
"""
|
||||||
|
qApp.closeAllWindows()
|
||||||
|
nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir])
|
||||||
|
qtbot.addWidget(nwGUI)
|
||||||
|
nwGUI.show()
|
||||||
|
qtbot.waitForWindowShown(nwGUI)
|
||||||
|
|
||||||
|
nwErr = NWErrorMessage(nwGUI)
|
||||||
|
qtbot.addWidget(nwErr)
|
||||||
|
nwErr.show()
|
||||||
|
|
||||||
|
# Invalid Error Message
|
||||||
|
nwErr.setMessage(Exception, "Faulty Error", 123)
|
||||||
|
assert nwErr.msgBody.toPlainText() == "Failed to generate error report ..."
|
||||||
|
|
||||||
|
# Valid Error Message
|
||||||
|
monkeypatch.setattr("PyQt5.QtCore.QSysInfo.kernelVersion", lambda: "1.2.3")
|
||||||
|
nwErr.setMessage(Exception, "Fine Error", None)
|
||||||
|
theMessage = nwErr.msgBody.toPlainText()
|
||||||
|
assert theMessage
|
||||||
|
assert "Fine Error" in theMessage
|
||||||
|
assert "Exception" in theMessage
|
||||||
|
assert "(1.2.3)" in theMessage
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# No kernel version retrieved
|
||||||
|
monkeypatch.setattr("PyQt5.QtCore.QSysInfo.kernelVersion", causeException)
|
||||||
|
nwErr.setMessage(Exception, "Almost Fine Error", None)
|
||||||
|
theMessage = nwErr.msgBody.toPlainText()
|
||||||
|
assert theMessage
|
||||||
|
assert "(Unknown)" in theMessage
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
nwErr._doClose()
|
||||||
|
nwErr.close()
|
||||||
|
nwGUI.closeMain()
|
||||||
|
|
||||||
|
# END Test testBaseError_Dialog
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseError_Handler(qtbot, monkeypatch, fncDir, tmpDir):
|
||||||
|
"""Test the error handler. This test doesn'thave any asserts, but it
|
||||||
|
checks that the error handler handles potential exceptions. The test
|
||||||
|
will fail if excpetions are not handled.
|
||||||
|
"""
|
||||||
|
qApp.closeAllWindows()
|
||||||
|
nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir])
|
||||||
|
qtbot.addWidget(nwGUI)
|
||||||
|
nwGUI.show()
|
||||||
|
qtbot.waitForWindowShown(nwGUI)
|
||||||
|
|
||||||
|
# Normal shutdown
|
||||||
|
monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None)
|
||||||
|
exceptionHandler(Exception, "Error Message", None)
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Should not crash when no GUI is found
|
||||||
|
monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", lambda: [])
|
||||||
|
exceptionHandler(Exception, "Error Message", None)
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Should handle qApp failing
|
||||||
|
monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", causeException)
|
||||||
|
exceptionHandler(Exception, "Error Message", None)
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# Should handle failing to close main GUI
|
||||||
|
monkeypatch.setattr(NWErrorMessage, "exec_", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None)
|
||||||
|
monkeypatch.setattr(nwGUI, "closeMain", causeException)
|
||||||
|
exceptionHandler(Exception, "Error Message", None)
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
nwGUI.closeMain()
|
||||||
|
|
||||||
|
# END Test testBaseError_Handler
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""novelWriter Main Init Tester
|
||||||
|
"""
|
||||||
|
|
||||||
|
import nw
|
||||||
|
import pytest
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from dummy import DummyMain
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseInit_Launch(caplog, monkeypatch, tmpDir):
|
||||||
|
"""Check launching the main GUI.
|
||||||
|
"""
|
||||||
|
monkeypatch.setattr("nw.guimain.GuiMain", DummyMain)
|
||||||
|
|
||||||
|
# Testmode launch
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert isinstance(nwGUI, DummyMain)
|
||||||
|
|
||||||
|
# Darwin launch
|
||||||
|
monkeypatch.setitem(sys.modules, "Foundation", None)
|
||||||
|
osDarwin = nw.CONFIG.osDarwin
|
||||||
|
nw.CONFIG.osDarwin = True
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert isinstance(nwGUI, DummyMain)
|
||||||
|
assert "Foundation" in caplog.messages[1]
|
||||||
|
nw.CONFIG.osDarwin = osDarwin
|
||||||
|
|
||||||
|
# Normal launch
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.__init__", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setApplicationName", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setApplicationVersion", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setWindowIcon", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setOrganizationDomain", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.exec_", lambda *args: 0)
|
||||||
|
with pytest.raises(SystemExit) as ex:
|
||||||
|
nw.main(["--config=%s" % tmpDir, "--data=%s" % tmpDir])
|
||||||
|
|
||||||
|
assert ex.value.code == 0
|
||||||
|
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# END Test testBaseInit_Launch
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseInit_Options(monkeypatch, tmpDir):
|
||||||
|
"""Test command line options for logging level.
|
||||||
|
"""
|
||||||
|
monkeypatch.setattr("nw.guimain.GuiMain", DummyMain)
|
||||||
|
monkeypatch.setattr(sys, "argv", [
|
||||||
|
"novelWriter.py", "--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir
|
||||||
|
])
|
||||||
|
|
||||||
|
# Defaults w/None Args
|
||||||
|
nwGUI = nw.main()
|
||||||
|
assert nw.logger.getEffectiveLevel() == logging.WARNING
|
||||||
|
assert nw.CONFIG.debugInfo is False
|
||||||
|
assert nw.CONFIG.showGUI is False
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir, "--style=Fusion"]
|
||||||
|
)
|
||||||
|
assert nw.logger.getEffectiveLevel() == logging.WARNING
|
||||||
|
assert nw.CONFIG.debugInfo is False
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
# Log Levels
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--info", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert nw.logger.getEffectiveLevel() == logging.INFO
|
||||||
|
assert nw.CONFIG.debugInfo is False
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--debug", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert nw.logger.getEffectiveLevel() == logging.DEBUG
|
||||||
|
assert nw.CONFIG.debugInfo is True
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--verbose", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert nw.logger.getEffectiveLevel() == 5
|
||||||
|
assert nw.CONFIG.debugInfo is True
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
# Help and Version
|
||||||
|
with pytest.raises(SystemExit) as ex:
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--help", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
assert ex.value.code == 0
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as ex:
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--version", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
assert ex.value.code == 0
|
||||||
|
|
||||||
|
# Invalid options
|
||||||
|
with pytest.raises(SystemExit) as ex:
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--invalid", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
assert ex.value.code == 2
|
||||||
|
|
||||||
|
# Project Path
|
||||||
|
nwGUI = nw.main(
|
||||||
|
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir, "sample/"]
|
||||||
|
)
|
||||||
|
assert nw.CONFIG.cmdOpen == "sample/"
|
||||||
|
assert nwGUI.closeMain() == "closeMain"
|
||||||
|
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# END Test testBaseInit_Options
|
||||||
|
|
||||||
|
@pytest.mark.base
|
||||||
|
def testBaseInit_Imports(caplog, monkeypatch, tmpDir):
|
||||||
|
"""Check import error handling.
|
||||||
|
"""
|
||||||
|
monkeypatch.setattr("nw.guimain.GuiMain", DummyMain)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.__init__", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.exec_", lambda *args: 0)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.__init__", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.resize", lambda *args: None)
|
||||||
|
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.showMessage", lambda *args: None)
|
||||||
|
monkeypatch.setitem(sys.modules, "lxml", None)
|
||||||
|
monkeypatch.setattr("sys.hexversion", 0x0)
|
||||||
|
monkeypatch.setattr("nw.CONFIG.verQtValue", 50000)
|
||||||
|
monkeypatch.setattr("nw.CONFIG.verPyQtValue", 50000)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit) as ex:
|
||||||
|
_ = nw.main(
|
||||||
|
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert ex.value.code & 4 == 4 # Python version not satisfied
|
||||||
|
assert ex.value.code & 8 == 8 # Qt version not satisfied
|
||||||
|
assert ex.value.code & 16 == 16 # PyQt version not satisfied
|
||||||
|
assert ex.value.code & 32 == 32 # lxml package missing
|
||||||
|
|
||||||
|
assert "At least Python" in caplog.messages[0]
|
||||||
|
assert "At least Qt5" in caplog.messages[1]
|
||||||
|
assert "At least PyQt5" in caplog.messages[2]
|
||||||
|
assert "lxml" in caplog.messages[3]
|
||||||
|
|
||||||
|
monkeypatch.undo()
|
||||||
|
|
||||||
|
# END Test testBaseInit_Imports
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""novelWriter Config Class Tester
|
|
||||||
"""
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import os
|
|
||||||
|
|
||||||
from tools import cmpFiles
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigCore(tmpConf, tmpDir, refDir):
|
|
||||||
refConf = os.path.join(refDir, "novelwriter.conf")
|
|
||||||
testConf = os.path.join(tmpConf.confPath, "novelwriter.conf")
|
|
||||||
|
|
||||||
assert tmpConf.confPath == tmpDir
|
|
||||||
assert tmpConf.saveConfig()
|
|
||||||
assert cmpFiles(testConf, refConf, [2, 9])
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
assert tmpConf.loadConfig()
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigSetConfPath(tmpConf, tmpDir):
|
|
||||||
assert tmpConf.setConfPath(None)
|
|
||||||
assert not tmpConf.setConfPath(os.path.join("somewhere", "over", "the", "rainbow"))
|
|
||||||
assert tmpConf.setConfPath(os.path.join(tmpDir, "novelwriter.conf"))
|
|
||||||
assert tmpConf.confPath == tmpDir
|
|
||||||
assert tmpConf.confFile == "novelwriter.conf"
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigSetDataPath(tmpConf, tmpDir):
|
|
||||||
assert tmpConf.setDataPath(None)
|
|
||||||
assert not tmpConf.setDataPath(os.path.join("somewhere", "over", "the", "rainbow"))
|
|
||||||
assert tmpConf.setDataPath(tmpDir)
|
|
||||||
assert tmpConf.dataPath == tmpDir
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigSetWinSize(tmpConf, tmpDir, refDir):
|
|
||||||
refConf = os.path.join(refDir, "novelwriter.conf")
|
|
||||||
testConf = os.path.join(tmpConf.confPath, "novelwriter.conf")
|
|
||||||
tmpConf.guiScale = 1.0
|
|
||||||
|
|
||||||
assert tmpConf.confPath == tmpDir
|
|
||||||
assert tmpConf.setWinSize(1205, 655)
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
assert tmpConf.setWinSize(70, 70)
|
|
||||||
assert tmpConf.confChanged
|
|
||||||
assert tmpConf.setWinSize(1200, 650)
|
|
||||||
assert tmpConf.saveConfig()
|
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2, 9])
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigSetTreeColWidths(tmpConf, tmpDir, refDir):
|
|
||||||
refConf = os.path.join(refDir, "novelwriter.conf")
|
|
||||||
testConf = os.path.join(tmpConf.confPath, "novelwriter.conf")
|
|
||||||
|
|
||||||
assert tmpConf.confPath == tmpDir
|
|
||||||
tmpConf.guiScale = 1.0
|
|
||||||
|
|
||||||
assert tmpConf.setTreeColWidths([10, 20, 25])
|
|
||||||
assert tmpConf.treeColWidth == [10, 20, 25]
|
|
||||||
assert tmpConf.setTreeColWidths([200, 50, 30])
|
|
||||||
|
|
||||||
assert tmpConf.setProjColWidths([10, 20, 30])
|
|
||||||
assert tmpConf.projColWidth == [10, 20, 30]
|
|
||||||
assert tmpConf.setProjColWidths([200, 60, 140])
|
|
||||||
|
|
||||||
assert tmpConf.confChanged
|
|
||||||
assert tmpConf.saveConfig()
|
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2, 9])
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigSetPanePos(tmpConf, tmpDir, refDir):
|
|
||||||
refConf = os.path.join(refDir, "novelwriter.conf")
|
|
||||||
testConf = os.path.join(tmpConf.confPath, "novelwriter.conf")
|
|
||||||
|
|
||||||
assert tmpConf.confPath == tmpDir
|
|
||||||
|
|
||||||
tmpConf.guiScale = 2.0
|
|
||||||
assert tmpConf.setMainPanePos([200, 700])
|
|
||||||
assert tmpConf.mainPanePos == [100, 350]
|
|
||||||
assert tmpConf.getMainPanePos() == [200, 700]
|
|
||||||
|
|
||||||
assert tmpConf.setDocPanePos([300, 300])
|
|
||||||
assert tmpConf.docPanePos == [150, 150]
|
|
||||||
assert tmpConf.getDocPanePos() == [300, 300]
|
|
||||||
|
|
||||||
assert tmpConf.setViewPanePos([400, 250])
|
|
||||||
assert tmpConf.viewPanePos == [200, 125]
|
|
||||||
assert tmpConf.getViewPanePos() == [400, 250]
|
|
||||||
|
|
||||||
assert tmpConf.setOutlinePanePos([400, 250])
|
|
||||||
assert tmpConf.outlnPanePos == [200, 125]
|
|
||||||
assert tmpConf.getOutlinePanePos() == [400, 250]
|
|
||||||
|
|
||||||
tmpConf.guiScale = 1.0
|
|
||||||
assert tmpConf.setMainPanePos([300, 800])
|
|
||||||
assert tmpConf.setDocPanePos([400, 400])
|
|
||||||
assert tmpConf.setViewPanePos([500, 150])
|
|
||||||
assert tmpConf.setOutlinePanePos([500, 150])
|
|
||||||
|
|
||||||
assert tmpConf.confChanged
|
|
||||||
assert tmpConf.saveConfig()
|
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2, 9])
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigFlags(tmpConf, tmpDir, refDir):
|
|
||||||
refConf = os.path.join(refDir, "novelwriter.conf")
|
|
||||||
testConf = os.path.join(tmpConf.confPath, "novelwriter.conf")
|
|
||||||
|
|
||||||
assert tmpConf.confPath == tmpDir
|
|
||||||
|
|
||||||
assert not tmpConf.setShowRefPanel(False)
|
|
||||||
assert tmpConf.setShowRefPanel(True)
|
|
||||||
|
|
||||||
assert not tmpConf.setViewComments(False)
|
|
||||||
assert not tmpConf.viewComments
|
|
||||||
assert tmpConf.setViewComments(True)
|
|
||||||
|
|
||||||
assert not tmpConf.setViewSynopsis(False)
|
|
||||||
assert not tmpConf.viewSynopsis
|
|
||||||
assert tmpConf.setViewSynopsis(True)
|
|
||||||
|
|
||||||
assert tmpConf.confChanged
|
|
||||||
assert tmpConf.saveConfig()
|
|
||||||
|
|
||||||
assert cmpFiles(testConf, refConf, [2, 9])
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testTextSizes(tmpConf, tmpDir, refDir):
|
|
||||||
assert tmpConf.confPath == tmpDir
|
|
||||||
|
|
||||||
tmpConf.guiScale = 2.0
|
|
||||||
assert tmpConf.getTextWidth() == 1200
|
|
||||||
assert tmpConf.getTextMargin() == 80
|
|
||||||
assert tmpConf.getTabWidth() == 80
|
|
||||||
assert tmpConf.getFocusWidth() == 1600
|
|
||||||
tmpConf.guiScale = 1.0
|
|
||||||
|
|
||||||
assert not tmpConf.confChanged
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigErrors(tmpConf):
|
|
||||||
nonPath = os.path.join("somewhere", "over", "the", "rainbow")
|
|
||||||
assert tmpConf.initConfig(nonPath, nonPath)
|
|
||||||
assert tmpConf.hasError
|
|
||||||
assert not tmpConf.loadConfig()
|
|
||||||
assert not tmpConf.saveConfig()
|
|
||||||
assert not tmpConf.loadRecentCache()
|
|
||||||
assert len(tmpConf.getErrData()) > 0
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testConfigInternals(tmpConf):
|
|
||||||
assert tmpConf._checkNone(None) is None
|
|
||||||
assert tmpConf._checkNone("None") is None
|
|
||||||
+10
-10
@@ -22,8 +22,8 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, tmpDir, dummyGUI):
|
|||||||
default setting, creating a Minimal project.
|
default setting, creating a Minimal project.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_1_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_1_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||||
|
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(dummyGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
@@ -70,8 +70,8 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
|
|||||||
Custom type with chapters and scenes.
|
Custom type with chapters and scenes.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_2_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewCustomA_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_2_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewCustomA_nwProject.nwx")
|
||||||
|
|
||||||
projData = {
|
projData = {
|
||||||
"projName": "Test Custom",
|
"projName": "Test Custom",
|
||||||
@@ -111,8 +111,8 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
|
|||||||
Custom type without chapters, but with scenes.
|
Custom type without chapters, but with scenes.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_3_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewCustomB_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_3_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewCustomB_nwProject.nwx")
|
||||||
|
|
||||||
projData = {
|
projData = {
|
||||||
"projName": "Test Custom",
|
"projName": "Test Custom",
|
||||||
@@ -238,8 +238,8 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, dummyGUI):
|
|||||||
"""Check that new root folders can be added to the project.
|
"""Check that new root folders can be added to the project.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_4_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_4_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
|
||||||
|
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(dummyGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
@@ -274,8 +274,8 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, dummyGUI):
|
|||||||
"""Check that new files can be added to the project.
|
"""Check that new files can be added to the project.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_5_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_5_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
|
||||||
|
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(dummyGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""novelWriter Error Tester
|
|
||||||
"""
|
|
||||||
|
|
||||||
import nw
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from PyQt5.QtWidgets import qApp
|
|
||||||
|
|
||||||
from nw.error import NWErrorMessage, exceptionHandler
|
|
||||||
|
|
||||||
@pytest.mark.error
|
|
||||||
def testErrorDialog(qtbot, fncDir, tmpDir):
|
|
||||||
qApp.closeAllWindows()
|
|
||||||
nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir])
|
|
||||||
qtbot.addWidget(nwGUI)
|
|
||||||
nwGUI.show()
|
|
||||||
qtbot.waitForWindowShown(nwGUI)
|
|
||||||
|
|
||||||
nwErr = NWErrorMessage(nwGUI)
|
|
||||||
qtbot.addWidget(nwErr)
|
|
||||||
nwErr.show()
|
|
||||||
|
|
||||||
# Invalid Error
|
|
||||||
nwErr.setMessage(Exception, "Faulty Error", 123)
|
|
||||||
assert nwErr.msgBody.toPlainText() == "Failed to generate error report ..."
|
|
||||||
|
|
||||||
# Valid Error
|
|
||||||
nwErr.setMessage(Exception, "First Error", None)
|
|
||||||
theMessage = nwErr.msgBody.toPlainText()
|
|
||||||
assert theMessage
|
|
||||||
assert "First Error" in theMessage
|
|
||||||
assert "Exception" in theMessage
|
|
||||||
nwErr._doClose()
|
|
||||||
nwErr.close()
|
|
||||||
|
|
||||||
theMessage = exceptionHandler(Exception, "Second Error", None, testMode=True)
|
|
||||||
assert theMessage
|
|
||||||
assert "Second Error" in theMessage
|
|
||||||
assert "Exception" in theMessage
|
|
||||||
|
|
||||||
nwGUI.closeMain()
|
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
|
||||||
@@ -4,9 +4,7 @@
|
|||||||
|
|
||||||
import nw
|
import nw
|
||||||
import pytest
|
import pytest
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from tools import cmpFiles
|
from tools import cmpFiles
|
||||||
@@ -26,82 +24,6 @@ keyDelay = 2
|
|||||||
typeDelay = 1
|
typeDelay = 1
|
||||||
stepDelay = 20
|
stepDelay = 20
|
||||||
|
|
||||||
@pytest.mark.gui
|
|
||||||
def testLaunch(qtbot, monkeypatch, fncDir, tmpDir):
|
|
||||||
|
|
||||||
# Defaults
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir, "--style=Fusion"]
|
|
||||||
)
|
|
||||||
assert nw.logger.getEffectiveLevel() == logging.WARNING
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
|
|
||||||
# Log Levels
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--info", "--config=%s" % fncDir, "--data=%s" % tmpDir]
|
|
||||||
)
|
|
||||||
assert nw.logger.getEffectiveLevel() == logging.INFO
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--debug", "--config=%s" % fncDir, "--data=%s" % tmpDir]
|
|
||||||
)
|
|
||||||
assert nw.logger.getEffectiveLevel() == logging.DEBUG
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--verbose", "--config=%s" % fncDir, "--data=%s" % tmpDir]
|
|
||||||
)
|
|
||||||
assert nw.logger.getEffectiveLevel() == 5
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
|
|
||||||
# Help and Version
|
|
||||||
with pytest.raises(SystemExit) as ex:
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--help", "--config=%s" % fncDir, "--data=%s" % tmpDir]
|
|
||||||
)
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
assert ex.value.code == 0
|
|
||||||
|
|
||||||
with pytest.raises(SystemExit) as ex:
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--version", "--config=%s" % fncDir, "--data=%s" % tmpDir]
|
|
||||||
)
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
assert ex.value.code == 0
|
|
||||||
|
|
||||||
# Invalid options
|
|
||||||
with pytest.raises(SystemExit) as ex:
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--invalid", "--config=%s" % fncDir, "--data=%s" % tmpDir]
|
|
||||||
)
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
assert ex.value.code == 2
|
|
||||||
|
|
||||||
# Simulate import error
|
|
||||||
monkeypatch.setitem(sys.modules, "lxml", None)
|
|
||||||
monkeypatch.setattr("sys.hexversion", 0x0)
|
|
||||||
monkeypatch.setattr("nw.CONFIG.verQtValue", 50000)
|
|
||||||
monkeypatch.setattr("nw.CONFIG.verPyQtValue", 50000)
|
|
||||||
with pytest.raises(SystemExit) as ex:
|
|
||||||
nwGUI = nw.main(
|
|
||||||
["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir]
|
|
||||||
)
|
|
||||||
nwGUI.closeMain()
|
|
||||||
nwGUI.close()
|
|
||||||
assert ex.value.code & 4 == 4 # Python version not satisfied
|
|
||||||
assert ex.value.code & 8 == 8 # Qt version not satisfied
|
|
||||||
assert ex.value.code & 16 == 16 # PyQt version not satisfied
|
|
||||||
assert ex.value.code & 32 == 32 # lxml package missing
|
|
||||||
monkeypatch.undo()
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDocEditor(qtbot, yesToAll, fncDir, nwTempGUI, refDir, tmpDir):
|
def testDocEditor(qtbot, yesToAll, fncDir, nwTempGUI, refDir, tmpDir):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user