From 70eadd11cbf515e0360f9391d0e7aec6ab1fb064 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 12 Dec 2020 14:44:33 +0100 Subject: [PATCH] Separated out writing stats test and updated it --- nw/gui/writingstats.py | 48 +++----- tests/README.md | 35 +++--- tests/conftest.py | 50 ++++++-- tests/test_gui_dialogs.py | 145 +--------------------- tests/test_gui_writingstats.py | 216 +++++++++++++++++++++++++++++++++ 5 files changed, 298 insertions(+), 196 deletions(-) create mode 100644 tests/test_gui_writingstats.py diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py index b796bbce..3b5bc5c5 100644 --- a/nw/gui/writingstats.py +++ b/nw/gui/writingstats.py @@ -39,7 +39,7 @@ from PyQt5.QtWidgets import ( QLabel, QGroupBox, QMenu, QAction, QFileDialog, QSpinBox, QHBoxLayout ) -from nw.common import formatTime +from nw.common import formatTime, checkInt from nw.constants import nwConst, nwFiles, nwAlert from nw.gui.custom import QSwitch @@ -316,37 +316,30 @@ class GuiWritingStats(QDialog): if dataFmt == self.FMT_JSON: fileExt = "json" textFmt = "JSON Data File" - elif dataFmt == self.FMT_CSV: fileExt = "csv" textFmt = "CSV Data File" - else: return False # Generate the file name - if fileExt: - saveDir = self.mainConf.lastPath - if not os.path.isdir(saveDir): - saveDir = os.path.expanduser("~") + saveDir = self.mainConf.lastPath + if not os.path.isdir(saveDir): + saveDir = os.path.expanduser("~") - fileName = "sessionStats.%s" % fileExt - savePath = os.path.join(saveDir, fileName) + fileName = "sessionStats.%s" % fileExt + savePath = os.path.join(saveDir, fileName) - dlgOpt = QFileDialog.Options() - dlgOpt |= QFileDialog.DontUseNativeDialog - savePath, _ = QFileDialog.getSaveFileName( - self, "Save Document As", savePath, options=dlgOpt - ) - - if not savePath: - return False - - self.mainConf.setLastPath(savePath) - - else: + dlgOpt = QFileDialog.Options() + dlgOpt |= QFileDialog.DontUseNativeDialog + savePath, _ = QFileDialog.getSaveFileName( + self, "Save Document As", savePath, options=dlgOpt + ) + if not savePath: return False + self.mainConf.setLastPath(savePath) + # Do the actual writing wSuccess = False errMsg = "" @@ -366,7 +359,7 @@ class GuiWritingStats(QDialog): json.dump(jsonData, outFile, indent=2) wSuccess = True - elif dataFmt == self.FMT_CSV: + if dataFmt == self.FMT_CSV: outFile.write( '"Date","Length (sec)","Words Changed","Novel Words","Note Words"\n' ) @@ -374,11 +367,9 @@ class GuiWritingStats(QDialog): outFile.write(f'"{sD}",{tT:.0f},{wD},{wA},{wB}\n') wSuccess = True - else: - errMsg = "Unknown format" - except Exception as e: - errMsg = str(e).replace("\n", "
") + errMsg = str(e) + wSuccess = False # Report to user if wSuccess: @@ -394,7 +385,7 @@ class GuiWritingStats(QDialog): ), nwAlert.ERROR ) - return True + return wSuccess ## # Internal Functions @@ -406,6 +397,7 @@ class GuiWritingStats(QDialog): logger.debug("Loading session log file") self.logData = [] + self.wordOffset = 0 ttNovel = 0 ttNotes = 0 @@ -417,7 +409,7 @@ class GuiWritingStats(QDialog): for inLine in inFile: if inLine.startswith("#"): if inLine.startswith("# Offset"): - self.wordOffset = int(inLine[9:].strip()) + self.wordOffset = checkInt(inLine[9:].strip(), 0) logger.verbose( "Initial word count when log was started is %d" % self.wordOffset ) diff --git a/tests/README.md b/tests/README.md index 7d8e378d..772ce216 100644 --- a/tests/README.md +++ b/tests/README.md @@ -59,20 +59,21 @@ Available markers are: To filter specific groups of tests, use the `-k` switch. 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` | -| Unit | NWItem class | nw/core/item.py | `-m core` | `-k testCoreItem` | -| Unit | NWProject class | nw/core/project.py | `-m core` | `-k testCoreProject` | -| Unit | NWSpell* classes | nw/core/spellcheck.py | `-m core` | `-k testCoreSpell` | -| Unit | NWStatus class | nw/core/status.py | `-m core` | `-k testCoreStatus` | -| Unit | NWTree class | nw/core/tree.py | `-m core` | `-k testCoreTree` | -| Unit | OptionsState class | nw/core/options.py | `-m core` | `-k testCoreOptions` | -| Unit | ToHtml class | nw/core/tohtml.py | `-m core` | `-k testCoreToHtml` | -| Unit | Tokenizer class | nw/core/tokenizer.py | `-m core` | `-k testCoreToken` | +| 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` | +| Unit | NWItem class | nw/core/item.py | `-m core` | `-k testCoreItem` | +| Unit | NWProject class | nw/core/project.py | `-m core` | `-k testCoreProject` | +| Unit | NWSpell* classes | nw/core/spellcheck.py | `-m core` | `-k testCoreSpell` | +| Unit | NWStatus class | nw/core/status.py | `-m core` | `-k testCoreStatus` | +| Unit | NWTree class | nw/core/tree.py | `-m core` | `-k testCoreTree` | +| Unit | OptionsState class | nw/core/options.py | `-m core` | `-k testCoreOptions` | +| Unit | ToHtml class | nw/core/tohtml.py | `-m core` | `-k testCoreToHtml` | +| Unit | Tokenizer class | nw/core/tokenizer.py | `-m core` | `-k testCoreToken` | +| Integration | Writing Stats GUI | nw/gui/writingstats.py | `-m gui` | `-k testGuiWritingStats` | diff --git a/tests/conftest.py b/tests/conftest.py index ea9a92b3..aef333ed 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,8 @@ from PyQt5.QtWidgets import QMessageBox sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) +import nw # noqa: E402 + from nw.config import Config # noqa: E402 ## @@ -55,16 +57,28 @@ def outDir(tmpDir): def fncDir(tmpDir): """A temporary folder for a single test function. """ - funcDir = os.path.join(tmpDir, "ftemp") - if os.path.isdir(funcDir): - shutil.rmtree(funcDir) - if not os.path.isdir(funcDir): - os.mkdir(funcDir) - yield funcDir - if os.path.isdir(funcDir): - shutil.rmtree(funcDir) + fncDir = os.path.join(tmpDir, "f_temp") + if os.path.isdir(fncDir): + shutil.rmtree(fncDir) + if not os.path.isdir(fncDir): + os.mkdir(fncDir) + yield fncDir + if os.path.isdir(fncDir): + shutil.rmtree(fncDir) return +@pytest.fixture(scope="function") +def fncProj(fncDir): + """A temporary folder for a single test function, + with a project folder. + """ + prjDir = os.path.join(fncDir, "project") + if os.path.isdir(prjDir): + shutil.rmtree(prjDir) + if not os.path.isdir(prjDir): + os.mkdir(prjDir) + return prjDir + ## # novelWriter Objects ## @@ -89,6 +103,26 @@ def dummyGUI(tmpConf): theDummy.mainConf = tmpConf return theDummy +@pytest.fixture(scope="function") +def nwGUI(qtbot, fncDir): + """Create an instance of the novelWriter GUI. + """ + nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir]) + qtbot.addWidget(nwGUI) + nwGUI.show() + qtbot.waitForWindowShown(nwGUI) + qtbot.wait(20) + + nwGUI.mainConf.lastPath = fncDir + + yield nwGUI + + qtbot.wait(20) + nwGUI.closeMain() + qtbot.wait(20) + + return + ## # Temp Project Folders ## diff --git a/tests/test_gui_dialogs.py b/tests/test_gui_dialogs.py index 96f64da4..49b7b203 100644 --- a/tests/test_gui_dialogs.py +++ b/tests/test_gui_dialogs.py @@ -4,7 +4,6 @@ import nw import pytest -import json import os import sys @@ -19,11 +18,10 @@ from PyQt5.QtWidgets import ( from nw.gui import ( GuiProjectSettings, GuiItemEditor, GuiAbout, GuiBuildNovel, - GuiDocMerge, GuiDocSplit, GuiWritingStats, GuiProjectWizard, - GuiProjectLoad, GuiPreferences + GuiDocMerge, GuiDocSplit, GuiProjectWizard, GuiProjectLoad, GuiPreferences ) from nw.gui.custom import QuotesDialog -from nw.constants import nwItemLayout, nwItemClass, nwFiles +from nw.constants import nwItemLayout, nwItemClass keyDelay = 2 typeDelay = 1 @@ -217,145 +215,6 @@ def testItemEditor(qtbot, yesToAll, monkeypatch, fncDir, nwTempGUI, refDir, tmpD # qtbot.stopForInteraction() nwGUI.closeMain() -@pytest.mark.gui -def testWritingStatsExport(qtbot, monkeypatch, yesToAll, fncDir, tmpDir): - nwGUI = nw.main(["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - # Create new, save, close project - nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.newProject({"projPath": fncDir}) - qtbot.wait(200) - assert nwGUI.saveProject() - assert nwGUI.closeProject() - qtbot.wait(stepDelay) - - sessFile = os.path.join(fncDir, "meta", nwFiles.SESS_STATS) - with open(sessFile, mode="w+", encoding="utf-8") as outFile: - outFile.write( - "# Start Time End Time Novel Notes\n" - "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0\n" - "2020-01-03 21:00:00 2020-01-03 21:00:15 125 0\n" - "2020-01-03 21:30:00 2020-01-03 21:30:15 125 5\n" - "2020-01-06 21:00:00 2020-01-06 21:00:10 125 5\n" - ) - - # Open again, and check the stats - assert nwGUI.openProject(fncDir) - qtbot.wait(stepDelay) - - nwGUI.mainConf.lastPath = fncDir - nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger) - qtbot.waitUntil(lambda: getGuiItem("GuiWritingStats") is not None, timeout=1000) - - sessLog = getGuiItem("GuiWritingStats") - assert isinstance(sessLog, GuiWritingStats) - qtbot.wait(stepDelay) - - monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda *args, **kwargs: ("", "")) - assert not sessLog._saveData(sessLog.FMT_CSV) - - monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, "")) - assert sessLog._saveData(sessLog.FMT_CSV) - qtbot.wait(100) - assert sessLog._saveData(sessLog.FMT_JSON) - qtbot.wait(100) - - jsonStats = os.path.join(fncDir, "sessionStats.json") - with open(jsonStats, mode="r", encoding="utf-8") as inFile: - jsonData = json.load(inFile) - - qtbot.wait(stepDelay) - - assert len(jsonData) == 3 - assert jsonData[1]["length"] >= 14.0 - assert jsonData[1]["newWords"] == 119 - assert jsonData[1]["novelWords"] == 125 - assert jsonData[1]["noteWords"] == 0 - - # No Novel Files - qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) - qtbot.wait(stepDelay) - assert sessLog._saveData(sessLog.FMT_JSON) - qtbot.wait(stepDelay) - - jsonStats = os.path.join(fncDir, "sessionStats.json") - with open(jsonStats, mode="r", encoding="utf-8") as inFile: - jsonData = json.loads(inFile.read()) - - assert len(jsonData) == 1 - assert jsonData[0]["length"] >= 14.0 - assert jsonData[0]["newWords"] == 5 - assert jsonData[0]["novelWords"] == 125 - assert jsonData[0]["noteWords"] == 5 - - # No Note Files - qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) - qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) - qtbot.wait(stepDelay) - assert sessLog._saveData(sessLog.FMT_JSON) - qtbot.wait(stepDelay) - - jsonStats = os.path.join(fncDir, "sessionStats.json") - with open(jsonStats, mode="r", encoding="utf-8") as inFile: - jsonData = json.load(inFile) - - assert len(jsonData) == 2 - assert jsonData[1]["length"] >= 14.0 - assert jsonData[1]["newWords"] == 119 - assert jsonData[1]["novelWords"] == 125 - assert jsonData[1]["noteWords"] == 0 - - # No Negative Entries - qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) - qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) - qtbot.wait(stepDelay) - assert sessLog._saveData(sessLog.FMT_JSON) - qtbot.wait(stepDelay) - - jsonStats = os.path.join(fncDir, "sessionStats.json") - with open(jsonStats, mode="r", encoding="utf-8") as inFile: - jsonData = json.load(inFile) - - assert len(jsonData) == 3 - - # Un-hide Zero Entries - qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) - qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton) - qtbot.wait(stepDelay) - assert sessLog._saveData(sessLog.FMT_JSON) - qtbot.wait(stepDelay) - - jsonStats = os.path.join(fncDir, "sessionStats.json") - with open(jsonStats, mode="r", encoding="utf-8") as inFile: - jsonData = json.load(inFile) - - assert len(jsonData) == 4 - - # Group by Day - qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton) - qtbot.wait(stepDelay) - assert sessLog._saveData(sessLog.FMT_JSON) - qtbot.wait(stepDelay) - - jsonStats = os.path.join(fncDir, "sessionStats.json") - with open(jsonStats, mode="r", encoding="utf-8") as inFile: - jsonData = json.load(inFile) - - # Check against both 1 and 2 as this can be 2 if test was started just before midnight. - # A failed test should in any case produce a 4 - assert len(jsonData) == 3 - - # qtbot.stopForInteraction() - - sessLog._doClose() - assert nwGUI.closeProject() - qtbot.wait(stepDelay) - nwGUI.closeMain() - @pytest.mark.gui def testAboutBox(qtbot, monkeypatch, fncDir, tmpDir): nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir]) diff --git a/tests/test_gui_writingstats.py b/tests/test_gui_writingstats.py new file mode 100644 index 00000000..86e8e2c9 --- /dev/null +++ b/tests/test_gui_writingstats.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +"""novelWriter Dialog Class Tester +""" + +import pytest +import json +import os + +from tools import getGuiItem, writeFile +from dummy import causeOSError + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox + +from nw.gui import GuiWritingStats +from nw.constants import nwFiles + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiWritingStats_All(qtbot, monkeypatch, nwGUI, fncDir, fncProj): + """Test the full writing stats tool. + """ + # Block questions dialog + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "warning", lambda *args: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "critical", lambda *args: QMessageBox.Yes) + + # Create a project to work on + assert nwGUI.newProject({"projPath": fncProj}) + qtbot.wait(100) + assert nwGUI.saveProject() + sessFile = os.path.join(fncProj, "meta", nwFiles.SESS_STATS) + + # Open the Writing Stats dialog + nwGUI.mainConf.lastPath = "" + nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger) + qtbot.waitUntil(lambda: getGuiItem("GuiWritingStats") is not None, timeout=1000) + + sessLog = getGuiItem("GuiWritingStats") + assert isinstance(sessLog, GuiWritingStats) + qtbot.wait(stepDelay) + + # Test Loading + # ============ + + # No initial logfile + assert not os.path.isfile(sessFile) + assert not sessLog._loadLogFile() + + # Make a test log file + writeFile(sessFile, ( + "# Offset 123\n" + "# Start Time End Time Novel Notes\n" + "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0\n" + "2020-01-03 21:00:00 2020-01-03 21:00:15 125 0\n" + "2020-01-03 21:30:00 2020-01-03 21:30:15 125 5\n" + "2020-01-06 21:00:00 2020-01-06 21:00:10 125 5\n" + )) + assert os.path.isfile(sessFile) + assert sessLog._loadLogFile() + assert sessLog.wordOffset == 123 + assert len(sessLog.logData) == 4 + + # Make sure a faulty file can still be read + writeFile(sessFile, ( + "# Offset abc123\n" + "# Start Time End Time Novel Notes\n" + "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0\n" + "2020-01-03 21:00:00 2020-01-03 21:00:15 125 0\n" + "2020-01-03 21:30:00 2020-01-03 21:30:15 125 5\n" + "2020-01-06 21:00:00 2020-01-06 21:00:10 125\n" + )) + assert sessLog._loadLogFile() + assert sessLog.wordOffset == 0 + assert len(sessLog.logData) == 3 + + # Test Exporting + # ============== + + writeFile(sessFile, ( + "# Start Time End Time Novel Notes\n" + "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0\n" + "2020-01-03 21:00:00 2020-01-03 21:00:15 125 0\n" + "2020-01-03 21:30:00 2020-01-03 21:30:15 125 5\n" + "2020-01-06 21:00:00 2020-01-06 21:00:10 125 5\n" + "2020-01-08 21:00:00 2020-01-08 21:00:10 120 5\n" + )) + sessLog.populateGUI() + + # Make the saving fail + monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda *args, **kwargs: ("", "")) + assert not sessLog._saveData(sessLog.FMT_CSV) + assert not sessLog._saveData(sessLog.FMT_JSON) + assert not sessLog._saveData(None) + + # Make the save succeed + monkeypatch.setattr("os.path.expanduser", lambda *args: fncDir) + monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, "")) + + assert sessLog._saveData(sessLog.FMT_CSV) + qtbot.wait(100) + + assert sessLog._saveData(sessLog.FMT_JSON) + qtbot.wait(100) + + assert nwGUI.mainConf.lastPath == fncDir + + # Check the exported files + jsonStats = os.path.join(fncDir, "sessionStats.json") + with open(jsonStats, mode="r", encoding="utf-8") as inFile: + jsonData = json.load(inFile) + + assert len(jsonData) == 4 + assert jsonData[1]["length"] >= 14.0 + assert jsonData[1]["newWords"] == 119 + assert jsonData[1]["novelWords"] == 125 + assert jsonData[1]["noteWords"] == 0 + + # Test Filters + # ============ + + # No Novel Files + qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) + qtbot.wait(stepDelay) + assert sessLog._saveData(sessLog.FMT_JSON) + qtbot.wait(stepDelay) + + assert sessLog.novelWords.text() == "{:n}".format(120) + assert sessLog.notesWords.text() == "{:n}".format(5) + assert sessLog.totalWords.text() == "{:n}".format(125) + + jsonStats = os.path.join(fncDir, "sessionStats.json") + with open(jsonStats, mode="r", encoding="utf-8") as inFile: + jsonData = json.loads(inFile.read()) + + assert len(jsonData) == 1 + assert jsonData[0]["length"] >= 14.0 + assert jsonData[0]["newWords"] == 5 + assert jsonData[0]["novelWords"] == 125 + assert jsonData[0]["noteWords"] == 5 + + # No Note Files + qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) + qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) + qtbot.wait(stepDelay) + assert sessLog._saveData(sessLog.FMT_JSON) + qtbot.wait(stepDelay) + + jsonStats = os.path.join(fncDir, "sessionStats.json") + with open(jsonStats, mode="r", encoding="utf-8") as inFile: + jsonData = json.load(inFile) + + assert len(jsonData) == 3 + assert jsonData[1]["length"] >= 14.0 + assert jsonData[1]["newWords"] == 119 + assert jsonData[1]["novelWords"] == 125 + assert jsonData[1]["noteWords"] == 0 + + # No Negative Entries + qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) + qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) + qtbot.wait(stepDelay) + assert sessLog._saveData(sessLog.FMT_JSON) + qtbot.wait(stepDelay) + + jsonStats = os.path.join(fncDir, "sessionStats.json") + with open(jsonStats, mode="r", encoding="utf-8") as inFile: + jsonData = json.load(inFile) + + assert len(jsonData) == 3 + + # Un-hide Zero Entries + qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) + qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton) + qtbot.wait(stepDelay) + assert sessLog._saveData(sessLog.FMT_JSON) + qtbot.wait(stepDelay) + + jsonStats = os.path.join(fncDir, "sessionStats.json") + with open(jsonStats, mode="r", encoding="utf-8") as inFile: + jsonData = json.load(inFile) + + assert len(jsonData) == 5 + + # Group by Day + qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton) + qtbot.wait(stepDelay) + assert sessLog._saveData(sessLog.FMT_JSON) + qtbot.wait(stepDelay) + + jsonStats = os.path.join(fncDir, "sessionStats.json") + with open(jsonStats, mode="r", encoding="utf-8") as inFile: + jsonData = json.load(inFile) + + # Check against both 1 and 2 as this can be 2 if test was started just before midnight. + # A failed test should in any case produce a 4 + assert len(jsonData) == 4 + + # IOError + # ======= + monkeypatch.setattr("builtins.open", causeOSError) + assert not sessLog._saveData(sessLog.FMT_CSV) + + # qtbot.stopForInteraction() + + sessLog._doClose() + assert nwGUI.closeProject() + qtbot.wait(stepDelay) + + monkeypatch.undo() + +# END Test testGuiWritingStats_All