From ff575e107e2961c498f878ebe2359174f619848d Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sat, 28 Jan 2017 18:07:01 +0000 Subject: [PATCH] Move GameSettings into GUI code and split Game GameSettings is only needed by the LOOT GUI, and many functions in Game could also be GUI-specific, so they were moved to a new loot::gui::Game class (the namespace is temporary, to prevent conflicts until the loot::Game class is hidden inside the API). This also involved minor modifications to LoadOrderHandler, the removal of Masterlist::Update(Game& game), and the (re)addition of a new API function for specifying the game's main master file. --- CMakeLists.txt | 17 +- include/loot/database_interface.h | 7 + src/api/api_database.cpp | 9 +- src/api/api_database.h | 4 + src/backend/game/game.cpp | 208 ++-------- src/backend/game/game.h | 33 +- src/backend/game/load_order_handler.cpp | 49 +-- src/backend/game/load_order_handler.h | 9 +- src/backend/masterlist.cpp | 4 - src/backend/masterlist.h | 1 - src/gui/query/clear_all_metadata_query.h | 4 +- src/gui/query/clear_plugin_metadata_query.h | 4 +- src/gui/query/get_conflicting_plugins_query.h | 4 +- src/gui/query/get_game_types_query.h | 2 +- src/gui/query/metadata_query.h | 1 - src/gui/query/update_masterlist_query.h | 6 +- src/gui/state/game.cpp | 268 +++++++++++++ src/gui/state/game.h | 73 ++++ .../game => gui/state}/game_settings.cpp | 16 +- .../game => gui/state}/game_settings.h | 8 +- src/gui/state/loot_settings.h | 2 +- src/gui/state/loot_state.cpp | 18 +- src/gui/state/loot_state.h | 12 +- src/tests/backend/game/game_cache_test.h | 2 +- src/tests/backend/game/game_test.h | 215 +++------- .../backend/game/load_order_handler_test.h | 135 +------ src/tests/backend/main.cpp | 1 - src/tests/backend/masterlist_test.h | 29 -- .../backend/metadata/condition_grammar_test.h | 57 ++- .../metadata/conditional_metadata_test.h | 8 +- .../metadata/plugin_cleaning_data_test.h | 6 +- .../backend/metadata/plugin_metadata_test.h | 2 +- src/tests/backend/metadata_list_test.h | 2 +- src/tests/backend/plugin/plugin_sorter_test.h | 35 +- src/tests/backend/plugin/plugin_test.h | 2 +- src/tests/gui/main.cpp | 2 + .../game => gui/state}/game_settings_test.h | 19 +- src/tests/gui/state/game_test.h | 372 ++++++++++++++++++ 38 files changed, 963 insertions(+), 683 deletions(-) create mode 100644 src/gui/state/game.cpp create mode 100644 src/gui/state/game.h rename src/{backend/game => gui/state}/game_settings.cpp (95%) rename src/{backend/game => gui/state}/game_settings.h (96%) rename src/tests/{backend/game => gui/state}/game_settings_test.h (92%) create mode 100644 src/tests/gui/state/game_test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 63010b60..c0198e3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,7 +176,6 @@ set (LOOT_SRC "${CMAKE_BINARY_DIR}/generated/loot_version.cpp" "${CMAKE_SOURCE_DIR}/src/backend/metadata/tag.cpp" "${CMAKE_SOURCE_DIR}/src/backend/game/game.cpp" "${CMAKE_SOURCE_DIR}/src/backend/game/game_cache.cpp" - "${CMAKE_SOURCE_DIR}/src/backend/game/game_settings.cpp" "${CMAKE_SOURCE_DIR}/src/backend/game/load_order_handler.cpp" "${CMAKE_SOURCE_DIR}/src/backend/metadata_list.cpp" "${CMAKE_SOURCE_DIR}/src/backend/masterlist.cpp" @@ -191,7 +190,6 @@ set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_evaluator. "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h" "${CMAKE_SOURCE_DIR}/src/backend/game/game.h" "${CMAKE_SOURCE_DIR}/src/backend/game/game_cache.h" - "${CMAKE_SOURCE_DIR}/src/backend/game/game_settings.h" "${CMAKE_SOURCE_DIR}/src/backend/game/load_order_handler.h" "${CMAKE_SOURCE_DIR}/src/backend/metadata_list.h" "${CMAKE_SOURCE_DIR}/src/backend/masterlist.h" @@ -236,6 +234,8 @@ set (LOOT_GUI_SRC "${CMAKE_SOURCE_DIR}/src/gui/main.cpp" "${CMAKE_SOURCE_DIR}/src/gui/loot_app.cpp" "${CMAKE_SOURCE_DIR}/src/gui/loot_scheme_handler_factory.cpp" "${CMAKE_SOURCE_DIR}/src/gui/query_handler.cpp" + "${CMAKE_SOURCE_DIR}/src/gui/state/game.cpp" + "${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.cpp" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.cpp" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.cpp" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.cpp" @@ -278,6 +278,8 @@ set (LOOT_GUI_HEADERS "${CMAKE_SOURCE_DIR}/src/gui/editor_message.h" "${CMAKE_SOURCE_DIR}/src/gui/query/sort_plugins_query.h" "${CMAKE_SOURCE_DIR}/src/gui/query/update_masterlist_query.h" "${CMAKE_SOURCE_DIR}/src/gui/query_handler.h" + "${CMAKE_SOURCE_DIR}/src/gui/state/game.h" + "${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.h" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.h" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.h" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.h" @@ -311,7 +313,6 @@ set (LOOT_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/tests/backend/main.cpp") set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/backend/game/game_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/game/game_cache_test.h" - "${CMAKE_SOURCE_DIR}/src/tests/backend/game/game_settings_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/game/load_order_handler_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/git_helper_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/helpers_test.h" @@ -343,14 +344,20 @@ set(LOOT_API_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/api/api_game_operatio "${CMAKE_SOURCE_DIR}/src/tests/api/is_compatible_test.h" "${CMAKE_SOURCE_DIR}/src/tests/common_game_test_fixture.h") -set(LOOT_GUI_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.cpp" +set(LOOT_GUI_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/gui/state/game.cpp" + "${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.cpp" + "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.cpp" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.cpp" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.cpp" "${CMAKE_SOURCE_DIR}/src/tests/gui/main.cpp") -set (LOOT_GUI_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.h" +set (LOOT_GUI_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/gui/state/game.h" + "${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.h" + "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.h" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.h" "${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.h" + "${CMAKE_SOURCE_DIR}/src/tests/gui/state/game_test.h" + "${CMAKE_SOURCE_DIR}/src/tests/gui/state/game_settings_test.h" "${CMAKE_SOURCE_DIR}/src/tests/gui/state/loot_paths_test.h" "${CMAKE_SOURCE_DIR}/src/tests/gui/state/loot_settings_test.h" "${CMAKE_SOURCE_DIR}/src/tests/gui/state/loot_state_test.h") diff --git a/include/loot/database_interface.h b/include/loot/database_interface.h index 4e5b0701..f45752e5 100644 --- a/include/loot/database_interface.h +++ b/include/loot/database_interface.h @@ -70,6 +70,13 @@ public: * @{ */ + /** + * @brief Identify the game's main master file. + * @details When sorting, LOOT always only loads the headers of the game's + * main master file as a performance optimisation. + */ + virtual void IdentifyMainMasterFile(const std::string& masterFile) = 0; + /** * @brief Calculates a new load order for the game's installed plugins * (including inactive plugins) and outputs the sorted order. diff --git a/src/api/api_database.cpp b/src/api/api_database.cpp index 4a979b70..b6d27065 100644 --- a/src/api/api_database.cpp +++ b/src/api/api_database.cpp @@ -36,8 +36,7 @@ namespace loot { ApiDatabase::ApiDatabase(const GameType game, const std::string& gamePath, const std::string& gameLocalDataPath) - : game_(Game(GameSettings(GameType(game)), "", gameLocalDataPath)) { - game_.SetGamePath(gamePath); + : game_(game, gamePath, gameLocalDataPath) { game_.Init(); } @@ -85,13 +84,17 @@ void ApiDatabase::EvalLists() { game_.GetUserlist() = userTemp; } +void ApiDatabase::IdentifyMainMasterFile(const std::string& masterFile) { + masterFile_ = masterFile; +} + //////////////////////////////////// // LOOT Functionality Functions //////////////////////////////////// std::vector ApiDatabase::SortPlugins(const std::vector& plugins) { // Always reload all the plugins. - game_.LoadPlugins(plugins, false); + game_.LoadPlugins(plugins, masterFile_, false); //Sort plugins into their load order. PluginSorter sorter; diff --git a/src/api/api_database.h b/src/api/api_database.h index f7e8d24e..25d577bf 100644 --- a/src/api/api_database.h +++ b/src/api/api_database.h @@ -43,6 +43,8 @@ struct ApiDatabase : public DatabaseInterface { void EvalLists(); + void IdentifyMainMasterFile(const std::string& masterFile); + std::vector SortPlugins(const std::vector& plugins); bool UpdateMasterlist(const std::string& masterlist_path, @@ -64,6 +66,8 @@ struct ApiDatabase : public DatabaseInterface { private: Game game_; + std::string masterFile_; + Masterlist unevaluatedMasterlist_; MetadataList unevaluatedUserlist_; }; diff --git a/src/backend/game/game.cpp b/src/backend/game/game.cpp index 6afafd4a..26d2ad3c 100644 --- a/src/backend/game/game.cpp +++ b/src/backend/game/game.cpp @@ -58,103 +58,36 @@ using std::vector; namespace fs = boost::filesystem; namespace loot { -Game::Game(const GameSettings& gameSettings, - const boost::filesystem::path& lootDataPath, +Game::Game(const GameType gameType, + const boost::filesystem::path& gamePath, const boost::filesystem::path& localDataPath) : - GameSettings(gameSettings), - lootDataPath_(lootDataPath), - localDataPath_(localDataPath), - pluginsFullyLoaded_(false) { - this->SetName(gameSettings.Name()) - .SetMaster(gameSettings.Master()) - .SetRepoURL(gameSettings.RepoURL()) - .SetRepoBranch(gameSettings.RepoBranch()) - .SetGamePath(gameSettings.GamePath()) - .SetRegistryKey(gameSettings.RegistryKey()); + type_(gameType), + gamePath_(gamePath), + localDataPath_(localDataPath) {} + +GameType Game::Type() const { + return type_; } -bool Game::IsInstalled() { - try { - BOOST_LOG_TRIVIAL(trace) << "Checking if game \"" << Name() << "\" is installed."; - if (!GamePath().empty() && fs::exists(GamePath() / "Data" / Master())) - return true; +boost::filesystem::path Game::DataPath() const { + return gamePath_ / "Data"; +} - if (fs::exists(fs::path("..") / "Data" / Master())) { - SetGamePath(".."); - return true; - } - -#ifdef _WIN32 - std::string path; - std::string key_parent = fs::path(RegistryKey()).parent_path().string(); - std::string key_name = fs::path(RegistryKey()).filename().string(); - path = RegKeyStringValue("HKEY_LOCAL_MACHINE", key_parent, key_name); - if (!path.empty() && fs::exists(fs::path(path) / "Data" / Master())) { - SetGamePath(path); - return true; - } -#endif - } catch (std::exception &e) { - BOOST_LOG_TRIVIAL(error) << "Error while checking if game \"" << Name() << "\" is installed: " << e.what(); - } - - return false; +std::string Game::GetArchiveFileExtension() const { + if (type_ == GameType::fo4) + return ".ba2"; + else + return ".bsa"; } void Game::Init() { - BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game: " << Name(); + BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game of type " << (int) type_ << " at: " << gamePath_; - if (!this->IsInstalled()) { - throw GameDetectionError("Game path could not be detected."); - } - - if (!lootDataPath_.empty()) { - //Make sure that the LOOT game path exists. - try { - if (!fs::exists(lootDataPath_ / FolderName())) - fs::create_directories(lootDataPath_ / FolderName()); - } catch (fs::filesystem_error& e) { - throw FileAccessError((boost::format("Could not create LOOT folder for game. Details: %1%") % e.what()).str()); - } - } - - loadOrderHandler_.Init(*this, localDataPath_); + loadOrderHandler_.Init(type_, gamePath_, localDataPath_); StoreLoadOrder(loadOrderHandler_.GetLoadOrder()); } -void Game::RedatePlugins() { - if (Type() != GameType::tes5 && Type() != GameType::tes5se) { - BOOST_LOG_TRIVIAL(warning) << "Cannot redate plugins for game " << Name(); - return; - } - - vector loadorder = GetLoadOrder(); - if (!loadorder.empty()) { - time_t lastTime = 0; - for (const auto &pluginName : loadorder) { - fs::path filepath = DataPath() / pluginName; - if (!fs::exists(filepath)) { - if (fs::exists(filepath.string() + ".ghost")) - filepath += ".ghost"; - else - continue; - } - - time_t thisTime = fs::last_write_time(filepath); - BOOST_LOG_TRIVIAL(info) << "Current timestamp for \"" << filepath.filename().string() << "\": " << thisTime; - if (thisTime >= lastTime) { - lastTime = thisTime; - BOOST_LOG_TRIVIAL(trace) << "No need to redate \"" << filepath.filename().string() << "\"."; - } else { - lastTime += 60; - fs::last_write_time(filepath, lastTime); //Space timestamps by a minute. - BOOST_LOG_TRIVIAL(info) << "Redated \"" << filepath.filename().string() << "\" to: " << lastTime; - } - } - } -} - -void Game::LoadPlugins(const std::vector& plugins, bool headersOnly) { +void Game::LoadPlugins(const std::vector& plugins, const std::string& masterFile, bool headersOnly) { uintmax_t meanFileSize = 0; std::multimap sizeMap; @@ -206,7 +139,7 @@ void Game::LoadPlugins(const std::vector& plugins, bool headersOnly threads.push_back(thread([&]() { for (auto pluginName : pluginGroup) { BOOST_LOG_TRIVIAL(trace) << "Loading " << pluginName; - if (boost::iequals(pluginName, Master())) + if (boost::iequals(pluginName, masterFile)) AddPlugin(Plugin(*this, pluginName, true)); else AddPlugin(Plugin(*this, pluginName, headersOnly)); @@ -219,28 +152,6 @@ void Game::LoadPlugins(const std::vector& plugins, bool headersOnly if (thread.joinable()) thread.join(); } - - pluginsFullyLoaded_ = !headersOnly; -} - -void Game::LoadAllInstalledPlugins(bool headersOnly) { - std::vector plugins; - - BOOST_LOG_TRIVIAL(trace) << "Scanning for plugins in " << this->DataPath(); - for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) { - if (fs::is_regular_file(it->status()) && Plugin::IsValid(it->path().filename().string(), *this)) { - string name = it->path().filename().string(); - BOOST_LOG_TRIVIAL(info) << "Found plugin: " << name; - - plugins.push_back(name); - } - } - - LoadPlugins(plugins, headersOnly); -} - -bool Game::ArePluginsFullyLoaded() const { - return pluginsFullyLoaded_; } bool Game::IsPluginActive(const std::string& pluginName) const { @@ -251,91 +162,16 @@ bool Game::IsPluginActive(const std::string& pluginName) const { } } -short Game::GetActiveLoadOrderIndex(const std::string & pluginName) const { - return GetActiveLoadOrderIndex(pluginName, GetLoadOrder()); -} - -short Game::GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector& loadOrder) const { - // Get the full load order, then count the number of active plugins until the - // given plugin is encountered. If the plugin isn't active or in the load - // order, return -1. - - if (!IsPluginActive(pluginName)) - return -1; - - short numberOfActivePlugins = 0; - for (const std::string& plugin : loadOrder) { - if (boost::iequals(plugin, pluginName)) - return numberOfActivePlugins; - - if (IsPluginActive(plugin)) - ++numberOfActivePlugins; - } - - return -1; -} - std::vector Game::GetLoadOrder() const { auto loadOrder = GameCache::GetLoadOrder(); + if (loadOrder.empty()) + return loadOrderHandler_.GetLoadOrder(); return loadOrder; } void Game::SetLoadOrder(const std::vector& loadOrder) { - loadOrderHandler_.BackupLoadOrder(GetLoadOrder(), lootDataPath_ / FolderName()); loadOrderHandler_.SetLoadOrder(loadOrder); StoreLoadOrder(loadOrder); } - -fs::path Game::MasterlistPath() const { - if (lootDataPath_.empty() || FolderName().empty()) - return ""; - else - return lootDataPath_ / FolderName() / "masterlist.yaml"; -} - -fs::path Game::UserlistPath() const { - if (lootDataPath_.empty() || FolderName().empty()) - return ""; - else - return lootDataPath_ / FolderName() / "userlist.yaml"; -} - -#ifdef _WIN32 -std::string Game::RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value) { - HKEY hKey = NULL; - DWORD len = MAX_PATH; - std::wstring wstr(MAX_PATH, 0); - - if (keyStr == "HKEY_CLASSES_ROOT") - hKey = HKEY_CLASSES_ROOT; - else if (keyStr == "HKEY_CURRENT_CONFIG") - hKey = HKEY_CURRENT_CONFIG; - else if (keyStr == "HKEY_CURRENT_USER") - hKey = HKEY_CURRENT_USER; - else if (keyStr == "HKEY_LOCAL_MACHINE") - hKey = HKEY_LOCAL_MACHINE; - else if (keyStr == "HKEY_USERS") - hKey = HKEY_USERS; - else - throw std::invalid_argument("Invalid registry key given."); - - BOOST_LOG_TRIVIAL(trace) << "Getting string for registry key, subkey and value: " << keyStr << " + " << subkey << " + " << value; - LONG ret = RegGetValue(hKey, - ToWinWide(subkey).c_str(), - ToWinWide(value).c_str(), - RRF_RT_REG_SZ | KEY_WOW64_32KEY, - NULL, - &wstr[0], - &len); - - if (ret == ERROR_SUCCESS) { - BOOST_LOG_TRIVIAL(info) << "Found string: " << wstr.c_str(); - return FromWinWide(wstr.c_str()); // Passing c_str() cuts off any unused buffer. - } else { - BOOST_LOG_TRIVIAL(info) << "Failed to get string value."; - return ""; - } -} -#endif } diff --git a/src/backend/game/game.h b/src/backend/game/game.h index 9a37d7e8..9b76b24a 100644 --- a/src/backend/game/game.h +++ b/src/backend/game/game.h @@ -30,45 +30,32 @@ #include #include "backend/game/game_cache.h" -#include "backend/game/game_settings.h" #include "backend/game/load_order_handler.h" namespace loot { -class Game : public GameSettings, public GameCache { +class Game : public GameCache { public: - Game(const GameSettings& gameSettings, - const boost::filesystem::path& lootDataPath, + Game(const GameType gameType, + const boost::filesystem::path& gamePath, const boost::filesystem::path& localDataPath = ""); - bool IsInstalled(); //Sets gamePath if the current value is not valid and a valid path is found. + GameType Type() const; + boost::filesystem::path DataPath() const; + std::string GetArchiveFileExtension() const; + void Init(); - void RedatePlugins(); //Change timestamps to match load order (Skyrim only). + void LoadPlugins(const std::vector& plugins, const std::string& masterFile, bool headersOnly); - void LoadPlugins(const std::vector& plugins, bool headersOnly); - void LoadAllInstalledPlugins(bool headersOnly); //Loads all installed plugins. - bool ArePluginsFullyLoaded() const; // Checks if the game's plugins have already been loaded. - - // Check if the plugin is active by using the cached value if - // available, and otherwise asking the load order handler. bool IsPluginActive(const std::string& pluginName) const; - short GetActiveLoadOrderIndex(const std::string & pluginName) const; - short GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector& loadOrder) const; std::vector GetLoadOrder() const; void SetLoadOrder(const std::vector& loadOrder); - - boost::filesystem::path MasterlistPath() const; - boost::filesystem::path UserlistPath() const; private: -#ifdef _WIN32 - std::string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value); -#endif - - const boost::filesystem::path lootDataPath_; + const GameType type_; + const boost::filesystem::path gamePath_; const boost::filesystem::path localDataPath_; - bool pluginsFullyLoaded_; LoadOrderHandler loadOrderHandler_; }; } diff --git a/src/backend/game/load_order_handler.cpp b/src/backend/game/load_order_handler.cpp index 67381e5e..dfb0b67c 100644 --- a/src/backend/game/load_order_handler.cpp +++ b/src/backend/game/load_order_handler.cpp @@ -40,8 +40,10 @@ LoadOrderHandler::~LoadOrderHandler() { lo_destroy_handle(gh_); } -void LoadOrderHandler::Init(const GameSettings& game, const boost::filesystem::path& gameLocalAppData) { - if (game.GamePath().empty()) { +void LoadOrderHandler::Init(const GameType& gameType, + const boost::filesystem::path& gamePath, + const boost::filesystem::path& gameLocalAppData) { + if (gamePath.empty()) { throw std::invalid_argument("Game path is not initialised."); } @@ -57,18 +59,18 @@ void LoadOrderHandler::Init(const GameSettings& game, const boost::filesystem::p } int ret; - if (game.Type() == GameType::tes4) - ret = lo_create_handle(&gh_, LIBLO_GAME_TES4, game.GamePath().string().c_str(), gameLocalDataPath); - else if (game.Type() == GameType::tes5) - ret = lo_create_handle(&gh_, LIBLO_GAME_TES5, game.GamePath().string().c_str(), gameLocalDataPath); - else if (game.Type() == GameType::tes5se) - ret = lo_create_handle(&gh_, LIBLO_GAME_TES5SE, game.GamePath().string().c_str(), gameLocalDataPath); - else if (game.Type() == GameType::fo3) - ret = lo_create_handle(&gh_, LIBLO_GAME_FO3, game.GamePath().string().c_str(), gameLocalDataPath); - else if (game.Type() == GameType::fonv) - ret = lo_create_handle(&gh_, LIBLO_GAME_FNV, game.GamePath().string().c_str(), gameLocalDataPath); - else if (game.Type() == GameType::fo4) - ret = lo_create_handle(&gh_, LIBLO_GAME_FO4, game.GamePath().string().c_str(), gameLocalDataPath); + if (gameType == GameType::tes4) + ret = lo_create_handle(&gh_, LIBLO_GAME_TES4, gamePath.string().c_str(), gameLocalDataPath); + else if (gameType == GameType::tes5) + ret = lo_create_handle(&gh_, LIBLO_GAME_TES5, gamePath.string().c_str(), gameLocalDataPath); + else if (gameType == GameType::tes5se) + ret = lo_create_handle(&gh_, LIBLO_GAME_TES5SE, gamePath.string().c_str(), gameLocalDataPath); + else if (gameType == GameType::fo3) + ret = lo_create_handle(&gh_, LIBLO_GAME_FO3, gamePath.string().c_str(), gameLocalDataPath); + else if (gameType == GameType::fonv) + ret = lo_create_handle(&gh_, LIBLO_GAME_FNV, gamePath.string().c_str(), gameLocalDataPath); + else if (gameType == GameType::fo4) + ret = lo_create_handle(&gh_, LIBLO_GAME_FO4, gamePath.string().c_str(), gameLocalDataPath); else ret = LIBLO_ERROR_INVALID_ARGS; @@ -166,23 +168,4 @@ void LoadOrderHandler::SetLoadOrder(const std::vector& loadOrder) c throw std::system_error(ret, libloadorder_category(), err); } } -void LoadOrderHandler::BackupLoadOrder(const std::vector& loadOrder, - const boost::filesystem::path & backupDirectory) { - const int maxBackupIndex = 2; - boost::format filenameFormat = boost::format("loadorder.bak.%1%"); - - boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % 2).str(); - if (boost::filesystem::exists(backupFilePath)) - boost::filesystem::remove(backupFilePath); - - for (int i = maxBackupIndex - 1; i > -1; --i) { - const boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % i).str(); - if (boost::filesystem::exists(backupFilePath)) - boost::filesystem::rename(backupFilePath, backupDirectory / (filenameFormat % (i + 1)).str()); - } - - boost::filesystem::ofstream out(backupDirectory / (filenameFormat % 0).str()); - for (const auto &plugin : loadOrder) - out << plugin << std::endl; -} } diff --git a/src/backend/game/load_order_handler.h b/src/backend/game/load_order_handler.h index cdb9680e..c73d8dfa 100644 --- a/src/backend/game/load_order_handler.h +++ b/src/backend/game/load_order_handler.h @@ -32,7 +32,7 @@ #include #include -#include "backend/game/game_settings.h" +#include "loot/enum/game_type.h" namespace loot { class LoadOrderHandler { @@ -40,16 +40,15 @@ public: LoadOrderHandler(); ~LoadOrderHandler(); - void Init(const GameSettings& game, const boost::filesystem::path& gameLocalAppData = ""); + void Init(const GameType& game, + const boost::filesystem::path& gamePath, + const boost::filesystem::path& gameLocalAppData = ""); std::vector GetLoadOrder() const; bool IsPluginActive(const std::string& pluginName) const; void SetLoadOrder(const std::vector& loadOrder) const; - - static void BackupLoadOrder(const std::vector& loadOrder, - const boost::filesystem::path& backupDirectory); private: lo_game_handle gh_; }; diff --git a/src/backend/masterlist.cpp b/src/backend/masterlist.cpp index abfbc408..7eb6509e 100644 --- a/src/backend/masterlist.cpp +++ b/src/backend/masterlist.cpp @@ -84,10 +84,6 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho return info; } -bool Masterlist::Update(const Game& game) { - return Update(game.MasterlistPath(), game.RepoURL(), game.RepoBranch()); -} - bool Masterlist::Update(const boost::filesystem::path& path, const std::string& repoUrl, const std::string& repoBranch) { GitHelper git; fs::path repoPath = path.parent_path(); diff --git a/src/backend/masterlist.h b/src/backend/masterlist.h index 7572206e..40e2c08a 100644 --- a/src/backend/masterlist.h +++ b/src/backend/masterlist.h @@ -37,7 +37,6 @@ class Game; class Masterlist : public MetadataList { public: - bool Update(const Game& game); bool Update(const boost::filesystem::path& path, const std::string& repoURL, const std::string& repoBranch); diff --git a/src/gui/query/clear_all_metadata_query.h b/src/gui/query/clear_all_metadata_query.h index 5dbae4c7..380f0b0c 100644 --- a/src/gui/query/clear_all_metadata_query.h +++ b/src/gui/query/clear_all_metadata_query.h @@ -25,7 +25,7 @@ along with LOOT. If not, see #ifndef LOOT_GUI_QUERY_CLEAR_ALL_METADATA_QUERY #define LOOT_GUI_QUERY_CLEAR_ALL_METADATA_QUERY -#include "backend/game/game.h" +#include "gui/state/game.h" #include "gui/query/json.h" #include "gui/query/metadata_query.h" @@ -77,7 +77,7 @@ private: return "[]"; } - Game& game_; + gui::Game& game_; }; } diff --git a/src/gui/query/clear_plugin_metadata_query.h b/src/gui/query/clear_plugin_metadata_query.h index 41e97813..0a3c00e4 100644 --- a/src/gui/query/clear_plugin_metadata_query.h +++ b/src/gui/query/clear_plugin_metadata_query.h @@ -25,7 +25,7 @@ along with LOOT. If not, see #ifndef LOOT_GUI_QUERY_CLEAR_PLUGIN_METADATA_QUERY #define LOOT_GUI_QUERY_CLEAR_PLUGIN_METADATA_QUERY -#include "backend/game/game.h" +#include "gui/state/game.h" #include "gui/query/json.h" #include "gui/query/metadata_query.h" @@ -52,7 +52,7 @@ public: } private: - Game& game_; + gui::Game& game_; const std::string pluginName_; }; } diff --git a/src/gui/query/get_conflicting_plugins_query.h b/src/gui/query/get_conflicting_plugins_query.h index 0449ac40..c32e4e61 100644 --- a/src/gui/query/get_conflicting_plugins_query.h +++ b/src/gui/query/get_conflicting_plugins_query.h @@ -25,7 +25,7 @@ along with LOOT. If not, see #ifndef LOOT_GUI_QUERY_GET_CONFLICTING_PLUGINS_QUERY #define LOOT_GUI_QUERY_GET_CONFLICTING_PLUGINS_QUERY -#include "backend/game/game.h" +#include "gui/state/game.h" #include "gui/query/json.h" #include "gui/query/metadata_query.h" @@ -75,7 +75,7 @@ private: return pluginNode; } - Game& game_; + gui::Game& game_; const std::string pluginName_; }; } diff --git a/src/gui/query/get_game_types_query.h b/src/gui/query/get_game_types_query.h index ae4cc109..8ab8adaf 100644 --- a/src/gui/query/get_game_types_query.h +++ b/src/gui/query/get_game_types_query.h @@ -25,7 +25,7 @@ along with LOOT. If not, see #ifndef LOOT_GUI_QUERY_GET_GAME_TYPES_QUERY #define LOOT_GUI_QUERY_GET_GAME_TYPES_QUERY -#include "backend/game/game_settings.h" +#include "gui/state/game_settings.h" #include "gui/query/json.h" #include "gui/query/query.h" diff --git a/src/gui/query/metadata_query.h b/src/gui/query/metadata_query.h index a8dbb962..46e0eac1 100644 --- a/src/gui/query/metadata_query.h +++ b/src/gui/query/metadata_query.h @@ -28,7 +28,6 @@ along with LOOT. If not, see #include #include -#include "backend/game/game.h" #include "backend/plugin/plugin.h" #include "gui/query/query.h" #include "loot/exception/file_access_error.h" diff --git a/src/gui/query/update_masterlist_query.h b/src/gui/query/update_masterlist_query.h index 302498d1..9bac6831 100644 --- a/src/gui/query/update_masterlist_query.h +++ b/src/gui/query/update_masterlist_query.h @@ -25,7 +25,7 @@ along with LOOT. If not, see #ifndef LOOT_GUI_QUERY_UPDATE_MASTERLIST_QUERY #define LOOT_GUI_QUERY_UPDATE_MASTERLIST_QUERY -#include "backend/game/game.h" +#include "gui/state/game.h" #include "gui/query/json.h" #include "gui/query/metadata_query.h" @@ -49,7 +49,7 @@ public: private: bool updateMasterlist() { try { - return game_.GetMasterlist().Update(game_); + return game_.GetMasterlist().Update(game_.MasterlistPath(), game_.RepoURL(), game_.RepoBranch()); } catch (std::exception&) { try { game_.GetMasterlist().Load(game_.MasterlistPath()); @@ -104,7 +104,7 @@ private: return pluginNode; } - Game& game_; + gui::Game& game_; }; } diff --git a/src/gui/state/game.cpp b/src/gui/state/game.cpp new file mode 100644 index 00000000..fb96ed57 --- /dev/null +++ b/src/gui/state/game.cpp @@ -0,0 +1,268 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2012-2016 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 + . + */ + +#include "gui/state/game.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "loot/exception/file_access_error.h" +#include "loot/exception/game_detection_error.h" +#include "backend/helpers/helpers.h" + +#ifdef _WIN32 +# ifndef UNICODE +# define UNICODE +# endif +# ifndef _UNICODE +# define _UNICODE +# endif +# define NOMINMAX +# include "windows.h" +# include "shlobj.h" +# include "shlwapi.h" +#endif + +using std::list; +using std::string; +using std::thread; +using std::vector; + +namespace fs = boost::filesystem; + +namespace loot { +namespace gui { +Game::Game(const GameSettings& gameSettings, + const boost::filesystem::path& lootDataPath, + const boost::filesystem::path& localDataPath) : + loot::Game(gameSettings.Type(), gameSettings.GamePath(), localDataPath), + GameSettings(gameSettings), + lootDataPath_(lootDataPath), + pluginsFullyLoaded_(false) {} + +bool Game::IsInstalled() { + try { + BOOST_LOG_TRIVIAL(trace) << "Checking if game \"" << Name() << "\" is installed."; + if (!GamePath().empty() && fs::exists(GamePath() / "Data" / Master())) + return true; + + if (fs::exists(fs::path("..") / "Data" / Master())) { + SetGamePath(".."); + return true; + } + +#ifdef _WIN32 + std::string path; + std::string key_parent = fs::path(RegistryKey()).parent_path().string(); + std::string key_name = fs::path(RegistryKey()).filename().string(); + path = RegKeyStringValue("HKEY_LOCAL_MACHINE", key_parent, key_name); + if (!path.empty() && fs::exists(fs::path(path) / "Data" / Master())) { + SetGamePath(path); + return true; + } +#endif + } catch (std::exception &e) { + BOOST_LOG_TRIVIAL(error) << "Error while checking if game \"" << Name() << "\" is installed: " << e.what(); + } + + return false; +} + +void Game::Init() { + BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game: " << Name(); + + if (!this->IsInstalled()) { + throw GameDetectionError("Game path could not be detected."); + } + + if (!lootDataPath_.empty()) { + //Make sure that the LOOT game path exists. + try { + if (!fs::exists(lootDataPath_ / FolderName())) + fs::create_directories(lootDataPath_ / FolderName()); + } catch (fs::filesystem_error& e) { + throw FileAccessError((boost::format("Could not create LOOT folder for game. Details: %1%") % e.what()).str()); + } + } + + loot::Game::Init(); +} + +void Game::RedatePlugins() { + if (Type() != GameType::tes5 && Type() != GameType::tes5se) { + BOOST_LOG_TRIVIAL(warning) << "Cannot redate plugins for game " << Name(); + return; + } + + vector loadorder = GetLoadOrder(); + if (!loadorder.empty()) { + time_t lastTime = 0; + for (const auto &pluginName : loadorder) { + fs::path filepath = DataPath() / pluginName; + if (!fs::exists(filepath)) { + if (fs::exists(filepath.string() + ".ghost")) + filepath += ".ghost"; + else + continue; + } + + time_t thisTime = fs::last_write_time(filepath); + BOOST_LOG_TRIVIAL(info) << "Current timestamp for \"" << filepath.filename().string() << "\": " << thisTime; + if (thisTime >= lastTime) { + lastTime = thisTime; + BOOST_LOG_TRIVIAL(trace) << "No need to redate \"" << filepath.filename().string() << "\"."; + } else { + lastTime += 60; + fs::last_write_time(filepath, lastTime); //Space timestamps by a minute. + BOOST_LOG_TRIVIAL(info) << "Redated \"" << filepath.filename().string() << "\" to: " << lastTime; + } + } + } +} + +void Game::LoadAllInstalledPlugins(bool headersOnly) { + std::vector plugins; + + BOOST_LOG_TRIVIAL(trace) << "Scanning for plugins in " << this->DataPath(); + for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) { + if (fs::is_regular_file(it->status()) && Plugin::IsValid(it->path().filename().string(), *this)) { + string name = it->path().filename().string(); + BOOST_LOG_TRIVIAL(info) << "Found plugin: " << name; + + plugins.push_back(name); + } + } + + LoadPlugins(plugins, Master(), headersOnly); + + pluginsFullyLoaded_ = !headersOnly; +} + +bool Game::ArePluginsFullyLoaded() const { + return pluginsFullyLoaded_; +} + +fs::path Game::MasterlistPath() const { + if (lootDataPath_.empty() || FolderName().empty()) + return ""; + else + return lootDataPath_ / FolderName() / "masterlist.yaml"; +} + +fs::path Game::UserlistPath() const { + if (lootDataPath_.empty() || FolderName().empty()) + return ""; + else + return lootDataPath_ / FolderName() / "userlist.yaml"; +} + +short Game::GetActiveLoadOrderIndex(const std::string & pluginName) const { + return GetActiveLoadOrderIndex(pluginName, GetLoadOrder()); +} + +short Game::GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector& loadOrder) const { + // Get the full load order, then count the number of active plugins until the + // given plugin is encountered. If the plugin isn't active or in the load + // order, return -1. + + if (!IsPluginActive(pluginName)) + return -1; + + short numberOfActivePlugins = 0; + for (const std::string& plugin : loadOrder) { + if (boost::iequals(plugin, pluginName)) + return numberOfActivePlugins; + + if (IsPluginActive(plugin)) + ++numberOfActivePlugins; + } + + return -1; +} + +void Game::BackupLoadOrder(const std::vector& loadOrder, + const boost::filesystem::path & backupDirectory) { + const int maxBackupIndex = 2; + boost::format filenameFormat = boost::format("loadorder.bak.%1%"); + + boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % 2).str(); + if (boost::filesystem::exists(backupFilePath)) + boost::filesystem::remove(backupFilePath); + + for (int i = maxBackupIndex - 1; i > -1; --i) { + const boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % i).str(); + if (boost::filesystem::exists(backupFilePath)) + boost::filesystem::rename(backupFilePath, backupDirectory / (filenameFormat % (i + 1)).str()); + } + + boost::filesystem::ofstream out(backupDirectory / (filenameFormat % 0).str()); + for (const auto &plugin : loadOrder) + out << plugin << std::endl; +} + +#ifdef _WIN32 +std::string Game::RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value) { + HKEY hKey = NULL; + DWORD len = MAX_PATH; + std::wstring wstr(MAX_PATH, 0); + + if (keyStr == "HKEY_CLASSES_ROOT") + hKey = HKEY_CLASSES_ROOT; + else if (keyStr == "HKEY_CURRENT_CONFIG") + hKey = HKEY_CURRENT_CONFIG; + else if (keyStr == "HKEY_CURRENT_USER") + hKey = HKEY_CURRENT_USER; + else if (keyStr == "HKEY_LOCAL_MACHINE") + hKey = HKEY_LOCAL_MACHINE; + else if (keyStr == "HKEY_USERS") + hKey = HKEY_USERS; + else + throw std::invalid_argument("Invalid registry key given."); + + BOOST_LOG_TRIVIAL(trace) << "Getting string for registry key, subkey and value: " << keyStr << " + " << subkey << " + " << value; + LONG ret = RegGetValue(hKey, + ToWinWide(subkey).c_str(), + ToWinWide(value).c_str(), + RRF_RT_REG_SZ | KEY_WOW64_32KEY, + NULL, + &wstr[0], + &len); + + if (ret == ERROR_SUCCESS) { + BOOST_LOG_TRIVIAL(info) << "Found string: " << wstr.c_str(); + return FromWinWide(wstr.c_str()); // Passing c_str() cuts off any unused buffer. + } else { + BOOST_LOG_TRIVIAL(info) << "Failed to get string value."; + return ""; + } +} +#endif +} +} diff --git a/src/gui/state/game.h b/src/gui/state/game.h new file mode 100644 index 00000000..db77e8db --- /dev/null +++ b/src/gui/state/game.h @@ -0,0 +1,73 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2012-2016 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 + . + */ + +#ifndef LOOT_GUI_STATE_GAME +#define LOOT_GUI_STATE_GAME + +#include + +#include + +#include "backend/game/game.h" +#include "gui/state/game_settings.h" + +namespace loot { +namespace gui { +class Game : public loot::Game, public GameSettings { +public: + Game(const GameSettings& gameSettings, + const boost::filesystem::path& lootDataPath, + const boost::filesystem::path& localDataPath = ""); + + using GameSettings::Type; + + bool IsInstalled(); //Sets gamePath if the current value is not valid and a valid path is found. + void Init(); + + void RedatePlugins(); //Change timestamps to match load order (Skyrim only). + + void LoadAllInstalledPlugins(bool headersOnly); //Loads all installed plugins. + bool ArePluginsFullyLoaded() const; // Checks if the game's plugins have already been loaded. + + boost::filesystem::path MasterlistPath() const; + boost::filesystem::path UserlistPath() const; + + short GetActiveLoadOrderIndex(const std::string & pluginName) const; + short GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector& loadOrder) const; + + static void BackupLoadOrder(const std::vector& loadOrder, + const boost::filesystem::path& backupDirectory); +private: +#ifdef _WIN32 + std::string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value); +#endif + + const boost::filesystem::path lootDataPath_; + + bool pluginsFullyLoaded_; +}; +} +} + +#endif diff --git a/src/backend/game/game_settings.cpp b/src/gui/state/game_settings.cpp similarity index 95% rename from src/backend/game/game_settings.cpp rename to src/gui/state/game_settings.cpp index d8b4304c..6f003227 100644 --- a/src/backend/game/game_settings.cpp +++ b/src/gui/state/game_settings.cpp @@ -22,7 +22,7 @@ . */ -#include "backend/game/game_settings.h" +#include "gui/state/game_settings.h" #include #include @@ -124,20 +124,6 @@ fs::path GameSettings::GamePath() const { return gamePath_; } -fs::path GameSettings::DataPath() const { - if (gamePath_.empty()) - return ""; - else - return gamePath_ / "Data"; -} - -std::string GameSettings::GetArchiveFileExtension() const { - if (type_ == GameType::fo4) - return ".ba2"; - else - return ".bsa"; -} - GameSettings& GameSettings::SetName(const std::string& name) { BOOST_LOG_TRIVIAL(trace) << "Setting \"" << name_ << "\" name to: " << name; name_ = name; diff --git a/src/backend/game/game_settings.h b/src/gui/state/game_settings.h similarity index 96% rename from src/backend/game/game_settings.h rename to src/gui/state/game_settings.h index 2c8540fa..2d01e280 100644 --- a/src/backend/game/game_settings.h +++ b/src/gui/state/game_settings.h @@ -22,8 +22,8 @@ . */ -#ifndef LOOT_BACKEND_GAME_GAME_SETTINGS -#define LOOT_BACKEND_GAME_GAME_SETTINGS +#ifndef LOOT_GUI_STATE_GAME_SETTINGS +#define LOOT_GUI_STATE_GAME_SETTINGS #include #include @@ -50,11 +50,7 @@ public: std::string RegistryKey() const; std::string RepoURL() const; std::string RepoBranch() const; - boost::filesystem::path GamePath() const; - boost::filesystem::path DataPath() const; - - std::string GetArchiveFileExtension() const; GameSettings& SetName(const std::string& name); GameSettings& SetMaster(const std::string& masterFile); diff --git a/src/gui/state/loot_settings.h b/src/gui/state/loot_settings.h index 1cc84a26..97fa3280 100644 --- a/src/gui/state/loot_settings.h +++ b/src/gui/state/loot_settings.h @@ -33,7 +33,7 @@ #include #include -#include "backend/game/game_settings.h" +#include "gui/state/game_settings.h" #include "loot/language.h" namespace loot { diff --git a/src/gui/state/loot_state.cpp b/src/gui/state/loot_state.cpp index 425a140a..d8a9ca65 100644 --- a/src/gui/state/loot_state.cpp +++ b/src/gui/state/loot_state.cpp @@ -82,7 +82,7 @@ void LootState::load(YAML::Node& settings) { .SetRegistryKey(gameSettings.RegistryKey()); } else { BOOST_LOG_TRIVIAL(trace) << "Adding new game entry for: " << gameSettings.FolderName(); - games_.push_back(Game(gameSettings, LootPaths::getLootDataPath())); + games_.push_back(gui::Game(gameSettings, LootPaths::getLootDataPath())); } newGameFolders.insert(gameSettings.FolderName()); @@ -214,7 +214,7 @@ void LootState::changeGame(const std::string& newGameFolder) { lock_guard guard(mutex_); BOOST_LOG_TRIVIAL(debug) << "Changing current game to that with folder: " << newGameFolder; - currentGame_ = find_if(games_.begin(), games_.end(), [&](const Game& game) { + currentGame_ = find_if(games_.begin(), games_.end(), [&](const gui::Game& game) { return boost::iequals(newGameFolder, game.FolderName()); }); currentGame_->Init(); @@ -224,7 +224,7 @@ void LootState::changeGame(const std::string& newGameFolder) { BOOST_LOG_TRIVIAL(debug) << "New game is " << currentGame_->Name(); } -Game& LootState::getCurrentGame() { +gui::Game& LootState::getCurrentGame() { lock_guard guard(mutex_); return *currentGame_; @@ -262,12 +262,12 @@ void LootState::selectGame(std::string preferredGame) { } // Get iterator to preferred game. - currentGame_ = find_if(begin(games_), end(games_), [&](Game& game) { + currentGame_ = find_if(begin(games_), end(games_), [&](gui::Game& game) { return (preferredGame.empty() || preferredGame == game.FolderName()) && game.IsInstalled(); }); // If the preferred game cannot be found, get the first installed game. if (currentGame_ == end(games_)) { - currentGame_ = find_if(begin(games_), end(games_), [](Game& game) { + currentGame_ = find_if(begin(games_), end(games_), [](gui::Game& game) { return game.IsInstalled(); }); } @@ -286,16 +286,16 @@ void LootState::enableDebugLogging(bool enable) { } } -std::list LootState::toGames(const std::vector& settings) { - std::list games; +std::list LootState::toGames(const std::vector& settings) { + std::list games; for (const auto& element : settings) { - games.push_back(Game(element, LootPaths::getLootDataPath())); + games.push_back(gui::Game(element, LootPaths::getLootDataPath())); } return games; } -std::vector LootState::toGameSettings(const std::list& games) { +std::vector LootState::toGameSettings(const std::list& games) { return vector(games.begin(), games.end()); } } diff --git a/src/gui/state/loot_state.h b/src/gui/state/loot_state.h index e5504a87..5a154990 100644 --- a/src/gui/state/loot_state.h +++ b/src/gui/state/loot_state.h @@ -26,7 +26,7 @@ #define LOOT_BACKEND_APP_LOOT_STATE #include "gui/state/loot_settings.h" -#include "backend/game/game.h" +#include "gui/state/game.h" namespace loot { class LootState : public LootSettings { @@ -39,7 +39,7 @@ public: void save(const boost::filesystem::path& file); - Game& getCurrentGame(); + gui::Game& getCurrentGame(); void changeGame(const std::string& newGameFolder); // Get the folder names of the installed games. @@ -53,11 +53,11 @@ private: void selectGame(std::string cmdLineGame); void enableDebugLogging(bool enable); - static std::list toGames(const std::vector& settings); - static std::vector toGameSettings(const std::list& games); + static std::list toGames(const std::vector& settings); + static std::vector toGameSettings(const std::list& games); - std::list games_; - std::list::iterator currentGame_; + std::list games_; + std::list::iterator currentGame_; std::vector initErrors_; // Used to check if LOOT has unaccepted sorting or metadata changes on quit. diff --git a/src/tests/backend/game/game_cache_test.h b/src/tests/backend/game/game_cache_test.h index 65b5f1d9..43bc7e34 100644 --- a/src/tests/backend/game/game_cache_test.h +++ b/src/tests/backend/game/game_cache_test.h @@ -37,7 +37,7 @@ protected: GameCacheTest() : condition("Condition"), conditionLowercase("condition"), - game_(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), lootDataPath, localPath) {} + game_(GetParam(), dataPath.parent_path(), localPath) {} Game game_; GameCache cache_; diff --git a/src/tests/backend/game/game_test.h b/src/tests/backend/game/game_test.h index 438344fe..cea980b7 100644 --- a/src/tests/backend/game/game_test.h +++ b/src/tests/backend/game/game_test.h @@ -28,11 +28,29 @@ along with LOOT. If not, see #include "backend/game/game.h" #include "loot/exception/game_detection_error.h" -#include "tests/backend/game/load_order_handler_test.h" +#include "tests/common_game_test_fixture.h" namespace loot { namespace test { -class GameTest : public CommonGameTestFixture {}; +class GameTest : public CommonGameTestFixture { +protected: + void loadInstalledPlugins(Game& game, bool headersOnly) { + const std::vector plugins({ + masterFile, + blankEsm, + blankDifferentEsm, + blankMasterDependentEsm, + blankDifferentMasterDependentEsm, + blankEsp, + blankDifferentEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + blankDifferentPluginDependentEsp, + }); + game.LoadPlugins(plugins, masterFile, headersOnly); + } +}; // Pass an empty first argument, as it's a prefix for the test instantation, // but we only have the one so no prefix is necessary. @@ -46,134 +64,52 @@ INSTANTIATE_TEST_CASE_P(, GameType::fo4, GameType::tes5se)); -TEST_P(GameTest, constructingFromGameSettingsShouldUseTheirValues) { - GameSettings settings = GameSettings(GetParam(), "folder"); - settings.SetName("foo"); - settings.SetMaster(blankEsm); - settings.SetRegistryKey("foo"); - settings.SetRepoURL("foo"); - settings.SetRepoBranch("foo"); - settings.SetGamePath(localPath); - Game game = Game(settings, lootDataPath, localPath); +TEST_P(GameTest, constructingShouldStoreTheGivenValues) { + Game game = Game(GetParam(), dataPath.parent_path(), localPath); EXPECT_EQ(GetParam(), game.Type()); - EXPECT_EQ(settings.Name(), game.Name()); - EXPECT_EQ(settings.FolderName(), game.FolderName()); - EXPECT_EQ(settings.Master(), game.Master()); - EXPECT_EQ(settings.RegistryKey(), game.RegistryKey()); - EXPECT_EQ(settings.RepoURL(), game.RepoURL()); - EXPECT_EQ(settings.RepoBranch(), game.RepoBranch()); - - EXPECT_EQ(settings.GamePath(), game.GamePath()); - EXPECT_EQ(lootDataPath / "folder" / "masterlist.yaml", game.MasterlistPath()); - EXPECT_EQ(lootDataPath / "folder" / "userlist.yaml", game.UserlistPath()); + EXPECT_EQ(dataPath, game.DataPath()); } -TEST_P(GameTest, constructingFromIdAndFolderShouldPassThemToGameSettingsConstructor) { - GameSettings settings = GameSettings(GetParam(), "folder"); - Game game = Game(settings, lootDataPath, localPath); +TEST_P(GameTest, getArchiveFileExtensionShouldReturnDotBa2IfGameIdIsFallout4AndDotBsaOtherwise) { + Game game = Game(GetParam(), dataPath.parent_path(), localPath); - EXPECT_EQ(settings.Type(), game.Type()); - EXPECT_EQ(settings.FolderName(), game.FolderName()); - EXPECT_EQ(lootDataPath / "folder" / "masterlist.yaml", game.MasterlistPath()); - EXPECT_EQ(lootDataPath / "folder" / "userlist.yaml", game.UserlistPath()); -} - -TEST_P(GameTest, isInstalledShouldBeFalseIfGamePathIsNotSet) { - Game game = Game(GameSettings(GetParam()), "", localPath); - EXPECT_FALSE(game.IsInstalled()); -} - -TEST_P(GameTest, isInstalledShouldBeTrueIfGamePathIsValid) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - EXPECT_TRUE(game.IsInstalled()); + if (game.Type() == GameType::fo4) + EXPECT_EQ(".ba2", game.GetArchiveFileExtension()); + else + EXPECT_EQ(".bsa", game.GetArchiveFileExtension()); } #ifndef _WIN32 // Testing on Windows will find real game installs in the Registry, so cannot // test autodetection fully unless on Linux. TEST_P(GameTest, initShouldThrowOnLinuxIfGamePathIsNotGiven) { - Game game = Game(GameSettings(GetParam()), ""); - EXPECT_THROW(game.Init(), GameDetectionError); + Game game = Game(GetParam(), "", localPath); + EXPECT_THROW(game.Init(), std::invalid_argument); } TEST_P(GameTest, initShouldThrowOnLinuxIfLocalPathIsNotGiven) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), lootDataPath); - ASSERT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); + Game game = Game(GetParam(), dataPath.parent_path()); EXPECT_THROW(game.Init(), std::system_error); } #else TEST_P(GameTest, initShouldNotThrowOnWindowsIfLocalPathIsNotGiven) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); EXPECT_NO_THROW(game.Init()); } #endif -TEST_P(GameTest, initShouldNotCreateAGameFolderIfTheLootDataPathIsEmpty) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - - ASSERT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); - EXPECT_NO_THROW(game.Init()); - - EXPECT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); -} - -TEST_P(GameTest, initShouldCreateAGameFolderIfTheCreateFolderArgumentIsTrue) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), lootDataPath, localPath); - - ASSERT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); - EXPECT_NO_THROW(game.Init()); - - EXPECT_TRUE(boost::filesystem::exists(lootDataPath / game.FolderName())); -} - TEST_P(GameTest, initShouldNotThrowIfGameAndLocalPathsAreNotEmpty) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); EXPECT_NO_THROW(game.Init()); } -TEST_P(GameTest, redatePluginsShouldThrowIfTheGameHasNotYetBeenInitialisedForSkyrimAndNotForOtherGames) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - - if (GetParam() == GameType::tes5 || GetParam() == GameType::tes5se) - EXPECT_THROW(game.RedatePlugins(), std::system_error); - else - EXPECT_NO_THROW(game.RedatePlugins()); -} - -TEST_P(GameTest, redatePluginsShouldRedatePluginsForSkyrimAndSkyrimSEAndDoNothingForOtherGames) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - game.Init(); - - std::vector> loadOrder = getInitialLoadOrder(); - - // First set reverse timestamps to be sure. - time_t time = boost::filesystem::last_write_time(dataPath / masterFile); - for (size_t i = 1; i < loadOrder.size(); ++i) { - if (!boost::filesystem::exists(dataPath / loadOrder[i].first)) - loadOrder[i].first += ".ghost"; - - boost::filesystem::last_write_time(dataPath / loadOrder[i].first, time - i * 60); - ASSERT_EQ(time - i * 60, boost::filesystem::last_write_time(dataPath / loadOrder[i].first)); - } - - EXPECT_NO_THROW(game.RedatePlugins()); - - time_t interval = 60; - if (GetParam() != GameType::tes5 && GetParam() != GameType::tes5se) - interval *= -1; - - for (size_t i = 0; i < loadOrder.size(); ++i) { - EXPECT_EQ(time + i * interval, boost::filesystem::last_write_time(dataPath / loadOrder[i].first)); - } -} - TEST_P(GameTest, loadAllInstalledPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfAllInstalledPlugins) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); - EXPECT_NO_THROW(game.LoadAllInstalledPlugins(true)); + EXPECT_NO_THROW(loadInstalledPlugins(game, true)); EXPECT_EQ(11, game.GetPlugins().size()); // Check that one plugin's header has been read. @@ -186,9 +122,9 @@ TEST_P(GameTest, loadAllInstalledPluginsWithHeadersOnlyTrueShouldLoadTheHeadersO } TEST_P(GameTest, loadAllInstalledPluginsWithHeadersOnlyFalseShouldFullyLoadAllInstalledPlugins) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); - EXPECT_NO_THROW(game.LoadAllInstalledPlugins(false)); + EXPECT_NO_THROW(loadInstalledPlugins(game, false)); EXPECT_EQ(11, game.GetPlugins().size()); // Check that one plugin's header has been read. @@ -200,122 +136,77 @@ TEST_P(GameTest, loadAllInstalledPluginsWithHeadersOnlyFalseShouldFullyLoadAllIn EXPECT_EQ(blankEsmCrc, plugin.Crc()); } -TEST_P(GameTest, pluginsShouldNotBeFullyLoadedByDefault) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - - EXPECT_FALSE(game.ArePluginsFullyLoaded()); -} - -TEST_P(GameTest, pluginsShouldNotBeFullyLoadedAfterLoadingHeadersOnly) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(true)); - - EXPECT_FALSE(game.ArePluginsFullyLoaded()); -} - -TEST_P(GameTest, pluginsShouldBeFullyLoadedAfterFullyLoadingThem) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(false)); - - EXPECT_TRUE(game.ArePluginsFullyLoaded()); -} - TEST_P(GameTest, shouldThrowIfCheckingIfPluginThatIsntLoadedIsActiveAndGameHasNotBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); EXPECT_THROW(game.IsPluginActive(blankEsm), std::system_error); } TEST_P(GameTest, shouldShowBlankEsmAsActiveIfItHasNotBeenLoadedAndTheGameHasBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); game.Init(); EXPECT_TRUE(game.IsPluginActive(blankEsm)); } TEST_P(GameTest, shouldShowBlankEspAsInactiveIfItHasNotBeenLoadedAndTheGameHasBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); game.Init(); EXPECT_FALSE(game.IsPluginActive(blankEsp)); } TEST_P(GameTest, shouldShowBlankEsmAsInactiveIfItsHeaderHasBeenLoadedAndGameHasNotBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game, true)); EXPECT_FALSE(game.IsPluginActive(blankEsm)); } TEST_P(GameTest, shouldShowBlankEspAsInactiveIfItsHeaderHasBeenLoadedAndGameHasNotBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game, true)); EXPECT_FALSE(game.IsPluginActive(blankEsp)); } TEST_P(GameTest, shouldShowBlankEsmAsActiveIfItsHeaderHasBeenLoadedAndTheGameHasBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); game.Init(); - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game, true)); EXPECT_TRUE(game.IsPluginActive(blankEsm)); } TEST_P(GameTest, shouldShowBlankEspAsInactiveIfItsHeaderHasBeenLoadedAndTheGameHasBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); game.Init(); - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game, true)); EXPECT_FALSE(game.IsPluginActive(blankEsp)); } TEST_P(GameTest, shouldShowBlankEsmAsActiveIfItHasBeenFullyLoadedAndTheGameHasBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); game.Init(); - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game, false)); EXPECT_TRUE(game.IsPluginActive(blankEsm)); } TEST_P(GameTest, shouldShowBlankEspAsInactiveIfItHasBeenFullyLoadedAndTheGameHasBeenInitialised) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game = Game(GetParam(), dataPath.parent_path(), localPath); game.Init(); - ASSERT_NO_THROW(game.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game, false)); EXPECT_FALSE(game.IsPluginActive(blankEsp)); } - -TEST_P(GameTest, GetActiveLoadOrderIndexShouldReturnNegativeOneForAPluginThatIsNotActive) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - game.Init(); - - short index = game.GetActiveLoadOrderIndex(blankEsp); - - EXPECT_EQ(-1, index); -} - -TEST_P(GameTest, GetActiveLoadOrderIndexShouldReturnTheLoadOrderIndexOmittingInactivePlugins) { - Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); - game.Init(); - - short index = game.GetActiveLoadOrderIndex(masterFile); - EXPECT_EQ(0, index); - - index = game.GetActiveLoadOrderIndex(blankEsm); - EXPECT_EQ(1, index); - - index = game.GetActiveLoadOrderIndex(blankDifferentMasterDependentEsp); - EXPECT_EQ(2, index); -} } } diff --git a/src/tests/backend/game/load_order_handler_test.h b/src/tests/backend/game/load_order_handler_test.h index 41d6a9b5..007e7ff5 100644 --- a/src/tests/backend/game/load_order_handler_test.h +++ b/src/tests/backend/game/load_order_handler_test.h @@ -45,32 +45,18 @@ protected: blankMasterDependentEsp, blankDifferentMasterDependentEsp, blankPluginDependentEsp, - }), - loadOrderBackupFile0(localPath / "loadorder.bak.0"), - loadOrderBackupFile1(localPath / "loadorder.bak.1"), - loadOrderBackupFile2(localPath / "loadorder.bak.2"), - loadOrderBackupFile3(localPath / "loadorder.bak.3") {} + }) {} void TearDown() { CommonGameTestFixture::TearDown(); - - boost::filesystem::remove(loadOrderBackupFile0); - boost::filesystem::remove(loadOrderBackupFile1); - boost::filesystem::remove(loadOrderBackupFile2); } void initialiseHandler() { - GameSettings game(GetParam()); - game.SetGamePath(dataPath.parent_path()); - ASSERT_NO_THROW(loadOrderHandler_.Init(game, localPath)); + ASSERT_NO_THROW(loadOrderHandler_.Init(GetParam(), dataPath.parent_path(), localPath)); } LoadOrderHandler loadOrderHandler_; std::vector loadOrderToSet_; - const boost::filesystem::path loadOrderBackupFile0; - const boost::filesystem::path loadOrderBackupFile1; - const boost::filesystem::path loadOrderBackupFile2; - const boost::filesystem::path loadOrderBackupFile3; }; // Pass an empty first argument, as it's a prefix for the test instantation, @@ -86,28 +72,20 @@ INSTANTIATE_TEST_CASE_P(, GameType::tes5se)); TEST_P(LoadOrderHandlerTest, initShouldThrowIfNoGamePathIsSet) { - GameSettings game(GetParam()); - - EXPECT_THROW(loadOrderHandler_.Init(game), std::invalid_argument); - EXPECT_THROW(loadOrderHandler_.Init(game), std::invalid_argument); - EXPECT_THROW(loadOrderHandler_.Init(game, localPath), std::invalid_argument); - EXPECT_THROW(loadOrderHandler_.Init(game, localPath), std::invalid_argument); + EXPECT_THROW(loadOrderHandler_.Init(GetParam(), ""), std::invalid_argument); + EXPECT_THROW(loadOrderHandler_.Init(GetParam(), ""), std::invalid_argument); + EXPECT_THROW(loadOrderHandler_.Init(GetParam(), "", localPath), std::invalid_argument); + EXPECT_THROW(loadOrderHandler_.Init(GetParam(), "", localPath), std::invalid_argument); } #ifndef _WIN32 TEST_P(LoadOrderHandlerTest, initShouldThrowOnLinuxIfNoLocalPathIsSet) { - GameSettings game(GetParam()); - game.SetGamePath(dataPath.parent_path()); - - EXPECT_THROW(loadOrderHandler_.Init(game), std::system_error); + EXPECT_THROW(loadOrderHandler_.Init(GetParam(), dataPath.parent_path()), std::system_error); } #endif TEST_P(LoadOrderHandlerTest, initShouldNotThrowIfAValidGameIdAndGamePathAndLocalPathAreSet) { - GameSettings game(GetParam()); - game.SetGamePath(dataPath.parent_path()); - - EXPECT_NO_THROW(loadOrderHandler_.Init(game, localPath)); + EXPECT_NO_THROW(loadOrderHandler_.Init(GetParam(), dataPath.parent_path(), localPath)); } TEST_P(LoadOrderHandlerTest, isPluginActiveShouldThrowIfTheHandlerHasNotBeenInitialised) { @@ -146,103 +124,6 @@ TEST_P(LoadOrderHandlerTest, setLoadOrderShouldSetTheLoadOrder) { EXPECT_EQ(loadOrderToSet_, getLoadOrder()); } - -TEST_P(LoadOrderHandlerTest, backupLoadOrderShouldCreateABackupOfTheCurrentLoadOrder) { - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile0)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); - - ASSERT_NO_THROW(LoadOrderHandler::BackupLoadOrder(loadOrderToSet_, localPath)); - - EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile0)); - EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); - EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); - EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); - - auto loadOrder = readFileLines(loadOrderBackupFile0); - - EXPECT_EQ(loadOrderToSet_, loadOrder); -} - -TEST_P(LoadOrderHandlerTest, backupLoadOrderShouldRollOverExistingBackups) { - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile0)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); - - ASSERT_NO_THROW(LoadOrderHandler::BackupLoadOrder(loadOrderToSet_, localPath)); - - auto firstSetLoadOrder = loadOrderToSet_; - - ASSERT_NE(blankPluginDependentEsp, loadOrderToSet_[9]); - ASSERT_NE(blankDifferentMasterDependentEsp, loadOrderToSet_[10]); - loadOrderToSet_[9] = blankPluginDependentEsp; - loadOrderToSet_[10] = blankDifferentMasterDependentEsp; - - ASSERT_NO_THROW(LoadOrderHandler::BackupLoadOrder(loadOrderToSet_, localPath)); - - EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile0)); - EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile1)); - EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); - EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); - - auto loadOrder = readFileLines(loadOrderBackupFile0); - EXPECT_EQ(loadOrderToSet_, loadOrder); - - loadOrder = readFileLines(loadOrderBackupFile1); - EXPECT_EQ(firstSetLoadOrder, loadOrder); -} - -TEST_P(LoadOrderHandlerTest, backupLoadOrderShouldKeepUpToThreeBackups) { - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile0)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); - ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); - - ASSERT_NO_THROW(LoadOrderHandler::BackupLoadOrder(loadOrderToSet_, localPath)); - - auto firstSetLoadOrder = loadOrderToSet_; - - ASSERT_NE(blankPluginDependentEsp, loadOrderToSet_[9]); - ASSERT_NE(blankDifferentMasterDependentEsp, loadOrderToSet_[10]); - loadOrderToSet_[9] = blankPluginDependentEsp; - loadOrderToSet_[10] = blankDifferentMasterDependentEsp; - - ASSERT_NO_THROW(LoadOrderHandler::BackupLoadOrder(loadOrderToSet_, localPath)); - - auto secondSetLoadOrder = loadOrderToSet_; - - ASSERT_NE(blankMasterDependentEsp, loadOrderToSet_[7]); - ASSERT_NE(blankEsp, loadOrderToSet_[8]); - loadOrderToSet_[7] = blankMasterDependentEsp; - loadOrderToSet_[8] = blankEsp; - - ASSERT_NO_THROW(LoadOrderHandler::BackupLoadOrder(loadOrderToSet_, localPath)); - - auto thirdSetLoadOrder = loadOrderToSet_; - - ASSERT_NE(blankMasterDependentEsm, loadOrderToSet_[7]); - ASSERT_NE(blankDifferentEsm, loadOrderToSet_[8]); - loadOrderToSet_[7] = blankMasterDependentEsm; - loadOrderToSet_[8] = blankDifferentEsm; - - ASSERT_NO_THROW(LoadOrderHandler::BackupLoadOrder(loadOrderToSet_, localPath)); - - EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile0)); - EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile1)); - EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile2)); - EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); - - auto loadOrder = readFileLines(loadOrderBackupFile0); - EXPECT_EQ(loadOrderToSet_, loadOrder); - - loadOrder = readFileLines(loadOrderBackupFile1); - EXPECT_EQ(thirdSetLoadOrder, loadOrder); - - loadOrder = readFileLines(loadOrderBackupFile2); - EXPECT_EQ(secondSetLoadOrder, loadOrder); -} } } diff --git a/src/tests/backend/main.cpp b/src/tests/backend/main.cpp index 40a7c8ac..a4ddb982 100644 --- a/src/tests/backend/main.cpp +++ b/src/tests/backend/main.cpp @@ -26,7 +26,6 @@ #include "tests/backend/game/game_test.h" #include "tests/backend/game/game_cache_test.h" -#include "tests/backend/game/game_settings_test.h" #include "tests/backend/game/load_order_handler_test.h" #include "tests/backend/helpers/git_helper_test.h" #include "tests/backend/helpers/helpers_test.h" diff --git a/src/tests/backend/masterlist_test.h b/src/tests/backend/masterlist_test.h index 29fe8d2e..a843c3d5 100644 --- a/src/tests/backend/masterlist_test.h +++ b/src/tests/backend/masterlist_test.h @@ -69,35 +69,6 @@ INSTANTIATE_TEST_CASE_P(, GameType::fo4, GameType::tes5se)); -TEST_P(MasterlistTest, updateWithGameParameterShouldReturnTrueIfNoMasterlistExists) { - Game game(GameSettings(GetParam()), lootDataPath, localPath); - game.SetGamePath(dataPath.parent_path()); - game.SetRepoURL(repoUrl); - game.SetRepoBranch(repoBranch); - ASSERT_NO_THROW(game.Init()); - - // This may fail on Windows if a 'real' LOOT install is also present. - Masterlist masterlist; - EXPECT_TRUE(masterlist.Update(game)); - EXPECT_TRUE(boost::filesystem::exists(game.MasterlistPath())); -} - -TEST_P(MasterlistTest, updateWithGameParameterShouldReturnFalseIfAnUpToDateMasterlistExists) { - Game game(GameSettings(GetParam()), lootDataPath, localPath); - game.SetGamePath(dataPath.parent_path()); - game.SetRepoURL(repoUrl); - game.SetRepoBranch(repoBranch); - ASSERT_NO_THROW(game.Init()); - - // This may fail on Windows if a 'real' LOOT install is also present. - Masterlist masterlist; - EXPECT_TRUE(masterlist.Update(game)); - EXPECT_TRUE(boost::filesystem::exists(game.MasterlistPath())); - - EXPECT_FALSE(masterlist.Update(game)); - EXPECT_TRUE(boost::filesystem::exists(game.MasterlistPath())); -} - TEST_P(MasterlistTest, updateWithSeparateParametersShouldThrowIfAnInvalidPathIsGiven) { Masterlist masterlist; diff --git a/src/tests/backend/metadata/condition_grammar_test.h b/src/tests/backend/metadata/condition_grammar_test.h index b9bd467a..414ca053 100644 --- a/src/tests/backend/metadata/condition_grammar_test.h +++ b/src/tests/backend/metadata/condition_grammar_test.h @@ -37,7 +37,7 @@ protected: ConditionGrammarTest() : resourcePath(dataPath / "resource" / "detail" / "resource.txt"), - game_(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath), + game_(GetParam(), dataPath.parent_path(), localPath), result_(false), success_(false) {} @@ -63,6 +63,23 @@ protected: return stream.str(); } + void loadInstalledPlugins(Game& game_, bool headersOnly) { + const std::vector plugins({ + masterFile, + blankEsm, + blankDifferentEsm, + blankMasterDependentEsm, + blankDifferentMasterDependentEsm, + blankEsp, + blankDifferentEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + blankDifferentPluginDependentEsp, + }); + game_.LoadPlugins(plugins, masterFile, headersOnly); + } + const boost::filesystem::path resourcePath; Game game_; @@ -254,7 +271,7 @@ TEST_P(ConditionGrammarTest, aChecksumConditionWithACrcThatMatchesTheActualPlugi } TEST_P(ConditionGrammarTest, aChecksumConditionWithACrcThatMatchesTheActualCachedPluginCrcShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); Grammar grammar(&game_); std::string condition("checksum(\"" + blankEsm + "\", " + IntToHexString(blankEsmCrc) + ")"); @@ -282,7 +299,7 @@ TEST_P(ConditionGrammarTest, aChecksumConditionWithACrcThatDoesNotMatchTheActual } TEST_P(ConditionGrammarTest, aVersionEqualityConditionWithAVersionThatEqualsTheActualPluginVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", ==)"); @@ -297,7 +314,7 @@ TEST_P(ConditionGrammarTest, aVersionEqualityConditionWithAVersionThatEqualsTheA } TEST_P(ConditionGrammarTest, aVersionEqualityConditionWithAVersionThatDoesNotEqualTheActualPluginVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", ==)"); @@ -312,7 +329,7 @@ TEST_P(ConditionGrammarTest, aVersionEqualityConditionWithAVersionThatDoesNotEqu } TEST_P(ConditionGrammarTest, aVersionEqualityConditionForAPluginWithNoVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsp + "\", \"6.0\", ==)"); @@ -327,7 +344,7 @@ TEST_P(ConditionGrammarTest, aVersionEqualityConditionForAPluginWithNoVersionSho } TEST_P(ConditionGrammarTest, aVersionInequalityConditionWithAVersionThatDoesNotEqualTheActualPluginVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", !=)"); @@ -342,7 +359,7 @@ TEST_P(ConditionGrammarTest, aVersionInequalityConditionWithAVersionThatDoesNotE } TEST_P(ConditionGrammarTest, aVersionInequalityConditionWithAVersionThatEqualsTheActualPluginVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", !=)"); @@ -357,7 +374,7 @@ TEST_P(ConditionGrammarTest, aVersionInequalityConditionWithAVersionThatEqualsTh } TEST_P(ConditionGrammarTest, aVersionInequalityConditionForAPluginWithNoVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsp + "\", \"6.0\", !=)"); @@ -372,7 +389,7 @@ TEST_P(ConditionGrammarTest, aVersionInequalityConditionForAPluginWithNoVersionS } TEST_P(ConditionGrammarTest, aVersionLessThanConditionWithAnActualPluginVersionLessThanTheGivenVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", <)"); @@ -387,7 +404,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanConditionWithAnActualPluginVersionL } TEST_P(ConditionGrammarTest, aVersionLessThanConditionWithAnActualPluginVersionEqualToTheGivenVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", <)"); @@ -402,7 +419,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanConditionWithAnActualPluginVersionE } TEST_P(ConditionGrammarTest, aVersionLessThanConditionForAPluginWithNoVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", <)"); @@ -417,7 +434,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanConditionForAPluginWithNoVersionSho } TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionWithAnActualPluginVersionGreaterThanTheGivenVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"4.0\", >)"); @@ -432,7 +449,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionWithAnActualPluginVersi } TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionWithAnActualPluginVersionEqualToTheGivenVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", >)"); @@ -447,7 +464,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionWithAnActualPluginVersi } TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionForAPluginWithNoVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", >)"); @@ -462,7 +479,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionForAPluginWithNoVersion } TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionWithAnActualPluginVersionEqualToTheGivenVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", <=)"); @@ -477,7 +494,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionWithAnActualPlugi } TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionWithAnActualPluginVersionGreaterThanTheGivenVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"4.0\", <=)"); @@ -492,7 +509,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionWithAnActualPlugi } TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionForAPluginWithNoVersionShouldEvaluateToTrue) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", <=)"); @@ -508,7 +525,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionForAPluginWithNoV TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionWithAnActualPluginVersionEqualToTheGivenVersionShouldEvaluateToTrue) { ASSERT_NO_THROW(game_.Init()); - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", >=)"); @@ -523,7 +540,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionWithAnActualPl } TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionWithAnActualPluginVersionLessThanTheGivenVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", >=)"); @@ -538,7 +555,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionWithAnActualPl } TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionForAPluginWithNoVersionShouldEvaluateToFalse) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(true)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); Grammar grammar(&game_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", >=)"); diff --git a/src/tests/backend/metadata/conditional_metadata_test.h b/src/tests/backend/metadata/conditional_metadata_test.h index f734fa0b..49b2f4c1 100644 --- a/src/tests/backend/metadata/conditional_metadata_test.h +++ b/src/tests/backend/metadata/conditional_metadata_test.h @@ -69,27 +69,27 @@ TEST_P(ConditionalMetadataTest, isConditionalShouldBeTrueForANonEmptyConditionSt } TEST_P(ConditionalMetadataTest, evalConditionShouldReturnTrueForAnEmptyCondition) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); EXPECT_TRUE(conditionalMetadata_.EvalCondition(game)); } TEST_P(ConditionalMetadataTest, evalConditionShouldThrowForAnInvalidCondition) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); conditionalMetadata_ = ConditionalMetadata("condition"); EXPECT_THROW(conditionalMetadata_.EvalCondition(game), ConditionSyntaxError); } TEST_P(ConditionalMetadataTest, evalConditionShouldReturnTrueForAConditionThatIsTrue) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); conditionalMetadata_ = ConditionalMetadata("file(\"" + blankEsm + "\")"); EXPECT_TRUE(conditionalMetadata_.EvalCondition(game)); } TEST_P(ConditionalMetadataTest, evalConditionShouldReturnFalseForAConditionThatIsFalse) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); conditionalMetadata_ = ConditionalMetadata("file(\"" + missingEsp + "\")"); EXPECT_FALSE(conditionalMetadata_.EvalCondition(game)); diff --git a/src/tests/backend/metadata/plugin_cleaning_data_test.h b/src/tests/backend/metadata/plugin_cleaning_data_test.h index 03144677..8f4a378b 100644 --- a/src/tests/backend/metadata/plugin_cleaning_data_test.h +++ b/src/tests/backend/metadata/plugin_cleaning_data_test.h @@ -146,21 +146,21 @@ TEST_P(PluginCleaningDataTest, LessThanOperatorShouldCompareCrcValues) { } TEST_P(PluginCleaningDataTest, evalConditionShouldBeTrueIfTheCrcGivenMatchesTheRealPluginCrc) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); PluginCleaningData dirtyInfo(blankEsmCrc, "cleaner", info_, 2, 10, 30); EXPECT_TRUE(dirtyInfo.EvalCondition(game, blankEsm)); } TEST_P(PluginCleaningDataTest, evalConditionShouldBeFalseIfTheCrcGivenDoesNotMatchTheRealPluginCrc) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", info_, 2, 10, 30); EXPECT_FALSE(dirtyInfo.EvalCondition(game, blankEsm)); } TEST_P(PluginCleaningDataTest, evalConditionShouldBeFalseIfAnEmptyPluginFilenameIsGiven) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); PluginCleaningData dirtyInfo; EXPECT_FALSE(dirtyInfo.EvalCondition(game, "")); diff --git a/src/tests/backend/metadata/plugin_metadata_test.h b/src/tests/backend/metadata/plugin_metadata_test.h index add940cf..84c3db2e 100644 --- a/src/tests/backend/metadata/plugin_metadata_test.h +++ b/src/tests/backend/metadata/plugin_metadata_test.h @@ -659,7 +659,7 @@ TEST_P(PluginMetadataTest, simpleMessagesShouldReturnMessagesAsSimpleMessages) { } TEST_P(PluginMetadataTest, evalAllConditionsShouldEvaluateAllMetadataConditions) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); PluginMetadata plugin(blankEsm); diff --git a/src/tests/backend/metadata_list_test.h b/src/tests/backend/metadata_list_test.h index dabcf872..99c6c38d 100644 --- a/src/tests/backend/metadata_list_test.h +++ b/src/tests/backend/metadata_list_test.h @@ -284,7 +284,7 @@ TEST_P(MetadataListTest, erasePluginShouldRemoveStoredMetadataForTheGivenPlugin) } TEST_P(MetadataListTest, evalAllConditionsShouldEvaluateTheConditionsForThePluginsStoredInTeMetadataList) { - Game game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + Game game(GetParam(), dataPath.parent_path(), localPath); game.Init(); MetadataList metadataList; diff --git a/src/tests/backend/plugin/plugin_sorter_test.h b/src/tests/backend/plugin/plugin_sorter_test.h index 406a8c7a..f546dea6 100644 --- a/src/tests/backend/plugin/plugin_sorter_test.h +++ b/src/tests/backend/plugin/plugin_sorter_test.h @@ -34,7 +34,7 @@ namespace loot { namespace test { class PluginSorterTest : public CommonGameTestFixture { protected: - PluginSorterTest() : game_(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath) {} + PluginSorterTest() : game_(GetParam(), dataPath.parent_path(), localPath) {} inline virtual void SetUp() { CommonGameTestFixture::SetUp(); @@ -42,6 +42,23 @@ protected: ASSERT_NO_THROW(game_.Init()); } + void loadInstalledPlugins(Game& game_, bool headersOnly) { + const std::vector plugins({ + masterFile, + blankEsm, + blankDifferentEsm, + blankMasterDependentEsm, + blankDifferentMasterDependentEsm, + blankEsp, + blankDifferentEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + blankDifferentPluginDependentEsp, + }); + game_.LoadPlugins(plugins, masterFile, headersOnly); + } + Game game_; }; @@ -60,7 +77,7 @@ TEST_P(PluginSorterTest, sortingWithNoLoadedPluginsShouldReturnAnEmptyList) { } TEST_P(PluginSorterTest, sortingShouldNotMakeUnnecessaryChangesToAnExistingLoadOrder) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); PluginSorter ps; std::vector expectedSortedOrder = getLoadOrder(); @@ -74,7 +91,7 @@ TEST_P(PluginSorterTest, sortingShouldNotMakeUnnecessaryChangesToAnExistingLoadO } TEST_P(PluginSorterTest, sortingShouldClearExistingGameMessages) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); game_.AppendMessage(Message(MessageType::say, "1")); ASSERT_FALSE(game_.GetMessages().empty()); @@ -84,7 +101,7 @@ TEST_P(PluginSorterTest, sortingShouldClearExistingGameMessages) { } TEST_P(PluginSorterTest, failedSortShouldNotClearExistingGameMessages) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); PluginMetadata plugin(blankEsm); plugin.LoadAfter({File(blankMasterDependentEsm)}); game_.GetUserlist().AddPlugin(plugin); @@ -97,7 +114,7 @@ TEST_P(PluginSorterTest, failedSortShouldNotClearExistingGameMessages) { } TEST_P(PluginSorterTest, sortingShouldEvaluateRelativeGlobalPriorities) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); PluginMetadata plugin(blankDifferentMasterDependentEsp); plugin.GlobalPriority(Priority(-100)); game_.GetUserlist().AddPlugin(plugin); @@ -122,7 +139,7 @@ TEST_P(PluginSorterTest, sortingShouldEvaluateRelativeGlobalPriorities) { } TEST_P(PluginSorterTest, sortingWithGlobalPrioritiesShouldInheritRecursivelyRegardlessOfEvaluationOrder) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); // Set Blank.esp's priority. PluginMetadata plugin(blankEsp); @@ -172,7 +189,7 @@ TEST_P(PluginSorterTest, sortingWithGlobalPrioritiesShouldInheritRecursivelyRega } TEST_P(PluginSorterTest, sortingShouldUseLoadAfterMetadataWhenDecidingRelativePluginPositions) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); PluginMetadata plugin(blankEsp); plugin.LoadAfter({ File(blankDifferentEsp), @@ -200,7 +217,7 @@ TEST_P(PluginSorterTest, sortingShouldUseLoadAfterMetadataWhenDecidingRelativePl } TEST_P(PluginSorterTest, sortingShouldUseRequirementMetadataWhenDecidingRelativePluginPositions) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); PluginMetadata plugin(blankEsp); plugin.Reqs({ File(blankDifferentEsp), @@ -228,7 +245,7 @@ TEST_P(PluginSorterTest, sortingShouldUseRequirementMetadataWhenDecidingRelative } TEST_P(PluginSorterTest, sortingShouldThrowIfACyclicInteractionIsEncountered) { - ASSERT_NO_THROW(game_.LoadAllInstalledPlugins(false)); + ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); PluginMetadata plugin(blankEsm); plugin.LoadAfter({File(blankMasterDependentEsm)}); game_.GetUserlist().AddPlugin(plugin); diff --git a/src/tests/backend/plugin/plugin_test.h b/src/tests/backend/plugin/plugin_test.h index 1ae3ccdf..726d46c5 100644 --- a/src/tests/backend/plugin/plugin_test.h +++ b/src/tests/backend/plugin/plugin_test.h @@ -37,7 +37,7 @@ protected: PluginTest() : emptyFile("EmptyFile.esm"), nonPluginFile("NotAPlugin.esm"), - game_(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath), + game_(GetParam(), dataPath.parent_path(), localPath), blankArchive("Blank" + game_.GetArchiveFileExtension()), blankSuffixArchive("Blank - Different - suffix" + game_.GetArchiveFileExtension()) {} diff --git a/src/tests/gui/main.cpp b/src/tests/gui/main.cpp index 623d230c..7a7fad2f 100644 --- a/src/tests/gui/main.cpp +++ b/src/tests/gui/main.cpp @@ -24,6 +24,8 @@ #include +#include "tests/gui/state/game_test.h" +#include "tests/gui/state/game_settings_test.h" #include "tests/gui/state/loot_paths_test.h" #include "tests/gui/state/loot_settings_test.h" #include "tests/gui/state/loot_state_test.h" diff --git a/src/tests/backend/game/game_settings_test.h b/src/tests/gui/state/game_settings_test.h similarity index 92% rename from src/tests/backend/game/game_settings_test.h rename to src/tests/gui/state/game_settings_test.h index 65e12029..eb8cc6c3 100644 --- a/src/tests/backend/game/game_settings_test.h +++ b/src/tests/gui/state/game_settings_test.h @@ -22,10 +22,10 @@ along with LOOT. If not, see . */ -#ifndef LOOT_TESTS_BACKEND_GAME_GAME_SETTINGS_TEST -#define LOOT_TESTS_BACKEND_GAME_GAME_SETTINGS_TEST +#ifndef LOOT_TESTS_GUI_STATE_GAME_SETTINGS_TEST +#define LOOT_TESTS_GUI_STATE_GAME_SETTINGS_TEST -#include "backend/game/game_settings.h" +#include "gui/state/game_settings.h" #include "tests/common_game_test_fixture.h" @@ -55,7 +55,6 @@ TEST_P(GameSettingsTest, defaultConstructorShouldInitialiseIdToTes4AndAllOtherSe EXPECT_EQ("", settings_.RepoBranch()); EXPECT_EQ("", settings_.GamePath()); - EXPECT_EQ("", settings_.DataPath()); } TEST_P(GameSettingsTest, idConstructorShouldInitialiseSettingsToDefaultsForThatGame) { @@ -71,7 +70,6 @@ TEST_P(GameSettingsTest, idConstructorShouldInitialiseSettingsToDefaultsForThatG EXPECT_NE("", settings_.RepoBranch()); EXPECT_EQ("", settings_.GamePath()); - EXPECT_EQ("", settings_.DataPath()); } TEST_P(GameSettingsTest, idConstructorShouldSetGameFolderIfGiven) { @@ -126,16 +124,6 @@ TEST_P(GameSettingsTest, gameSettingsWithDifferentIdsAndNamesShouldNotBeEqual) { EXPECT_FALSE(game1 == game2); } -TEST_P(GameSettingsTest, getArchiveFileExtensionShouldReturnDotBa2IfGameIdIsFallout4) { - GameSettings settings_(GameType::fo4); - EXPECT_EQ(".ba2", settings_.GetArchiveFileExtension()); -} - -TEST_P(GameSettingsTest, getArchiveFileExtensionShouldReturnDotBsaIfGameIdIsNotFallout4) { - GameSettings settings_; - EXPECT_EQ(".bsa", settings_.GetArchiveFileExtension()); -} - TEST_P(GameSettingsTest, setNameShouldStoreGivenValue) { GameSettings settings_; settings_.SetName("name"); @@ -172,7 +160,6 @@ TEST_P(GameSettingsTest, setGamePathShouldStoreGivenValue) { settings_.SetGamePath(pathValue); EXPECT_EQ(pathValue, settings_.GamePath().string()); - EXPECT_EQ(boost::filesystem::path(pathValue) / "Data", settings_.DataPath()); } TEST_P(GameSettingsTest, emittingYamlShouldSerialiseDataCorrectly) { diff --git a/src/tests/gui/state/game_test.h b/src/tests/gui/state/game_test.h new file mode 100644 index 00000000..91754294 --- /dev/null +++ b/src/tests/gui/state/game_test.h @@ -0,0 +1,372 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2016 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 +. +*/ + +#ifndef LOOT_TESTS_BACKEND_GAME_GAME_TEST +#define LOOT_TESTS_BACKEND_GAME_GAME_TEST + +#include "gui/state/game.h" + +#include "loot/exception/game_detection_error.h" +#include "tests/common_game_test_fixture.h" + +namespace loot { +namespace gui { +namespace test { +class GameTest : public loot::test::CommonGameTestFixture { +protected: + GameTest() : loadOrderToSet_({ + masterFile, + blankEsm, + blankMasterDependentEsm, + blankDifferentEsm, + blankDifferentMasterDependentEsm, + blankDifferentEsp, + blankDifferentPluginDependentEsp, + blankEsp, + blankMasterDependentEsp, + blankDifferentMasterDependentEsp, + blankPluginDependentEsp, + }), + loadOrderBackupFile0(localPath / "loadorder.bak.0"), + loadOrderBackupFile1(localPath / "loadorder.bak.1"), + loadOrderBackupFile2(localPath / "loadorder.bak.2"), + loadOrderBackupFile3(localPath / "loadorder.bak.3") {} + + void TearDown() { + CommonGameTestFixture::TearDown(); + + boost::filesystem::remove(loadOrderBackupFile0); + boost::filesystem::remove(loadOrderBackupFile1); + boost::filesystem::remove(loadOrderBackupFile2); + } + + std::vector loadOrderToSet_; + const boost::filesystem::path loadOrderBackupFile0; + const boost::filesystem::path loadOrderBackupFile1; + const boost::filesystem::path loadOrderBackupFile2; + const boost::filesystem::path loadOrderBackupFile3; +}; + +// Pass an empty first argument, as it's a prefix for the test instantation, +// but we only have the one so no prefix is necessary. +INSTANTIATE_TEST_CASE_P(, + GameTest, + ::testing::Values( + GameType::tes4, + GameType::tes5, + GameType::fo3, + GameType::fonv, + GameType::fo4, + GameType::tes5se)); + +TEST_P(GameTest, constructingFromGameSettingsShouldUseTheirValues) { + GameSettings settings = GameSettings(GetParam(), "folder"); + settings.SetName("foo"); + settings.SetMaster(blankEsm); + settings.SetRegistryKey("foo"); + settings.SetRepoURL("foo"); + settings.SetRepoBranch("foo"); + settings.SetGamePath(localPath); + Game game = Game(settings, lootDataPath, localPath); + + EXPECT_EQ(GetParam(), game.Type()); + EXPECT_EQ(settings.Name(), game.Name()); + EXPECT_EQ(settings.FolderName(), game.FolderName()); + EXPECT_EQ(settings.Master(), game.Master()); + EXPECT_EQ(settings.RegistryKey(), game.RegistryKey()); + EXPECT_EQ(settings.RepoURL(), game.RepoURL()); + EXPECT_EQ(settings.RepoBranch(), game.RepoBranch()); + + EXPECT_EQ(settings.GamePath(), game.GamePath()); + EXPECT_EQ(lootDataPath / "folder" / "masterlist.yaml", game.MasterlistPath()); + EXPECT_EQ(lootDataPath / "folder" / "userlist.yaml", game.UserlistPath()); +} + +TEST_P(GameTest, isInstalledShouldBeFalseIfGamePathIsNotSet) { + Game game = Game(GameSettings(GetParam()), "", localPath); + EXPECT_FALSE(game.IsInstalled()); +} + +TEST_P(GameTest, isInstalledShouldBeTrueIfGamePathIsValid) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + EXPECT_TRUE(game.IsInstalled()); +} + +#ifndef _WIN32 + // Testing on Windows will find real game installs in the Registry, so cannot + // test autodetection fully unless on Linux. +TEST_P(GameTest, initShouldThrowOnLinuxIfGamePathIsNotGiven) { + Game game = Game(GameSettings(GetParam()), ""); + EXPECT_THROW(game.Init(), GameDetectionError); +} + +TEST_P(GameTest, initShouldThrowOnLinuxIfLocalPathIsNotGiven) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), lootDataPath); + ASSERT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); + EXPECT_THROW(game.Init(), std::system_error); +} +#else +TEST_P(GameTest, initShouldNotThrowOnWindowsIfLocalPathIsNotGiven) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + EXPECT_NO_THROW(game.Init()); +} +#endif + +TEST_P(GameTest, initShouldNotCreateAGameFolderIfTheLootDataPathIsEmpty) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + ASSERT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); + EXPECT_NO_THROW(game.Init()); + + EXPECT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); +} + +TEST_P(GameTest, initShouldCreateAGameFolderIfTheCreateFolderArgumentIsTrue) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), lootDataPath, localPath); + + ASSERT_FALSE(boost::filesystem::exists(lootDataPath / game.FolderName())); + EXPECT_NO_THROW(game.Init()); + + EXPECT_TRUE(boost::filesystem::exists(lootDataPath / game.FolderName())); +} + +TEST_P(GameTest, initShouldNotThrowIfGameAndLocalPathsAreNotEmpty) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + EXPECT_NO_THROW(game.Init()); +} + +TEST_P(GameTest, redatePluginsShouldThrowIfTheGameHasNotYetBeenInitialisedForSkyrimAndNotForOtherGames) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + if (GetParam() == GameType::tes5 || GetParam() == GameType::tes5se) + EXPECT_THROW(game.RedatePlugins(), std::system_error); + else + EXPECT_NO_THROW(game.RedatePlugins()); +} + +TEST_P(GameTest, redatePluginsShouldRedatePluginsForSkyrimAndSkyrimSEAndDoNothingForOtherGames) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.Init(); + + std::vector> loadOrder = getInitialLoadOrder(); + + // First set reverse timestamps to be sure. + time_t time = boost::filesystem::last_write_time(dataPath / masterFile); + for (size_t i = 1; i < loadOrder.size(); ++i) { + if (!boost::filesystem::exists(dataPath / loadOrder[i].first)) + loadOrder[i].first += ".ghost"; + + boost::filesystem::last_write_time(dataPath / loadOrder[i].first, time - i * 60); + ASSERT_EQ(time - i * 60, boost::filesystem::last_write_time(dataPath / loadOrder[i].first)); + } + + EXPECT_NO_THROW(game.RedatePlugins()); + + time_t interval = 60; + if (GetParam() != GameType::tes5 && GetParam() != GameType::tes5se) + interval *= -1; + + for (size_t i = 0; i < loadOrder.size(); ++i) { + EXPECT_EQ(time + i * interval, boost::filesystem::last_write_time(dataPath / loadOrder[i].first)); + } +} + +TEST_P(GameTest, loadAllInstalledPluginsWithHeadersOnlyTrueShouldLoadTheHeadersOfAllInstalledPlugins) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + EXPECT_NO_THROW(game.LoadAllInstalledPlugins(true)); + EXPECT_EQ(11, game.GetPlugins().size()); + + // Check that one plugin's header has been read. + ASSERT_NO_THROW(game.GetPlugin(masterFile)); + Plugin plugin = game.GetPlugin(masterFile); + EXPECT_EQ("v5.0", plugin.getDescription()); + + // Check that only the header has been read. + EXPECT_EQ(0, plugin.Crc()); +} + +TEST_P(GameTest, loadAllInstalledPluginsWithHeadersOnlyFalseShouldFullyLoadAllInstalledPlugins) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + EXPECT_NO_THROW(game.LoadAllInstalledPlugins(false)); + EXPECT_EQ(11, game.GetPlugins().size()); + + // Check that one plugin's header has been read. + ASSERT_NO_THROW(game.GetPlugin(blankEsm)); + Plugin plugin = game.GetPlugin(blankEsm); + EXPECT_EQ("v5.0", plugin.getDescription()); + + // Check that not only the header has been read. + EXPECT_EQ(blankEsmCrc, plugin.Crc()); +} + +TEST_P(GameTest, pluginsShouldNotBeFullyLoadedByDefault) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + EXPECT_FALSE(game.ArePluginsFullyLoaded()); +} + +TEST_P(GameTest, pluginsShouldNotBeFullyLoadedAfterLoadingHeadersOnly) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + ASSERT_NO_THROW(game.LoadAllInstalledPlugins(true)); + + EXPECT_FALSE(game.ArePluginsFullyLoaded()); +} + +TEST_P(GameTest, pluginsShouldBeFullyLoadedAfterFullyLoadingThem) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + + ASSERT_NO_THROW(game.LoadAllInstalledPlugins(false)); + + EXPECT_TRUE(game.ArePluginsFullyLoaded()); +} + +TEST_P(GameTest, GetActiveLoadOrderIndexShouldReturnNegativeOneForAPluginThatIsNotActive) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.Init(); + + short index = game.GetActiveLoadOrderIndex(blankEsp); + + EXPECT_EQ(-1, index); +} + +TEST_P(GameTest, GetActiveLoadOrderIndexShouldReturnTheLoadOrderIndexOmittingInactivePlugins) { + Game game = Game(GameSettings(GetParam()).SetGamePath(dataPath.parent_path()), "", localPath); + game.Init(); + + short index = game.GetActiveLoadOrderIndex(masterFile); + EXPECT_EQ(0, index); + + index = game.GetActiveLoadOrderIndex(blankEsm); + EXPECT_EQ(1, index); + + index = game.GetActiveLoadOrderIndex(blankDifferentMasterDependentEsp); + EXPECT_EQ(2, index); +} + +TEST_P(GameTest, backupLoadOrderShouldCreateABackupOfTheCurrentLoadOrder) { + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile0)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); + + ASSERT_NO_THROW(Game::BackupLoadOrder(loadOrderToSet_, localPath)); + + EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile0)); + EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); + EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); + EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); + + auto loadOrder = readFileLines(loadOrderBackupFile0); + + EXPECT_EQ(loadOrderToSet_, loadOrder); +} + +TEST_P(GameTest, backupLoadOrderShouldRollOverExistingBackups) { + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile0)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); + + ASSERT_NO_THROW(Game::BackupLoadOrder(loadOrderToSet_, localPath)); + + auto firstSetLoadOrder = loadOrderToSet_; + + ASSERT_NE(blankPluginDependentEsp, loadOrderToSet_[9]); + ASSERT_NE(blankDifferentMasterDependentEsp, loadOrderToSet_[10]); + loadOrderToSet_[9] = blankPluginDependentEsp; + loadOrderToSet_[10] = blankDifferentMasterDependentEsp; + + ASSERT_NO_THROW(Game::BackupLoadOrder(loadOrderToSet_, localPath)); + + EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile0)); + EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile1)); + EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); + EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); + + auto loadOrder = readFileLines(loadOrderBackupFile0); + EXPECT_EQ(loadOrderToSet_, loadOrder); + + loadOrder = readFileLines(loadOrderBackupFile1); + EXPECT_EQ(firstSetLoadOrder, loadOrder); +} + +TEST_P(GameTest, backupLoadOrderShouldKeepUpToThreeBackups) { + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile0)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile1)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile2)); + ASSERT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); + + ASSERT_NO_THROW(Game::BackupLoadOrder(loadOrderToSet_, localPath)); + + auto firstSetLoadOrder = loadOrderToSet_; + + ASSERT_NE(blankPluginDependentEsp, loadOrderToSet_[9]); + ASSERT_NE(blankDifferentMasterDependentEsp, loadOrderToSet_[10]); + loadOrderToSet_[9] = blankPluginDependentEsp; + loadOrderToSet_[10] = blankDifferentMasterDependentEsp; + + ASSERT_NO_THROW(Game::BackupLoadOrder(loadOrderToSet_, localPath)); + + auto secondSetLoadOrder = loadOrderToSet_; + + ASSERT_NE(blankMasterDependentEsp, loadOrderToSet_[7]); + ASSERT_NE(blankEsp, loadOrderToSet_[8]); + loadOrderToSet_[7] = blankMasterDependentEsp; + loadOrderToSet_[8] = blankEsp; + + ASSERT_NO_THROW(Game::BackupLoadOrder(loadOrderToSet_, localPath)); + + auto thirdSetLoadOrder = loadOrderToSet_; + + ASSERT_NE(blankMasterDependentEsm, loadOrderToSet_[7]); + ASSERT_NE(blankDifferentEsm, loadOrderToSet_[8]); + loadOrderToSet_[7] = blankMasterDependentEsm; + loadOrderToSet_[8] = blankDifferentEsm; + + ASSERT_NO_THROW(Game::BackupLoadOrder(loadOrderToSet_, localPath)); + + EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile0)); + EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile1)); + EXPECT_TRUE(boost::filesystem::exists(loadOrderBackupFile2)); + EXPECT_FALSE(boost::filesystem::exists(loadOrderBackupFile3)); + + auto loadOrder = readFileLines(loadOrderBackupFile0); + EXPECT_EQ(loadOrderToSet_, loadOrder); + + loadOrder = readFileLines(loadOrderBackupFile1); + EXPECT_EQ(thirdSetLoadOrder, loadOrder); + + loadOrder = readFileLines(loadOrderBackupFile2); + EXPECT_EQ(secondSetLoadOrder, loadOrder); +} +} +} +} + +#endif