From 63c3182a0e8acc95aaf49316f2664f014abcb2f9 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Thu, 16 May 2019 20:27:56 +0200 Subject: [PATCH 01/20] More tests for class Config --- nw/config.py | 23 +++++++++++++++-------- tests/test_config.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/nw/config.py b/nw/config.py index 254c06df..5805396b 100644 --- a/nw/config.py +++ b/nw/config.py @@ -117,7 +117,11 @@ class Config: logger.debug("Loading config file") confParser = configparser.ConfigParser() - confParser.read_file(open(path.join(self.confPath,self.confFile))) + try: + confParser.read_file(open(path.join(self.confPath,self.confFile))) + except Exception as e: + logger.error("Could not load config file") + return False # Get options @@ -182,7 +186,7 @@ class Config: if confParser.has_option(cnfSec,"recent%d" % i): self.recentList[i] = confParser.get(cnfSec,"recent%d" % i) - return + return True def saveConfig(self): @@ -266,13 +270,14 @@ class Config: return def setConfPath(self, newPath): - if newPath is None: return + if newPath is None: + return True if not path.isfile(newPath): logger.error("Config: File not found. Using default config path instead.") - return + return False self.confPath = path.dirname(newPath) self.confFile = path.basename(newPath) - return + return True def setWinSize(self, newWidth, newHeight): if abs(self.winGeometry[self.WIN_WIDTH] - newWidth) >= 10: @@ -281,14 +286,16 @@ class Config: if abs(self.winGeometry[self.WIN_HEIGHT] - newHeight) >= 10: self.winGeometry[self.WIN_HEIGHT] = newHeight self.confChanged = True - return + return True def setTreeColWidths(self, colWidths): self.treeColWidth = colWidths - return + self.confChanged = True + return True def setMainPanePos(self, panePos): self.mainPanePos = panePos - return + self.confChanged = True + return True # End Class Config diff --git a/tests/test_config.py b/tests/test_config.py index 90c68d8b..555ccf97 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -23,7 +23,47 @@ if path.isfile(tmpConf): def testConfigInit(): assert theConf.initConfig(testTemp) assert cmpFiles(tmpConf, refConf, [2]) + assert not theConf.confChanged def testConfigSave(): assert theConf.saveConfig() assert cmpFiles(tmpConf, refConf, [2]) + assert not theConf.confChanged + +def testConfigSetConfPath(): + assert theConf.setConfPath(None) + assert not theConf.setConfPath(path.join("somewhere","over","the","rainbow")) + assert theConf.setConfPath(path.join(testTemp,"novelwriter.conf")) + assert theConf.confPath == testTemp + assert theConf.confFile == "novelwriter.conf" + assert not theConf.confChanged + +def testConfigLoad(): + assert theConf.loadConfig() + assert not theConf.confChanged + +def testConfigSetWinSize(): + assert theConf.setWinSize(1105, 655) + assert not theConf.confChanged + assert theConf.setWinSize(70,70) + assert theConf.confChanged + assert theConf.setWinSize(1100, 650) + assert theConf.saveConfig() + assert cmpFiles(tmpConf, refConf, [2]) + assert not theConf.confChanged + +def testConfigSetTreeColWidths(): + assert theConf.setTreeColWidths([0, 0, 0]) + assert theConf.confChanged + assert theConf.setTreeColWidths([120, 30, 50]) + assert theConf.saveConfig() + assert cmpFiles(tmpConf, refConf, [2]) + assert not theConf.confChanged + +def testConfigSetMainPanePos(): + assert theConf.setMainPanePos([0, 0]) + assert theConf.confChanged + assert theConf.setMainPanePos([300, 800]) + assert theConf.saveConfig() + assert cmpFiles(tmpConf, refConf, [2]) + assert not theConf.confChanged From 66f6494894c4d0423c7de9e8f544c29df1d06da6 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Thu, 16 May 2019 23:20:44 +0200 Subject: [PATCH 02/20] Added a basic test of the GUI --- .travis.yml | 11 ++-- nw/__init__.py | 22 ++++---- nw/gui/winmain.py | 2 +- nw/main.py | 29 ----------- tests/nwtools.py | 14 ++++- tests/reference/gui_1489056e0916_main.nwd | 5 ++ tests/reference/gui_nwProject.nwx | 58 +++++++++++++++++++++ tests/test_common.py | 6 ++- tests/test_config.py | 9 +++- tests/test_gui.py | 63 +++++++++++++++++++++++ tests/test_project.py | 7 ++- 11 files changed, 176 insertions(+), 50 deletions(-) delete mode 100644 nw/main.py create mode 100644 tests/reference/gui_1489056e0916_main.nwd create mode 100644 tests/reference/gui_nwProject.nwx create mode 100644 tests/test_gui.py diff --git a/.travis.yml b/.travis.yml index 304d68ee..ce16ec73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: xenial # required for Python >= 3.7 +dist: bionic language: python sudo: required addons: @@ -6,15 +6,16 @@ addons: packages: - libenchant-dev python: - # - "3.5" - # - "3.6" +# - "3.5" +# - "3.6" - "3.7" - # - "3.8-dev" +# - "3.8-dev" install: - pip install -r requirements.txt - pip install pytest-cov +# - pip install pytest-qt - pip install codecov script: - - python -m pytest --cov=nw -v + - python -m pytest --cov=nw -m "project|core" -v after_success: - codecov diff --git a/nw/__init__.py b/nw/__init__.py index f9f642cd..656c32fe 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -15,7 +15,7 @@ import getopt from os import path, remove, rename from PyQt5.QtWidgets import QApplication -from nw.main import NovelWriter +from nw.gui.winmain import GuiMain from nw.config import Config __package__ = "novelWriter" @@ -78,7 +78,7 @@ def main(sysArgs): "logfile=", "version", "config=", - "headless", + "testmode", ] helpMsg = ( @@ -111,7 +111,7 @@ def main(sysArgs): toStd = True showTime = False confPath = None - showGUI = True + testMode = False debugGUI = False # Parse Options @@ -143,15 +143,15 @@ def main(sysArgs): showTime = True elif inOpt in ("--config"): confPath = inArg - elif inOpt in ("--headless"): - showGUI = False + elif inOpt in ("--testmode"): + testMode = True elif inOpt in ("-D","--debuggui"): debugLevel = logging.DEBUG debugStr = "{name:>20}:{lineno:<4d} {levelname:8} {message:}" debugGUI = True # Set Config Options - CONFIG.showGUI = showGUI + CONFIG.showGUI = not testMode CONFIG.debugGUI = debugGUI # Set Logging @@ -179,8 +179,12 @@ def main(sysArgs): CONFIG.initConfig(confPath) - nwApp = QApplication([]) - nwGUI = NovelWriter() - exit(nwApp.exec_()) + if testMode: + nwGUI = GuiMain() + return nwGUI + else: + nwApp = QApplication([]) + nwGUI = GuiMain() + exit(nwApp.exec_()) return diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 8b1b3357..5f9077dc 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -170,7 +170,7 @@ class GuiMain(QMainWindow): if self.saveProject(): self.theProject.newProject() self.treeView.buildTree() - return + return True def openProject(self, projFile=None): if projFile is None: diff --git a/nw/main.py b/nw/main.py deleted file mode 100644 index f7a90f10..00000000 --- a/nw/main.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -"""novelWriter Main Class - - novelWriter – Main Class -========================== - Sets up the main GUI and holds action and event functions - - File History: - Created: 2018-09-22 [0.0.1] - -""" - -import logging -import nw - -from nw.gui.winmain import GuiMain - -logger = logging.getLogger(__name__) - -class NovelWriter(): - - def __init__(self): - super().__init__() - - self.winMain = GuiMain() - - return - -# END Class NovelWriter diff --git a/tests/nwtools.py b/tests/nwtools.py index 34063c4f..9ae3e6ee 100644 --- a/tests/nwtools.py +++ b/tests/nwtools.py @@ -11,8 +11,18 @@ def ensureDir(theDir): return def cmpFiles(fileOne, fileTwo, ignoreLines=[]): - foOne = open(fileOne,mode="r") - foTwo = open(fileTwo,mode="r") + + try: + foOne = open(fileOne,mode="r") + except Exception as e: + print(str(e)) + return False + + try: + foTwo = open(fileTwo,mode="r") + except Exception as e: + print(str(e)) + return False txtOne = foOne.readlines() txtTwo = foTwo.readlines() diff --git a/tests/reference/gui_1489056e0916_main.nwd b/tests/reference/gui_1489056e0916_main.nwd new file mode 100644 index 00000000..ad283738 --- /dev/null +++ b/tests/reference/gui_1489056e0916_main.nwd @@ -0,0 +1,5 @@ +# Hello World! + +This is a paragraph of dummy text. + +This is another paragraph of much longer dummy text. It is in fact very very dum dummy text! We can also try replacing “quotes”, even single’s quotes are replaced. We can hyphen-ate, make dashes – and even longer dashes — if we want. Ellipsis? Not a problem either … \ No newline at end of file diff --git a/tests/reference/gui_nwProject.nwx b/tests/reference/gui_nwProject.nwx new file mode 100644 index 00000000..80caebae --- /dev/null +++ b/tests/reference/gui_nwProject.nwx @@ -0,0 +1,58 @@ + + + + + + + + False + + + + Novel + ROOT + NOVEL + 0 + False + + + New Chapter + FOLDER + NOVEL + 0 + False + + + New Scene + FILE + NOVEL + 0 + False + SCENE + 0 + 0 + 0 + + + Characters + ROOT + CHARACTER + 0 + False + + + Plot + ROOT + PLOT + 0 + False + + + World + ROOT + WORLD + 0 + False + + + diff --git a/tests/test_common.py b/tests/test_common.py index d9694249..6e5b1e5b 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -2,10 +2,11 @@ """novelWriter Common Class Tester """ -import nw +import nw, pytest from nw.common import * from nwtools import cmpList +@pytest.mark.core def testCheckString(): assert checkString(None, "NotNone",True) is None assert checkString("None","NotNone",True) is None @@ -15,6 +16,7 @@ def testCheckString(): assert checkString(1.0, "NotNone",False) == "NotNone" assert checkString(True, "NotNone",False) == "NotNone" +@pytest.mark.core def testCheckInt(): assert checkInt(None, 3,True) is None assert checkInt("None",3,True) is None @@ -23,6 +25,7 @@ def testCheckInt(): assert checkInt(1.0, 3,False) == 1 assert checkInt(True, 3,False) == 1 +@pytest.mark.core def testCheckBool(): assert checkBool(None, 3, True) is None assert checkBool("None", 3, True) is None @@ -36,6 +39,7 @@ def testCheckBool(): assert checkBool(1.0, None, False) is None assert checkBool(2.0, None, False) is None +@pytest.mark.core def testColRange(): assert colRange([0,0], [0,0], 0) is None assert cmpList(colRange([200,50,0], [50,200,0], 1), [200,50,0]) diff --git a/tests/test_config.py b/tests/test_config.py index 555ccf97..4301886a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,7 +2,7 @@ """novelWriter Config Class Tester """ -import nw +import nw, pytest from nwtools import * from os import path, unlink from nw.config import Config @@ -20,16 +20,19 @@ ensureDir(testTemp) if path.isfile(tmpConf): unlink(tmpConf) +@pytest.mark.core def testConfigInit(): assert theConf.initConfig(testTemp) assert cmpFiles(tmpConf, refConf, [2]) assert not theConf.confChanged +@pytest.mark.core def testConfigSave(): assert theConf.saveConfig() assert cmpFiles(tmpConf, refConf, [2]) assert not theConf.confChanged +@pytest.mark.core def testConfigSetConfPath(): assert theConf.setConfPath(None) assert not theConf.setConfPath(path.join("somewhere","over","the","rainbow")) @@ -38,10 +41,12 @@ def testConfigSetConfPath(): assert theConf.confFile == "novelwriter.conf" assert not theConf.confChanged +@pytest.mark.core def testConfigLoad(): assert theConf.loadConfig() assert not theConf.confChanged +@pytest.mark.core def testConfigSetWinSize(): assert theConf.setWinSize(1105, 655) assert not theConf.confChanged @@ -52,6 +57,7 @@ def testConfigSetWinSize(): assert cmpFiles(tmpConf, refConf, [2]) assert not theConf.confChanged +@pytest.mark.core def testConfigSetTreeColWidths(): assert theConf.setTreeColWidths([0, 0, 0]) assert theConf.confChanged @@ -60,6 +66,7 @@ def testConfigSetTreeColWidths(): assert cmpFiles(tmpConf, refConf, [2]) assert not theConf.confChanged +@pytest.mark.core def testConfigSetMainPanePos(): assert theConf.setMainPanePos([0, 0]) assert theConf.confChanged diff --git a/tests/test_gui.py b/tests/test_gui.py new file mode 100644 index 00000000..df20f68e --- /dev/null +++ b/tests/test_gui.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +"""novelWriter Main GUI Class Tester +""" + +import nw, pytest +from nwtools import * +from os import path, unlink +from PyQt5.QtCore import Qt + +keyDelay = 10 +testDir = path.dirname(__file__) +testRef = path.join(testDir,"reference") + +@pytest.mark.gui +def testMainWindows(qtbot, tmpdir): + confDir = tmpdir.mkdir("conf") + projDir = tmpdir.mkdir("project") + nwGUI = nw.main(["--testmode","--config=%s" % confDir]) + qtbot.addWidget(nwGUI) + nwGUI.show() + qtbot.waitForWindowShown(nwGUI) + qtbot.wait(500) + nwGUI.theProject.handleSeed = 42 + assert nwGUI.theProject.setProjectPath(projDir) + assert nwGUI.newProject() + assert nwGUI.theProject.setProjectPath(projDir) + assert nwGUI.saveProject() + qtbot.wait(500) + assert nwGUI.openProject(projDir) + qtbot.keyClick(nwGUI.treeView, Qt.Key_1, modifier=Qt.ControlModifier, delay=keyDelay) + qtbot.keyClick(nwGUI.treeView, Qt.Key_Down, delay=keyDelay) + qtbot.keyClick(nwGUI.treeView, Qt.Key_Right, delay=keyDelay) + qtbot.keyClick(nwGUI.treeView, Qt.Key_Down, delay=keyDelay) + qtbot.keyClick(nwGUI.treeView, Qt.Key_Right, delay=keyDelay) + qtbot.keyClick(nwGUI.treeView, Qt.Key_Down, delay=keyDelay) + qtbot.keyClick(nwGUI.treeView, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.treeView, Qt.Key_2, modifier=Qt.ControlModifier, delay=keyDelay) + for c in "# Hello World!": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "This is a paragraph of dummy text.": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "This is another paragraph of much longer dummy text. It is in fact very very dum dummy text! ": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + for c in "We can also try replacing \"quotes\", even single's quotes are replaced. ": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + for c in "Ellipsis? Not a problem either ... ": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.wait(500) + qtbot.keyClick(nwGUI, "s", modifier=Qt.ControlModifier, delay=keyDelay) + qtbot.wait(500) + + projFile = projDir.join("nwProject.nwx") + assert cmpFiles(projFile, path.join(testRef,"gui_nwProject.nwx"), [2]) + sceneFile = projDir.join("data_3","1489056e0916_main.nwd") + assert cmpFiles(sceneFile, path.join(testRef,"gui_1489056e0916_main.nwd")) + + # qtbot.stopForInteraction() diff --git a/tests/test_project.py b/tests/test_project.py index 9aa0d2d1..14406287 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -2,8 +2,7 @@ """novelWriter Project Class Tester """ -import nw -import types +import nw, pytest, types from os import path, unlink from nwtools import * @@ -31,20 +30,24 @@ theProject.handleSeed = 42 projFile = path.join(testProj,"nwProject.nwx") +@pytest.mark.project def testProjectNew(): assert theProject.newProject() assert theProject.setProjectPath(testProj) assert theProject.saveProject() assert cmpFiles(projFile, path.join(testRef,"new_nwProject.nwx"), [2]) +@pytest.mark.project def testProjectOpen(): assert theProject.openProject(projFile) +@pytest.mark.project def testProjectSave(): assert theProject.saveProject() assert cmpFiles(projFile, path.join(testRef,"new_nwProject.nwx"), [2]) assert not theProject.projChanged +@pytest.mark.project def testProjectNewRoot(): assert theProject.openProject(projFile) assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), type(None)) From 814f7a6331be5dae7008af6f1cf8576d2b934f2d Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Thu, 16 May 2019 23:24:01 +0200 Subject: [PATCH 03/20] Updated travis file --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce16ec73..173624cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,14 +6,14 @@ addons: packages: - libenchant-dev python: -# - "3.5" -# - "3.6" + # - "3.5" + # - "3.6" - "3.7" -# - "3.8-dev" + # - "3.8-dev" install: - pip install -r requirements.txt - pip install pytest-cov -# - pip install pytest-qt + # - pip install pytest-qt - pip install codecov script: - python -m pytest --cov=nw -m "project|core" -v From e1b6b4c81f3820e738953150d8af63444de86112 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Thu, 16 May 2019 23:25:18 +0200 Subject: [PATCH 04/20] Back to ubuntu 16.04 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 173624cb..868cce71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: bionic +dist: xenial language: python sudo: required addons: From 740a5c808f61de8f2e1f896cc8f3921901a3bb1e Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Thu, 16 May 2019 23:34:05 +0200 Subject: [PATCH 05/20] Does gui test work? --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 868cce71..2f4b15ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,6 @@ install: # - pip install pytest-qt - pip install codecov script: - - python -m pytest --cov=nw -m "project|core" -v + - python -m pytest --cov=nw -m "project|core|gui" -v after_success: - codecov From f8d015840a86bcd282c5bdf13fb360ded25efc0c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Thu, 16 May 2019 23:35:49 +0200 Subject: [PATCH 06/20] Does gui test work? --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2f4b15ea..99231f9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ python: install: - pip install -r requirements.txt - pip install pytest-cov - # - pip install pytest-qt + - pip install pytest-qt - pip install codecov script: - python -m pytest --cov=nw -m "project|core|gui" -v From 13cb526ff5ed63d29ee66ef184efc38c5376de74 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Thu, 16 May 2019 23:37:53 +0200 Subject: [PATCH 07/20] GUI test does NOT work. --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 99231f9a..1caf14d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,16 +6,16 @@ addons: packages: - libenchant-dev python: - # - "3.5" - # - "3.6" +# - "3.5" +# - "3.6" - "3.7" - # - "3.8-dev" +# - "3.8-dev" install: - pip install -r requirements.txt - pip install pytest-cov - - pip install pytest-qt +# - pip install pytest-qt - pip install codecov script: - - python -m pytest --cov=nw -m "project|core|gui" -v + - python -m pytest --cov=nw -m "project|core" -v after_success: - codecov From bc39ec7886f35528e9958288000635972c7cc902 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 01:55:37 +0200 Subject: [PATCH 08/20] GUI tests working locally when headless. --- .travis.yml | 7 ++++--- nw/gui/doctree.py | 19 +++++++++--------- nw/gui/winmain.py | 9 +++++---- tests/test_gui.py | 50 ++++++++++++++++++++++++++++++++--------------- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1caf14d2..8b718d72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ dist: xenial +# services: +# - xvfb language: python sudo: required addons: @@ -6,16 +8,15 @@ addons: packages: - libenchant-dev python: -# - "3.5" -# - "3.6" - "3.7" -# - "3.8-dev" install: - pip install -r requirements.txt +# - pip install pytest-faulthandler - pip install pytest-cov # - pip install pytest-qt - pip install codecov script: +# - xvfb-run -a python -m pytest --cov=nw -m "project|core|gui" -v -s - python -m pytest --cov=nw -m "project|core" -v after_success: - codecov diff --git a/nw/gui/doctree.py b/nw/gui/doctree.py index ef80d98a..38135ec5 100644 --- a/nw/gui/doctree.py +++ b/nw/gui/doctree.py @@ -282,15 +282,16 @@ class GuiDocTree(QTreeWidget): def propagateCount(self, tHandle, theCount, nDepth=0): tItem = self._getTreeItem(tHandle) - tItem.setText(self.C_COUNT,str(theCount)) - pItem = tItem.parent() - if pItem is not None: - pCount = 0 - for i in range(pItem.childCount()): - pCount += int(pItem.child(i).text(self.C_COUNT)) - pHandle = pItem.text(self.C_HANDLE) - if not nDepth > 200 and pHandle != "": - self.propagateCount(pHandle, pCount, nDepth+1) + if tItem is not None: + tItem.setText(self.C_COUNT,str(theCount)) + pItem = tItem.parent() + if pItem is not None: + pCount = 0 + for i in range(pItem.childCount()): + pCount += int(pItem.child(i).text(self.C_COUNT)) + pHandle = pItem.text(self.C_HANDLE) + if not nDepth > 200 and pHandle != "": + self.propagateCount(pHandle, pCount, nDepth+1) return def buildTree(self): diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 5f9077dc..87386287 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -207,7 +207,7 @@ class GuiMain(QMainWindow): self.stackPane.setCurrentIndex(self.stackDoc) self.docEditor.setText(self.theDocument.openDocument(tHandle)) self.docEditor.changeWidth() - return + return True def saveDocument(self): if self.theDocument.theItem is not None: @@ -217,7 +217,7 @@ class GuiMain(QMainWindow): self.theDocument.theItem.setParaCount(self.docEditor.paraCount) self.theDocument.saveDocument(docHtml) self.docEditor.setDocumentChanged(False) - return + return True def _previewDocument(self): @@ -246,7 +246,7 @@ class GuiMain(QMainWindow): tHandle = self.treeView.getSelectedHandle() if tHandle is None: logger.warning("No item selected") - return + return False logger.verbose("Opening item %s" % tHandle) nwItem = self.theProject.getItem(tHandle) @@ -255,7 +255,8 @@ class GuiMain(QMainWindow): self.openDocument(tHandle) else: logger.verbose("Requested item %s is not a file" % tHandle) - return + + return True def editItem(self): tHandle = self.treeView.getSelectedHandle() diff --git a/tests/test_gui.py b/tests/test_gui.py index df20f68e..bfdf0541 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -7,9 +7,10 @@ from nwtools import * from os import path, unlink from PyQt5.QtCore import Qt -keyDelay = 10 -testDir = path.dirname(__file__) -testRef = path.join(testDir,"reference") +keyDelay = 10 +stepDelay = 100 +testDir = path.dirname(__file__) +testRef = path.join(testDir,"reference") @pytest.mark.gui def testMainWindows(qtbot, tmpdir): @@ -19,22 +20,35 @@ def testMainWindows(qtbot, tmpdir): qtbot.addWidget(nwGUI) nwGUI.show() qtbot.waitForWindowShown(nwGUI) - qtbot.wait(500) + qtbot.wait(stepDelay) + + # Create new, save, open project nwGUI.theProject.handleSeed = 42 assert nwGUI.theProject.setProjectPath(projDir) assert nwGUI.newProject() assert nwGUI.theProject.setProjectPath(projDir) assert nwGUI.saveProject() - qtbot.wait(500) + qtbot.wait(stepDelay) assert nwGUI.openProject(projDir) - qtbot.keyClick(nwGUI.treeView, Qt.Key_1, modifier=Qt.ControlModifier, delay=keyDelay) - qtbot.keyClick(nwGUI.treeView, Qt.Key_Down, delay=keyDelay) - qtbot.keyClick(nwGUI.treeView, Qt.Key_Right, delay=keyDelay) - qtbot.keyClick(nwGUI.treeView, Qt.Key_Down, delay=keyDelay) - qtbot.keyClick(nwGUI.treeView, Qt.Key_Right, delay=keyDelay) - qtbot.keyClick(nwGUI.treeView, Qt.Key_Down, delay=keyDelay) - qtbot.keyClick(nwGUI.treeView, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.treeView, Qt.Key_2, modifier=Qt.ControlModifier, delay=keyDelay) + qtbot.wait(stepDelay) + + # Check that tree items have been created + assert nwGUI.treeView._getTreeItem("73475cb40a568") is not None + assert nwGUI.treeView._getTreeItem("25fc0e7096fc6") is not None + assert nwGUI.treeView._getTreeItem("31489056e0916") is not None + assert nwGUI.treeView._getTreeItem("44cb730c42048") is not None + assert nwGUI.treeView._getTreeItem("71ee45a3c0db9") is not None + assert nwGUI.treeView._getTreeItem("811786ad1ae74") is not None + + # Select the 'New Scene' file + nwGUI.treeView.setFocus() + nwGUI.treeView._getTreeItem("73475cb40a568").setExpanded(True) + nwGUI.treeView._getTreeItem("25fc0e7096fc6").setExpanded(True) + nwGUI.treeView._getTreeItem("31489056e0916").setSelected(True) + assert nwGUI.openSelectedItem() + + # Type something into the document + nwGUI.docEditor.setFocus() for c in "# Hello World!": qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) @@ -51,10 +65,14 @@ def testMainWindows(qtbot, tmpdir): qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) for c in "Ellipsis? Not a problem either ... ": qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) - qtbot.wait(500) - qtbot.keyClick(nwGUI, "s", modifier=Qt.ControlModifier, delay=keyDelay) - qtbot.wait(500) + qtbot.wait(stepDelay) + # Save the document + assert nwGUI.docEditor.docChanged + assert nwGUI.saveDocument() + qtbot.wait(stepDelay) + + # Check the files projFile = projDir.join("nwProject.nwx") assert cmpFiles(projFile, path.join(testRef,"gui_nwProject.nwx"), [2]) sceneFile = projDir.join("data_3","1489056e0916_main.nwd") From d79c774aea27dd3469f520b12168884ce19ce42c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 09:39:33 +0200 Subject: [PATCH 09/20] Updated gui test --- tests/reference/gui_1489056e0916_main.nwd | 12 +++++++- tests/test_gui.py | 34 +++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/tests/reference/gui_1489056e0916_main.nwd b/tests/reference/gui_1489056e0916_main.nwd index ad283738..2a3624ae 100644 --- a/tests/reference/gui_1489056e0916_main.nwd +++ b/tests/reference/gui_1489056e0916_main.nwd @@ -1,5 +1,15 @@ # Hello World! +## With a Subtitle + +### An Even Subier Title + +#### Basically Not a Title at All + +% How about a comment? +@keyword: value + This is a paragraph of dummy text. -This is another paragraph of much longer dummy text. It is in fact very very dum dummy text! We can also try replacing “quotes”, even single’s quotes are replaced. We can hyphen-ate, make dashes – and even longer dashes — if we want. Ellipsis? Not a problem either … \ No newline at end of file +This is another paragraph of much longer dummy text. It is in fact very very dumb dummy text! We can also try replacing “quotes”, even single’s quotes are replaced. We can hyphen-ate, make dashes – and even longer dashes — if we want. Ellipsis? Not a problem either … + diff --git a/tests/test_gui.py b/tests/test_gui.py index bfdf0541..92c0d7c5 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -8,7 +8,7 @@ from os import path, unlink from PyQt5.QtCore import Qt keyDelay = 10 -stepDelay = 100 +stepDelay = 50 testDir = path.dirname(__file__) testRef = path.join(testDir,"reference") @@ -53,11 +53,36 @@ def testMainWindows(qtbot, tmpdir): qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "## With a Subtitle": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "### An Even Subier Title": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "#### Basically Not a Title at All": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "% How about a comment?": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@keyword: value": + qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "This is a paragraph of dummy text.": qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "This is another paragraph of much longer dummy text. It is in fact very very dum dummy text! ": + + for c in "This is another paragraph of much longer dummy text. It is in fact very very dumb dummy text! ": qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) for c in "We can also try replacing \"quotes\", even single's quotes are replaced. ": qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) @@ -65,7 +90,12 @@ def testMainWindows(qtbot, tmpdir): qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) for c in "Ellipsis? Not a problem either ... ": qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.wait(stepDelay) + nwGUI.docEditor._runCounter() + qtbot.wait(1000) # Save the document assert nwGUI.docEditor.docChanged From 1caf96b2dff3139af9ac83f1380b211d84d76a9e Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 09:41:52 +0200 Subject: [PATCH 10/20] Make WordCounter return properly --- nw/gui/wordcounter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nw/gui/wordcounter.py b/nw/gui/wordcounter.py index f15f5947..b44d1e60 100644 --- a/nw/gui/wordcounter.py +++ b/nw/gui/wordcounter.py @@ -78,6 +78,6 @@ class WordCounter(QThread): self.paraCount += 1 prevEmpty = countPara == False - pass + return -## END Class _WordCounter +## END Class WordCounter From c13e140e37113648a8916bd96b25cd32ab7bee7e Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:11:37 +0200 Subject: [PATCH 11/20] Tree must be built from list treeOrder, not the dict keys on python 3.5 --- nw/gui/doctree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nw/gui/doctree.py b/nw/gui/doctree.py index 38135ec5..c35d443c 100644 --- a/nw/gui/doctree.py +++ b/nw/gui/doctree.py @@ -296,7 +296,7 @@ class GuiDocTree(QTreeWidget): def buildTree(self): self.clear() - for tHandle in self.theProject.projTree: + for tHandle in self.theProject.treeOrder: nwItem = self.theProject.projTree[tHandle] self._addTreeItem(nwItem) return True From 1c35b54a3dcc0c8a47c9007b4fc801e435262e8d Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:13:57 +0200 Subject: [PATCH 12/20] The GUI test now run on a local virtualbox, testing on Travis --- .travis.yml | 12 ++++++------ pytest.ini | 6 ++++++ tests/test_gui.py | 8 ++++---- 3 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 pytest.ini diff --git a/.travis.yml b/.travis.yml index 8b718d72..93a5935f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ dist: xenial -# services: -# - xvfb +services: + - xvfb language: python sudo: required addons: @@ -11,12 +11,12 @@ python: - "3.7" install: - pip install -r requirements.txt -# - pip install pytest-faulthandler + - pip install pytest-faulthandler - pip install pytest-cov -# - pip install pytest-qt + - pip install pytest-qt - pip install codecov script: -# - xvfb-run -a python -m pytest --cov=nw -m "project|core|gui" -v -s - - python -m pytest --cov=nw -m "project|core" -v + - xvfb-run -a python -m pytest --cov=nw -m "project|core|gui" -v -s +# - python -m pytest --cov=nw -m "project|core" -v after_success: - codecov diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..17f2ac93 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,6 @@ +[pytest] +markers = + project: Project classes tests + core: Core functionality tests + gui: Qt5 GUI tests + serial diff --git a/tests/test_gui.py b/tests/test_gui.py index 92c0d7c5..5f91550c 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -14,8 +14,8 @@ testRef = path.join(testDir,"reference") @pytest.mark.gui def testMainWindows(qtbot, tmpdir): - confDir = tmpdir.mkdir("conf") - projDir = tmpdir.mkdir("project") + confDir = str(tmpdir.mkdir("conf")) + projDir = str(tmpdir.mkdir("project")) nwGUI = nw.main(["--testmode","--config=%s" % confDir]) qtbot.addWidget(nwGUI) nwGUI.show() @@ -103,9 +103,9 @@ def testMainWindows(qtbot, tmpdir): qtbot.wait(stepDelay) # Check the files - projFile = projDir.join("nwProject.nwx") + projFile = str(projDir.join("nwProject.nwx")) assert cmpFiles(projFile, path.join(testRef,"gui_nwProject.nwx"), [2]) - sceneFile = projDir.join("data_3","1489056e0916_main.nwd") + sceneFile = str(projDir.join("data_3","1489056e0916_main.nwd")) assert cmpFiles(sceneFile, path.join(testRef,"gui_1489056e0916_main.nwd")) # qtbot.stopForInteraction() From ae35bd388801f15ce187b2984c5110a8aa0ded10 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:17:28 +0200 Subject: [PATCH 13/20] Trying with python 3.5 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 93a5935f..04925989 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ addons: packages: - libenchant-dev python: - - "3.7" + - "3.5" install: - pip install -r requirements.txt - pip install pytest-faulthandler From b1bb89c33b38bd3f9ee2f4c6e6e2551d3fc88cda Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:36:51 +0200 Subject: [PATCH 14/20] Add cache, disable faulthandler --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 04925989..64988b2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,19 @@ +os: linux dist: xenial services: - xvfb language: python +cache: bundler sudo: required addons: apt: packages: - libenchant-dev python: - - "3.5" + - "3.7" install: - pip install -r requirements.txt - - pip install pytest-faulthandler +# - pip install pytest-faulthandler - pip install pytest-cov - pip install pytest-qt - pip install codecov From 217d7af2e99ef27ff4b2c34377dc294dcb0edf5c Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:44:55 +0200 Subject: [PATCH 15/20] Use pytest-xvfb instead --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 64988b2f..fbdb1e3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,11 +14,12 @@ python: install: - pip install -r requirements.txt # - pip install pytest-faulthandler + - pip install pytest-xvfb - pip install pytest-cov - pip install pytest-qt - pip install codecov script: - - xvfb-run -a python -m pytest --cov=nw -m "project|core|gui" -v -s + - python -m pytest --cov=nw -m "project|core|gui" -v # - python -m pytest --cov=nw -m "project|core" -v after_success: - codecov From a0671469bb5793c05ab7585d42b1fd605011a0e9 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:52:11 +0200 Subject: [PATCH 16/20] Added some debug stuff --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index fbdb1e3f..a3c2bc98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,3 +23,6 @@ script: # - python -m pytest --cov=nw -m "project|core" -v after_success: - codecov +after_failure: + - echo "Peak Memory: $(cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes) bytes" + From 7f6e78dbe7e54d3398019b066de3c16803f38002 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:55:03 +0200 Subject: [PATCH 17/20] Added some debug stuff --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a3c2bc98..5aefa24e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,5 +24,5 @@ script: after_success: - codecov after_failure: - - echo "Peak Memory: $(cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes) bytes" + - cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes From 3bf772ca2e9ee43b5947d40c004297c12f59bfd6 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 10:59:39 +0200 Subject: [PATCH 18/20] Added some more packages --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5aefa24e..a2c57d5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ addons: apt: packages: - libenchant-dev + - python3-pyqt5 + - python3-pyqt5.qtsvg python: - "3.7" install: From 4e2a1fa2c5f544a51e60c22b0320772142f273b9 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 11:09:11 +0200 Subject: [PATCH 19/20] Fixed a path bug ... --- tests/test_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_gui.py b/tests/test_gui.py index 5f91550c..842158cc 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -103,9 +103,9 @@ def testMainWindows(qtbot, tmpdir): qtbot.wait(stepDelay) # Check the files - projFile = str(projDir.join("nwProject.nwx")) + projFile = path.join(projDir,"nwProject.nwx") assert cmpFiles(projFile, path.join(testRef,"gui_nwProject.nwx"), [2]) - sceneFile = str(projDir.join("data_3","1489056e0916_main.nwd")) + sceneFile = path.join(projDir,"data_3","1489056e0916_main.nwd") assert cmpFiles(sceneFile, path.join(testRef,"gui_1489056e0916_main.nwd")) # qtbot.stopForInteraction() From 23435ade1c7607cb1338ac32d678ebae42c3c49a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Fri, 17 May 2019 11:12:11 +0200 Subject: [PATCH 20/20] Add python 3.5 to default builds --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a2c57d5c..d40b1c68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ addons: - python3-pyqt5 - python3-pyqt5.qtsvg python: + - "3.5" - "3.7" install: - pip install -r requirements.txt @@ -22,7 +23,6 @@ install: - pip install codecov script: - python -m pytest --cov=nw -m "project|core|gui" -v -# - python -m pytest --cov=nw -m "project|core" -v after_success: - codecov after_failure: