From cb755ba820a394a2e50d41101aef3b9d3ee070de Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 9 Nov 2022 20:54:13 +0100
Subject: [PATCH] Move recent cache out of config, and update all tests
---
novelwriter/config.py | 206 ++++++++-------
novelwriter/core/project.py | 6 +-
novelwriter/dialogs/projload.py | 26 +-
novelwriter/guimain.py | 2 +-
tests/test_base/test_base_config.py | 378 ++++++++++++++--------------
5 files changed, 313 insertions(+), 305 deletions(-)
diff --git a/novelwriter/config.py b/novelwriter/config.py
index af523dc5..8c21dc99 100644
--- a/novelwriter/config.py
+++ b/novelwriter/config.py
@@ -74,8 +74,8 @@ class Config:
self._appPath = self._appRoot
# Runtime Settings and Variables
- self.hasError = False # True if the config class encountered an error
- self.errData = [] # List of error messages
+ self._hasError = False # True if the config class encountered an error
+ self._errData = [] # List of error messages
self.confChanged = False # True whenever the config has chenged, false after save
self.cmdOpen = None # Path from command line for project to be opened on launch
@@ -92,6 +92,8 @@ class Config:
# User Settings
# =============
+ self._recentProj = RecentProjects(self._dataPath)
+
# General GUI Settings
self.guiLang = self._qLocal.name()
self.guiTheme = "" # GUI theme
@@ -245,6 +247,18 @@ class Config:
return
+ ##
+ # Properties
+ ##
+
+ @property
+ def hasError(self):
+ return self._hasError
+
+ @property
+ def recentProjects(self):
+ return self._recentProj
+
##
# Methods
##
@@ -282,6 +296,15 @@ class Config:
return self._lastPath
return Path.home().absolute()
+ def errorText(self):
+ """Compile and return error messages from the initialisation of
+ the Config class, and clear the error buffer.
+ """
+ errMessage = "
".join(self._errData)
+ self._hasError = False
+ self._errData = []
+ return errMessage
+
##
# Config Actions
##
@@ -321,12 +344,12 @@ class Config:
else:
self.saveConfig()
- self.loadRecentCache()
+ self._recentProj.loadCache()
self._checkOptionalPackages()
logger.debug("Config initialisation complete")
- return True
+ return
def initLocalisation(self, nwApp):
"""Initialise the localisation of the GUI.
@@ -345,7 +368,7 @@ class Config:
qTrans = QTranslator()
lngFile = "%s_%s" % (lngBase, lngCode.replace("-", "_"))
if lngFile not in self._qtTrans:
- if qTrans.load(lngFile, lngPath):
+ if qTrans.load(lngFile, str(lngPath)):
logger.debug("Loaded: %s", os.path.join(lngPath, lngFile))
nwApp.installTranslator(qTrans)
self._qtTrans[lngFile] = qTrans
@@ -367,12 +390,12 @@ class Config:
else:
return []
- for qmFile in os.listdir(self._nwLangPath):
- if not os.path.isfile(os.path.join(self._nwLangPath, qmFile)):
+ for qmFile in Path(self._nwLangPath).iterdir():
+ qmName = qmFile.name
+ if not (qmFile.is_file() and qmName.startswith(fPre) and qmName.endswith(fExt)):
continue
- if not qmFile.startswith(fPre) or not qmFile.endswith(fExt):
- continue
- qmLang = qmFile[len(fPre):-len(fExt)]
+
+ qmLang = qmName[len(fPre):-len(fExt)]
qmName = QLocale(qmLang).nativeLanguageName().title()
if qmLang and qmName and qmLang != "en_GB":
langList[qmLang] = qmName
@@ -392,9 +415,9 @@ class Config:
except Exception as exc:
logger.error("Could not load config file")
logException()
- self.hasError = True
- self.errData.append("Could not load config file")
- self.errData.append(formatException(exc))
+ self._hasError = True
+ self._errData.append("Could not load config file")
+ self._errData.append(formatException(exc))
return False
# Main
@@ -606,80 +629,13 @@ class Config:
except Exception as exc:
logger.error("Could not save config file")
logException()
- self.hasError = True
- self.errData.append("Could not save config file")
- self.errData.append(formatException(exc))
+ self._hasError = True
+ self._errData.append("Could not save config file")
+ self._errData.append(formatException(exc))
return False
return True
- def loadRecentCache(self):
- """Load the cache file for recent projects.
- """
- self.recentProj = {}
-
- cacheFile = self._dataPath / nwFiles.RECENT_FILE
- if not os.path.isfile(cacheFile):
- return True
-
- try:
- with open(cacheFile, mode="r", encoding="utf-8") as inFile:
- theData = json.load(inFile)
-
- for projPath, theEntry in theData.items():
- self.recentProj[projPath] = {
- "title": theEntry.get("title", ""),
- "time": theEntry.get("time", 0),
- "words": theEntry.get("words", 0),
- }
-
- except Exception as exc:
- self.hasError = True
- self.errData.append("Could not load recent project cache")
- self.errData.append(formatException(exc))
- return False
-
- return True
-
- def saveRecentCache(self):
- """Save the cache dictionary of recent projects.
- """
- cacheFile = self._dataPath / nwFiles.RECENT_FILE
- cacheTemp = cacheFile.with_suffix(".tmp")
- try:
- with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
- json.dump(self.recentProj, outFile, indent=2)
- cacheTemp.replace(cacheFile)
- except Exception as exc:
- self.hasError = True
- self.errData.append("Could not save recent project cache")
- self.errData.append(formatException(exc))
- return False
-
- return True
-
- def updateRecentCache(self, projPath, projTitle, wordCount, saveTime):
- """Add or update recent cache information on a given project.
- """
- self.recentProj[os.path.abspath(projPath)] = {
- "title": projTitle,
- "time": int(saveTime),
- "words": int(wordCount),
- }
- return True
-
- def removeFromRecentCache(self, thePath):
- """Trying to remove a path from the recent projects cache.
- """
- if thePath in self.recentProj:
- del self.recentProj[thePath]
- logger.debug("Removed recent: %s", thePath)
- self.saveRecentCache()
- else:
- logger.error("Unknown recent: %s", thePath)
- return False
- return True
-
##
# Setters
##
@@ -828,15 +784,6 @@ class Config:
def getTabWidth(self):
return self.pxInt(max(self.tabWidth, 0))
- def getErrData(self):
- """Compile and return error messages from the initialisation of
- the Config class, and clear the error buffer.
- """
- errMessage = "
".join(self.errData)
- self.hasError = False
- self.errData = []
- return errMessage
-
##
# Internal Functions
##
@@ -872,3 +819,78 @@ class Config:
return
# END Class Config
+
+
+class RecentProjects:
+
+ def __init__(self, dataPath):
+ self._dataPath = dataPath
+ self._data = {}
+ return
+
+ def loadCache(self):
+ """Load the cache file for recent projects.
+ """
+ self._data = {}
+
+ cacheFile = self._dataPath / nwFiles.RECENT_FILE
+ if not cacheFile.is_file():
+ return True
+
+ try:
+ with open(cacheFile, mode="r", encoding="utf-8") as inFile:
+ theData = json.load(inFile)
+ for projPath, theEntry in theData.items():
+ self._data[projPath] = {
+ "title": theEntry.get("title", ""),
+ "words": theEntry.get("words", 0),
+ "time": theEntry.get("time", 0),
+ }
+ except Exception:
+ logger.error("Could not load recent project cache")
+ logException()
+ return False
+
+ return True
+
+ def saveCache(self):
+ """Save the cache dictionary of recent projects.
+ """
+ cacheFile = self._dataPath / nwFiles.RECENT_FILE
+ cacheTemp = cacheFile.with_suffix(".tmp")
+ try:
+ with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
+ json.dump(self._data, outFile, indent=2)
+ cacheTemp.replace(cacheFile)
+ except Exception:
+ logger.error("Could not save recent project cache")
+ logException()
+ return False
+
+ return True
+
+ def listEntries(self):
+ """List all items in the cache.
+ """
+ return [(k, e["title"], e["words"], e["time"]) for k, e in self._data.items()]
+
+ def update(self, projPath, projTitle, wordCount, saveTime):
+ """Add or update recent cache information on a given project.
+ """
+ self._data[str(projPath)] = {
+ "title": projTitle,
+ "words": int(wordCount),
+ "time": int(saveTime),
+ }
+ self.saveCache()
+ return
+
+ def remove(self, projPath):
+ """Try to remove a path from the recent projects cache.
+ """
+ if self._data.pop(str(projPath), None) is not None:
+ logger.debug("Removed recent: %s", projPath)
+ self.saveCache()
+ return
+
+# END Class RecentProjects
diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py
index 49df331a..ae4f7bab 100644
--- a/novelwriter/core/project.py
+++ b/novelwriter/core/project.py
@@ -354,10 +354,9 @@ class NWProject(QObject):
self._loadProjectLocalisation()
# Update recent projects
- self.mainConf.updateRecentCache(
+ self.mainConf.recentProjects.update(
self._storage.storagePath, self._data.name, sum(self._data.initCounts), time()
)
- self.mainConf.saveRecentCache()
# Check the project tree consistency
for tItem in self._tree:
@@ -424,10 +423,9 @@ class NWProject(QObject):
self._storage.runPostSaveTasks(autoSave=autoSave)
# Update recent projects
- self.mainConf.updateRecentCache(
+ self.mainConf.recentProjects.update(
self._storage.storagePath, self._data.name, sum(self._data.currCounts), saveTime
)
- self.mainConf.saveRecentCache()
self._storage.writeLockFile()
self.mainGui.setStatus(self.tr("Saved Project: {0}").format(self._data.name))
diff --git a/novelwriter/dialogs/projload.py b/novelwriter/dialogs/projload.py
index c5568f62..ecd2f9f4 100644
--- a/novelwriter/dialogs/projload.py
+++ b/novelwriter/dialogs/projload.py
@@ -229,7 +229,7 @@ class GuiProjectLoad(QDialog):
).format(projName)
)
if msgYes:
- self.mainConf.removeFromRecentCache(
+ self.mainConf.recentProjects.remove(
selList[0].data(self.C_NAME, Qt.UserRole)
)
self._populateList()
@@ -264,23 +264,17 @@ class GuiProjectLoad(QDialog):
def _populateList(self):
"""Populate the list box with recent project data.
"""
- dataList = []
- for projPath in self.mainConf.recentProj:
- theEntry = self.mainConf.recentProj[projPath]
- theTitle = theEntry.get("title", "")
- theTime = theEntry.get("time", 0)
- theWords = theEntry.get("words", 0)
- dataList.append([theTitle, theTime, theWords, projPath])
-
self.listBox.clear()
- sortList = sorted(dataList, key=lambda x: x[1], reverse=True)
- for theTitle, theTime, theWords, projPath in sortList:
+ dataList = self.mainConf.recentProjects.listEntries()
+ sortList = sorted(dataList, key=lambda x: x[3], reverse=True)
+ nwxIcon = self.mainGui.mainTheme.getIcon("proj_nwx")
+ for path, title, words, time in sortList:
newItem = QTreeWidgetItem([""]*4)
- newItem.setIcon(self.C_NAME, self.mainGui.mainTheme.getIcon("proj_nwx"))
- newItem.setText(self.C_NAME, theTitle)
- newItem.setData(self.C_NAME, Qt.UserRole, projPath)
- newItem.setText(self.C_COUNT, formatInt(theWords))
- newItem.setText(self.C_TIME, datetime.fromtimestamp(theTime).strftime("%x %X"))
+ newItem.setIcon(self.C_NAME, nwxIcon)
+ newItem.setText(self.C_NAME, title)
+ newItem.setData(self.C_NAME, Qt.UserRole, path)
+ newItem.setText(self.C_COUNT, formatInt(words))
+ newItem.setText(self.C_TIME, datetime.fromtimestamp(time).strftime("%x %X"))
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index fb77ca8a..09df46fb 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -1144,7 +1144,7 @@ class GuiMain(QMainWindow):
errors since it is initialised before the GUI itself.
"""
if self.mainConf.hasError:
- self.makeAlert(self.mainConf.getErrData(), nwAlert.ERROR)
+ self.makeAlert(self.mainConf.errorText(), nwAlert.ERROR)
return True
return False
diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index fdca0b8b..bac296ce 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -19,16 +19,16 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
-import os
import sys
import pytest
from shutil import copyfile
+from pathlib import Path
from mock import causeOSError, MockApp
from tools import cmpFiles, writeFile
-from novelwriter.config import Config
+from novelwriter.config import Config, RecentProjects
from novelwriter.constants import nwFiles
@@ -37,173 +37,140 @@ def testBaseConfig_Constructor(monkeypatch):
"""Test config contructor.
"""
# Linux
- monkeypatch.setattr("sys.platform", "linux")
- tstConf = Config()
- assert tstConf.osLinux is True
- assert tstConf.osDarwin is False
- assert tstConf.osWindows is False
- assert tstConf.osUnknown is False
+ with monkeypatch.context() as mp:
+ mp.setattr("sys.platform", "linux")
+ tstConf = Config()
+ assert tstConf.osLinux is True
+ assert tstConf.osDarwin is False
+ assert tstConf.osWindows is False
+ assert tstConf.osUnknown is False
# macOS
- monkeypatch.setattr("sys.platform", "darwin")
- tstConf = Config()
- assert tstConf.osLinux is False
- assert tstConf.osDarwin is True
- assert tstConf.osWindows is False
- assert tstConf.osUnknown is False
+ with monkeypatch.context() as mp:
+ mp.setattr("sys.platform", "darwin")
+ tstConf = Config()
+ assert tstConf.osLinux is False
+ assert tstConf.osDarwin is True
+ assert tstConf.osWindows is False
+ assert tstConf.osUnknown is False
# Windows
- monkeypatch.setattr("sys.platform", "win32")
- tstConf = Config()
- assert tstConf.osLinux is False
- assert tstConf.osDarwin is False
- assert tstConf.osWindows is True
- assert tstConf.osUnknown is False
+ with monkeypatch.context() as mp:
+ mp.setattr("sys.platform", "win32")
+ tstConf = Config()
+ assert tstConf.osLinux is False
+ assert tstConf.osDarwin is False
+ assert tstConf.osWindows is True
+ assert tstConf.osUnknown is False
# Cygwin
- monkeypatch.setattr("sys.platform", "cygwin")
- tstConf = Config()
- assert tstConf.osLinux is False
- assert tstConf.osDarwin is False
- assert tstConf.osWindows is True
- assert tstConf.osUnknown is False
+ with monkeypatch.context() as mp:
+ mp.setattr("sys.platform", "cygwin")
+ tstConf = Config()
+ assert tstConf.osLinux is False
+ assert tstConf.osDarwin is False
+ assert tstConf.osWindows is True
+ assert tstConf.osUnknown is False
# Other
- monkeypatch.setattr("sys.platform", "some_other_os")
- tstConf = Config()
- assert tstConf.osLinux is False
- assert tstConf.osDarwin is False
- assert tstConf.osWindows is False
- assert tstConf.osUnknown is True
+ with monkeypatch.context() as mp:
+ mp.setattr("sys.platform", "some_other_os")
+ tstConf = Config()
+ assert tstConf.osLinux is False
+ assert tstConf.osDarwin is False
+ assert tstConf.osWindows is False
+ assert tstConf.osUnknown is True
+
+ # App is single file
+ with monkeypatch.context() as mp:
+ mp.setattr("pathlib.Path.is_file", lambda *a: True)
+ tstConf = Config()
+ assert tstConf._appPath == tstConf._appRoot
# END Test testBaseConfig_Constructor
@pytest.mark.base
-@pytest.mark.skip
-def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
+def testBaseConfig_InitLoadSave(monkeypatch, fncPath, tstPaths):
"""Test config intialisation.
"""
tstConf = Config()
- confFile = os.path.join(tmpDir, "novelwriter.conf")
- testFile = os.path.join(outDir, "baseConfig_novelwriter.conf")
- compFile = os.path.join(refDir, "baseConfig_novelwriter.conf")
+ confFile = fncPath / nwFiles.CONF_FILE
+ testFile = tstPaths.outDir / "baseConfig_novelwriter.conf"
+ compFile = tstPaths.refDir / "baseConfig_novelwriter.conf"
# Make sure we don't have any old conf file
- if os.path.isfile(confFile):
- os.unlink(confFile)
+ if confFile.is_file():
+ confFile.unlink()
- # Let the config class figure out the path
- with monkeypatch.context() as mp:
- mp.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *a: fncDir)
- 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)
+ # Running init against a new oath should write a new config file
+ tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
+ assert tstConf._confPath == fncPath
+ assert tstConf._dataPath == fncPath
+ assert confFile.exists()
- # Fail to make folders
- with monkeypatch.context() as mp:
- mp.setattr("os.mkdir", causeOSError)
+ # Check that we have a default file
+ copyfile(confFile, testFile)
+ ignore = ("timestamp", "lastnotes", "guilang", "lastpath")
+ assert cmpFiles(testFile, compFile, ignoreStart=ignore)
+ tstConf.errorText() # This clears the error cache
- 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)
-
- # Test load/save with no path
- tstConf._confPath = None
- assert tstConf.loadConfig() is False
- assert tstConf.saveConfig() is False
-
- # Run again and set the paths directly and correctly
- # This should create a config file as well
- with monkeypatch.context() as mp:
- mp.setattr("os.path.expanduser", lambda *a: "")
- 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, ignoreStart=("timestamp", "lastnotes", "guilang"))
-
- # Load and save with OSError
+ # Block saving the file
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
-
- assert not tstConf.loadConfig()
+ assert tstConf.saveConfig() is False
assert tstConf.hasError is True
- assert tstConf.errData != []
- assert tstConf.getErrData().startswith("Could not")
- assert tstConf.hasError is False
- assert tstConf.errData == []
+ assert tstConf.errorText().startswith("Could not save config file")
- 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 == []
-
- # Check handling of novelWriter as a package
+ # Block loading the file
with monkeypatch.context() as mp:
- tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
- assert tstConf._confPath == tmpDir
- assert tstConf._dataPath == tmpDir
- appRoot = tstConf._appRoot
+ mp.setattr("builtins.open", causeOSError)
+ assert tstConf.loadConfig() is False
+ assert tstConf.hasError is True
+ assert tstConf.errorText().startswith("Could not load config file")
- mp.setattr("os.path.isfile", lambda *a: True)
- tstConf.initConfig(confPath=tmpDir, dataPath=tmpDir)
- assert tstConf._confPath == tmpDir
- assert tstConf._dataPath == tmpDir
- assert tstConf._appRoot == os.path.dirname(appRoot)
- assert tstConf._appPath == os.path.dirname(appRoot)
-
- assert tstConf.loadConfig() is True
+ # Change a few settings, save, reset, and reload
+ tstConf.guiTheme = "foo"
+ tstConf.guiSyntax = "bar"
assert tstConf.saveConfig() is True
- # Test Correcting Quote Settings
- origDbl = tstConf.fmtDoubleQuotes
- origSng = tstConf.fmtSingleQuotes
- orDoDbl = tstConf.doReplaceDQuote
- orDoSng = tstConf.doReplaceSQuote
+ newConf = Config()
+ newConf.initConfig(confPath=fncPath, dataPath=fncPath)
+ assert newConf.guiTheme == "foo"
+ assert newConf.guiSyntax == "bar"
+ # Test Correcting Quote Settings
tstConf.fmtDoubleQuotes = ["\"", "\""]
tstConf.fmtSingleQuotes = ["'", "'"]
tstConf.doReplaceDQuote = True
tstConf.doReplaceSQuote = True
assert tstConf.saveConfig() is True
- assert tstConf.loadConfig() is True
- assert tstConf.doReplaceDQuote is False
- assert tstConf.doReplaceSQuote is False
+ assert newConf.loadConfig() is True
+ assert newConf.doReplaceDQuote is False
+ assert newConf.doReplaceSQuote is False
- tstConf.fmtDoubleQuotes = origDbl
- tstConf.fmtSingleQuotes = origSng
- tstConf.doReplaceDQuote = orDoDbl
- tstConf.doReplaceSQuote = orDoSng
- assert tstConf.saveConfig() is True
+# END Test testBaseConfig_InitLoadSave
+
+
+@pytest.mark.base
+def testBaseConfig_Localisation(fncPath, tstPaths):
+ """Test localisation.
+ """
+ tstConf = Config()
+ tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
# Localisation
# ============
- i18nDir = os.path.join(fncDir, "i18n")
- os.mkdir(i18nDir)
- os.mkdir(os.path.join(i18nDir, "stuff"))
+ i18nDir = fncPath / "i18n"
+ i18nDir.mkdir()
tstConf._nwLangPath = i18nDir
- copyfile(os.path.join(filesDir, "nw_en_GB.qm"), os.path.join(i18nDir, "nw_en_GB.qm"))
- writeFile(os.path.join(i18nDir, "nw_en_GB.ts"), "")
- writeFile(os.path.join(i18nDir, "nw_abcd.qm"), "")
+ copyfile(tstPaths.filesDir / "nw_en_GB.qm", i18nDir / "nw_en_GB.qm")
+ writeFile(i18nDir / "nw_en_GB.ts", "")
+ writeFile(i18nDir / "nw_abcd.qm", "")
tstApp = MockApp()
tstConf.initLocalisation(tstApp)
@@ -217,82 +184,55 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
assert theList == []
# Add Language
- copyfile(os.path.join(filesDir, "nw_en_GB.qm"), os.path.join(i18nDir, "nw_fr.qm"))
- writeFile(os.path.join(i18nDir, "nw_fr.ts"), "")
+ copyfile(tstPaths.filesDir / "nw_en_GB.qm", i18nDir / "nw_fr.qm")
+ writeFile(i18nDir / "nw_fr.ts", "")
theList = tstConf.listLanguages(tstConf.LANG_NW)
assert theList == [("en_GB", "British English"), ("fr", "Français")]
- copyfile(confFile, testFile)
- assert cmpFiles(testFile, compFile, ignoreStart=("timestamp", "lastnotes", "guilang"))
-
-# END Test testBaseConfig_Init
+# END Test testBaseConfig_Localisation
@pytest.mark.base
-def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir):
- """Test recent cache file.
+def testBaseConfig_Methods(tmpConf, tmpPath):
+ """Check class methods.
"""
- # Add a couple of values
- pathOne = os.path.join(fncDir, "projPathOne", nwFiles.PROJ_FILE)
- pathTwo = os.path.join(fncDir, "projPathTwo", nwFiles.PROJ_FILE)
- assert tmpConf.updateRecentCache(pathOne, "Proj One", 100, 1600002000)
- assert tmpConf.updateRecentCache(pathTwo, "Proj Two", 200, 1600005600)
- assert tmpConf.recentProj == {
- pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
- pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
- }
+ # Data Path
+ assert tmpConf.dataPath() == tmpPath
+ assert tmpConf.dataPath("stuff") == tmpPath / "stuff"
- # Fail to Save
- with monkeypatch.context() as mp:
- mp.setattr("builtins.open", causeOSError)
- assert not tmpConf.saveRecentCache()
+ # Assets Path
+ appPath = tmpConf._appPath
+ assert tmpConf.assetPath() == appPath / "assets"
+ assert tmpConf.assetPath("stuff") == appPath / "assets" / "stuff"
- # Save Proper
- cacheFile = os.path.join(tmpDir, nwFiles.RECENT_FILE)
- assert tmpConf.saveRecentCache()
- assert tmpConf.saveRecentCache()
- assert os.path.isfile(cacheFile)
+ # Last Path
+ assert tmpConf.lastPath() == tmpPath
- # Fail to Load
- with monkeypatch.context() as mp:
- mp.setattr("builtins.open", causeOSError)
- tmpConf.recentProj = {}
- assert not tmpConf.loadRecentCache()
- assert tmpConf.recentProj == {}
+ tmpStuff = tmpPath / "stuff"
+ tmpStuff.mkdir()
+ tmpConf.setLastPath(tmpStuff)
+ assert tmpConf.lastPath() == tmpStuff
- # Load Proper
- tmpConf.recentProj = {}
- assert tmpConf.loadRecentCache()
- assert tmpConf.recentProj == {
- pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
- pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
- }
+ fileStuff = tmpStuff / "more_stuff.txt"
+ fileStuff.write_text("Stuff")
+ tmpConf.setLastPath(fileStuff)
+ assert tmpConf.lastPath() == tmpStuff
- # Remove Non-Existent Entry
- assert not tmpConf.removeFromRecentCache("stuff")
- assert tmpConf.recentProj == {
- pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
- pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
- }
+ fileStuff.unlink()
+ tmpStuff.rmdir()
+ assert tmpConf.lastPath() == Path.home().absolute()
- # Remove Second Entry
- assert tmpConf.removeFromRecentCache(pathTwo)
- assert tmpConf.recentProj == {
- pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
- }
+ # Recent Projects
+ assert isinstance(tmpConf.recentProjects, RecentProjects)
-# END Test testBaseConfig_RecentCache
+# END Test testBaseConfig_Methods
@pytest.mark.base
-def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
+def testBaseConfig_SettersGetters(tmpConf):
"""Set various sizes and positions
"""
- confFile = os.path.join(tmpDir, "novelwriter.conf")
- testFile = os.path.join(outDir, "baseConfig_novelwriter.conf")
- compFile = os.path.join(refDir, "baseConfig_novelwriter.conf")
-
# GUI Scaling
# ===========
@@ -439,17 +379,6 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
tmpConf.setViewSynopsis(True)
assert tmpConf.viewSynopsis is True
- # Check Final File
- # ================
-
- assert tmpConf.confChanged is True
- assert tmpConf.saveConfig() is True
- assert tmpConf.confChanged is False
-
- copyfile(confFile, testFile)
- ignore = ("timestamp", "lastnotes", "guilang", "lastpath")
- assert cmpFiles(testFile, compFile, ignoreStart=ignore)
-
# END Test testBaseConfig_SettersGetters
@@ -479,3 +408,68 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
assert tmpConf.hasEnchant is False
# END Test testBaseConfig_Internal
+
+
+@pytest.mark.base
+def testBaseConfig_RecentCache(monkeypatch, fncPath):
+ """Test recent cache file.
+ """
+ cacheFile = fncPath / nwFiles.RECENT_FILE
+ recent = RecentProjects(fncPath)
+
+ # Load when there is no file should pass, but load nothing
+ assert not cacheFile.exists()
+ assert recent.loadCache() is True
+ assert recent.listEntries() == []
+
+ # Add a couple of values
+ pathOne = fncPath / "projPathOne" / nwFiles.PROJ_FILE
+ pathTwo = fncPath / "projPathTwo" / nwFiles.PROJ_FILE
+
+ recent.update(pathOne, "Proj One", 100, 1600002000)
+ recent.update(pathTwo, "Proj Two", 200, 1600005600)
+ assert recent.listEntries() == [
+ (str(pathOne), "Proj One", 100, 1600002000),
+ (str(pathTwo), "Proj Two", 200, 1600005600),
+ ]
+ assert cacheFile.exists()
+ cacheFile.unlink()
+ assert not cacheFile.exists()
+
+ # Fail to Save
+ with monkeypatch.context() as mp:
+ mp.setattr("builtins.open", causeOSError)
+ assert recent.saveCache() is False
+ assert not cacheFile.exists()
+
+ # Save Proper
+ assert recent.saveCache() is True
+ assert cacheFile.exists()
+
+ # Fail to Load
+ with monkeypatch.context() as mp:
+ mp.setattr("builtins.open", causeOSError)
+ assert recent.loadCache() is False
+ assert recent.listEntries() == []
+
+ # Load Proper
+ assert recent.loadCache() is True
+ assert recent.listEntries() == [
+ (str(pathOne), "Proj One", 100, 1600002000),
+ (str(pathTwo), "Proj Two", 200, 1600005600),
+ ]
+
+ # Remove Non-Existent Entry
+ recent.remove("stuff")
+ assert recent.listEntries() == [
+ (str(pathOne), "Proj One", 100, 1600002000),
+ (str(pathTwo), "Proj Two", 200, 1600005600),
+ ]
+
+ # Remove Second Entry
+ recent.remove(pathTwo)
+ assert recent.listEntries() == [
+ (str(pathOne), "Proj One", 100, 1600002000),
+ ]
+
+# END Test testBaseConfig_RecentCache