Move game caches into a new class.

Make cache storage private and access to it thread-safe.
This commit is contained in:
Oliver Hamlet
2015-09-13 17:46:06 +01:00
parent 3dfac89ef2
commit 33a3fd5fb6
13 changed files with 361 additions and 59 deletions
+3
View File
@@ -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"
+1 -2
View File
@@ -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;
+1 -1
View File
@@ -83,7 +83,7 @@ namespace loot {
}
void Game::RefreshActivePluginsList() {
activePlugins = GetActivePlugins();
CacheActivePlugins(GetActivePlugins());
}
void Game::RedatePlugins() {
+2 -13
View File
@@ -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 <string>
#include <vector>
#include <cstdint>
#include <unordered_map>
#include <unordered_set>
#include <boost/filesystem.hpp>
#include <boost/locale.hpp>
#include <api/libloadorder.h>
#include <yaml-cpp/yaml.h>
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<std::string, bool> conditionCache; //Holds lowercased strings.
std::unordered_map<std::string, uint32_t> crcCache; //Holds lowercased strings.
std::unordered_set<std::string> activePlugins; //Holds lowercased strings.
//Plugin data and metadata lists.
Masterlist masterlist;
MetadataList userlist;
+107
View File
@@ -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
<http://www.gnu.org/licenses/>.
*/
#include "game_cache.h"
#include "../globals.h"
#include "../helpers/helpers.h"
#include "../error.h"
#include "../helpers/streams.h"
#include <thread>
#include <boost/algorithm/string.hpp>
#include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
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<std::mutex> guard(mutex);
crcCache.insert(pair<string, uint32_t>(boost::locale::to_lower(plugin), crc));
}
void GameCache::CacheCondition(const std::string& condition, bool result) {
std::lock_guard<std::mutex> guard(mutex);
conditionCache.insert(pair<string, bool>(boost::locale::to_lower(condition), result));
}
void GameCache::CacheActivePlugins(const std::unordered_set<std::string>& plugins) {
std::lock_guard<std::mutex> guard(mutex);
activePlugins = plugins;
}
uint32_t GameCache::GetCachedCrc(const std::string& plugin) const {
std::lock_guard<std::mutex> guard(mutex);
auto it = crcCache.find(boost::locale::to_lower(plugin));
if (it != crcCache.end())
return it->second;
else
return 0;
}
std::pair<bool, bool> GameCache::GetCachedCondition(const std::string& condition) const {
std::lock_guard<std::mutex> guard(mutex);
auto it = conditionCache.find(boost::locale::to_lower(condition));
if (it != conditionCache.end())
return std::pair<bool, bool>(it->second, true);
else
return std::pair<bool, bool>(false, false);
}
bool GameCache::IsPluginActive(const std::string& plugin) const {
std::lock_guard<std::mutex> guard(mutex);
return activePlugins.find(boost::locale::to_lower(plugin)) != activePlugins.end();
}
void GameCache::ClearCache() {
std::lock_guard<std::mutex> guard(mutex);
conditionCache.clear();
crcCache.clear();
activePlugins.clear();
}
}
+63
View File
@@ -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
<http://www.gnu.org/licenses/>.
*/
#ifndef __LOOT_GAME_CRC_CACHE__
#define __LOOT_GAME_CRC_CACHE__
#include <string>
#include <cstdint>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
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<std::string>& 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<bool, bool> 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<std::string, bool> conditionCache;
std::unordered_map<std::string, uint32_t> crcCache;
std::unordered_set<std::string> activePlugins;
mutable std::mutex mutex;
};
}
#endif
+3 -6
View File
@@ -287,12 +287,9 @@ namespace loot {
if (_game == nullptr)
return;
uint32_t crc;
std::unordered_map<std::string, uint32_t>::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<std::string, uint32_t>(boost::locale::to_lower(file), crc));
_game->CacheCrc(file, crc);
}
result = checksum == crc;
@@ -51,9 +51,9 @@ namespace loot {
BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << _condition;
unordered_map<std::string, bool>::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<std::string::const_iterator, boost::spirit::qi::space_type> 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<string, bool>(boost::locale::to_lower(_condition), eval));
game.CacheCondition(_condition, eval);
return eval;
}
+16 -16
View File
@@ -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<std::string, uint32_t>::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<string, uint32_t>(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();) {
+2 -2
View File
@@ -107,7 +107,7 @@ namespace loot {
if (!headerOnly) {
BOOST_LOG_TRIVIAL(trace) << name << ": Caching CRC value.";
crc = loader.Crc();
game.crcCache.insert(pair<string, uint32_t>(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 {
+1 -2
View File
@@ -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();
+62 -13
View File
@@ -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) {
+96
View File
@@ -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
<http://www.gnu.org/licenses/>.
*/
#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<std::string> 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<std::string> 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<std::string> 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