Remove code for mocking CONFIG object and instead reset it for each test

This commit is contained in:
Veronica Berglyd Olsen
2023-05-16 00:34:11 +02:00
parent 76daa3f463
commit e8c6e50e92
12 changed files with 268 additions and 284 deletions
+69 -69
View File
@@ -28,19 +28,60 @@ from pathlib import Path
from mock import MockGuiMain
from tools import cleanProject
from PyQt5.QtWidgets import QMessageBox
sys.path.insert(1, str(Path(__file__).parent.parent.absolute()))
import novelwriter # noqa: E402
from novelwriter import CONFIG, main # noqa: E402
from PyQt5.QtWidgets import QMessageBox # noqa: E402
from novelwriter.config import Config # noqa: E402
_TST_ROOT = Path(__file__).parent
_TMP_ROOT = _TST_ROOT / "temp"
_TMP_CONF = _TMP_ROOT / "conf"
@pytest.fixture(autouse=True)
def initQt(qtbot):
"""Ensures that the qt main thread is always available in all tests.
##
# Helper Functions
##
def resetConfigVars():
"""Reset the CONFIG object and set various values for testing to
prevent interfering with local OS.
"""
CONFIG.setLastPath(_TMP_ROOT)
CONFIG.setBackupPath(_TMP_ROOT)
CONFIG._homePath = _TMP_ROOT
CONFIG.guiLocale = "en_GB"
return
##
# Auto Fixtures
##
@pytest.fixture(scope="session", autouse=True)
def sessionFixture():
"""A session wide fixture to set up the test environment.
"""
if _TMP_ROOT.exists():
shutil.rmtree(_TMP_ROOT)
_TMP_ROOT.mkdir()
_TMP_CONF.mkdir()
return
@pytest.fixture(scope="function", autouse=True)
def functionFixture(qtbot):
"""Ensures that the main Qt thread is always available, and reset
the config object for each function and redirect its storage paths.
"""
if _TMP_CONF.exists():
shutil.rmtree(_TMP_CONF)
_TMP_CONF.mkdir()
CONFIG.__init__()
CONFIG.initConfig(confPath=_TMP_CONF, dataPath=_TMP_CONF)
resetConfigVars()
return
@@ -49,26 +90,17 @@ def initQt(qtbot):
##
@pytest.fixture(scope="session")
def tmpPath():
"""A temporary folder for the test session. Path version.
"""
theTemp = Path(__file__).parent / "temp"
if theTemp.exists():
shutil.rmtree(theTemp)
theTemp.mkdir(exist_ok=True)
return theTemp
@pytest.fixture(scope="session")
def tstPaths(tmpPath):
def tstPaths():
"""Returns an object that can provide the various paths needed for
running tests.
"""
class _Store:
testDir = Path(__file__).parent
filesDir = testDir / "files"
refDir = testDir / "reference"
outDir = tmpPath / "results"
testDir = _TST_ROOT
filesDir = _TST_ROOT / "files"
refDir = _TST_ROOT / "reference"
outDir = _TMP_ROOT / "results"
tmpDir = _TMP_ROOT
cnfDir = _TMP_CONF
store = _Store()
store.outDir.mkdir(exist_ok=True)
@@ -77,10 +109,10 @@ def tstPaths(tmpPath):
@pytest.fixture(scope="function")
def fncPath(tmpPath):
"""A temporary folder for a single test function. Path version.
def fncPath():
"""A temporary folder for a single test function.
"""
fncPath = tmpPath / "function"
fncPath = _TMP_ROOT / "function"
if fncPath.is_dir():
shutil.rmtree(fncPath)
fncPath.mkdir(exist_ok=True)
@@ -103,46 +135,17 @@ def projPath(fncPath):
# novelWriter Objects
##
@pytest.fixture(scope="function")
def tmpConf(tmpPath):
"""Create a temporary novelWriter configuration object.
"""
confFile = tmpPath / "novelwriter.conf"
if confFile.is_file():
confFile.unlink()
theConf = Config()
theConf.initConfig(tmpPath, tmpPath)
theConf.setLastPath(tmpPath)
theConf.guiLocale = "en_GB"
return theConf
@pytest.fixture(scope="function")
def fncConf(fncPath):
"""Create a temporary novelWriter configuration object.
"""
confFile = fncPath / "novelwriter.conf"
if confFile.is_file():
confFile.unlink()
theConf = Config()
theConf.initConfig(fncPath, fncPath)
theConf.setLastPath(fncPath)
theConf.guiLocale = "en_GB"
return theConf
@pytest.fixture(scope="function")
def mockGUI(monkeypatch, tmpConf):
def mockGUI():
"""Create a mock instance of novelWriter's main GUI class.
"""
monkeypatch.setattr("novelwriter.CONFIG", tmpConf)
theGui = MockGuiMain()
theGui.mainConf = tmpConf
return theGui
@pytest.fixture(scope="function")
def nwGUI(qtbot, monkeypatch, fncPath, fncConf):
def nwGUI(qtbot, monkeypatch, functionFixture):
"""Create an instance of the novelWriter GUI.
"""
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Ok)
@@ -150,14 +153,13 @@ def nwGUI(qtbot, monkeypatch, fncPath, fncConf):
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr("novelwriter.CONFIG", fncConf)
nwGUI = novelwriter.main(["--testmode", f"--config={fncPath}", f"--data={fncPath}"])
nwGUI = main(["--testmode", f"--config={_TMP_CONF}", f"--data={_TMP_CONF}"])
qtbot.addWidget(nwGUI)
resetConfigVars()
nwGUI.show()
qtbot.wait(20)
nwGUI.mainConf.setLastPath(fncPath)
yield nwGUI
qtbot.wait(20)
@@ -198,13 +200,12 @@ def mockRnd(monkeypatch):
##
@pytest.fixture(scope="function")
def nwLipsum(tmpPath):
def nwLipsum():
"""A medium sized novelWriter example project with a lot of Lorem
Ipsum text.
"""
tstDir = Path(__file__).parent
srcDir = tstDir / "lipsum"
dstDir = tmpPath / "lipsum"
srcDir = _TST_ROOT / "lipsum"
dstDir = _TMP_ROOT / "lipsum"
if dstDir.exists():
shutil.rmtree(dstDir)
@@ -220,13 +221,12 @@ def nwLipsum(tmpPath):
@pytest.fixture(scope="function")
def prjLipsum(tmpPath):
def prjLipsum():
"""A medium sized novelWriter example project with a lot of Lorem
Ipsum text.
"""
tstDir = Path(__file__).parent
srcDir = tstDir / "lipsum"
dstDir = tmpPath / "lipsum"
srcDir = _TST_ROOT / "lipsum"
dstDir = _TMP_ROOT / "lipsum"
if dstDir.exists():
shutil.rmtree(dstDir)
+119 -109
View File
@@ -28,6 +28,7 @@ from pathlib import Path
from mock import causeOSError, MockApp
from tools import cmpFiles, writeFile
from novelwriter import CONFIG
from novelwriter.config import Config, RecentProjects
from novelwriter.constants import nwFiles
@@ -196,197 +197,206 @@ def testBaseConfig_Localisation(fncPath, tstPaths):
@pytest.mark.base
def testBaseConfig_Methods(tmpConf, tmpPath):
def testBaseConfig_Methods(fncPath):
"""Check class methods.
"""
tstConf = Config()
tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
# Data Path
assert tmpConf.dataPath() == tmpPath
assert tmpConf.dataPath("stuff") == tmpPath / "stuff"
assert tstConf.dataPath() == fncPath
assert tstConf.dataPath("stuff") == fncPath / "stuff"
# Assets Path
appPath = tmpConf._appPath
assert tmpConf.assetPath() == appPath / "assets"
assert tmpConf.assetPath("stuff") == appPath / "assets" / "stuff"
appPath = tstConf._appPath
assert tstConf.assetPath() == appPath / "assets"
assert tstConf.assetPath("stuff") == appPath / "assets" / "stuff"
# Last Path
assert tmpConf.lastPath() == tmpPath
assert tstConf.lastPath() == Path.home().absolute()
tmpStuff = tmpPath / "stuff"
tmpStuff = fncPath / "stuff"
tmpStuff.mkdir()
tmpConf.setLastPath(tmpStuff)
assert tmpConf.lastPath() == tmpStuff
tstConf.setLastPath(tmpStuff)
assert tstConf.lastPath() == tmpStuff
fileStuff = tmpStuff / "more_stuff.txt"
fileStuff.write_text("Stuff")
tmpConf.setLastPath(fileStuff)
assert tmpConf.lastPath() == tmpStuff
tstConf.setLastPath(fileStuff)
assert tstConf.lastPath() == tmpStuff
fileStuff.unlink()
tmpStuff.rmdir()
assert tmpConf.lastPath() == Path.home().absolute()
assert tstConf.lastPath() == Path.home().absolute()
# Recent Projects
assert isinstance(tmpConf.recentProjects, RecentProjects)
assert isinstance(tstConf.recentProjects, RecentProjects)
# END Test testBaseConfig_Methods
@pytest.mark.base
def testBaseConfig_SettersGetters(tmpConf):
def testBaseConfig_SettersGetters(fncPath):
"""Set various sizes and positions
"""
tstConf = Config()
tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
# 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
tstConf.guiScale = 1.0
assert tstConf.pxInt(10) == 10
assert tstConf.pxInt(13) == 13
assert tstConf.rpxInt(10) == 10
assert tstConf.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
tstConf.guiScale = 2.0
assert tstConf.pxInt(10) == 20
assert tstConf.pxInt(13) == 26
assert tstConf.rpxInt(10) == 5
assert tstConf.rpxInt(13) == 6
# Setter + Getter Combos
# ======================
# Window Size
tmpConf.guiScale = 1.0
tmpConf.setMainWinSize(1205, 655)
assert tmpConf.mainWinSize == [1200, 650]
tstConf.guiScale = 1.0
tstConf.setMainWinSize(1205, 655)
assert tstConf.mainWinSize == [1200, 650]
tmpConf.guiScale = 2.0
tmpConf.setMainWinSize(70, 70)
assert tmpConf.mainWinSize == [70, 70]
assert tmpConf._mainWinSize == [35, 35]
tstConf.guiScale = 2.0
tstConf.setMainWinSize(70, 70)
assert tstConf.mainWinSize == [70, 70]
assert tstConf._mainWinSize == [35, 35]
tmpConf.guiScale = 1.0
tmpConf.setMainWinSize(70, 70)
assert tmpConf.mainWinSize == [70, 70]
assert tmpConf._mainWinSize == [70, 70]
tstConf.guiScale = 1.0
tstConf.setMainWinSize(70, 70)
assert tstConf.mainWinSize == [70, 70]
assert tstConf._mainWinSize == [70, 70]
tmpConf.setMainWinSize(1200, 650)
tstConf.setMainWinSize(1200, 650)
# Preferences Size
tmpConf.guiScale = 2.0
tmpConf.setPreferencesWinSize(70, 70)
assert tmpConf.preferencesWinSize == [70, 70]
assert tmpConf._prefsWinSize == [35, 35]
tstConf.guiScale = 2.0
tstConf.setPreferencesWinSize(70, 70)
assert tstConf.preferencesWinSize == [70, 70]
assert tstConf._prefsWinSize == [35, 35]
tmpConf.guiScale = 1.0
tmpConf.setPreferencesWinSize(70, 70)
assert tmpConf.preferencesWinSize == [70, 70]
assert tmpConf._prefsWinSize == [70, 70]
tstConf.guiScale = 1.0
tstConf.setPreferencesWinSize(70, 70)
assert tstConf.preferencesWinSize == [70, 70]
assert tstConf._prefsWinSize == [70, 70]
tmpConf.setPreferencesWinSize(700, 615)
tstConf.setPreferencesWinSize(700, 615)
# Project Settings Tree Columns
tmpConf.guiScale = 2.0
tmpConf.setProjLoadColWidths([10, 20, 30])
assert tmpConf.projLoadColWidths == [10, 20, 30]
assert tmpConf._projLoadCols == [5, 10, 15]
tstConf.guiScale = 2.0
tstConf.setProjLoadColWidths([10, 20, 30])
assert tstConf.projLoadColWidths == [10, 20, 30]
assert tstConf._projLoadCols == [5, 10, 15]
tmpConf.guiScale = 1.0
tmpConf.setProjLoadColWidths([10, 20, 30])
assert tmpConf.projLoadColWidths == [10, 20, 30]
assert tmpConf._projLoadCols == [10, 20, 30]
tstConf.guiScale = 1.0
tstConf.setProjLoadColWidths([10, 20, 30])
assert tstConf.projLoadColWidths == [10, 20, 30]
assert tstConf._projLoadCols == [10, 20, 30]
tmpConf.setProjLoadColWidths([200, 60, 140])
tstConf.setProjLoadColWidths([200, 60, 140])
# Main Pane Splitter
tmpConf.guiScale = 2.0
tmpConf.setMainPanePos([200, 700])
assert tmpConf.mainPanePos == [200, 700]
assert tmpConf._mainPanePos == [100, 350]
tstConf.guiScale = 2.0
tstConf.setMainPanePos([200, 700])
assert tstConf.mainPanePos == [200, 700]
assert tstConf._mainPanePos == [100, 350]
tmpConf.guiScale = 1.0
tmpConf.setMainPanePos([200, 700])
assert tmpConf.mainPanePos == [200, 700]
assert tmpConf._mainPanePos == [200, 700]
tstConf.guiScale = 1.0
tstConf.setMainPanePos([200, 700])
assert tstConf.mainPanePos == [200, 700]
assert tstConf._mainPanePos == [200, 700]
tmpConf.setMainPanePos([300, 800])
tstConf.setMainPanePos([300, 800])
# View Pane Splitter
tmpConf.guiScale = 2.0
tmpConf.setViewPanePos([400, 250])
assert tmpConf.viewPanePos == [400, 250]
assert tmpConf._viewPanePos == [200, 125]
tstConf.guiScale = 2.0
tstConf.setViewPanePos([400, 250])
assert tstConf.viewPanePos == [400, 250]
assert tstConf._viewPanePos == [200, 125]
tmpConf.guiScale = 1.0
tmpConf.setViewPanePos([400, 250])
assert tmpConf.viewPanePos == [400, 250]
assert tmpConf._viewPanePos == [400, 250]
tstConf.guiScale = 1.0
tstConf.setViewPanePos([400, 250])
assert tstConf.viewPanePos == [400, 250]
assert tstConf._viewPanePos == [400, 250]
tmpConf.setViewPanePos([500, 150])
tstConf.setViewPanePos([500, 150])
# Outline Pane Splitter
tmpConf.guiScale = 2.0
tmpConf.setOutlinePanePos([400, 250])
assert tmpConf.outlinePanePos == [400, 250]
assert tmpConf._outlnPanePos == [200, 125]
tstConf.guiScale = 2.0
tstConf.setOutlinePanePos([400, 250])
assert tstConf.outlinePanePos == [400, 250]
assert tstConf._outlnPanePos == [200, 125]
tmpConf.guiScale = 1.0
tmpConf.setOutlinePanePos([400, 250])
assert tmpConf.outlinePanePos == [400, 250]
assert tmpConf._outlnPanePos == [400, 250]
tstConf.guiScale = 1.0
tstConf.setOutlinePanePos([400, 250])
assert tstConf.outlinePanePos == [400, 250]
assert tstConf._outlnPanePos == [400, 250]
tmpConf.setOutlinePanePos([500, 150])
tstConf.setOutlinePanePos([500, 150])
# Getters Only
# ============
tmpConf.guiScale = 1.0
assert tmpConf.getTextWidth(False) == 700
assert tmpConf.getTextWidth(True) == 800
assert tmpConf.getTextMargin() == 40
assert tmpConf.getTabWidth() == 40
tstConf.guiScale = 1.0
assert tstConf.getTextWidth(False) == 700
assert tstConf.getTextWidth(True) == 800
assert tstConf.getTextMargin() == 40
assert tstConf.getTabWidth() == 40
tmpConf.guiScale = 2.0
assert tmpConf.getTextWidth(False) == 1400
assert tmpConf.getTextWidth(True) == 1600
assert tmpConf.getTextMargin() == 80
assert tmpConf.getTabWidth() == 80
tstConf.guiScale = 2.0
assert tstConf.getTextWidth(False) == 1400
assert tstConf.getTextWidth(True) == 1600
assert tstConf.getTextMargin() == 80
assert tstConf.getTabWidth() == 80
# END Test testBaseConfig_SettersGetters
@pytest.mark.base
def testBaseConfig_Internal(monkeypatch, tmpConf):
def testBaseConfig_Internal(monkeypatch, fncPath):
"""Check internal functions.
"""
tstConf = Config()
tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
# Function _packList
assert tmpConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False"
assert tstConf._packList(["A", 1, 2.0, None, False]) == "A, 1, 2.0, None, False"
# Function _checkNone
assert tmpConf._checkNone(None) is None
assert tmpConf._checkNone("None") is None
assert tmpConf._checkNone("none") is None
assert tmpConf._checkNone("NONE") is None
assert tmpConf._checkNone("NoNe") is None
assert tmpConf._checkNone(123456) == 123456
assert tstConf._checkNone(None) is None
assert tstConf._checkNone("None") is None
assert tstConf._checkNone("none") is None
assert tstConf._checkNone("NONE") is None
assert tstConf._checkNone("NoNe") is None
assert tstConf._checkNone(123456) == 123456
# Function _checkOptionalPackages
# (Assumes enchant package exists and is importable)
tmpConf._checkOptionalPackages()
assert tmpConf.hasEnchant is True
tstConf._checkOptionalPackages()
assert tstConf.hasEnchant is True
with monkeypatch.context() as mp:
mp.setitem(sys.modules, "enchant", None)
tmpConf._checkOptionalPackages()
assert tmpConf.hasEnchant is False
tstConf._checkOptionalPackages()
assert tstConf.hasEnchant is False
# END Test testBaseConfig_Internal
@pytest.mark.base
def testBaseConfig_RecentCache(monkeypatch, fncConf, fncPath):
def testBaseConfig_RecentCache(monkeypatch, tstPaths):
"""Test recent cache file.
"""
cacheFile = fncPath / nwFiles.RECENT_FILE
recent = RecentProjects(fncConf)
cacheFile = tstPaths.cnfDir / nwFiles.RECENT_FILE
recent = RecentProjects(CONFIG)
# Load when there is no file should pass, but load nothing
assert not cacheFile.exists()
@@ -394,8 +404,8 @@ def testBaseConfig_RecentCache(monkeypatch, fncConf, fncPath):
assert recent.listEntries() == []
# Add a couple of values
pathOne = fncPath / "projPathOne" / nwFiles.PROJ_FILE
pathTwo = fncPath / "projPathTwo" / nwFiles.PROJ_FILE
pathOne = tstPaths.cnfDir / "projPathOne" / nwFiles.PROJ_FILE
pathTwo = tstPaths.cnfDir / "projPathTwo" / nwFiles.PROJ_FILE
recent.update(pathOne, "Proj One", 100, 1600002000)
recent.update(pathTwo, "Proj Two", 200, 1600005600)
+16 -16
View File
@@ -28,13 +28,13 @@ from mock import MockGuiMain
@pytest.mark.base
def testBaseInit_Launch(caplog, monkeypatch, tmpPath):
def testBaseInit_Launch(caplog, monkeypatch, fncPath):
"""Check launching the main GUI.
"""
monkeypatch.setattr("novelwriter.guimain.GuiMain", MockGuiMain)
# TestMode Launch
nwGUI = novelwriter.main(["--testmode", f"--config={tmpPath}", f"--data={tmpPath}"])
nwGUI = novelwriter.main(["--testmode", f"--config={fncPath}", f"--data={fncPath}"])
assert isinstance(nwGUI, MockGuiMain)
# Darwin Launch
@@ -43,7 +43,7 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpPath):
novelwriter.CONFIG.osDarwin = True
with monkeypatch.context() as mp:
mp.setitem(sys.modules, "Foundation", None)
nwGUI = novelwriter.main(["--testmode", f"--config={tmpPath}", f"--data={tmpPath}"])
nwGUI = novelwriter.main(["--testmode", f"--config={fncPath}", f"--data={fncPath}"])
assert isinstance(nwGUI, MockGuiMain)
assert "Failed" in caplog.text
@@ -55,7 +55,7 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpPath):
novelwriter.CONFIG.osWindows = True
with monkeypatch.context() as mp:
mp.setitem(sys.modules, "ctypes", None)
nwGUI = novelwriter.main(["--testmode", f"--config={tmpPath}", f"--data={tmpPath}"])
nwGUI = novelwriter.main(["--testmode", f"--config={fncPath}", f"--data={fncPath}"])
assert isinstance(nwGUI, MockGuiMain)
if not sys.platform.startswith("darwin"):
# For some reason, the test doesn't work on macOS
@@ -71,19 +71,19 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpPath):
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setOrganizationDomain", lambda *a: None)
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.exec_", lambda *a: 0)
with pytest.raises(SystemExit) as ex:
novelwriter.main([f"--config={tmpPath}", f"--data={tmpPath}"])
novelwriter.main([f"--config={fncPath}", f"--data={fncPath}"])
assert ex.value.code == 0
# END Test testBaseInit_Launch
@pytest.mark.base
def testBaseInit_Options(monkeypatch, tmpPath):
def testBaseInit_Options(monkeypatch, fncPath):
"""Test command line options for logging level.
"""
monkeypatch.setattr("novelwriter.guimain.GuiMain", MockGuiMain)
monkeypatch.setattr(sys, "argv", [
"novelWriter.py", "--testmode", f"--config={tmpPath}", f"--data={tmpPath}"
"novelWriter.py", "--testmode", f"--config={fncPath}", f"--data={fncPath}"
])
# Defaults w/None Args
@@ -93,20 +93,20 @@ def testBaseInit_Options(monkeypatch, tmpPath):
# Defaults
nwGUI = novelwriter.main(
["--testmode", f"--config={tmpPath}", f"--data={tmpPath}", "--style=Fusion"]
["--testmode", f"--config={fncPath}", f"--data={fncPath}", "--style=Fusion"]
)
assert novelwriter.logger.getEffectiveLevel() == logging.WARNING
assert nwGUI.closeMain() == "closeMain"
# Log Levels
nwGUI = novelwriter.main(
["--testmode", "--info", f"--config={tmpPath}", f"--data={tmpPath}"]
["--testmode", "--info", f"--config={fncPath}", f"--data={fncPath}"]
)
assert novelwriter.logger.getEffectiveLevel() == logging.INFO
assert nwGUI.closeMain() == "closeMain"
nwGUI = novelwriter.main(
["--testmode", "--debug", f"--config={tmpPath}", f"--data={tmpPath}"]
["--testmode", "--debug", f"--config={fncPath}", f"--data={fncPath}"]
)
assert novelwriter.logger.getEffectiveLevel() == logging.DEBUG
assert nwGUI.closeMain() == "closeMain"
@@ -114,14 +114,14 @@ def testBaseInit_Options(monkeypatch, tmpPath):
# Help and Version
with pytest.raises(SystemExit) as ex:
nwGUI = novelwriter.main(
["--testmode", "--help", f"--config={tmpPath}", f"--data={tmpPath}"]
["--testmode", "--help", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI.closeMain() == "closeMain"
assert ex.value.code == 0
with pytest.raises(SystemExit) as ex:
nwGUI = novelwriter.main(
["--testmode", "--version", f"--config={tmpPath}", f"--data={tmpPath}"]
["--testmode", "--version", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI.closeMain() == "closeMain"
assert ex.value.code == 0
@@ -129,14 +129,14 @@ def testBaseInit_Options(monkeypatch, tmpPath):
# Invalid options
with pytest.raises(SystemExit) as ex:
nwGUI = novelwriter.main(
["--testmode", "--invalid", f"--config={tmpPath}", f"--data={tmpPath}"]
["--testmode", "--invalid", f"--config={fncPath}", f"--data={fncPath}"]
)
assert nwGUI.closeMain() == "closeMain"
assert ex.value.code == 2
# Project Path
nwGUI = novelwriter.main(
["--testmode", f"--config={tmpPath}", f"--data={tmpPath}", "sample/"]
["--testmode", f"--config={fncPath}", f"--data={fncPath}", "sample/"]
)
assert nwGUI.closeMain() == "closeMain"
@@ -144,7 +144,7 @@ def testBaseInit_Options(monkeypatch, tmpPath):
@pytest.mark.base
def testBaseInit_Imports(caplog, monkeypatch, tmpPath):
def testBaseInit_Imports(caplog, monkeypatch, fncPath):
"""Check import error handling.
"""
monkeypatch.setattr("novelwriter.guimain.GuiMain", MockGuiMain)
@@ -160,7 +160,7 @@ def testBaseInit_Imports(caplog, monkeypatch, tmpPath):
with pytest.raises(SystemExit) as ex:
_ = novelwriter.main(
["--testmode", f"--config={tmpPath}", f"--data={tmpPath}"]
["--testmode", f"--config={fncPath}", f"--data={fncPath}"]
)
assert ex.value.code & 4 == 4 # Python version not satisfied
+5 -4
View File
@@ -28,6 +28,7 @@ from zipfile import ZipFile
from mock import causeOSError
from tools import C, buildTestProject, cmpFiles, XML_IGNORE
from novelwriter import CONFIG
from novelwriter.constants import nwItemClass
from novelwriter.core.project import NWProject
from novelwriter.core.coretools import DocMerger, DocSplitter, ProjectBuilder
@@ -371,7 +372,7 @@ def testCoreTools_NewCustomB(monkeypatch, fncPath, tstPaths, mockGUI, mockRnd):
@pytest.mark.core
def testCoreTools_NewSample(monkeypatch, fncPath, tmpConf, tmpPath, mockGUI):
def testCoreTools_NewSample(monkeypatch, fncPath, tstPaths, mockGUI):
"""Check that we can create a new project can be created from the
provided sample project via a zip file.
"""
@@ -391,10 +392,10 @@ def testCoreTools_NewSample(monkeypatch, fncPath, tmpConf, tmpPath, mockGUI):
assert projBuild.buildProject({"popSample": True}) is False
# Force the lookup path for assets to our temp folder
srcSample = tmpConf._appRoot / "sample"
dstSample = tmpPath / "sample.zip"
srcSample = CONFIG._appRoot / "sample"
dstSample = tstPaths.tmpDir / "sample.zip"
monkeypatch.setattr(
"novelwriter.config.Config.assetPath", lambda *a: tmpPath / "sample.zip"
"novelwriter.config.Config.assetPath", lambda *a: tstPaths.tmpDir / "sample.zip"
)
# Cannot extract when the zip does not exist
+7 -12
View File
@@ -704,7 +704,7 @@ def testCoreProject_OrphanedFiles(mockGUI, prjLipsum):
@pytest.mark.core
def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tmpPath):
def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tstPaths):
"""Test the automated backup feature of the project class. The test
creates a backup of the Minimal test project, and then unzips the
backupd file and checks that the project XML file is identical to
@@ -720,23 +720,18 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tmpPath):
# Invalid Settings
# ================
# No project
mockGUI.hasProject = False
assert theProject.backupProject(doNotify=False) is False
mockGUI.hasProject = True
# Invalid path
theProject.mainConf._backupPath = None
assert theProject.backupProject(doNotify=False) is False
# Missing project name
theProject.mainConf._backupPath = tmpPath
theProject.mainConf._backupPath = tstPaths.tmpDir
theProject.data.setName("")
assert theProject.backupProject(doNotify=False) is False
# Valid Settings
# ==============
theProject.mainConf._backupPath = tmpPath
theProject.mainConf._backupPath = tstPaths.tmpDir
theProject.data.setName("Test Minimal")
# Can't make folder
@@ -752,7 +747,7 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tmpPath):
# Test correct settings
assert theProject.backupProject(doNotify=True) is True
theFiles = list((tmpPath / "Test Minimal").iterdir())
theFiles = list((tstPaths.tmpDir / "Test Minimal").iterdir())
assert len(theFiles) == 1
theZip = theFiles[0].name
@@ -760,13 +755,13 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tmpPath):
assert theZip[-4:] == ".zip"
# Extract the archive
with ZipFile(tmpPath / "Test Minimal" / theZip, mode="r") as inZip:
inZip.extractall(tmpPath / "extract")
with ZipFile(tstPaths.tmpDir / "Test Minimal" / theZip, mode="r") as inZip:
inZip.extractall(tstPaths.tmpDir / "extract")
# Check that the main project file was restored
assert cmpFiles(
fncPath / "nwProject.nwx",
tmpPath / "extract" / "nwProject.nwx"
tstPaths.tmpDir / "extract" / "nwProject.nwx"
)
# END Test testCoreProject_Backup
+2 -2
View File
@@ -299,10 +299,10 @@ def testCoreStorage_PrepareStorage(monkeypatch, fncPath):
@pytest.mark.core
def testCoreStorage_ZipIt(monkeypatch, mockGUI, fncPath, tmpPath, mockRnd):
def testCoreStorage_ZipIt(monkeypatch, mockGUI, fncPath, tstPaths, mockRnd):
"""Test making a zip archive of a project.
"""
zipFile = tmpPath / "project.zip"
zipFile = tstPaths.tmpDir / "project.zip"
theProject = NWProject(mockGUI)
storage = theProject.storage
+5 -5
View File
@@ -437,7 +437,7 @@ def testCoreTree_Reorder(caplog, mockGUI, mockItems):
@pytest.mark.core
def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpPath):
def testCoreTree_ToCFile(monkeypatch, tstPaths, mockGUI, mockItems):
"""Test writing the ToC.txt file.
"""
theProject = NWProject(mockGUI)
@@ -463,20 +463,20 @@ def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpPath):
theProject._storage._runtimePath = None
assert theTree.writeToCFile() is False
theProject._storage._runtimePath = tmpPath
theProject._storage._runtimePath = tstPaths.tmpDir
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert theTree.writeToCFile() is False
theProject._storage._runtimePath = tmpPath
(tmpPath / "content").mkdir()
theProject._storage._runtimePath = tstPaths.tmpDir
(tstPaths.tmpDir / "content").mkdir()
assert theTree.writeToCFile() is True
pathA = str(Path("content") / "c000000000001.nwd")
pathB = str(Path("content") / "c000000000002.nwd")
pathC = str(Path("content") / "b000000000002.nwd")
assert readFile(tmpPath / nwFiles.TOC_TXT) == (
assert readFile(tstPaths.tmpDir / nwFiles.TOC_TXT) == (
"\n"
"Table of Contents\n"
"=================\n"
+2 -6
View File
@@ -37,12 +37,9 @@ KEY_DELAY = 1
@pytest.mark.gui
def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
"""Test the load project wizard.
"""
theConf = nwGUI.mainConf
assert theConf._confPath == fncPath
monkeypatch.setattr(GuiPreferences, "exec_", lambda *a: None)
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")])
@@ -58,7 +55,6 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
nwPrefs = getGuiItem("GuiPreferences")
assert isinstance(nwPrefs, GuiPreferences)
nwPrefs.show()
assert nwPrefs.mainConf._confPath == fncPath
assert nwPrefs.updateTheme is False
assert nwPrefs.updateSyntax is False
@@ -216,7 +212,7 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
nwPrefs._doClose()
assert nwGUI.mainConf.saveConfig()
projFile = fncPath / "novelwriter.conf"
projFile = tstPaths.cnfDir / "novelwriter.conf"
testFile = tstPaths.outDir / "guiPreferences_novelwriter.conf"
compFile = tstPaths.refDir / "guiPreferences_novelwriter.conf"
copyfile(projFile, testFile)
+3 -3
View File
@@ -91,7 +91,7 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR
# Create new project
buildTestProject(nwGUI, projPath)
mockRnd.reset()
nwGUI.mainConf.backupPath = fncPath
nwGUI.mainConf.setBackupPath(fncPath)
# Set some values
theProject = nwGUI.theProject
@@ -156,7 +156,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
# Create new project
mockRnd.reset()
buildTestProject(nwGUI, projPath)
nwGUI.mainConf.backupPath = fncPath
nwGUI.mainConf.setBackupPath(fncPath)
# Set some values
theProject = nwGUI.theProject
@@ -357,7 +357,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mo
# Create new project
mockRnd.reset()
buildTestProject(nwGUI, projPath)
nwGUI.mainConf.backupPath = fncPath
nwGUI.mainConf.setBackupPath(fncPath)
# Set some values
theProject = nwGUI.theProject
+7 -12
View File
@@ -20,20 +20,20 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import sys
import novelwriter
import pytest
from PyQt5.QtWidgets import qApp, QMessageBox
LANG_DATA = novelwriter.CONFIG.listLanguages(novelwriter.CONFIG.LANG_NW)
from novelwriter import CONFIG, main
LANG_DATA = CONFIG.listLanguages(CONFIG.LANG_NW)
@pytest.mark.gui
@pytest.mark.skipif(not sys.platform.startswith("linux"), reason="Linux Only")
@pytest.mark.skipif(not LANG_DATA, reason="No i18n Data")
@pytest.mark.parametrize("language", [a for a, b in LANG_DATA])
def testI18n_Localisation(qtbot, monkeypatch, language, fncPath, fncConf):
def testI18n_Localisation(qtbot, monkeypatch, language, fncPath):
"""test loading the gui with a specific language.
"""
monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Ok)
@@ -42,18 +42,13 @@ def testI18n_Localisation(qtbot, monkeypatch, language, fncPath, fncConf):
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
# Set the test langauge
monkeypatch.setattr("novelwriter.CONFIG", fncConf)
fncConf.guiLocale = language
fncConf.initLocalisation(qApp)
CONFIG.guiLocale = language
CONFIG.initLocalisation(qApp)
nwGUI = novelwriter.main(["--testmode", f"--config={fncPath}", f"--data={fncPath}"])
nwGUI = main(["--testmode", f"--config={fncPath}", f"--data={fncPath}"])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.wait(20)
nwGUI.closeMain()
# Reset the app language
fncConf.guiLocale = "en_GB"
fncConf.initLocalisation(qApp)
# END Test testI18n_Localisation
+26 -36
View File
@@ -19,7 +19,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import shutil
import pytest
from pathlib import Path
@@ -31,18 +30,17 @@ from tools import writeFile
from PyQt5.QtGui import QIcon, QPalette, QPixmap
from PyQt5.QtWidgets import QApplication
from novelwriter import CONFIG
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
from novelwriter.config import Config
from novelwriter.constants import nwLabels
from novelwriter.gui.theme import GuiIcons, GuiTheme
@pytest.mark.gui
def testGuiTheme_Main(qtbot, nwGUI, fncPath):
def testGuiTheme_Main(qtbot, nwGUI, tstPaths):
"""Test the theme class init.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
mainConf: Config = nwGUI.mainConf
# Methods
# =======
@@ -55,35 +53,35 @@ def testGuiTheme_Main(qtbot, nwGUI, fncPath):
# ==========
# The defaults should be set
defaultFont = mainConf.guiFont
defaultSize = mainConf.guiFontSize
defaultFont = CONFIG.guiFont
defaultSize = CONFIG.guiFontSize
# CHange them to nonsense values
mainConf.guiFont = "notafont"
mainConf.guiFontSize = 99
CONFIG.guiFont = "notafont"
CONFIG.guiFontSize = 99
# Let the theme class set them back to default
mainTheme._setGuiFont()
assert mainConf.guiFont == defaultFont
assert mainConf.guiFontSize == defaultSize
assert CONFIG.guiFont == defaultFont
assert CONFIG.guiFontSize == defaultSize
# A second call should just restore the defaults again
mainTheme._setGuiFont()
assert mainConf.guiFont == defaultFont
assert mainConf.guiFontSize == defaultSize
assert CONFIG.guiFont == defaultFont
assert CONFIG.guiFontSize == defaultSize
# Scan for Themes
# ===============
assert mainTheme._listConf({}, Path("not_a_path")) is False
themeOne = fncPath / "themes" / "themeone.conf"
themeTwo = fncPath / "themes" / "themetwo.conf"
themeOne = tstPaths.cnfDir / "themes" / "themeone.conf"
themeTwo = tstPaths.cnfDir / "themes" / "themetwo.conf"
writeFile(themeOne, "# Stuff")
writeFile(themeTwo, "# Stuff")
result = {}
assert mainTheme._listConf(result, fncPath / "themes") is True
assert mainTheme._listConf(result, tstPaths.cnfDir / "themes") is True
assert result["themeone"] == themeOne
assert result["themetwo"] == themeTwo
@@ -123,18 +121,14 @@ def testGuiTheme_Main(qtbot, nwGUI, fncPath):
@pytest.mark.gui
def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncPath):
def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI):
"""Test the theme part of the class.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
mainConf: Config = nwGUI.mainConf
# List Themes
# ===========
shutil.copy(mainConf.assetPath("themes") / "default_dark.conf", fncPath / "themes")
shutil.copy(mainConf.assetPath("themes") / "default.conf", fncPath / "themes")
# Block the reading of the files
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
@@ -149,14 +143,14 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncPath):
assert mainTheme.listThemes() == mainTheme._themeList
# Check handling of broken theme settings
mainConf.guiTheme = "not_a_theme"
CONFIG.guiTheme = "not_a_theme"
availThemes = mainTheme._availThemes
mainTheme._availThemes = {}
assert mainTheme.loadTheme() is False
mainTheme._availThemes = availThemes
# Check handling of unreadable file
mainConf.guiTheme = "default"
CONFIG.guiTheme = "default"
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert mainTheme.loadTheme() is False
@@ -168,7 +162,7 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncPath):
mainTheme._guiPalette.color(QPalette.Window).setRgb(0, 0, 0, 0)
# Load the default theme
mainConf.guiTheme = "default"
CONFIG.guiTheme = "default"
assert mainTheme.loadTheme() is True
# This should load a standard palette
@@ -178,7 +172,7 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncPath):
# Load Default Dark Theme
# =======================
mainConf.guiTheme = "default_dark"
CONFIG.guiTheme = "default_dark"
assert mainTheme.loadTheme() is True
# Check a few values
@@ -193,18 +187,14 @@ def testGuiTheme_Theme(qtbot, monkeypatch, nwGUI, fncPath):
@pytest.mark.gui
def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncPath):
def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI):
"""Test the syntax part of the class.
"""
mainTheme: GuiTheme = nwGUI.mainTheme
mainConf: Config = nwGUI.mainConf
# List Themes
# ===========
shutil.copy(mainConf.assetPath("syntax") / "default_dark.conf", fncPath / "syntax")
shutil.copy(mainConf.assetPath("syntax") / "default_light.conf", fncPath / "syntax")
# Block the reading of the files
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
@@ -221,12 +211,12 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncPath):
# Check handling of broken theme settings
availSyntax = mainTheme._availSyntax
mainTheme._availSyntax = {}
mainConf.guiSyntax = "not_a_syntax"
CONFIG.guiSyntax = "not_a_syntax"
assert mainTheme.loadSyntax() is False
mainTheme._availSyntax = availSyntax
# Check handling of unreadable file
mainConf.guiSyntax = "default_light"
CONFIG.guiSyntax = "default_light"
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert mainTheme.loadSyntax() is False
@@ -235,7 +225,7 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncPath):
# =========================
# Load the default syntax
mainConf.guiSyntax = "default_light"
CONFIG.guiSyntax = "default_light"
assert mainTheme.loadSyntax() is True
# Check some values
@@ -248,7 +238,7 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncPath):
# =======================
# Load the default syntax
mainConf.guiSyntax = "default_dark"
CONFIG.guiSyntax = "default_dark"
assert mainTheme.loadSyntax() is True
# Check some values
@@ -263,7 +253,7 @@ def testGuiTheme_Syntax(qtbot, monkeypatch, nwGUI, fncPath):
@pytest.mark.gui
def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncPath):
def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, tstPaths):
"""Test the icon cache class.
"""
iconCache: GuiIcons = nwGUI.mainTheme.iconCache
@@ -280,7 +270,7 @@ def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncPath):
assert iconCache.loadTheme("typicons_dark") is False
# Load a broken theme file
iconsDir = fncPath / "icons"
iconsDir = tstPaths.cnfDir / "icons"
testIcons = iconsDir / "testicons"
testIcons.mkdir()
writeFile(testIcons / "icons.conf", (
@@ -293,7 +283,7 @@ def testGuiTheme_Icons(qtbot, caplog, monkeypatch, nwGUI, fncPath):
))
iconPath = iconCache._iconPath
iconCache._iconPath = fncPath / "icons"
iconCache._iconPath = tstPaths.cnfDir / "icons"
caplog.clear()
assert iconCache.loadTheme("testicons") is True
+7 -10
View File
@@ -33,7 +33,7 @@ from novelwriter.tools.writingstats import GuiWritingStats
@pytest.mark.gui
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath):
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, projPath, tstPaths):
"""Test the full writing stats tool.
"""
# Create a project to work on
@@ -126,13 +126,10 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath):
assert sessLog.listBox.topLevelItem(7).text(sessLog.C_COUNT) == "{:n}".format(200)
assert sessLog._saveData(sessLog.FMT_CSV)
qtbot.wait(100)
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(100)
# Check the exported files
jsonStats = fncPath / "sessionStats.json"
jsonStats = tstPaths.tmpDir / "sessionStats.json"
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
@@ -171,7 +168,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath):
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = fncPath / "sessionStats.json"
jsonStats = tstPaths.tmpDir / "sessionStats.json"
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
@@ -217,7 +214,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath):
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = fncPath / "sessionStats.json"
jsonStats = tstPaths.tmpDir / "sessionStats.json"
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
@@ -265,7 +262,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath):
# qtbot.stop()
jsonStats = fncPath / "sessionStats.json"
jsonStats = tstPaths.tmpDir / "sessionStats.json"
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
@@ -295,7 +292,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath):
qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = fncPath / "sessionStats.json"
jsonStats = tstPaths.tmpDir / "sessionStats.json"
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)
@@ -348,7 +345,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath):
qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton)
assert sessLog._saveData(sessLog.FMT_JSON)
jsonStats = fncPath / "sessionStats.json"
jsonStats = tstPaths.tmpDir / "sessionStats.json"
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.load(inFile)