From e4d03f95814133086b31a7f56c29b3baa5d113d9 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 18 Feb 2021 19:21:46 +0100 Subject: [PATCH] Fix tests for variable change, and change monkeypatches to use the context manager --- sample/nwProject.nwx | 7 +- tests/conftest.py | 18 ++- tests/minimal/nwProject.nwx | 1 + tests/profilestats.py | 15 -- .../coreProject_NewCustomA_nwProject.nwx | 1 + .../coreProject_NewCustomB_nwProject.nwx | 1 + .../coreProject_NewFile_nwProject.nwx | 1 + .../coreProject_NewMinimal_nwProject.nwx | 1 + .../coreProject_NewRoot_nwProject.nwx | 1 + .../guiEditor_Main_Final_nwProject.nwx | 1 + .../guiEditor_Main_Initial_nwProject.nwx | 1 + .../guiItemEditor_Dialog_nwProject.nwx | 1 + .../guiProjSettings_Dialog_nwProject.nwx | 1 + tests/test_base/test_base_config.py | 143 ++++++++-------- tests/test_base/test_base_error.py | 66 ++++---- tests/test_base/test_base_init.py | 6 - tests/test_core/test_core_document.py | 18 +-- tests/test_core/test_core_index.py | 17 +- tests/test_core/test_core_options.py | 8 +- tests/test_core/test_core_project.py | 152 +++++++++--------- tests/test_core/test_core_spell.py | 56 +++---- tests/test_core/test_core_tokenizer.py | 20 ++- tests/test_core/test_core_toodt.py | 4 +- tests/test_core/test_core_tree.py | 2 - tests/test_gui/test_gui_mainmenu.py | 6 +- tests/test_gui/test_gui_projdetails.py | 1 - tests/test_gui/test_gui_projwizard.py | 19 ++- tests/test_gui/test_gui_writingstats.py | 2 - 28 files changed, 274 insertions(+), 296 deletions(-) delete mode 100755 tests/profilestats.py diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 52fba625..5f4ff79c 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,16 +1,17 @@ - + Sample Project Sample Project Jane Smith Jay Doh - 1021 + 1022 161 - 48283 + 48346 False + None True None True diff --git a/tests/conftest.py b/tests/conftest.py index 59fd5c5d..d7001c1d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -121,17 +121,31 @@ def tmpConf(tmpDir): return theConf @pytest.fixture(scope="function") -def dummyGUI(tmpConf): +def fncConf(fncDir): + """Create a temporary novelWriter configuration object. + """ + confFile = os.path.join(fncDir, "novelwriter.conf") + if os.path.isfile(confFile): + os.unlink(confFile) + theConf = Config() + theConf.initConfig(fncDir, fncDir) + theConf.setLastPath("") + return theConf + +@pytest.fixture(scope="function") +def dummyGUI(monkeypatch, tmpConf): """Create a dummy instance of novelWriter's main GUI class. """ + monkeypatch.setattr("nw.CONFIG", tmpConf) theDummy = DummyMain() theDummy.mainConf = tmpConf return theDummy @pytest.fixture(scope="function") -def nwGUI(qtbot, fncDir): +def nwGUI(qtbot, monkeypatch, fncDir, fncConf): """Create an instance of the novelWriter GUI. """ + monkeypatch.setattr("nw.CONFIG", fncConf) nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir]) qtbot.addWidget(nwGUI) nwGUI.show() diff --git a/tests/minimal/nwProject.nwx b/tests/minimal/nwProject.nwx index d45f9bfa..0405ea25 100644 --- a/tests/minimal/nwProject.nwx +++ b/tests/minimal/nwProject.nwx @@ -11,6 +11,7 @@ True + None False None True diff --git a/tests/profilestats.py b/tests/profilestats.py deleted file mode 100755 index 550aba00..00000000 --- a/tests/profilestats.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import pstats -import os - -profDir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, "prof")) - -print("") -print("Profiles directory: %s" % profDir) -print("") - -profMainWindows = pstats.Stats(os.path.join(profDir, "testMainWindows.prof")) -profMainWindows.sort_stats("cumtime") -profMainWindows.print_stats("nw/") diff --git a/tests/reference/coreProject_NewCustomA_nwProject.nwx b/tests/reference/coreProject_NewCustomA_nwProject.nwx index fc7e7ab2..7673815d 100644 --- a/tests/reference/coreProject_NewCustomA_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomA_nwProject.nwx @@ -11,6 +11,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewCustomB_nwProject.nwx b/tests/reference/coreProject_NewCustomB_nwProject.nwx index 11d007aa..baaf3298 100644 --- a/tests/reference/coreProject_NewCustomB_nwProject.nwx +++ b/tests/reference/coreProject_NewCustomB_nwProject.nwx @@ -11,6 +11,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewFile_nwProject.nwx b/tests/reference/coreProject_NewFile_nwProject.nwx index 66488434..83d80233 100644 --- a/tests/reference/coreProject_NewFile_nwProject.nwx +++ b/tests/reference/coreProject_NewFile_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewMinimal_nwProject.nwx b/tests/reference/coreProject_NewMinimal_nwProject.nwx index d8b774bb..ba867df1 100644 --- a/tests/reference/coreProject_NewMinimal_nwProject.nwx +++ b/tests/reference/coreProject_NewMinimal_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/coreProject_NewRoot_nwProject.nwx b/tests/reference/coreProject_NewRoot_nwProject.nwx index 9ea0617f..d437d4e2 100644 --- a/tests/reference/coreProject_NewRoot_nwProject.nwx +++ b/tests/reference/coreProject_NewRoot_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/guiEditor_Main_Final_nwProject.nwx b/tests/reference/guiEditor_Main_Final_nwProject.nwx index 764c4610..6e511bca 100644 --- a/tests/reference/guiEditor_Main_Final_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Final_nwProject.nwx @@ -9,6 +9,7 @@ True + None True None True diff --git a/tests/reference/guiEditor_Main_Initial_nwProject.nwx b/tests/reference/guiEditor_Main_Initial_nwProject.nwx index 77eff6e9..96af56bb 100644 --- a/tests/reference/guiEditor_Main_Initial_nwProject.nwx +++ b/tests/reference/guiEditor_Main_Initial_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/guiItemEditor_Dialog_nwProject.nwx b/tests/reference/guiItemEditor_Dialog_nwProject.nwx index 7bcf7553..89beddc9 100644 --- a/tests/reference/guiItemEditor_Dialog_nwProject.nwx +++ b/tests/reference/guiItemEditor_Dialog_nwProject.nwx @@ -9,6 +9,7 @@ True + None False None True diff --git a/tests/reference/guiProjSettings_Dialog_nwProject.nwx b/tests/reference/guiProjSettings_Dialog_nwProject.nwx index ea349d11..a935bee7 100644 --- a/tests/reference/guiProjSettings_Dialog_nwProject.nwx +++ b/tests/reference/guiProjSettings_Dialog_nwProject.nwx @@ -11,6 +11,7 @@ True + None False en True diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py index 1197ec19..9a0a561b 100644 --- a/tests/test_base/test_base_config.py +++ b/tests/test_base/test_base_config.py @@ -44,7 +44,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is False assert tstConf.osUnknown is False - monkeypatch.undo() # macOS monkeypatch.setattr("sys.platform", "darwin") @@ -53,7 +52,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is True assert tstConf.osWindows is False assert tstConf.osUnknown is False - monkeypatch.undo() # Windows monkeypatch.setattr("sys.platform", "win32") @@ -62,7 +60,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is True assert tstConf.osUnknown is False - monkeypatch.undo() # Cygwin monkeypatch.setattr("sys.platform", "cygwin") @@ -71,7 +68,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is True assert tstConf.osUnknown is False - monkeypatch.undo() # Other monkeypatch.setattr("sys.platform", "some_other_os") @@ -80,7 +76,6 @@ def testBaseConfig_Constructor(monkeypatch): assert tstConf.osDarwin is False assert tstConf.osWindows is False assert tstConf.osUnknown is True - monkeypatch.undo() # END Test testBaseConfig_Constructor @@ -99,36 +94,35 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir): 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() + with monkeypatch.context() as mp: + mp.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) # Fail to make folders - monkeypatch.setattr("os.mkdir", causeOSError) + with monkeypatch.context() as mp: + mp.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) + 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() + 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) # Test load/save with no path tstConf.confPath = None @@ -137,35 +131,34 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir): # 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) + with monkeypatch.context() as mp: + mp.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, 10]) - monkeypatch.undo() + copyfile(confFile, testFile) + assert cmpFiles(testFile, compFile, [2, 9, 10]) # Load and save with OSError - monkeypatch.setattr("builtins.open", causeOSError) + with monkeypatch.context() as mp: + mp.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.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 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 == [] assert tstConf.loadConfig() assert tstConf.saveConfig() @@ -233,9 +226,9 @@ def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir): } # Fail to Save - monkeypatch.setattr("builtins.open", causeOSError) - assert not tmpConf.saveRecentCache() - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not tmpConf.saveRecentCache() # Save Proper cacheFile = os.path.join(tmpDir, nwFiles.RECENT_FILE) @@ -244,11 +237,11 @@ def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir): assert os.path.isfile(cacheFile) # Fail to Load - monkeypatch.setattr("builtins.open", causeOSError) - tmpConf.recentProj = {} - assert not tmpConf.loadRecentCache() - assert tmpConf.recentProj == {} - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + tmpConf.recentProj = {} + assert not tmpConf.loadRecentCache() + assert tmpConf.recentProj == {} # Load Proper tmpConf.recentProj = {} @@ -555,19 +548,19 @@ def testBaseConfig_Internal(monkeypatch, tmpConf): tmpConf._checkOptionalPackages() assert tmpConf.hasEnchant is True - monkeypatch.setitem(sys.modules, "enchant", None) - tmpConf._checkOptionalPackages() - assert tmpConf.hasEnchant is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setitem(sys.modules, "enchant", None) + tmpConf._checkOptionalPackages() + assert tmpConf.hasEnchant is False - monkeypatch.setattr("shutil.which", lambda *args: "dummy") - tmpConf._checkOptionalPackages() - assert tmpConf.hasAssistant is True - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("shutil.which", lambda *args: "dummy") + tmpConf._checkOptionalPackages() + assert tmpConf.hasAssistant is True - monkeypatch.setattr("shutil.which", lambda *args: None) - tmpConf._checkOptionalPackages() - assert tmpConf.hasAssistant is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("shutil.which", lambda *args: None) + tmpConf._checkOptionalPackages() + assert tmpConf.hasAssistant is False # END Test testBaseConfig_Internal diff --git a/tests/test_base/test_base_error.py b/tests/test_base/test_base_error.py index 45151413..9976fae2 100644 --- a/tests/test_base/test_base_error.py +++ b/tests/test_base/test_base_error.py @@ -48,22 +48,22 @@ def testBaseError_Dialog(qtbot, monkeypatch, fncDir, tmpDir): 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() + with monkeypatch.context() as mp: + mp.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 # 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() + with monkeypatch.context() as mp: + mp.setattr("PyQt5.QtCore.QSysInfo.kernelVersion", causeException) + nwErr.setMessage(Exception, "Almost Fine Error", None) + theMessage = nwErr.msgBody.toPlainText() + assert theMessage + assert "(Unknown)" in theMessage nwErr._doClose() nwErr.close() @@ -84,31 +84,31 @@ def testBaseError_Handler(qtbot, monkeypatch, fncDir, tmpDir): 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() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + exceptionHandler(Exception, "Error Message", None) # 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() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", lambda: []) + exceptionHandler(Exception, "Error Message", None) # 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() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.topLevelWidgets", causeException) + exceptionHandler(Exception, "Error Message", None) # 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() + with monkeypatch.context() as mp: + mp.setattr(NWErrorMessage, "exec_", lambda *args: None) + mp.setattr("PyQt5.QtWidgets.qApp.exit", lambda *args: None) + mp.setattr(nwGUI, "closeMain", causeException) + exceptionHandler(Exception, "Error Message", None) nwGUI.closeMain() diff --git a/tests/test_base/test_base_init.py b/tests/test_base/test_base_init.py index 923cfd4e..fb906786 100644 --- a/tests/test_base/test_base_init.py +++ b/tests/test_base/test_base_init.py @@ -62,8 +62,6 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpDir): assert ex.value.code == 0 - monkeypatch.undo() - # END Test testBaseInit_Launch @pytest.mark.base @@ -136,8 +134,6 @@ def testBaseInit_Options(monkeypatch, tmpDir): assert nw.CONFIG.cmdOpen == "sample/" assert nwGUI.closeMain() == "closeMain" - monkeypatch.undo() - # END Test testBaseInit_Options @pytest.mark.base @@ -170,6 +166,4 @@ def testBaseInit_Imports(caplog, monkeypatch, tmpDir): assert "At least PyQt5" in caplog.messages[2] assert "lxml" in caplog.messages[3] - monkeypatch.undo() - # END Test testBaseInit_Imports diff --git a/tests/test_core/test_core_document.py b/tests/test_core/test_core_document.py index bbfe658b..f1084f84 100644 --- a/tests/test_core/test_core_document.py +++ b/tests/test_core/test_core_document.py @@ -50,9 +50,9 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): def dummyOpen(*args, **kwargs): raise OSError - monkeypatch.setattr("builtins.open", dummyOpen) - assert theDoc.openDocument(sHandle) is None - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", dummyOpen) + assert theDoc.openDocument(sHandle) is None # Load the text assert theDoc.openDocument(sHandle) == "### New Scene\n\n" @@ -95,9 +95,9 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): assert inFile.read() == theText # Cause open() to fail while saving - monkeypatch.setattr("builtins.open", causeOSError) - assert not theDoc.saveDocument(theText) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not theDoc.saveDocument(theText) # Saving with no handle theDoc.clearDocument() @@ -108,9 +108,9 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): assert os.path.isfile(docPath) # Cause the delete to fail - monkeypatch.setattr("os.unlink", causeOSError) - assert not theDoc.deleteDocument(xHandle) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.unlink", causeOSError) + assert not theDoc.deleteDocument(xHandle) # Make the delete pass assert theDoc.deleteDocument(xHandle) diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 2d5f8f12..6cec1aab 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -26,6 +26,7 @@ import json from shutil import copyfile +from dummy import causeException from tools import cmpFiles from nw.core.project import NWProject @@ -61,16 +62,12 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir): assert not theIndex.reIndexHandle(None) - # Dummy exception function - def doPanic(*arg, **kwargs): - raise Exception - # Make the save fail - monkeypatch.setattr(json, "dump", doPanic) - assert not theIndex.saveIndex() + with monkeypatch.context() as mp: + mp.setattr(json, "dump", causeException) + assert not theIndex.saveIndex() # Make the save pass - monkeypatch.undo() assert theIndex.saveIndex() # Take a copy of the index @@ -100,11 +97,11 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir): assert not theIndex._textCounts # Make the load fail - monkeypatch.setattr(json, "load", doPanic) - assert not theIndex.loadIndex() + with monkeypatch.context() as mp: + mp.setattr(json, "load", causeException) + assert not theIndex.loadIndex() # Make the load pass - monkeypatch.undo() assert theIndex.loadIndex() assert str(theIndex._tagIndex) == tagIndex diff --git a/tests/test_core/test_core_options.py b/tests/test_core/test_core_options.py index 20b0b6a3..6bd26b29 100644 --- a/tests/test_core/test_core_options.py +++ b/tests/test_core/test_core_options.py @@ -64,10 +64,10 @@ def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir): assert theProject.projMeta == tmpDir # Cause open() to fail - monkeypatch.setattr("builtins.open", causeOSError) - assert not theOpts.loadSettings() - assert not theOpts.saveSettings() - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not theOpts.loadSettings() + assert not theOpts.saveSettings() # Load proper assert theOpts.loadSettings() diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index d6806984..41026efc 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -180,7 +180,6 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir): } theProject = NWProject(dummyGUI) theProject.projTree.setSeed(42) - theProject.mainConf = tmpConf # Sample set, but no path assert not theProject.newProject({"popSample": True}) @@ -229,7 +228,6 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir): } theProject = NWProject(dummyGUI) theProject.projTree.setSeed(42) - theProject.mainConf = tmpConf # Make sure we do not pick up the nw/assets/sample.zip file tmpConf.assetPath = tmpDir @@ -330,9 +328,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI): os.rename(wName, rName) # Fail on folder structure check - monkeypatch.setattr("os.mkdir", causeOSError) - assert theProject.openProject(nwMinimal) is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.mkdir", causeOSError) + assert theProject.openProject(nwMinimal) is False # Fail on lock file theProject.setProjectPath(nwMinimal) @@ -340,9 +338,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI): assert theProject.openProject(nwMinimal) is False # Fail to read lockfile (which still opens the project) - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject.openProject(nwMinimal) is True - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject.openProject(nwMinimal) is True assert theProject.closeProject() # Force open with lockfile @@ -452,14 +450,14 @@ def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir): assert theProject.openProject(nwMinimal) # Fail on folder structure check - monkeypatch.setattr("os.path.isdir", lambda *args: False) - assert theProject.saveProject() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.path.isdir", lambda *args: False) + assert theProject.saveProject() is False # Fail on open file - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject.saveProject() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject.saveProject() is False # Successful save saveCount = theProject.saveCount @@ -501,30 +499,30 @@ def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI): theProject.mainConf.kernelVer = "1.0" # Block open - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject._writeLockFile() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject._writeLockFile() is False # Write lock file - monkeypatch.setattr("nw.core.project.time", lambda: 123.4) - assert theProject._writeLockFile() is True - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.core.project.time", lambda: 123.4) + assert theProject._writeLockFile() is True assert readFile(lockFile) == "TestHost\nTestOS\n1.0\n123\n" # Block open - monkeypatch.setattr("builtins.open", causeOSError) - assert theProject._readLockFile() == ["ERROR"] - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert theProject._readLockFile() == ["ERROR"] # Read lock file assert theProject._readLockFile() == ["TestHost", "TestOS", "1.0", "123"] # Block unlink - monkeypatch.setattr("os.unlink", causeOSError) - assert os.path.isfile(lockFile) - assert theProject._clearLockFile() is False - assert os.path.isfile(lockFile) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.unlink", causeOSError) + assert os.path.isfile(lockFile) + assert theProject._clearLockFile() is False + assert os.path.isfile(lockFile) # Clear file assert os.path.isfile(lockFile) @@ -554,9 +552,9 @@ def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI): theProject.projPath = fncDir # Block user's home folder - monkeypatch.setattr("os.path.expanduser", lambda *args, **kwargs: fncDir) - assert theProject.ensureFolderStructure() is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.path.expanduser", lambda *args, **kwargs: fncDir) + assert theProject.ensureFolderStructure() is False # Create a file to block meta folder metaDir = os.path.join(fncDir, "meta") @@ -702,9 +700,9 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): # Edit Time theProject.editTime = 1234 theProject.projOpened = 1600000000 - monkeypatch.setattr("nw.core.project.time", lambda: 1600005600) - assert theProject.getCurrentEditTime() == 6834 - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.core.project.time", lambda: 1600005600) + assert theProject.getCurrentEditTime() == 6834 # Trash folder # Should create on first call, and just returned on later calls @@ -735,11 +733,11 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): # Spell language theProject.projChanged = False assert theProject.setSpellLang(None) - assert theProject.projLang is None + assert theProject.projSpell is None assert theProject.setSpellLang("None") - assert theProject.projLang is None + assert theProject.projSpell is None assert theProject.setSpellLang("en_GB") - assert theProject.projLang == "en_GB" + assert theProject.projSpell == "en_GB" assert theProject.projChanged # Automatic outline update @@ -839,14 +837,14 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): assert theProject.getSessionWordCount() == 100 # Session stats - monkeypatch.setattr("os.path.isdir", lambda *args, **kwargs: False) - assert not theProject._appendSessionStats(idleTime=0) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.path.isdir", lambda *args, **kwargs: False) + assert not theProject._appendSessionStats(idleTime=0) # Block open - monkeypatch.setattr("builtins.open", causeOSError) - assert not theProject._appendSessionStats(idleTime=0) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert not theProject._appendSessionStats(idleTime=0) # Write entry assert theProject.projMeta == os.path.join(nwMinimal, "meta") @@ -856,9 +854,9 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir): theProject.novelWCount = 200 theProject.notesWCount = 100 - monkeypatch.setattr("nw.core.project.time", lambda: 1600005600) - assert theProject._appendSessionStats(idleTime=99) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.core.project.time", lambda: 1600005600) + assert theProject._appendSessionStats(idleTime=99) assert readFile(statsFile) == ( "# Offset 100\n" @@ -1076,9 +1074,9 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir): writeFile(tstFile, "dummy") assert os.path.isfile(tstFile) - monkeypatch.setattr("os.unlink", causeOSError) - assert not theProject._deprecatedFiles() - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.unlink", causeOSError) + assert not theProject._deprecatedFiles() assert theProject._deprecatedFiles() assert not os.path.isfile(tstFile) @@ -1101,18 +1099,18 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir): assert os.path.isdir(errItem) # This causes a failure to create the 'junk' folder - monkeypatch.setattr("os.mkdir", causeOSError) - errList = [] - errList = theProject._legacyDataFolder(tstData, errList) - assert len(errList) > 0 - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.mkdir", causeOSError) + errList = [] + errList = theProject._legacyDataFolder(tstData, errList) + assert len(errList) > 0 # This causes a failure to move 'stuff' to 'junk' - monkeypatch.setattr("os.rename", causeOSError) - errList = [] - errList = theProject._legacyDataFolder(tstData, errList) - assert len(errList) > 0 - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.rename", causeOSError) + errList = [] + errList = theProject._legacyDataFolder(tstData, errList) + assert len(errList) > 0 # This should be successful errList = [] @@ -1138,18 +1136,18 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir): writeFile(tstDoc3b, "dummy") # Make the above fail - monkeypatch.setattr("os.rename", causeOSError) - monkeypatch.setattr("os.unlink", causeOSError) - errList = [] - errList = theProject._legacyDataFolder(tstData, errList) - assert len(errList) > 0 - assert os.path.isfile(tstDoc1m) - assert os.path.isfile(tstDoc1b) - assert os.path.isfile(tstDoc2m) - assert os.path.isfile(tstDoc2b) - assert os.path.isfile(tstDoc3m) - assert os.path.isfile(tstDoc3b) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.rename", causeOSError) + mp.setattr("os.unlink", causeOSError) + errList = [] + errList = theProject._legacyDataFolder(tstData, errList) + assert len(errList) > 0 + assert os.path.isfile(tstDoc1m) + assert os.path.isfile(tstDoc1b) + assert os.path.isfile(tstDoc2m) + assert os.path.isfile(tstDoc2b) + assert os.path.isfile(tstDoc3m) + assert os.path.isfile(tstDoc3b) # And succeed ... errList = [] @@ -1203,14 +1201,14 @@ def testCoreProject_Backup(monkeypatch, dummyGUI, nwMinimal, tmpDir): theProject.mainConf.backupPath = tmpDir # Can't make folder - monkeypatch.setattr("os.mkdir", causeOSError) - assert not theProject.zipIt(doNotify=False) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("os.mkdir", causeOSError) + assert not theProject.zipIt(doNotify=False) # Can't write archive - monkeypatch.setattr("shutil.make_archive", causeOSError) - assert not theProject.zipIt(doNotify=False) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("shutil.make_archive", causeOSError) + assert not theProject.zipIt(doNotify=False) # Test correct settings assert theProject.zipIt(doNotify=True) diff --git a/tests/test_core/test_core_spell.py b/tests/test_core/test_core_spell.py index 113ed343..c1d8ae3d 100644 --- a/tests/test_core/test_core_spell.py +++ b/tests/test_core/test_core_spell.py @@ -30,14 +30,13 @@ from tools import readFile, writeFile from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple @pytest.mark.core -def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf): +def testCoreSpell_Super(monkeypatch, tmpDir): """Test the spell checker super class """ wList = os.path.join(tmpDir, "wordlist.txt") writeFile(wList, "a_word\nb_word\nc_word\n") spChk = NWSpellCheck() - spChk.mainConf = tmpConf # Check that dummy functions return results that reflects that spell # checking is effectively disabled @@ -49,16 +48,16 @@ def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf): # Add a word to the user's dictionary assert spChk._readProjectDictionary("dummy") is False - monkeypatch.setattr("builtins.open", causeOSError) - assert spChk._readProjectDictionary(wList) is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert spChk._readProjectDictionary(wList) is False assert spChk._readProjectDictionary(wList) is True assert spChk.projectDict == wList # Cannot write to file - monkeypatch.setattr("builtins.open", causeOSError) - assert spChk.addWord("d_word") is False - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert spChk.addWord("d_word") is False assert readFile(wList) == "a_word\nb_word\nc_word\n" # First time, OK @@ -72,28 +71,26 @@ def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf): # END Test testCoreSpell_Super @pytest.mark.core -def testCoreSpell_Enchant(monkeypatch, tmpDir, tmpConf): +def testCoreSpell_Enchant(monkeypatch, tmpDir): """Test the pyenchant spell checker """ wList = os.path.join(tmpDir, "wordlist.txt") writeFile(wList, "a_word\nb_word\nc_word\n") # Block the enchant package (and trigger the dummy class) - monkeypatch.setitem(sys.modules, "enchant", None) - spChk = NWSpellEnchant() + with monkeypatch.context() as mp: + mp.setitem(sys.modules, "enchant", None) + spChk = NWSpellEnchant() - spChk.setLanguage("en", wList) - assert spChk.setLanguage("", "") is None - assert spChk.checkWord("") - assert spChk.suggestWords("") == [] - assert spChk.listDictionaries() == [] - assert spChk.describeDict() == ("", "") - - monkeypatch.undo() + spChk.setLanguage("en", wList) + assert spChk.setLanguage("", "") is None + assert spChk.checkWord("") + assert spChk.suggestWords("") == [] + assert spChk.listDictionaries() == [] + assert spChk.describeDict() == ("", "") # Load the proper enchant package spChk = NWSpellEnchant() - spChk.mainConf = tmpConf spChk.setLanguage("en", wList) assert spChk.checkWord("a_word") @@ -118,7 +115,7 @@ def testCoreSpell_Enchant(monkeypatch, tmpDir, tmpConf): # END Test testCoreSpell_Enchant @pytest.mark.core -def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf): +def testCoreSpell_Simple(monkeypatch, tmpDir): """Test the fallback simple spell checker """ wList = os.path.join(tmpDir, "wordlist.txt") @@ -127,15 +124,14 @@ def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf): writeFile(wDict, "# Comment\ne_word\nf_word\ng_word\n") spChk = NWSpellSimple() - spChk.mainConf = tmpConf spChk.mainConf.dictPath = tmpDir # Load dictionary, but fail - monkeypatch.setattr("builtins.open", causeOSError) - spChk.setLanguage("en", wList) - assert spChk.spellLanguage is None - assert spChk.theWords == set(spChk.projDict) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + spChk.setLanguage("en", wList) + assert spChk.spellLanguage is None + assert spChk.theWords == set(spChk.projDict) # Load dictionary properly spChk.setLanguage("en", wList) @@ -163,9 +159,9 @@ def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf): assert "d_word" in wSuggest # Break the matching - monkeypatch.setattr("difflib.get_close_matches", lambda *args, **kwargs: [""]) - assert spChk.suggestWords("word") == [] - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("difflib.get_close_matches", lambda *args, **kwargs: [""]) + assert spChk.suggestWords("word") == [] # Capitalisation wSuggest = spChk.suggestWords("D_wrod") diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index cad615fd..07dc5241 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -112,11 +112,10 @@ def testCoreToken_Setters(dummyGUI): # END Test testCoreToken_Setters @pytest.mark.core -def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI, tmpConf): +def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI): """Test handling files and text in the Tokenizer class. """ theProject = NWProject(dummyGUI) - theProject.mainConf = tmpConf theProject.projTree.setSeed(42) theProject.loadProjectLocalisation("en") @@ -157,13 +156,13 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI, tmpConf): assert theToken.setText(sHandle) is True assert theToken.theText == docText - monkeypatch.setattr("nw.constants.nwConst.MAX_DOCSIZE", 100) - assert theToken.setText(sHandle, docText) is True - assert theToken.theText == ( - "# ERROR\n\n" - "Document 'New Scene' is too big (0.00 MB). Skipping.\n\n" - ) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr("nw.constants.nwConst.MAX_DOCSIZE", 100) + assert theToken.setText(sHandle, docText) is True + assert theToken.theText == ( + "# ERROR\n\n" + "Document 'New Scene' is too big (0.00 MB). Skipping.\n\n" + ) assert theToken.setText(sHandle, docText) is True assert theToken.theText == docText @@ -411,11 +410,10 @@ def testCoreToken_Tokenize(dummyGUI): # END Test testCoreToken_Tokenize @pytest.mark.core -def testCoreToken_Headers(dummyGUI, tmpConf): +def testCoreToken_Headers(dummyGUI): """Test the header and page parser of the Tokenizer class. """ theProject = NWProject(dummyGUI) - theProject.mainConf = tmpConf theProject.loadProjectLocalisation("en") theToken = Tokenizer(theProject, dummyGUI) diff --git a/tests/test_core/test_core_toodt.py b/tests/test_core/test_core_toodt.py index 6126be99..f076c85d 100644 --- a/tests/test_core/test_core_toodt.py +++ b/tests/test_core/test_core_toodt.py @@ -45,11 +45,9 @@ def xmlToText(xElem): return rTxt @pytest.mark.core -def testCoreToOdt_Convert(tmpConf, dummyGUI): +def testCoreToOdt_Convert(dummyGUI): """Test the converter of the ToHtml class. """ - nw.CONFIG = tmpConf - theProject = NWProject(dummyGUI) dummyGUI.theIndex = NWIndex(theProject, dummyGUI) theDoc = ToOdt(theProject, dummyGUI, isFlat=True) diff --git a/tests/test_core/test_core_tree.py b/tests/test_core/test_core_tree.py index d4588b37..b80bb784 100644 --- a/tests/test_core/test_core_tree.py +++ b/tests/test_core/test_core_tree.py @@ -410,8 +410,6 @@ def testCoreTree_MakeHandles(monkeypatch, dummyGUI): theTree._projTree[tHandle] = None assert tHandle == "a79acf4c634a7" - monkeypatch.undo() - # END Test testCoreTree_MakeHandles @pytest.mark.core diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py index f66108d5..3e8886a8 100644 --- a/tests/test_gui/test_gui_mainmenu.py +++ b/tests/test_gui/test_gui_mainmenu.py @@ -534,9 +534,9 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj): # Faulty Keyword Inserts assert not nwGUI.docEditor.insertKeyWord("blabla") - monkeypatch.setattr(QTextBlock, "isValid", lambda *args, **kwards: False) - assert not nwGUI.docEditor.insertKeyWord(nwKeyWords.TAG_KEY) - monkeypatch.undo() + with monkeypatch.context() as mp: + mp.setattr(QTextBlock, "isValid", lambda *args, **kwards: False) + assert not nwGUI.docEditor.insertKeyWord(nwKeyWords.TAG_KEY) nwGUI.docEditor.clear() diff --git a/tests/test_gui/test_gui_projdetails.py b/tests/test_gui/test_gui_projdetails.py index 4919bb8d..e0ead1f9 100644 --- a/tests/test_gui/test_gui_projdetails.py +++ b/tests/test_gui/test_gui_projdetails.py @@ -110,6 +110,5 @@ def testGuiProjDetails_Dialog(qtbot, monkeypatch, nwGUI, nwLipsum): # Clean Up projDet._doClose() nwGUI.closeMain() - monkeypatch.undo() # END Test testGuiProjDetails_Dialog diff --git a/tests/test_gui/test_gui_projwizard.py b/tests/test_gui/test_gui_projwizard.py index 4208bc8f..0825eea6 100644 --- a/tests/test_gui/test_gui_projwizard.py +++ b/tests/test_gui/test_gui_projwizard.py @@ -62,18 +62,17 @@ def testGuiProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal): # Close project, but call with invalid path assert nwGUI.closeProject() - monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: None) - assert not nwGUI.newProject() + with monkeypatch.context() as mp: + mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: None) + assert not nwGUI.newProject() - # Now, with an empty dictionary - monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {}) - assert not nwGUI.newProject() + # Now, with an empty dictionary + mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: {}) + assert not nwGUI.newProject() - # Now, with a non-empty folder - monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal}) - assert not nwGUI.newProject() - - monkeypatch.undo() + # Now, with a non-empty folder + mp.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal}) + assert not nwGUI.newProject() ## # Test the Wizard diff --git a/tests/test_gui/test_gui_writingstats.py b/tests/test_gui/test_gui_writingstats.py index 8fb9d538..bf5a9691 100644 --- a/tests/test_gui/test_gui_writingstats.py +++ b/tests/test_gui/test_gui_writingstats.py @@ -428,6 +428,4 @@ def testGuiWritingStats_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj): assert nwGUI.closeProject() qtbot.wait(stepDelay) - monkeypatch.undo() - # END Test testGuiWritingStats_Dialog