From 33a3fd5fb6f0e1dd3a5e05d53fb189cc03127691 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 13 Sep 2015 10:43:57 +0100 Subject: [PATCH] Move game caches into a new class. Make cache storage private and access to it thread-safe. --- CMakeLists.txt | 3 + src/api/api.cpp | 3 +- src/backend/game/game.cpp | 2 +- src/backend/game/game.h | 15 +-- src/backend/game/game_cache.cpp | 107 ++++++++++++++++++ src/backend/game/game_cache.h | 63 +++++++++++ src/backend/metadata/condition_grammar.h | 9 +- src/backend/metadata/conditional_metadata.cpp | 8 +- src/backend/metadata/plugin_metadata.cpp | 32 +++--- src/backend/plugin/plugin.cpp | 4 +- src/gui/handler.cpp | 3 +- src/tests/backend/game/test_game.h | 75 +++++++++--- src/tests/backend/game/test_game_cache.h | 96 ++++++++++++++++ 13 files changed, 361 insertions(+), 59 deletions(-) create mode 100644 src/backend/game/game_cache.cpp create mode 100644 src/backend/game/game_cache.h create mode 100644 src/tests/backend/game/test_game_cache.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f5973c8..93b93b57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata/conditional_metadata.c "${CMAKE_SOURCE_DIR}/src/backend/metadata/plugin_metadata.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" @@ -114,6 +115,7 @@ set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h" "${CMAKE_SOURCE_DIR}/src/backend/metadata/plugin_metadata.h" "${CMAKE_SOURCE_DIR}/src/backend/metadata/tag.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" @@ -167,6 +169,7 @@ set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/fixtures.h" "${CMAKE_SOURCE_DIR}/src/tests/printers.h" "${CMAKE_SOURCE_DIR}/src/tests/api/test_api.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/game/test_game.h" + "${CMAKE_SOURCE_DIR}/src/tests/backend/game/test_game_cache.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/game/test_game_settings.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/game/test_load_order_handler.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/test_git_helper.h" diff --git a/src/api/api.cpp b/src/api/api.cpp index c372280b..aafa7978 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -388,8 +388,7 @@ LOOT_API unsigned int loot_eval_lists(loot_db db, const unsigned int language) { return c_error(loot_error_invalid_args, "Invalid language code given."); // Clear caches before evaluating conditions. - db->conditionCache.clear(); - db->crcCache.clear(); + db->ClearCache(); loot::Masterlist temp = db->rawMetadata; loot::MetadataList userTemp = db->rawUserMetadata; diff --git a/src/backend/game/game.cpp b/src/backend/game/game.cpp index 918c8a9c..0e1e8413 100644 --- a/src/backend/game/game.cpp +++ b/src/backend/game/game.cpp @@ -83,7 +83,7 @@ namespace loot { } void Game::RefreshActivePluginsList() { - activePlugins = GetActivePlugins(); + CacheActivePlugins(GetActivePlugins()); } void Game::RedatePlugins() { diff --git a/src/backend/game/game.h b/src/backend/game/game.h index d3ec731d..51a6bd6d 100644 --- a/src/backend/game/game.h +++ b/src/backend/game/game.h @@ -25,6 +25,7 @@ #ifndef __LOOT_GAME__ #define __LOOT_GAME__ +#include "game_cache.h" #include "game_settings.h" #include "load_order_handler.h" #include "../plugin/plugin.h" @@ -32,19 +33,12 @@ #include "../masterlist.h" #include -#include -#include #include -#include #include -#include - -#include -#include namespace loot { - class Game : public GameSettings, public LoadOrderHandler { + class Game : public GameSettings, public LoadOrderHandler, public GameCache { public: //Game functions. Game(); //Sets game to LOOT_Game::autodetect, with all other vars being empty. @@ -59,11 +53,6 @@ namespace loot { void LoadPlugins(bool headersOnly); //Loads all installed plugins. bool ArePluginsFullyLoaded() const; // Checks if the game's plugins have already been loaded. - //Caches for condition results, active plugins and CRCs. - std::unordered_map conditionCache; //Holds lowercased strings. - std::unordered_map crcCache; //Holds lowercased strings. - std::unordered_set activePlugins; //Holds lowercased strings. - //Plugin data and metadata lists. Masterlist masterlist; MetadataList userlist; diff --git a/src/backend/game/game_cache.cpp b/src/backend/game/game_cache.cpp new file mode 100644 index 00000000..0a249851 --- /dev/null +++ b/src/backend/game/game_cache.cpp @@ -0,0 +1,107 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2012-2015 WrinklyNinja + + This file is part of LOOT. + + LOOT is free software: you can redistribute + it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + LOOT is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LOOT. If not, see + . + */ + +#include "game_cache.h" +#include "../globals.h" +#include "../helpers/helpers.h" +#include "../error.h" +#include "../helpers/streams.h" + +#include + +#include +#include +#include + +using namespace std; + +namespace fs = boost::filesystem; +namespace lc = boost::locale; + +namespace loot { + GameCache::GameCache() {} + GameCache::GameCache(const GameCache& cache) + : conditionCache(cache.conditionCache), + crcCache(cache.crcCache), + activePlugins(cache.activePlugins) {} + + GameCache& GameCache::operator=(const GameCache& cache) { + conditionCache = cache.conditionCache; + crcCache = cache.crcCache; + activePlugins = cache.activePlugins; + + return *this; + } + + void GameCache::CacheCrc(const std::string& plugin, uint32_t crc) { + std::lock_guard guard(mutex); + crcCache.insert(pair(boost::locale::to_lower(plugin), crc)); + } + + void GameCache::CacheCondition(const std::string& condition, bool result) { + std::lock_guard guard(mutex); + conditionCache.insert(pair(boost::locale::to_lower(condition), result)); + } + + void GameCache::CacheActivePlugins(const std::unordered_set& plugins) { + std::lock_guard guard(mutex); + activePlugins = plugins; + } + + uint32_t GameCache::GetCachedCrc(const std::string& plugin) const { + std::lock_guard guard(mutex); + + auto it = crcCache.find(boost::locale::to_lower(plugin)); + + if (it != crcCache.end()) + return it->second; + else + return 0; + } + + std::pair GameCache::GetCachedCondition(const std::string& condition) const { + std::lock_guard guard(mutex); + + auto it = conditionCache.find(boost::locale::to_lower(condition)); + + if (it != conditionCache.end()) + return std::pair(it->second, true); + else + return std::pair(false, false); + } + + bool GameCache::IsPluginActive(const std::string& plugin) const { + std::lock_guard guard(mutex); + + return activePlugins.find(boost::locale::to_lower(plugin)) != activePlugins.end(); + } + + void GameCache::ClearCache() { + std::lock_guard guard(mutex); + + conditionCache.clear(); + crcCache.clear(); + activePlugins.clear(); + } +} diff --git a/src/backend/game/game_cache.h b/src/backend/game/game_cache.h new file mode 100644 index 00000000..ebc06881 --- /dev/null +++ b/src/backend/game/game_cache.h @@ -0,0 +1,63 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2012-2015 WrinklyNinja + + This file is part of LOOT. + + LOOT is free software: you can redistribute + it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + LOOT is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LOOT. If not, see + . + */ + +#ifndef __LOOT_GAME_CRC_CACHE__ +#define __LOOT_GAME_CRC_CACHE__ + +#include +#include +#include +#include +#include + +namespace loot { + class GameCache { + public: + GameCache(); + GameCache(const GameCache& cache); + + GameCache& operator=(const GameCache& cache); + + void CacheCrc(const std::string& plugin, uint32_t crc); + void CacheCondition(const std::string& condition, bool result); + void CacheActivePlugins(const std::unordered_set& plugins); + + // Returns 0 if no cached CRC. + uint32_t GetCachedCrc(const std::string& plugin) const; + // Returns false for second bool if no cached condition. + std::pair GetCachedCondition(const std::string& condition) const; + bool IsPluginActive(const std::string& plugin) const; + + void ClearCache(); + private: + //Caches for condition results, CRCs and active plugins. + std::unordered_map conditionCache; + std::unordered_map crcCache; + std::unordered_set activePlugins; + + mutable std::mutex mutex; + }; +} + +#endif diff --git a/src/backend/metadata/condition_grammar.h b/src/backend/metadata/condition_grammar.h index 3d12aead..b5380683 100644 --- a/src/backend/metadata/condition_grammar.h +++ b/src/backend/metadata/condition_grammar.h @@ -287,12 +287,9 @@ namespace loot { if (_game == nullptr) return; - uint32_t crc; - std::unordered_map::iterator it = _game->crcCache.find(boost::locale::to_lower(file)); + uint32_t crc = _game->GetCachedCrc(file); - if (it != _game->crcCache.end()) - crc = it->second; - else { + if (crc == 0) { if (file == "LOOT") crc = GetCrc32(boost::filesystem::absolute("LOOT.exe")); if (boost::filesystem::exists(_game->DataPath() / file)) @@ -304,7 +301,7 @@ namespace loot { return; } - _game->crcCache.insert(std::pair(boost::locale::to_lower(file), crc)); + _game->CacheCrc(file, crc); } result = checksum == crc; diff --git a/src/backend/metadata/conditional_metadata.cpp b/src/backend/metadata/conditional_metadata.cpp index 078877aa..31708f45 100644 --- a/src/backend/metadata/conditional_metadata.cpp +++ b/src/backend/metadata/conditional_metadata.cpp @@ -51,9 +51,9 @@ namespace loot { BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << _condition; - unordered_map::const_iterator it = game.conditionCache.find(boost::locale::to_lower(_condition)); - if (it != game.conditionCache.end()) - return it->second; + auto cachedValue = game.GetCachedCondition(_condition); + if (cachedValue.second) + return cachedValue.first; ConditionGrammar grammar(&game); boost::spirit::qi::space_type skipper; @@ -77,7 +77,7 @@ namespace loot { throw loot::error(loot::error::condition_eval_fail, (boost::format(lc::translate("Failed to parse condition \"%1%\".")) % _condition).str()); } - game.conditionCache.insert(pair(boost::locale::to_lower(_condition), eval)); + game.CacheCondition(_condition, eval); return eval; } diff --git a/src/backend/metadata/plugin_metadata.cpp b/src/backend/metadata/plugin_metadata.cpp index 4ef39356..718d56d5 100644 --- a/src/backend/metadata/plugin_metadata.cpp +++ b/src/backend/metadata/plugin_metadata.cpp @@ -298,23 +298,23 @@ namespace loot { //First need to get plugin's CRC, if it is an exact plugin and it does not have its CRC set. if (!IsRegexPlugin()) { - uint32_t crc = 0; - unordered_map::iterator it = game.crcCache.find(boost::locale::to_lower(name)); - if (it != game.crcCache.end()) - crc = it->second; - else if (boost::filesystem::exists(game.DataPath() / name)) { - crc = GetCrc32(game.DataPath() / name); - } - else if (boost::filesystem::exists(game.DataPath() / (name + ".ghost"))) { - crc = GetCrc32(game.DataPath() / (name + ".ghost")); - } - else { - // The plugin isn't installed, discard the dirty info. - _dirtyInfo.clear(); - } + uint32_t crc = game.GetCachedCrc(name); + if (crc == 0) { + if (boost::filesystem::exists(game.DataPath() / name)) { + crc = GetCrc32(game.DataPath() / name); + } + else if (boost::filesystem::exists(game.DataPath() / (name + ".ghost"))) { + crc = GetCrc32(game.DataPath() / (name + ".ghost")); + } + else { + // The plugin isn't installed, discard the dirty info. + _dirtyInfo.clear(); + } - // Store the CRC in the cache in case it's not already in there. - game.crcCache.insert(pair(boost::locale::to_lower(name), crc)); + // Store the CRC in the cache in case it's not already in there. + if (crc != 0) + game.CacheCrc(name, crc); + } // Now use the CRC to evaluate the dirty info. for (auto it = _dirtyInfo.begin(); it != _dirtyInfo.end();) { diff --git a/src/backend/plugin/plugin.cpp b/src/backend/plugin/plugin.cpp index 285ef982..3b559f15 100644 --- a/src/backend/plugin/plugin.cpp +++ b/src/backend/plugin/plugin.cpp @@ -107,7 +107,7 @@ namespace loot { if (!headerOnly) { BOOST_LOG_TRIVIAL(trace) << name << ": Caching CRC value."; crc = loader.Crc(); - game.crcCache.insert(pair(boost::locale::to_lower(name), crc)); + game.CacheCrc(name, crc); } BOOST_LOG_TRIVIAL(trace) << name << ": Counting override FormIDs."; @@ -235,7 +235,7 @@ namespace loot { } bool Plugin::IsActive(const Game& game) const { - return game.activePlugins.find(boost::locale::to_lower(name)) != game.activePlugins.end(); + return game.IsPluginActive(name); } std::string Plugin::Version() const { diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index 0f97ad27..d190805a 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -639,8 +639,7 @@ namespace loot { SendProgressUpdate(frame, loc::translate("Loading plugin headers...")); // First clear CRC and condition caches, otherwise they could lead to incorrect evaluations. - _lootState.CurrentGame().conditionCache.clear(); - _lootState.CurrentGame().crcCache.clear(); + _lootState.CurrentGame().ClearCache(); // Also refresh active plugins list. _lootState.CurrentGame().RefreshActivePluginsList(); diff --git a/src/tests/backend/game/test_game.h b/src/tests/backend/game/test_game.h index b0ba8fbf..cf64d5fb 100644 --- a/src/tests/backend/game/test_game.h +++ b/src/tests/backend/game/test_game.h @@ -133,19 +133,63 @@ TEST_F(Game, Init) { ASSERT_FALSE(boost::filesystem::exists(loot::g_path_local / game.FolderName())); EXPECT_THROW(game.Init(false), loot::error); EXPECT_FALSE(boost::filesystem::exists(loot::g_path_local / game.FolderName())); - EXPECT_TRUE(game.activePlugins.empty()); + EXPECT_FALSE(game.IsPluginActive("Skyrim.esm")); + EXPECT_FALSE(game.IsPluginActive("skyrim.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Master Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Plugin Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Plugin Dependent.esp")); game = loot::Game(loot::Game::tes5).SetGamePath(dataPath.parent_path()); - ASSERT_TRUE(game.activePlugins.empty()); + EXPECT_FALSE(game.IsPluginActive("Skyrim.esm")); + EXPECT_FALSE(game.IsPluginActive("skyrim.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Master Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Plugin Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Plugin Dependent.esp")); + EXPECT_NO_THROW(game.Init(false, localPath)); EXPECT_FALSE(boost::filesystem::exists(loot::g_path_local / game.FolderName())); - EXPECT_FALSE(game.activePlugins.empty()); + EXPECT_TRUE(game.IsPluginActive("Skyrim.esm")); + EXPECT_TRUE(game.IsPluginActive("skyrim.esm")); + EXPECT_TRUE(game.IsPluginActive("Blank.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esp")); + EXPECT_TRUE(game.IsPluginActive("Blank - Different Master Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Plugin Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Plugin Dependent.esp")); game = loot::Game(loot::Game::tes5).SetGamePath(dataPath.parent_path()); - ASSERT_TRUE(game.activePlugins.empty()); EXPECT_NO_THROW(game.Init(true, localPath)); EXPECT_TRUE(boost::filesystem::exists(loot::g_path_local / game.FolderName())); - EXPECT_FALSE(game.activePlugins.empty()); + EXPECT_TRUE(game.IsPluginActive("Skyrim.esm")); + EXPECT_TRUE(game.IsPluginActive("skyrim.esm")); + EXPECT_TRUE(game.IsPluginActive("Blank.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esp")); + EXPECT_TRUE(game.IsPluginActive("Blank - Different Master Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Plugin Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Plugin Dependent.esp")); #else game = loot::Game(loot::Game::tes5).SetGamePath(dataPath.parent_path()); EXPECT_NO_THROW(game.Init(false)); @@ -161,20 +205,25 @@ TEST_F(Game, RefreshActivePluginsList) { game.SetGamePath(dataPath.parent_path()); // Throw because the load order handler hasn't been initialised. - ASSERT_TRUE(game.activePlugins.empty()); EXPECT_THROW(game.RefreshActivePluginsList(), loot::error); - EXPECT_TRUE(game.activePlugins.empty()); // Calling Init calls RefreshActivePluginsList, so clear it before testing // separately. game.Init(false, localPath); - EXPECT_NO_THROW(game.activePlugins.clear()); + EXPECT_NO_THROW(game.ClearCache()); EXPECT_NO_THROW(game.RefreshActivePluginsList()); - EXPECT_FALSE(game.activePlugins.empty()); - ASSERT_EQ(3, game.activePlugins.size()); - EXPECT_EQ(1, game.activePlugins.count("skyrim.esm")); - EXPECT_EQ(1, game.activePlugins.count("blank.esm")); - EXPECT_EQ(1, game.activePlugins.count("blank - different master dependent.esp")); + EXPECT_TRUE(game.IsPluginActive("Skyrim.esm")); + EXPECT_TRUE(game.IsPluginActive("skyrim.esm")); + EXPECT_TRUE(game.IsPluginActive("Blank.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Master Dependent.esm")); + EXPECT_FALSE(game.IsPluginActive("Blank.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Master Dependent.esp")); + EXPECT_TRUE(game.IsPluginActive("Blank - Different Master Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Plugin Dependent.esp")); + EXPECT_FALSE(game.IsPluginActive("Blank - Different Plugin Dependent.esp")); } TEST_F(Game, RedatePlugins) { diff --git a/src/tests/backend/game/test_game_cache.h b/src/tests/backend/game/test_game_cache.h new file mode 100644 index 00000000..311091ad --- /dev/null +++ b/src/tests/backend/game/test_game_cache.h @@ -0,0 +1,96 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-2015 WrinklyNinja + +This file is part of LOOT. + +LOOT is free software: you can redistribute +it and/or modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +LOOT is distributed in the hope that it will +be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LOOT. If not, see +. +*/ + +#ifndef LOOT_TEST_BACKEND_GAME +#define LOOT_TEST_BACKEND_GAME + +#include "backend/game/game_cache.h" + +#include "tests/fixtures.h" + +class GameCache : public SkyrimTest {}; + +TEST_F(GameCache, Constructors) { + loot::GameCache cache; + std::unordered_set plugins({"skyrim.esm"}); + + EXPECT_NO_THROW(cache.CacheCrc("Blank.esp", 5)); + EXPECT_NO_THROW(cache.CacheCondition("True Condition", true)); + EXPECT_NO_THROW(cache.CacheActivePlugins(plugins)); + + loot::GameCache cache2(cache); + EXPECT_EQ(5, cache2.GetCachedCrc("blank.Esp")); + EXPECT_EQ(std::make_pair(true, true), cache2.GetCachedCondition("true Condition")); + EXPECT_TRUE(cache2.IsPluginActive("Skyrim.esm")); +} + +TEST_F(GameCache, AssignmentOperator) { + loot::GameCache cache; + std::unordered_set plugins({"skyrim.esm"}); + + EXPECT_NO_THROW(cache.CacheCrc("Blank.esp", 5)); + EXPECT_NO_THROW(cache.CacheCondition("True Condition", true)); + EXPECT_NO_THROW(cache.CacheActivePlugins(plugins)); + + loot::GameCache cache2 = cache; + EXPECT_EQ(5, cache2.GetCachedCrc("blank.Esp")); + EXPECT_EQ(std::make_pair(true, true), cache2.GetCachedCondition("true Condition")); + EXPECT_TRUE(cache2.IsPluginActive("Skyrim.esm")); +} + +TEST_F(GameCache, CacheCrc) { + loot::GameCache cache; + EXPECT_NO_THROW(cache.CacheCrc("Blank.esp", 5)); + EXPECT_EQ(5, cache.GetCachedCrc("blank.Esp")); + EXPECT_EQ(0, cache.GetCachedCrc("Blank.missing.esp")); +} + +TEST_F(GameCache, CacheCondition) { + loot::GameCache cache; + EXPECT_NO_THROW(cache.CacheCondition("True Condition", true)); + EXPECT_NO_THROW(cache.CacheCondition("False Condition", false)); + + EXPECT_EQ(std::make_pair(true, true), cache.GetCachedCondition("true Condition")); + EXPECT_EQ(std::make_pair(false, true), cache.GetCachedCondition("false Condition")); + + EXPECT_EQ(std::make_pair(false, false), cache.GetCachedCondition("true missing Condition")); + EXPECT_EQ(std::make_pair(false, false), cache.GetCachedCondition("false missing Condition")); +} + +TEST_F(GameCache, CacheActivePlugins) { + loot::GameCache cache; + std::unordered_set plugins({ + "skyrim.esm", + "blank.esp" + }); + EXPECT_NO_THROW(cache.CacheActivePlugins(plugins)); + + EXPECT_TRUE(cache.IsPluginActive("Skyrim.esm")); + EXPECT_TRUE(cache.IsPluginActive("Blank.esp")); + EXPECT_FALSE(cache.IsPluginActive("Blank.missing.esp")); +} + +TEST_F(GameCache, ClearCache) {} + +#endif