Reimplement C++ LOOT settings handling

It deserves its own class. Improvements over the previous implementation
include:

* Unit tests
* If a setting is missing in loaded data, its default value is used
* Invalid game settings are skipped
* Settings are stored in named member variables instead of as untyped
map elements, so it's clear what settings exist and what their types
are.

Thread safety is ensured using a reader/writer mutex lock. A
readers/writer lock would be better, but isn't available in C++11 (it's
in C++17). A lock may not be necessary at all, as most settings changes
are called from JavaScript callbacks, which are single-threaded, but
I'm not sure how V8 actually runs them, so better to be safe than sorry.
This commit is contained in:
Oliver Hamlet
2016-01-12 10:56:18 +00:00
parent 7ddb9d4b49
commit 19ad6b15ef
8 changed files with 873 additions and 71 deletions
+8 -3
View File
@@ -132,6 +132,7 @@ set (LOOT_GUI_SRC ${LOOT_SRC}
"${CMAKE_SOURCE_DIR}/src/gui/handler.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_handler.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_app.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_settings.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_state.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/scheme.cpp"
"${CMAKE_SOURCE_DIR}/src/resource.rc")
@@ -143,6 +144,7 @@ set (LOOT_GUI_HEADERS ${LOOT_HEADERS}
"${CMAKE_SOURCE_DIR}/src/gui/handler.h"
"${CMAKE_SOURCE_DIR}/src/gui/loot_handler.h"
"${CMAKE_SOURCE_DIR}/src/gui/loot_app.h"
"${CMAKE_SOURCE_DIR}/src/gui/loot_settings.h"
"${CMAKE_SOURCE_DIR}/src/gui/loot_state.h"
"${CMAKE_SOURCE_DIR}/src/gui/scheme.h"
"${CMAKE_SOURCE_DIR}/src/gui/resource.h")
@@ -160,7 +162,9 @@ set (LOOT_VALIDATOR_SRC ${LOOT_SRC}
set (LOOT_VALIDATOR_HEADERS ${LOOT_HEADERS})
set (LOOT_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/api/loot_db.cpp"
set (LOOT_TESTS_SRC ${LOOT_SRC}
"${CMAKE_SOURCE_DIR}/src/api/loot_db.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_settings.cpp"
"${CMAKE_SOURCE_DIR}/src/tests/main.cpp")
set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/fixtures.h"
@@ -188,7 +192,8 @@ set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/fixtures.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/plugin/test_plugin.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/test_metadata_list.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/test_masterlist.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/test_plugin_sorter.h")
"${CMAKE_SOURCE_DIR}/src/tests/backend/test_plugin_sorter.h"
"${CMAKE_SOURCE_DIR}/src/tests/gui/test_loot_settings.h")
source_group("Header Files" FILES ${LOOT_HEADERS} ${LOOT_GUI_HEADERS} ${LOOT_API_HEADERS} ${LOOT_TESTS_HEADERS})
@@ -309,7 +314,7 @@ target_link_libraries (loot${PROJECT_ARCH} ${Boost_LIBRARIES} ${YAML_CPP_LIBRARI
IF (${GTEST_FOUND})
# Build tests.
add_executable(tests ${LOOT_TESTS_SRC} ${LOOT_SRC} ${LOOT_TESTS_HEADERS})
add_executable(tests ${LOOT_TESTS_SRC} ${LOOT_TESTS_HEADERS})
target_link_libraries(tests loot${PROJECT_ARCH} ${Boost_LIBRARIES} ${YAML_CPP_LIBRARIES} ${LOOT_LIBS} ${GTEST_BOTH_LIBRARIES})
ENDIF ()
-27
View File
@@ -226,33 +226,6 @@ namespace loot {
_gamePath = path;
return *this;
}
std::list<GameSettings> GetGameSettings(YAML::Node& settings) {
list<GameSettings> games;
if (settings["games"])
games = settings["games"].as< list<GameSettings> >();
if (find(games.begin(), games.end(), GameSettings(GameSettings::tes4)) == games.end())
games.push_back(GameSettings(GameSettings::tes4));
if (find(games.begin(), games.end(), GameSettings(GameSettings::tes5)) == games.end())
games.push_back(GameSettings(GameSettings::tes5));
if (find(games.begin(), games.end(), GameSettings(GameSettings::fo3)) == games.end())
games.push_back(GameSettings(GameSettings::fo3));
if (find(games.begin(), games.end(), GameSettings(GameSettings::fonv)) == games.end())
games.push_back(GameSettings(GameSettings::fonv));
if (find(games.begin(), games.end(), GameSettings(GameSettings::fo4)) == games.end())
games.push_back(GameSettings(GameSettings::fo4));
// If there were any missing defaults, make sure they're in settings now.
settings["games"] = games;
return games;
}
}
namespace YAML {
-2
View File
@@ -85,8 +85,6 @@ namespace loot {
boost::filesystem::path _gamePath; //Path to the game's folder.
};
std::list<GameSettings> GetGameSettings(YAML::Node& settings);
}
namespace YAML {
+295
View File
@@ -0,0 +1,295 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2014-2015 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#include "loot_settings.h"
#include "backend/globals.h"
#include <thread>
#include <boost/filesystem/fstream.hpp>
using namespace std;
namespace loot {
LootSettings::WindowPosition::WindowPosition() : top(0), bottom(0), left(0), right(0) {}
LootSettings::LootSettings() :
gameSettings({
GameSettings(GameSettings::tes4),
GameSettings(GameSettings::tes5),
GameSettings(GameSettings::fo3),
GameSettings(GameSettings::fonv),
GameSettings(GameSettings::fo4),
GameSettings(GameSettings::tes4, "Nehrim")
.SetName("Nehrim - At Fate's Edge")
.SetMaster("Nehrim.esm")
.SetRegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation"),
}),
enableDebugLogging(false),
updateMasterlist(true),
game("auto"),
language(Language(Language::english)),
lastGame("auto"),
lastVersion(std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch)) {}
void LootSettings::load(YAML::Node& settings) {
std::lock_guard<std::recursive_mutex> guard(mutex);
upgradeYaml(settings);
if (settings["enableDebugLogging"])
enableDebugLogging = settings["enableDebugLogging"].as<bool>();
if (settings["updateMasterlist"])
updateMasterlist = settings["updateMasterlist"].as<bool>();
if (settings["game"])
game = settings["game"].as<string>();
if (settings["language"])
language = Language(settings["language"].as<string>());
if (settings["lastGame"])
lastGame = settings["lastGame"].as<string>();
if (settings["lastVersion"])
lastVersion = settings["lastVersion"].as<string>();
if (settings["window"]
&& settings["window"]["top"] && settings["window"]["bottom"]
&& settings["window"]["left"] && settings["window"]["right"]) {
windowPosition.top = settings["window"]["top"].as<long>();
windowPosition.bottom = settings["window"]["bottom"].as<long>();
windowPosition.left = settings["window"]["left"].as<long>();
windowPosition.right = settings["window"]["right"].as<long>();
}
if (settings["games"]) {
gameSettings = settings["games"].as<vector<GameSettings>>();
// If a base game isn't in the settings, add it.
if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::tes4)) == end(gameSettings))
gameSettings.push_back(GameSettings(GameSettings::tes4));
if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::tes5)) == end(gameSettings))
gameSettings.push_back(GameSettings(GameSettings::tes5));
if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::fo3)) == end(gameSettings))
gameSettings.push_back(GameSettings(GameSettings::fo3));
if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::fonv)) == end(gameSettings))
gameSettings.push_back(GameSettings(GameSettings::fonv));
if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::fo4)) == end(gameSettings))
gameSettings.push_back(GameSettings(GameSettings::fo4));
}
if (settings["filters"])
filters = settings["filters"].as<map<string, bool>>();
}
void LootSettings::load(const boost::filesystem::path& file) {
boost::filesystem::ifstream in(file);
YAML::Node content = YAML::Load(in);
load(content);
}
void LootSettings::save(const boost::filesystem::path& file) {
std::lock_guard<std::recursive_mutex> guard(mutex);
YAML::Emitter yout;
yout.SetIndent(2);
yout << toYaml();
boost::filesystem::ofstream out(file);
out << yout.c_str();
}
bool LootSettings::isDebugLoggingEnabled() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return enableDebugLogging;
}
bool LootSettings::isWindowPositionStored() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return windowPosition.top != 0 || windowPosition.bottom != 0 || windowPosition.left != 0 || windowPosition.right != 0;
}
std::string LootSettings::getGame() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return game;
}
std::string LootSettings::getLastGame() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return lastGame;
}
std::string LootSettings::getLastVersion() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return lastVersion;
}
const Language& LootSettings::getLanguage() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return language;
}
const LootSettings::WindowPosition& LootSettings::getWindowPosition() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return windowPosition;
}
std::vector<GameSettings> LootSettings::getGameSettings() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
return gameSettings;
}
void LootSettings::storeLastGame(const std::string& lastGame) {
std::lock_guard<std::recursive_mutex> guard(mutex);
this->lastGame = lastGame;
}
void LootSettings::storeWindowPosition(const WindowPosition& position) {
std::lock_guard<std::recursive_mutex> guard(mutex);
windowPosition = position;
}
void LootSettings::storeGameSettings(const std::vector<GameSettings>& gameSettings) {
std::lock_guard<std::recursive_mutex> guard(mutex);
this->gameSettings = gameSettings;
}
void LootSettings::storeFilterState(const std::string& filterId, bool enabled) {
std::lock_guard<std::recursive_mutex> guard(mutex);
filters[filterId] = enabled;
}
void LootSettings::updateLastVersion() {
std::lock_guard<std::recursive_mutex> guard(mutex);
lastVersion = std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch);
}
YAML::Node LootSettings::toYaml() const {
std::lock_guard<std::recursive_mutex> guard(mutex);
YAML::Node node;
node["enableDebugLogging"] = enableDebugLogging;
node["updateMasterlist"] = updateMasterlist;
node["game"] = game;
node["language"] = language.Locale();
node["lastGame"] = lastGame;
node["lastVersion"] = lastVersion;
if (isWindowPositionStored()) {
node["window"]["top"] = windowPosition.top;
node["window"]["bottom"] = windowPosition.bottom;
node["window"]["left"] = windowPosition.left;
node["window"]["right"] = windowPosition.right;
}
node["games"] = gameSettings;
if (!filters.empty())
node["filters"] = filters;
return node;
}
void LootSettings::upgradeYaml(YAML::Node& yaml) {
// Upgrade YAML settings' keys and values from those used in earlier
// versions of LOOT.
if (yaml["Debug Verbosity"] && !yaml["enableDebugLogging"])
yaml["enableDebugLogging"] = yaml["Debug Verbosity"].as<unsigned int>() > 0;
if (yaml["Update Masterlist"] && !yaml["updateMasterlist"])
yaml["updateMasterlist"] = yaml["Update Masterlist"];
if (yaml["Game"] && !yaml["game"])
yaml["game"] = yaml["Game"];
if (yaml["Language"] && !yaml["language"])
yaml["language"] = yaml["Language"];
if (yaml["Last Game"] && !yaml["lastGame"])
yaml["lastGame"] = yaml["Last Game"];
if (yaml["Games"] && !yaml["games"]) {
yaml["games"] = yaml["Games"];
for (auto node : yaml["games"]) {
if (node["url"]) {
node["repo"] = node["url"];
node["branch"] = "v0.8";
}
}
}
if (yaml["games"]) {
const set<string> oldDefaultBranches({
"master",
"v0.7",
});
// Handle exception if YAML is invalid, eg. if an unrecognised
// game type is used (which can happen if downgrading from a
// later version of LOOT that supports more game types).
// However, can't remove elements from a sequence Node, so have to
// copy the valid elements into a new node then overwrite the
// original.
YAML::Node validGames;
for (auto node : yaml["games"]) {
try {
GameSettings settings(node.as<GameSettings>());
if (!yaml["Games"]) {
// Update existing default branch, if the default
// repositories are used.
if (settings.RepoURL() == GameSettings(settings.Id()).RepoURL()
&& oldDefaultBranches.count(settings.RepoBranch()) == 1) {
settings.SetRepoBranch("v0.8");
}
}
validGames.push_back(settings);
}
catch (...) {}
}
yaml["games"] = validGames;
}
if (yaml["filters"])
yaml["filters"].remove("contentFilter");
}
}
+90
View File
@@ -0,0 +1,90 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2014-2015 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef LOOT_GUI_LOOT_SETTINGS
#define LOOT_GUI_LOOT_SETTINGS
#include "backend/game/game_settings.h"
#include "backend/helpers/language.h"
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include <yaml-cpp/yaml.h>
namespace loot {
class LootSettings {
public:
struct WindowPosition {
WindowPosition();
long top;
long bottom;
long left;
long right;
};
LootSettings();
void load(YAML::Node& settings);
void load(const boost::filesystem::path& file);
void save(const boost::filesystem::path& file);
bool isDebugLoggingEnabled() const;
bool isWindowPositionStored() const;
std::string getGame() const;
std::string getLastGame() const;
std::string getLastVersion() const;
const Language& getLanguage() const;
const WindowPosition& getWindowPosition() const;
std::vector<GameSettings> getGameSettings() const;
void storeLastGame(const std::string& lastGame);
void storeWindowPosition(const WindowPosition& position);
void storeGameSettings(const std::vector<GameSettings>& gameSettings);
void storeFilterState(const std::string& filterId, bool enabled);
void updateLastVersion();
YAML::Node toYaml() const;
private:
bool enableDebugLogging;
bool updateMasterlist;
std::string game;
std::string lastGame;
std::string lastVersion;
Language language;
WindowPosition windowPosition;
std::vector<GameSettings> gameSettings;
std::map<std::string, bool> filters;
mutable std::recursive_mutex mutex;
static void upgradeYaml(YAML::Node& yaml);
};
}
#endif
@@ -251,43 +251,4 @@ TEST_F(GameSettings, YamlDecode) {
EXPECT_EQ("branch1", game.RepoBranch());
EXPECT_EQ("", game.GamePath());
}
TEST(GetGameSettings, AllMissing) {
YAML::Node settings;
std::list<loot::GameSettings> expected = {
loot::GameSettings(loot::GameSettings::tes4),
loot::GameSettings(loot::GameSettings::tes5),
loot::GameSettings(loot::GameSettings::fo3),
loot::GameSettings(loot::GameSettings::fonv),
loot::GameSettings(loot::GameSettings::fo4)
};
EXPECT_FALSE(settings["games"]);
EXPECT_EQ(expected, loot::GetGameSettings(settings));
EXPECT_EQ(expected, settings["games"].as<std::list<loot::GameSettings>>());
}
TEST(GetGameSettings, MissingFallout3) {
std::list<loot::GameSettings> initial({
loot::GameSettings(loot::GameSettings::tes4, "folder1"),
loot::GameSettings(loot::GameSettings::tes5, "folder2"),
loot::GameSettings(loot::GameSettings::fonv, "folder3"),
loot::GameSettings(loot::GameSettings::fo4, "folder4")
});
YAML::Node settings;
settings["games"] = initial;
std::list<loot::GameSettings> expected = {
loot::GameSettings(loot::GameSettings::tes4, "folder1"),
loot::GameSettings(loot::GameSettings::tes5, "folder2"),
loot::GameSettings(loot::GameSettings::fonv, "folder3"),
loot::GameSettings(loot::GameSettings::fo4, "folder4"),
loot::GameSettings(loot::GameSettings::fo3)
};
EXPECT_EQ(expected, loot::GetGameSettings(settings));
EXPECT_EQ(expected, settings["games"].as<std::list<loot::GameSettings>>());
}
#endif
+479
View File
@@ -0,0 +1,479 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2014-2015 WrinklyNinja
This file is part of LOOT.
LOOT is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
LOOT is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef LOOT_TEST_GUI_LOOT_SETTINGS
#define LOOT_TEST_GUI_LOOT_SETTINGS
#include "gui/loot_settings.h"
#include "backend/globals.h"
#include "tests/fixtures.h"
namespace loot {
namespace test {
class LootSettings : public ::testing::Test {
protected:
LootSettings() : settingsFile("./settings.yaml") {}
~LootSettings() {
boost::filesystem::remove(settingsFile);
}
boost::filesystem::path settingsFile;
loot::LootSettings settings;
};
TEST_F(LootSettings, defaultConstructorShouldSetDefaultValues) {
const std::string currentVersion = std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch);
const std::vector<GameSettings> expectedGameSettings({
GameSettings(GameSettings::tes4),
GameSettings(GameSettings::tes5),
GameSettings(GameSettings::fo3),
GameSettings(GameSettings::fonv),
GameSettings(GameSettings::fo4),
GameSettings(GameSettings::tes4, "Nehrim")
.SetName("Nehrim - At Fate's Edge")
.SetMaster("Nehrim.esm")
.SetRegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation"),
});
EXPECT_FALSE(settings.isDebugLoggingEnabled());
EXPECT_EQ("auto", settings.getGame());
EXPECT_EQ("en", settings.getLanguage().Locale());
EXPECT_EQ("auto", settings.getLastGame());
EXPECT_FALSE(settings.isWindowPositionStored());
const YAML::Node node = settings.toYaml();
EXPECT_TRUE(node["updateMasterlist"].as<bool>());
EXPECT_EQ(currentVersion, node["lastVersion"].as<std::string>());
EXPECT_FALSE(node["filters"]);
// GameSettings equality only checks name and folder, so check
// other settings individually.
const std::vector<GameSettings> actualGameSettings = settings.getGameSettings();
EXPECT_EQ(expectedGameSettings, actualGameSettings);
EXPECT_EQ(expectedGameSettings[0].Id(), actualGameSettings[0].Id());
EXPECT_EQ(expectedGameSettings[0].Master(), actualGameSettings[0].Master());
EXPECT_EQ(expectedGameSettings[0].RegistryKey(), actualGameSettings[0].RegistryKey());
EXPECT_EQ(expectedGameSettings[0].RepoURL(), actualGameSettings[0].RepoURL());
EXPECT_EQ(expectedGameSettings[0].RepoBranch(), actualGameSettings[0].RepoBranch());
EXPECT_EQ(expectedGameSettings[1].Id(), actualGameSettings[1].Id());
EXPECT_EQ(expectedGameSettings[1].Master(), actualGameSettings[1].Master());
EXPECT_EQ(expectedGameSettings[1].RegistryKey(), actualGameSettings[1].RegistryKey());
EXPECT_EQ(expectedGameSettings[1].RepoURL(), actualGameSettings[1].RepoURL());
EXPECT_EQ(expectedGameSettings[1].RepoBranch(), actualGameSettings[1].RepoBranch());
EXPECT_EQ(expectedGameSettings[2].Id(), actualGameSettings[2].Id());
EXPECT_EQ(expectedGameSettings[2].Master(), actualGameSettings[2].Master());
EXPECT_EQ(expectedGameSettings[2].RegistryKey(), actualGameSettings[2].RegistryKey());
EXPECT_EQ(expectedGameSettings[2].RepoURL(), actualGameSettings[2].RepoURL());
EXPECT_EQ(expectedGameSettings[2].RepoBranch(), actualGameSettings[2].RepoBranch());
EXPECT_EQ(expectedGameSettings[3].Id(), actualGameSettings[3].Id());
EXPECT_EQ(expectedGameSettings[3].Master(), actualGameSettings[3].Master());
EXPECT_EQ(expectedGameSettings[3].RegistryKey(), actualGameSettings[3].RegistryKey());
EXPECT_EQ(expectedGameSettings[3].RepoURL(), actualGameSettings[3].RepoURL());
EXPECT_EQ(expectedGameSettings[3].RepoBranch(), actualGameSettings[3].RepoBranch());
EXPECT_EQ(expectedGameSettings[4].Id(), actualGameSettings[4].Id());
EXPECT_EQ(expectedGameSettings[4].Master(), actualGameSettings[4].Master());
EXPECT_EQ(expectedGameSettings[4].RegistryKey(), actualGameSettings[4].RegistryKey());
EXPECT_EQ(expectedGameSettings[4].RepoURL(), actualGameSettings[4].RepoURL());
EXPECT_EQ(expectedGameSettings[4].RepoBranch(), actualGameSettings[4].RepoBranch());
EXPECT_EQ(expectedGameSettings[5].Id(), actualGameSettings[5].Id());
EXPECT_EQ(expectedGameSettings[5].Master(), actualGameSettings[5].Master());
EXPECT_EQ(expectedGameSettings[5].RegistryKey(), actualGameSettings[5].RegistryKey());
EXPECT_EQ(expectedGameSettings[5].RepoURL(), actualGameSettings[5].RepoURL());
EXPECT_EQ(expectedGameSettings[5].RepoBranch(), actualGameSettings[5].RepoBranch());
}
TEST_F(LootSettings, loadingFromFileShouldLoadContentAsYaml) {
boost::filesystem::ofstream out(settingsFile);
out << "enableDebugLogging: true" << std::endl;
out.close();
settings.load(settingsFile);
EXPECT_TRUE(settings.isDebugLoggingEnabled());
}
TEST_F(LootSettings, loadingFromYamlShouldStoreLoadedValues) {
const bool enableDebugLogging = true;
const bool updateMasterlist = true;
const std::string game = "Oblivion";
const std::string language = "fr";
const std::string lastGame = "Skyrim";
const std::string lastVersion = "0.7.1";
const std::map<std::string, long> window({
{"top", 1},
{"bottom", 2},
{"left", 3},
{"right", 4},
});
const std::vector<GameSettings> games({
GameSettings(GameSettings::tes4).SetName("Game Name"),
});
const std::map<std::string, bool> filters({
{"hideBashTags", false},
{"hideCRCs", true},
});
YAML::Node inputYaml;
inputYaml["enableDebugLogging"] = enableDebugLogging;
inputYaml["updateMasterlist"] = updateMasterlist;
inputYaml["game"] = game;
inputYaml["language"] = language;
inputYaml["lastGame"] = lastGame;
inputYaml["lastVersion"] = lastVersion;
inputYaml["window"] = window;
inputYaml["games"] = games;
inputYaml["filters"] = filters;
settings.load(inputYaml);
EXPECT_EQ(enableDebugLogging, settings.isDebugLoggingEnabled());
EXPECT_EQ(game, settings.getGame());
EXPECT_EQ(language, settings.getLanguage().Locale());
EXPECT_EQ(lastGame, settings.getLastGame());
EXPECT_EQ(1, settings.getWindowPosition().top);
EXPECT_EQ(2, settings.getWindowPosition().bottom);
EXPECT_EQ(3, settings.getWindowPosition().left);
EXPECT_EQ(4, settings.getWindowPosition().right);
const YAML::Node outputYaml = settings.toYaml();
EXPECT_EQ(updateMasterlist, outputYaml["updateMasterlist"].as<bool>());
EXPECT_EQ(lastVersion, outputYaml["lastVersion"].as<std::string>());
for (const auto& filter : filters) {
EXPECT_EQ(filter.second, outputYaml["filters"][filter.first].as<bool>());
}
EXPECT_EQ(games[0].Name(), settings.getGameSettings()[0].Name());
}
TEST_F(LootSettings, loadingFromEmptyYamlShouldNotThrow) {
YAML::Node yaml;
EXPECT_NO_THROW(settings.load(yaml));
}
TEST_F(LootSettings, loadingFromYamlShouldUpgradeFromVersion0Point6Format) {
const unsigned int DebugVerbosity = 3;
const bool UpdateMasterlist = true;
const std::string Game = "Oblivion";
const std::string Language = "fr";
const std::string LastGame = "Skyrim";
const std::vector<GameSettings> Games({
GameSettings(GameSettings::tes4).SetName("Game Name"),
});
YAML::Node inputYaml;
inputYaml["Debug Verbosity"] = DebugVerbosity;
inputYaml["Update Masterlist"] = UpdateMasterlist;
inputYaml["Game"] = Game;
inputYaml["Language"] = Language;
inputYaml["Last Game"] = LastGame;
inputYaml["Games"] = Games;
inputYaml["Games"][0]["url"] = inputYaml["Games"][0]["repo"];
inputYaml["Games"][0].remove("repo");
inputYaml["Games"][0].remove("branch");
settings.load(inputYaml);
const YAML::Node outputYaml = settings.toYaml();
EXPECT_TRUE(settings.isDebugLoggingEnabled());
EXPECT_EQ(UpdateMasterlist, outputYaml["updateMasterlist"].as<bool>());
EXPECT_EQ(Game, settings.getGame());
EXPECT_EQ(Language, settings.getLanguage().Locale());
EXPECT_EQ(LastGame, settings.getLastGame());
EXPECT_EQ(Games[0].Name(), settings.getGameSettings()[0].Name());
EXPECT_EQ(Games[0].RepoURL(), settings.getGameSettings()[0].RepoURL());
EXPECT_EQ(Games[0].RepoBranch(), settings.getGameSettings()[0].RepoBranch());
}
TEST_F(LootSettings, loadingFromYamlShouldNotUpgradeVersion0Point6SettingsIfEquivalentsAlreadyExist) {
const unsigned int DebugVerbosity = 3;
const bool enableDebugLogging = false;
const bool UpdateMasterlist = true;
const bool updateMasterlist = false;
const std::string Game = "Oblivion";
const std::string game = "auto";
const std::string Language = "fr";
const std::string language = "en";
const std::string LastGame = "Skyrim";
const std::string lastGame = "auto";
const std::vector<GameSettings> Games({
GameSettings(GameSettings::tes4).SetName("Old Game Name"),
});
const std::vector<GameSettings> games({
GameSettings(GameSettings::fo3).SetName("Game Name"),
});
YAML::Node inputYaml;
inputYaml["Debug Verbosity"] = DebugVerbosity;
inputYaml["enableDebugLogging"] = enableDebugLogging;
inputYaml["Update Masterlist"] = UpdateMasterlist;
inputYaml["updateMasterlist"] = updateMasterlist;
inputYaml["Game"] = Game;
inputYaml["game"] = game;
inputYaml["Language"] = Language;
inputYaml["language"] = language;
inputYaml["Last Game"] = LastGame;
inputYaml["lastGame"] = lastGame;
inputYaml["Games"] = Games;
inputYaml["Games"][0]["url"] = inputYaml["Games"][0]["repo"];
inputYaml["Games"][0].remove("repo");
inputYaml["Games"][0].remove("branch");
inputYaml["games"] = games;
settings.load(inputYaml);
const YAML::Node outputYaml = settings.toYaml();
EXPECT_EQ(enableDebugLogging, settings.isDebugLoggingEnabled());
EXPECT_EQ(updateMasterlist, outputYaml["updateMasterlist"].as<bool>());
EXPECT_EQ(game, settings.getGame());
EXPECT_EQ(language, settings.getLanguage().Locale());
EXPECT_EQ(lastGame, settings.getLastGame());
EXPECT_EQ(games[0].Name(), settings.getGameSettings()[0].Name());
}
TEST_F(LootSettings, loadingFromYamlShouldUpgradeOldDefaultGameRepositoryBranches) {
const std::vector<GameSettings> games({GameSettings(GameSettings::tes4)});
YAML::Node inputYaml;
inputYaml["games"] = games;
inputYaml["games"][0]["branch"] = "v0.7";
settings.load(inputYaml);
EXPECT_EQ(games[0].RepoBranch(), settings.getGameSettings()[0].RepoBranch());
}
TEST_F(LootSettings, loadingFromYamlShouldNotUpgradeNonDefaultGameRepositoryBranches) {
const std::vector<GameSettings> games({GameSettings(GameSettings::tes4)});
YAML::Node inputYaml;
inputYaml["games"] = games;
inputYaml["games"][0]["branch"] = "foo";
settings.load(inputYaml);
EXPECT_EQ("foo", settings.getGameSettings()[0].RepoBranch());
}
TEST_F(LootSettings, loadingFromYamlShouldAddMissingBaseGames) {
const std::vector<GameSettings> games({GameSettings(GameSettings::tes4)});
YAML::Node inputYaml;
inputYaml["games"] = games;
settings.load(inputYaml);
const std::vector<GameSettings> expectedGameSettings({
GameSettings(GameSettings::tes4),
GameSettings(GameSettings::tes5),
GameSettings(GameSettings::fo3),
GameSettings(GameSettings::fonv),
GameSettings(GameSettings::fo4),
});
EXPECT_EQ(expectedGameSettings, settings.getGameSettings());
}
TEST_F(LootSettings, loadingFromYamlShouldSkipUnrecognisedGames) {
YAML::Node inputYaml;
inputYaml["games"][0] = GameSettings(GameSettings::tes4);
inputYaml["games"][0]["type"] = "Foobar";
inputYaml["games"][0]["name"] = "Foobar";
inputYaml["games"][1] = GameSettings(GameSettings::tes5).SetName("Game Name");
settings.load(inputYaml);
EXPECT_EQ("Game Name", settings.getGameSettings()[0].Name());
}
TEST_F(LootSettings, loadingFromYamlShouldRemoveTheContentFilterSetting) {
YAML::Node inputYaml;
inputYaml["filters"]["contentFilter"] = "foo";
settings.load(inputYaml);
}
TEST_F(LootSettings, saveShouldWriteSettingsAsYamlToPassedFile) {
settings.storeLastGame("Skyrim");
settings.save(settingsFile);
settings.storeLastGame("auto");
settings.load(settingsFile);
EXPECT_EQ("Skyrim", settings.getLastGame());
}
TEST_F(LootSettings, getLanguageShouldReturnTheCurrentValue) {
YAML::Node inputYaml;
inputYaml["language"] = "fr";
settings.load(inputYaml);
EXPECT_EQ("fr", settings.getLanguage().Locale());
}
TEST_F(LootSettings, isWindowPositionStoredShouldReturnFalseIfAllPositionValuesAreZero) {
loot::LootSettings::WindowPosition position;
settings.storeWindowPosition(position);
EXPECT_FALSE(settings.isWindowPositionStored());
}
TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfTopPositionValueIsNonZero) {
loot::LootSettings::WindowPosition position;
position.top = 1;
settings.storeWindowPosition(position);
EXPECT_TRUE(settings.isWindowPositionStored());
}
TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfBottomPositionValueIsNonZero) {
loot::LootSettings::WindowPosition position;
position.bottom = 1;
settings.storeWindowPosition(position);
EXPECT_TRUE(settings.isWindowPositionStored());
}
TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfLeftPositionValueIsNonZero) {
loot::LootSettings::WindowPosition position;
position.left = 1;
settings.storeWindowPosition(position);
EXPECT_TRUE(settings.isWindowPositionStored());
}
TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfRightPositionValueIsNonZero) {
loot::LootSettings::WindowPosition position;
position.right = 1;
settings.storeWindowPosition(position);
EXPECT_TRUE(settings.isWindowPositionStored());
}
TEST_F(LootSettings, storeGameSettingsShouldReplaceExistingGameSettings) {
const std::vector<GameSettings> gameSettings({GameSettings(GameSettings::tes5)});
settings.storeGameSettings(gameSettings);
EXPECT_EQ(gameSettings, settings.getGameSettings());
}
TEST_F(LootSettings, storeLastGameShouldReplaceExistingValue) {
settings.storeLastGame("Fallout3");
EXPECT_EQ("Fallout3", settings.getLastGame());
}
TEST_F(LootSettings, storeWindowPositionShouldReplaceExistingValue) {
loot::LootSettings::WindowPosition expectedPosition;
expectedPosition.top = 1;
settings.storeWindowPosition(expectedPosition);
loot::LootSettings::WindowPosition actualPosition = settings.getWindowPosition();
EXPECT_EQ(expectedPosition.top, actualPosition.top);
EXPECT_EQ(expectedPosition.bottom, actualPosition.bottom);
EXPECT_EQ(expectedPosition.left, actualPosition.left);
EXPECT_EQ(expectedPosition.right, actualPosition.right);
}
TEST_F(LootSettings, updateLastVersionShouldSetValueToCurrentLootVersion) {
const std::string currentVersion = std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch);
YAML::Node inputYaml;
inputYaml["lastVersion"] = "v0.7.1";
settings.load(inputYaml);
settings.updateLastVersion();
EXPECT_EQ(currentVersion, settings.getLastVersion());
}
TEST_F(LootSettings, toYamlShouldOutputStoredSettings) {
const bool enableDebugLogging = true;
const bool updateMasterlist = true;
const std::string game = "Oblivion";
const std::string language = "fr";
const std::string lastGame = "Skyrim";
const std::string lastVersion = "0.7.1";
const std::map<std::string, long> window({
{"top", 1},
{"bottom", 2},
{"left", 3},
{"right", 4},
});
const std::vector<GameSettings> games({
GameSettings(GameSettings::tes4).SetName("Game Name"),
});
const std::map<std::string, bool> filters({
{"hideBashTags", false},
{"hideCRCs", true},
});
YAML::Node inputYaml;
inputYaml["enableDebugLogging"] = enableDebugLogging;
inputYaml["updateMasterlist"] = updateMasterlist;
inputYaml["game"] = game;
inputYaml["language"] = language;
inputYaml["lastGame"] = lastGame;
inputYaml["lastVersion"] = lastVersion;
inputYaml["window"] = window;
inputYaml["games"] = games;
inputYaml["filters"] = filters;
settings.load(inputYaml);
const YAML::Node outputYaml = settings.toYaml();
EXPECT_EQ(enableDebugLogging, outputYaml["enableDebugLogging"].as<bool>());
EXPECT_EQ(updateMasterlist, outputYaml["updateMasterlist"].as<bool>());
EXPECT_EQ(game, outputYaml["game"].as<std::string>());
EXPECT_EQ(language, outputYaml["language"].as<std::string>());
EXPECT_EQ(lastGame, outputYaml["lastGame"].as<std::string>());
EXPECT_EQ(lastVersion, outputYaml["lastVersion"].as<std::string>());
for (const auto& position : window) {
EXPECT_EQ(position.second, outputYaml["window"][position.first].as<long>());
}
for (const auto& filter : filters) {
EXPECT_EQ(filter.second, outputYaml["filters"][filter.first].as<bool>());
}
EXPECT_EQ(games[0].Name(), outputYaml["games"][0]["name"].as<std::string>());
}
}
}
#endif
+1
View File
@@ -51,6 +51,7 @@
#include "backend/test_metadata_list.h"
#include "backend/test_masterlist.h"
#include "backend/test_plugin_sorter.h"
#include "gui/test_loot_settings.h"
#include <boost/log/core.hpp>