Move recent cache out of config, and update all tests
This commit is contained in:
+186
-192
@@ -19,16 +19,16 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user