diff --git a/src/api/api.cpp b/src/api/api.cpp index 94a6a597..26805bdb 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -297,7 +297,7 @@ LOOT_API unsigned int loot_eval_lists(loot_db * const db, const unsigned int lan return c_error(loot_error_invalid_args, "Invalid language code given."); // Clear caches before evaluating conditions. - db->ClearCache(); + db->ClearCachedConditions(); 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 d7ae7071..ec823149 100644 --- a/src/backend/game/game.cpp +++ b/src/backend/game/game.cpp @@ -154,6 +154,9 @@ namespace loot { ++currentGroup; } + // Clear the existing plugin cache. + ClearCachedPlugins(); + // Load the plugins. BOOST_LOG_TRIVIAL(trace) << "Starting plugin loading."; vector threads; diff --git a/src/backend/game/game_cache.cpp b/src/backend/game/game_cache.cpp index e818a840..7fe6f748 100644 --- a/src/backend/game/game_cache.cpp +++ b/src/backend/game/game_cache.cpp @@ -102,12 +102,21 @@ namespace loot { void GameCache::AddPlugin(const Plugin&& plugin) { std::lock_guard lock(mutex); - plugins.emplace(boost::locale::to_lower(plugin.Name()), plugin); + + auto pair = plugins.emplace(boost::locale::to_lower(plugin.Name()), plugin); + if (!pair.second) + pair.first->second = plugin; } - void GameCache::ClearCache() { + void GameCache::ClearCachedConditions() { std::lock_guard guard(mutex); conditionCache.clear(); } + + void GameCache::ClearCachedPlugins() { + std::lock_guard guard(mutex); + + plugins.clear(); + } } diff --git a/src/backend/game/game_cache.h b/src/backend/game/game_cache.h index c0f63f97..90eef604 100644 --- a/src/backend/game/game_cache.h +++ b/src/backend/game/game_cache.h @@ -52,7 +52,8 @@ namespace loot { const Plugin& GetPlugin(const std::string& pluginName) const; void AddPlugin(const Plugin&& plugin); - void ClearCache(); + void ClearCachedConditions(); + void ClearCachedPlugins(); private: Masterlist masterlist; MetadataList userlist; diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index f62eb811..e58d3609 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -632,7 +632,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().ClearCache(); + _lootState.CurrentGame().ClearCachedConditions(); bool isFirstLoad = _lootState.CurrentGame().GetPlugins().empty(); _lootState.CurrentGame().LoadPlugins(true); @@ -735,7 +735,7 @@ namespace loot { pluginNode["isEmpty"] = plugin.IsEmpty(); pluginNode["isMaster"] = plugin.isMasterFile(); pluginNode["loadsArchive"] = plugin.LoadsArchive(); - pluginNode["crc"] = IntToHexString(plugin.Crc()); + pluginNode["crc"] = plugin.Crc(); pluginNode["version"] = Version(plugin.getDescription()).AsString(); if (!mlistPlugin.HasNameOnly()) { diff --git a/src/tests/backend/game/test_game_cache.h b/src/tests/backend/game/test_game_cache.h index 0fad8384..6530af79 100644 --- a/src/tests/backend/game/test_game_cache.h +++ b/src/tests/backend/game/test_game_cache.h @@ -22,47 +22,141 @@ along with LOOT. If not, see . */ -#ifndef LOOT_TEST_BACKEND_GAME -#define LOOT_TEST_BACKEND_GAME +#ifndef LOOT_TEST_BACKEND_GAME_CACHE +#define LOOT_TEST_BACKEND_GAME_CACHE #include "backend/game/game_cache.h" #include "tests/fixtures.h" -class GameCache : public SkyrimTest {}; +namespace loot { + namespace test { + class GameCache : public SkyrimTest { + protected: + loot::GameCache cache; + }; -TEST_F(GameCache, Constructors) { - loot::GameCache cache; - std::unordered_set plugins({"skyrim.esm"}); + TEST_F(GameCache, copyConstructorShouldCopyCachedData) { + loot::Game game(loot::Game::tes5); + game.SetGamePath(dataPath.parent_path()); + ASSERT_NO_THROW(game.Init(false, localPath)); - EXPECT_NO_THROW(cache.CacheCondition("True Condition", true)); + cache.CacheCondition("True Condition", true); + cache.AddPlugin(loot::Plugin(game, "Blank.esm", true)); - loot::GameCache cache2(cache); - EXPECT_EQ(std::make_pair(true, true), cache2.GetCachedCondition("true Condition")); + loot::GameCache otherCache(cache); + EXPECT_EQ(std::make_pair(true, true), otherCache.GetCachedCondition("true Condition")); + EXPECT_EQ("Blank.esm", otherCache.GetPlugin("Blank.esm").Name()); + } + + TEST_F(GameCache, assignmentOperatorShouldCopyCachedData) { + loot::Game game(loot::Game::tes5); + game.SetGamePath(dataPath.parent_path()); + ASSERT_NO_THROW(game.Init(false, localPath)); + + cache.CacheCondition("True Condition", true); + cache.AddPlugin(loot::Plugin(game, "Blank.esm", true)); + + loot::GameCache otherCache = cache; + EXPECT_EQ(std::make_pair(true, true), otherCache.GetCachedCondition("true Condition")); + EXPECT_EQ("Blank.esm", otherCache.GetPlugin("Blank.esm").Name()); + } + + TEST_F(GameCache, gettingATrueConditionShouldReturnATrueTruePair) { + EXPECT_NO_THROW(cache.CacheCondition("True Condition", true)); + + EXPECT_EQ(std::make_pair(true, true), cache.GetCachedCondition("true Condition")); + } + + TEST_F(GameCache, gettingAFalseConditionShouldReturnAFalseTruePair) { + EXPECT_NO_THROW(cache.CacheCondition("False Condition", false)); + + EXPECT_EQ(std::make_pair(false, true), cache.GetCachedCondition("false Condition")); + } + + TEST_F(GameCache, gettingANonCachedConditionShouldReturnAFalseFalsePair) { + EXPECT_EQ(std::make_pair(false, false), cache.GetCachedCondition("true missing Condition")); + } + + TEST_F(GameCache, addingAPluginThatDoesNotExistShouldSucceed) { + loot::Game game(loot::Game::tes5); + game.SetGamePath(dataPath.parent_path()); + ASSERT_NO_THROW(game.Init(false, localPath)); + + cache.AddPlugin(loot::Plugin(game, "Blank.esm", true)); + EXPECT_EQ("Blank.esm", cache.GetPlugin("Blank.esm").Name()); + } + + TEST_F(GameCache, addingAPluginThatIsAlreadyCachedShouldOverwriteExistingEntry) { + loot::Game game(loot::Game::tes5); + game.SetGamePath(dataPath.parent_path()); + ASSERT_NO_THROW(game.Init(false, localPath)); + + cache.AddPlugin(loot::Plugin(game, "Blank.esm", true)); + EXPECT_EQ(0, cache.GetPlugin("Blank.esm").Crc()); + + cache.AddPlugin(loot::Plugin(game, "Blank.esm", false)); + EXPECT_EQ(0x187BE342, cache.GetPlugin("Blank.esm").Crc()); + } + + TEST_F(GameCache, gettingAPluginThatIsNotCachedShouldThrow) { + EXPECT_ANY_THROW(cache.GetPlugin("Blank.esm")); + } + + TEST_F(GameCache, gettingAPluginShouldBeCaseInsensitive) { + loot::Game game(loot::Game::tes5); + game.SetGamePath(dataPath.parent_path()); + ASSERT_NO_THROW(game.Init(false, localPath)); + + cache.AddPlugin(loot::Plugin(game, "Blank.esm", true)); + EXPECT_EQ("Blank.esm", cache.GetPlugin("blanK.esm").Name()); + } + + TEST_F(GameCache, gettingPluginsShouldReturnAnEmptySetIfNoPluginsHaveBeenCached) { + EXPECT_TRUE(cache.GetPlugins().empty()); + } + + TEST_F(GameCache, gettingPluginsShouldReturnASetOfCachedPluginsIfPluginsHaveBeenCached) { + loot::Game game(loot::Game::tes5); + game.SetGamePath(dataPath.parent_path()); + ASSERT_NO_THROW(game.Init(false, localPath)); + + cache.AddPlugin(loot::Plugin(game, "Blank.esm", true)); + cache.AddPlugin(loot::Plugin(game, "Blank - Master Dependent.esp", true)); + + EXPECT_EQ(std::set({ + loot::Plugin(game, "Blank.esm", true), + loot::Plugin(game, "Blank - Master Dependent.esp", true), + }), cache.GetPlugins()); + } + + TEST_F(GameCache, clearingCachedConditionsShouldNotThrowIfNoConditionsAreCached) { + EXPECT_NO_THROW(cache.ClearCachedConditions()); + } + + TEST_F(GameCache, clearingCachedConditionsShouldClearAnyCachedConditions) { + EXPECT_NO_THROW(cache.CacheCondition("True Condition", true)); + + EXPECT_NO_THROW(cache.ClearCachedConditions()); + + EXPECT_EQ(std::make_pair(false, false), cache.GetCachedCondition("true Condition")); + } + + TEST_F(GameCache, clearingCachedPluginsShouldNotThrowIfNoPluginsAreCached) { + EXPECT_NO_THROW(cache.ClearCachedPlugins()); + } + + TEST_F(GameCache, clearingCachedPluginsShouldClearAnyCachedPlugins) { + loot::Game game(loot::Game::tes5); + game.SetGamePath(dataPath.parent_path()); + ASSERT_NO_THROW(game.Init(false, localPath)); + + cache.AddPlugin(loot::Plugin(game, "Blank.esm", true)); + cache.ClearCachedPlugins(); + + EXPECT_TRUE(cache.GetPlugins().empty()); + } + } } -TEST_F(GameCache, AssignmentOperator) { - loot::GameCache cache; - std::unordered_set plugins({"skyrim.esm"}); - - EXPECT_NO_THROW(cache.CacheCondition("True Condition", true)); - - loot::GameCache cache2 = cache; - EXPECT_EQ(std::make_pair(true, true), cache2.GetCachedCondition("true Condition")); -} - -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, ClearCache) {} - #endif diff --git a/src/tests/main.cpp b/src/tests/main.cpp index 94bab5a6..ea814bea 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -30,6 +30,7 @@ #include "api/test_api.h" #include "api/test_loot_db.h" #include "backend/game/test_game.h" +#include "backend/game/test_game_cache.h" #include "backend/game/test_game_settings.h" #include "backend/game/test_load_order_handler.h" #include "backend/helpers/test_git_helper.h"