Merge pull request #514 from vkbo/base_tests

Refactor Base Tests
This commit is contained in:
Veronica K. Berglyd Olsen
2020-12-08 00:03:47 +01:00
committed by GitHub
21 changed files with 926 additions and 385 deletions
+15 -14
View File
@@ -246,19 +246,20 @@ def main(sysArgs=None):
errorCode |= 32
if errorData:
if not testMode:
errApp = QApplication([])
errMsg = QErrorMessage()
errMsg.resize(500, 300)
errMsg.showMessage((
"<h3>A critical error has been encountered</h3>"
"<p>novelWriter cannot start due to the following issues:<p>"
"<p>&nbsp;-&nbsp;%s</p>"
"<p>Shutting down ...</p>"
) % (
"<br>&nbsp;-&nbsp;".join(errorData)
))
errApp.exec_()
errApp = QApplication([])
errMsg = QErrorMessage()
errMsg.resize(500, 300)
errMsg.showMessage((
"<h3>A critical error has been encountered</h3>"
"<p>novelWriter cannot start due to the following issues:<p>"
"<p>&nbsp;-&nbsp;%s</p>"
"<p>Shutting down ...</p>"
) % (
"<br>&nbsp;-&nbsp;".join(errorData)
))
for errMsg in errorData:
logger.critical(errMsg)
errApp.exec_()
sys.exit(errorCode)
# Finish initialising config
@@ -293,4 +294,4 @@ def main(sysArgs=None):
nwGUI = GuiMain()
sys.exit(nwApp.exec_())
return
# END Function main
+51 -40
View File
@@ -27,12 +27,12 @@
import logging
import configparser
import shutil
import json
import sys
import os
from time import time
from shutil import which
from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
@@ -44,10 +44,11 @@ logger = logging.getLogger(__name__)
class Config:
CNF_STR = 0
CNF_INT = 1
CNF_BOOL = 2
CNF_LIST = 3
CNF_STR = 0
CNF_INT = 1
CNF_BOOL = 2
CNF_S_LST = 3
CNF_I_LST = 4
def __init__(self):
@@ -211,12 +212,8 @@ class Config:
self.osUnknown = True
# Other System Info
if self.verQtValue >= 50600:
self.hostName = QSysInfo.machineHostName()
self.kernelVer = QSysInfo.kernelVersion()
else:
self.hostName = "Unknown"
self.kernelVer = "Unknown"
self.hostName = "Unknown"
self.kernelVer = "Unknown"
# Packages
self.hasEnchant = False # The pyenchant package
@@ -321,6 +318,11 @@ class Config:
self.errData.append(str(e))
self.dataPath = None
# Host and Kernel
if self.verQtValue >= 50600:
self.hostName = QSysInfo.machineHostName()
self.kernelVer = QSysInfo.kernelVersion()
# Load recent projects cache
self.loadRecentCache()
@@ -388,25 +390,25 @@ class Config:
## Sizes
cnfSec = "Sizes"
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(
cnfParse, cnfSec, "treecols", self.CNF_LIST, self.treeColWidth
cnfParse, cnfSec, "treecols", self.CNF_I_LST, self.treeColWidth
)
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(
cnfParse, cnfSec, "mainpane", self.CNF_LIST, self.mainPanePos
cnfParse, cnfSec, "mainpane", self.CNF_I_LST, self.mainPanePos
)
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(
cnfParse, cnfSec, "viewpane", self.CNF_LIST, self.viewPanePos
cnfParse, cnfSec, "viewpane", self.CNF_I_LST, self.viewPanePos
)
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(
cnfParse, cnfSec, "fullscreen", self.CNF_BOOL, self.isFullScreen
@@ -484,10 +486,10 @@ class Config:
cnfParse, cnfSec, "autoscrollpos", self.CNF_INT, self.autoScrollPos
)
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(
cnfParse, cnfSec, "fmtdoublequote", self.CNF_LIST, self.fmtDoubleQuotes
cnfParse, cnfSec, "fmtdoublequote", self.CNF_S_LST, self.fmtDoubleQuotes
)
self.spellTool = self._parseLine(
cnfParse, cnfSec, "spelltool", self.CNF_STR, self.spellTool
@@ -901,23 +903,28 @@ class Config:
# 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):
"""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])
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):
"""Parse a line and return the correct datatype.
"""
@@ -930,18 +937,24 @@ class Config:
return cnfParse.getint(cnfSec, cnfName)
elif cnfType == self.CNF_BOOL:
return cnfParse.getboolean(cnfSec, cnfName)
elif cnfType == self.CNF_LIST:
elif cnfType == self.CNF_I_LST:
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:
logger.error("Failed to load value from config file.")
logger.error(str(e))
return cnfDefault
return cnfDefault
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:
return None
@@ -961,10 +974,8 @@ class Config:
self.hasEnchant = False
logger.debug("Checking package 'pyenchant': Missing")
try:
self.hasAssistant = which("assistant")
except Exception:
self.hasAssistant = False
assistPath = shutil.which("assistant")
self.hasAssistant = assistPath is not None
if self.hasAssistant:
logger.debug("Checking executable 'assistant': Ok")
else:
+3 -8
View File
@@ -135,10 +135,9 @@ class NWErrorMessage(QDialog):
# END Class NWErrorMessage
def exceptionHandler(exType, exValue, exTrace, testMode=False):
def exceptionHandler(exType, exValue, exTrace):
"""Function to catch unhandled global exceptions.
"""
import nw
import logging
from traceback import print_tb
from PyQt5.QtWidgets import qApp
@@ -160,8 +159,7 @@ def exceptionHandler(exType, exValue, exTrace, testMode=False):
errMsg = NWErrorMessage(nwGUI)
errMsg.setMessage(exType, exValue, exTrace)
if nw.CONFIG.showGUI:
errMsg.exec_()
errMsg.exec_()
try:
# 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(str(e))
if testMode:
return errMsg.msgBody.toPlainText()
else:
qApp.exit(1)
qApp.exit(1)
except Exception as e:
logger.critical(str(e))
+1 -1
View File
@@ -1,6 +1,6 @@
[pytest]
markers =
error: Test various error handling scenarios
base: Base functionality tests
core: Core functionality tests
gui: Qt5 GUI tests
serial
+4
View File
@@ -61,6 +61,10 @@ The commands for the respective test categories are listed below.
| 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 | NWDoc class | nw/core/document.py | `-m core` | `-k testCoreDocument` |
| Unit | NWIndex class | nw/core/index.py | `-m core` | `-k testCoreIndex` |
+3
View File
@@ -73,6 +73,9 @@ def fncDir(tmpDir):
def tmpConf(tmpDir):
"""Create a temporary novelWriter configuration object.
"""
confFile = os.path.join(tmpDir, "novelwriter.conf")
if os.path.isfile(confFile):
os.unlink(confFile)
theConf = Config()
theConf.initConfig(tmpDir, tmpDir)
theConf.setLastPath("")
+13 -4
View File
@@ -13,7 +13,7 @@ class DummyMain():
self.hasProject = True
self.theIndex = None
self.theProject = None
self.statusBar = StatusBar()
self.statusBar = DummyStatusBar()
# Test Variables
self.askResponse = True
@@ -42,6 +42,12 @@ class DummyMain():
def rebuildIndex(self):
return
def closeMain(self):
return "closeMain"
def close(self):
return "close"
# Test Functions
def undo(self):
@@ -52,9 +58,9 @@ class DummyMain():
self.lastAlert = ""
return
# END Class GuiMain
# END Class DummyMain
class StatusBar():
class DummyStatusBar():
def __init__(self):
return
@@ -62,7 +68,7 @@ class StatusBar():
def setStatus(self, theText):
return
# END Class StatusBar
# END Class DummyStatusBar
# =========================================================================== #
# Error Functions
@@ -71,3 +77,6 @@ class StatusBar():
def causeOSError(*args, **kwargs):
raise OSError
def causeException(*args, **kwargs):
raise Exception
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""novelWriter Common Class Tester
"""novelWriter Common Functions Tester
"""
import time
@@ -11,8 +11,10 @@ from nw.common import (
)
from tools import cmpList
@pytest.mark.core
def testCheckString():
@pytest.mark.base
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", False) == "None"
@@ -21,8 +23,12 @@ def testCheckString():
assert checkString(1.0, "NotNone", False) == "NotNone"
assert checkString(True, "NotNone", False) == "NotNone"
@pytest.mark.core
def testCheckInt():
# END Test testBaseCommon_CheckString
@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, False) == 3
@@ -30,8 +36,12 @@ def testCheckInt():
assert checkInt(1.0, 3, False) == 1
assert checkInt(True, 3, False) == 1
@pytest.mark.core
def testCheckBool():
# END Test testBaseCommon_CheckInt
@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("True", False, False)
@@ -44,8 +54,12 @@ def testCheckBool():
assert checkBool(1.0, None, False) is None
assert checkBool(2.0, None, False) is None
@pytest.mark.core
def testCheckHandle():
# END Test testBaseCommon_CheckBool
@pytest.mark.base
def testBaseCommon_CheckHandle():
"""Test the checkHandle function.
"""
assert checkHandle("None", 1, True) is None
assert checkHandle("None", 1, False) == 1
assert checkHandle(None, 1, True) is None
@@ -53,8 +67,12 @@ def testCheckHandle():
assert checkHandle("47666c91c7ccf", None, False) == "47666c91c7ccf"
assert checkHandle("h7666c91c7ccf", None, False) is None
@pytest.mark.core
def testColRange():
# END Test testBaseCommon_CheckHandle
@pytest.mark.base
def testBaseCommon_ColRange():
"""Test the colRange function.
"""
assert colRange([0, 0], [0, 0], 0) is None
assert cmpList(
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]]
)
@pytest.mark.core
def testFormatTimeStamp():
# END Test testBaseCommon_ColRange
@pytest.mark.base
def testBaseCommon_FormatTimeStamp():
"""Test the formatTimeStamp function.
"""
tTime = time.mktime(time.gmtime(0))
assert formatTimeStamp(tTime, False) == "1970-01-01 00:00:00"
assert formatTimeStamp(tTime, True) == "1970-01-01 00.00.00"
@pytest.mark.core
def testFormatTime():
# END Test testBaseCommon_FormatTimeStamp
@pytest.mark.base
def testBaseCommon_FormatTime():
"""Test the formatTime function.
"""
assert formatTime("1") == "ERROR"
assert formatTime(1.0) == "ERROR"
assert formatTime(1) == "00:00:01"
@@ -101,8 +127,12 @@ def testFormatTime():
assert formatTime(86400) == "1-00:00:00"
assert formatTime(360000) == "4-04:00:00"
@pytest.mark.core
def testFormatInt():
# END Test testBaseCommon_FormatTime
@pytest.mark.base
def testBaseCommon_FormatInt():
"""Test the formatInt function.
"""
assert formatInt(1000) == "1000"
assert formatInt(1234) == "1.23\u2009k"
assert formatInt(12345) == "12.3\u2009k"
@@ -112,8 +142,12 @@ def testFormatInt():
assert formatInt(123456789) == "123\u2009M"
assert formatInt(1234567890) == "1.23\u2009G"
@pytest.mark.core
def testTransferCase():
# END Test testBaseCommon_FormatInt
@pytest.mark.base
def testBaseCommon_TransferCase():
"""Test the transferCase function.
"""
assert transferCase(1, "TaRgEt") == "TaRgEt"
assert transferCase("source", 1) == 1
assert transferCase("", "TaRgEt") == "TaRgEt"
@@ -122,8 +156,12 @@ def testTransferCase():
assert transferCase("SOURCE", "target") == "TARGET"
assert transferCase("source", "TARGET") == "target"
@pytest.mark.core
def testFuzzyTime():
# END Test testBaseCommon_TransferCase
@pytest.mark.base
def testBaseCommon_FuzzyTime():
"""Test the fuzzyTime function.
"""
assert fuzzyTime(-1) == "in the future"
assert fuzzyTime(0) == "just now"
assert fuzzyTime(29) == "just now"
@@ -152,3 +190,5 @@ def testFuzzyTime():
assert fuzzyTime(29808000) == "a year ago"
assert fuzzyTime(47336399) == "a year ago"
assert fuzzyTime(47336400) == "2 years ago"
# END Test testBaseCommon_FuzzyTime
+505
View File
@@ -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
+97
View File
@@ -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
+163
View File
@@ -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
-165
View File
@@ -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
View File
@@ -22,8 +22,8 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, tmpDir, dummyGUI):
default setting, creating a Minimal project.
"""
projFile = os.path.join(fncDir, "nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_1_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_1_nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
theProject = NWProject(dummyGUI)
theProject.projTree.setSeed(42)
@@ -70,8 +70,8 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
Custom type with chapters and scenes.
"""
projFile = os.path.join(fncDir, "nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_2_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_2_nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_NewCustomA_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewCustomA_nwProject.nwx")
projData = {
"projName": "Test Custom",
@@ -111,8 +111,8 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
Custom type without chapters, but with scenes.
"""
projFile = os.path.join(fncDir, "nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_3_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_3_nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_NewCustomB_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewCustomB_nwProject.nwx")
projData = {
"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.
"""
projFile = os.path.join(fncDir, "nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_4_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_4_nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
theProject = NWProject(dummyGUI)
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.
"""
projFile = os.path.join(fncDir, "nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_5_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_5_nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
theProject = NWProject(dummyGUI)
theProject.projTree.setSeed(42)
-44
View File
@@ -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()
-78
View File
@@ -4,9 +4,7 @@
import nw
import pytest
import logging
import os
import sys
from shutil import copyfile
from tools import cmpFiles
@@ -26,82 +24,6 @@ keyDelay = 2
typeDelay = 1
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
def testDocEditor(qtbot, yesToAll, fncDir, nwTempGUI, refDir, tmpDir):