Separated out writing stats test and updated it
This commit is contained in:
+20
-28
@@ -39,7 +39,7 @@ from PyQt5.QtWidgets import (
|
|||||||
QLabel, QGroupBox, QMenu, QAction, QFileDialog, QSpinBox, QHBoxLayout
|
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.constants import nwConst, nwFiles, nwAlert
|
||||||
from nw.gui.custom import QSwitch
|
from nw.gui.custom import QSwitch
|
||||||
|
|
||||||
@@ -316,37 +316,30 @@ class GuiWritingStats(QDialog):
|
|||||||
if dataFmt == self.FMT_JSON:
|
if dataFmt == self.FMT_JSON:
|
||||||
fileExt = "json"
|
fileExt = "json"
|
||||||
textFmt = "JSON Data File"
|
textFmt = "JSON Data File"
|
||||||
|
|
||||||
elif dataFmt == self.FMT_CSV:
|
elif dataFmt == self.FMT_CSV:
|
||||||
fileExt = "csv"
|
fileExt = "csv"
|
||||||
textFmt = "CSV Data File"
|
textFmt = "CSV Data File"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Generate the file name
|
# Generate the file name
|
||||||
if fileExt:
|
saveDir = self.mainConf.lastPath
|
||||||
saveDir = self.mainConf.lastPath
|
if not os.path.isdir(saveDir):
|
||||||
if not os.path.isdir(saveDir):
|
saveDir = os.path.expanduser("~")
|
||||||
saveDir = os.path.expanduser("~")
|
|
||||||
|
|
||||||
fileName = "sessionStats.%s" % fileExt
|
fileName = "sessionStats.%s" % fileExt
|
||||||
savePath = os.path.join(saveDir, fileName)
|
savePath = os.path.join(saveDir, fileName)
|
||||||
|
|
||||||
dlgOpt = QFileDialog.Options()
|
dlgOpt = QFileDialog.Options()
|
||||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||||
savePath, _ = QFileDialog.getSaveFileName(
|
savePath, _ = QFileDialog.getSaveFileName(
|
||||||
self, "Save Document As", savePath, options=dlgOpt
|
self, "Save Document As", savePath, options=dlgOpt
|
||||||
)
|
)
|
||||||
|
if not savePath:
|
||||||
if not savePath:
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.mainConf.setLastPath(savePath)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
self.mainConf.setLastPath(savePath)
|
||||||
|
|
||||||
# Do the actual writing
|
# Do the actual writing
|
||||||
wSuccess = False
|
wSuccess = False
|
||||||
errMsg = ""
|
errMsg = ""
|
||||||
@@ -366,7 +359,7 @@ class GuiWritingStats(QDialog):
|
|||||||
json.dump(jsonData, outFile, indent=2)
|
json.dump(jsonData, outFile, indent=2)
|
||||||
wSuccess = True
|
wSuccess = True
|
||||||
|
|
||||||
elif dataFmt == self.FMT_CSV:
|
if dataFmt == self.FMT_CSV:
|
||||||
outFile.write(
|
outFile.write(
|
||||||
'"Date","Length (sec)","Words Changed","Novel Words","Note Words"\n'
|
'"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')
|
outFile.write(f'"{sD}",{tT:.0f},{wD},{wA},{wB}\n')
|
||||||
wSuccess = True
|
wSuccess = True
|
||||||
|
|
||||||
else:
|
|
||||||
errMsg = "Unknown format"
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
errMsg = str(e).replace("\n", "<br>")
|
errMsg = str(e)
|
||||||
|
wSuccess = False
|
||||||
|
|
||||||
# Report to user
|
# Report to user
|
||||||
if wSuccess:
|
if wSuccess:
|
||||||
@@ -394,7 +385,7 @@ class GuiWritingStats(QDialog):
|
|||||||
), nwAlert.ERROR
|
), nwAlert.ERROR
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return wSuccess
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
@@ -406,6 +397,7 @@ class GuiWritingStats(QDialog):
|
|||||||
logger.debug("Loading session log file")
|
logger.debug("Loading session log file")
|
||||||
|
|
||||||
self.logData = []
|
self.logData = []
|
||||||
|
self.wordOffset = 0
|
||||||
|
|
||||||
ttNovel = 0
|
ttNovel = 0
|
||||||
ttNotes = 0
|
ttNotes = 0
|
||||||
@@ -417,7 +409,7 @@ class GuiWritingStats(QDialog):
|
|||||||
for inLine in inFile:
|
for inLine in inFile:
|
||||||
if inLine.startswith("#"):
|
if inLine.startswith("#"):
|
||||||
if inLine.startswith("# Offset"):
|
if inLine.startswith("# Offset"):
|
||||||
self.wordOffset = int(inLine[9:].strip())
|
self.wordOffset = checkInt(inLine[9:].strip(), 0)
|
||||||
logger.verbose(
|
logger.verbose(
|
||||||
"Initial word count when log was started is %d" % self.wordOffset
|
"Initial word count when log was started is %d" % self.wordOffset
|
||||||
)
|
)
|
||||||
|
|||||||
+18
-17
@@ -59,20 +59,21 @@ Available markers are:
|
|||||||
To filter specific groups of tests, use the `-k` switch.
|
To filter specific groups of tests, use the `-k` switch.
|
||||||
The commands for the respective test categories are listed below.
|
The commands for the respective test categories are listed below.
|
||||||
|
|
||||||
| Type | Test Target | Source File(s) | Marker | Filter |
|
| Type | Test Target | Source File(s) | Marker | Filter |
|
||||||
| :--- | :----------------- | :-------------------- | :-------- | :-------------------- |
|
| :---------- | :----------------- | :--------------------- | :-------- | :----------------------- |
|
||||||
| Unit | Main function | nw/\_\_init\_\_.py | `-m base` | `-k testBaseInit` |
|
| Unit | Main function | nw/\_\_init\_\_.py | `-m base` | `-k testBaseInit` |
|
||||||
| Unit | Common functions | nw/common.py | `-m base` | `-k testBaseCommon` |
|
| Unit | Common functions | nw/common.py | `-m base` | `-k testBaseCommon` |
|
||||||
| Unit | Config class | nw/config.py | `-m base` | `-k testBaseConfig` |
|
| Unit | Config class | nw/config.py | `-m base` | `-k testBaseConfig` |
|
||||||
| Unit | Error handlers | nw/error.py | `-m base` | `-k testBaseError` |
|
| Unit | Error handlers | nw/error.py | `-m base` | `-k testBaseError` |
|
||||||
| Unit | Core functions | nw/core/tools.py | `-m core` | `-k testCoreTools` |
|
| Unit | Core functions | nw/core/tools.py | `-m core` | `-k testCoreTools` |
|
||||||
| Unit | NWDoc class | nw/core/document.py | `-m core` | `-k testCoreDocument` |
|
| Unit | NWDoc class | nw/core/document.py | `-m core` | `-k testCoreDocument` |
|
||||||
| Unit | NWIndex class | nw/core/index.py | `-m core` | `-k testCoreIndex` |
|
| Unit | NWIndex class | nw/core/index.py | `-m core` | `-k testCoreIndex` |
|
||||||
| Unit | NWItem class | nw/core/item.py | `-m core` | `-k testCoreItem` |
|
| Unit | NWItem class | nw/core/item.py | `-m core` | `-k testCoreItem` |
|
||||||
| Unit | NWProject class | nw/core/project.py | `-m core` | `-k testCoreProject` |
|
| Unit | NWProject class | nw/core/project.py | `-m core` | `-k testCoreProject` |
|
||||||
| Unit | NWSpell* classes | nw/core/spellcheck.py | `-m core` | `-k testCoreSpell` |
|
| Unit | NWSpell* classes | nw/core/spellcheck.py | `-m core` | `-k testCoreSpell` |
|
||||||
| Unit | NWStatus class | nw/core/status.py | `-m core` | `-k testCoreStatus` |
|
| Unit | NWStatus class | nw/core/status.py | `-m core` | `-k testCoreStatus` |
|
||||||
| Unit | NWTree class | nw/core/tree.py | `-m core` | `-k testCoreTree` |
|
| Unit | NWTree class | nw/core/tree.py | `-m core` | `-k testCoreTree` |
|
||||||
| Unit | OptionsState class | nw/core/options.py | `-m core` | `-k testCoreOptions` |
|
| Unit | OptionsState class | nw/core/options.py | `-m core` | `-k testCoreOptions` |
|
||||||
| Unit | ToHtml class | nw/core/tohtml.py | `-m core` | `-k testCoreToHtml` |
|
| Unit | ToHtml class | nw/core/tohtml.py | `-m core` | `-k testCoreToHtml` |
|
||||||
| Unit | Tokenizer class | nw/core/tokenizer.py | `-m core` | `-k testCoreToken` |
|
| Unit | Tokenizer class | nw/core/tokenizer.py | `-m core` | `-k testCoreToken` |
|
||||||
|
| Integration | Writing Stats GUI | nw/gui/writingstats.py | `-m gui` | `-k testGuiWritingStats` |
|
||||||
|
|||||||
+42
-8
@@ -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)))
|
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
|
from nw.config import Config # noqa: E402
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -55,16 +57,28 @@ def outDir(tmpDir):
|
|||||||
def fncDir(tmpDir):
|
def fncDir(tmpDir):
|
||||||
"""A temporary folder for a single test function.
|
"""A temporary folder for a single test function.
|
||||||
"""
|
"""
|
||||||
funcDir = os.path.join(tmpDir, "ftemp")
|
fncDir = os.path.join(tmpDir, "f_temp")
|
||||||
if os.path.isdir(funcDir):
|
if os.path.isdir(fncDir):
|
||||||
shutil.rmtree(funcDir)
|
shutil.rmtree(fncDir)
|
||||||
if not os.path.isdir(funcDir):
|
if not os.path.isdir(fncDir):
|
||||||
os.mkdir(funcDir)
|
os.mkdir(fncDir)
|
||||||
yield funcDir
|
yield fncDir
|
||||||
if os.path.isdir(funcDir):
|
if os.path.isdir(fncDir):
|
||||||
shutil.rmtree(funcDir)
|
shutil.rmtree(fncDir)
|
||||||
return
|
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
|
# novelWriter Objects
|
||||||
##
|
##
|
||||||
@@ -89,6 +103,26 @@ def dummyGUI(tmpConf):
|
|||||||
theDummy.mainConf = tmpConf
|
theDummy.mainConf = tmpConf
|
||||||
return theDummy
|
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
|
# Temp Project Folders
|
||||||
##
|
##
|
||||||
|
|||||||
+2
-143
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import nw
|
import nw
|
||||||
import pytest
|
import pytest
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -19,11 +18,10 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from nw.gui import (
|
from nw.gui import (
|
||||||
GuiProjectSettings, GuiItemEditor, GuiAbout, GuiBuildNovel,
|
GuiProjectSettings, GuiItemEditor, GuiAbout, GuiBuildNovel,
|
||||||
GuiDocMerge, GuiDocSplit, GuiWritingStats, GuiProjectWizard,
|
GuiDocMerge, GuiDocSplit, GuiProjectWizard, GuiProjectLoad, GuiPreferences
|
||||||
GuiProjectLoad, GuiPreferences
|
|
||||||
)
|
)
|
||||||
from nw.gui.custom import QuotesDialog
|
from nw.gui.custom import QuotesDialog
|
||||||
from nw.constants import nwItemLayout, nwItemClass, nwFiles
|
from nw.constants import nwItemLayout, nwItemClass
|
||||||
|
|
||||||
keyDelay = 2
|
keyDelay = 2
|
||||||
typeDelay = 1
|
typeDelay = 1
|
||||||
@@ -217,145 +215,6 @@ def testItemEditor(qtbot, yesToAll, monkeypatch, fncDir, nwTempGUI, refDir, tmpD
|
|||||||
# qtbot.stopForInteraction()
|
# qtbot.stopForInteraction()
|
||||||
nwGUI.closeMain()
|
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
|
@pytest.mark.gui
|
||||||
def testAboutBox(qtbot, monkeypatch, fncDir, tmpDir):
|
def testAboutBox(qtbot, monkeypatch, fncDir, tmpDir):
|
||||||
nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir])
|
nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % tmpDir])
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user