From 174dea8b8d117a8cf36a6c3afa0bcd53b798ecb1 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 29 Jan 2017 12:04:35 +0000 Subject: [PATCH] Move condition evaluation out of metadata classes So that the API doesn't expose any references to the Game class. Evaluation is now done in the ConditionEvaluator class. This complicates evaluating conditions for Plugin objects, these have been checked to ensure that metadata is only merged into them after evaluation. --- CMakeLists.txt | 1 + include/loot/metadata/conditional_metadata.h | 7 +- include/loot/metadata/plugin_cleaning_data.h | 4 - include/loot/metadata/plugin_metadata.h | 3 - resources/l10n/template.pot | 12 +- src/backend/metadata/condition_evaluator.cpp | 130 +++++++++++++++++ src/backend/metadata/condition_evaluator.h | 8 ++ src/backend/metadata/condition_grammar.h | 20 +-- src/backend/metadata/conditional_metadata.cpp | 42 +----- src/backend/metadata/plugin_cleaning_data.cpp | 24 ---- src/backend/metadata/plugin_metadata.cpp | 57 -------- src/backend/metadata_list.cpp | 16 ++- src/backend/plugin/plugin_sorter.cpp | 33 +++-- src/gui/query/metadata_query.h | 31 ++-- src/tests/backend/main.cpp | 1 + .../metadata/condition_evaluator_test.h | 132 ++++++++++++++++++ .../backend/metadata/condition_grammar_test.h | 102 +++++++------- .../metadata/conditional_metadata_test.h | 27 ---- .../metadata/plugin_cleaning_data_test.h | 21 --- .../backend/metadata/plugin_metadata_test.h | 36 ----- 20 files changed, 390 insertions(+), 317 deletions(-) create mode 100644 src/tests/backend/metadata/condition_evaluator_test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 19f2568b..bdd39c9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,6 +320,7 @@ set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/backend/game/game_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/language_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/version_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/yaml_set_helpers_test.h" + "${CMAKE_SOURCE_DIR}/src/tests/backend/metadata/condition_evaluator_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/metadata/condition_grammar_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/metadata/conditional_metadata_test.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/metadata/file_test.h" diff --git a/include/loot/metadata/conditional_metadata.h b/include/loot/metadata/conditional_metadata.h index af758709..05c69904 100644 --- a/include/loot/metadata/conditional_metadata.h +++ b/include/loot/metadata/conditional_metadata.h @@ -27,21 +27,16 @@ #include namespace loot { -class Game; - class ConditionalMetadata { public: ConditionalMetadata(); ConditionalMetadata(const std::string& condition); bool IsConditional() const; - bool EvalCondition(Game& game) const; - void ParseCondition() const; // Throws error on parsing failure. + void ParseCondition() const; std::string Condition() const; private: - bool ParseCondition(Game * game) const; // Throws error on parsing failure. - std::string condition_; }; } diff --git a/include/loot/metadata/plugin_cleaning_data.h b/include/loot/metadata/plugin_cleaning_data.h index 330b45eb..9686cdc4 100644 --- a/include/loot/metadata/plugin_cleaning_data.h +++ b/include/loot/metadata/plugin_cleaning_data.h @@ -31,8 +31,6 @@ #include "loot/metadata/message.h" namespace loot { -class Game; - class PluginCleaningData { public: PluginCleaningData(); @@ -56,8 +54,6 @@ public: MessageContent ChooseInfo(const LanguageCode language) const; Message AsMessage() const; - - bool EvalCondition(Game& game, const std::string& pluginName) const; private: uint32_t crc_; unsigned int itm_; diff --git a/include/loot/metadata/plugin_metadata.h b/include/loot/metadata/plugin_metadata.h index 247a040d..8d6a16d4 100644 --- a/include/loot/metadata/plugin_metadata.h +++ b/include/loot/metadata/plugin_metadata.h @@ -40,8 +40,6 @@ #include "loot/metadata/tag.h" namespace loot { -class Game; - class PluginMetadata { public: PluginMetadata(); @@ -84,7 +82,6 @@ public: void CleanInfo(const std::set& info); void Locations(const std::set& locations); - PluginMetadata& EvalAllConditions(Game& game); bool HasNameOnly() const; bool IsRegexPlugin() const; diff --git a/resources/l10n/template.pot b/resources/l10n/template.pot index 56e40fda..d0c9b935 100644 --- a/resources/l10n/template.pot +++ b/resources/l10n/template.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: LOOT 0.10.3\n" "Report-Msgid-Bugs-To: https://github.com/loot/loot/issues\n" -"POT-Creation-Date: 2017-01-29 11:37+0000\n" +"POT-Creation-Date: 2017-01-29 13:46+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -700,25 +700,25 @@ msgid "" "[LOOT's website](https://loot.github.io/)." msgstr "" -#: src/gui/query/metadata_query.h:90 src/gui/query/metadata_query.h:91 +#: src/gui/query/metadata_query.h:91 src/gui/query/metadata_query.h:92 msgid "N/A: No masterlist present" msgstr "" -#: src/gui/query/metadata_query.h:94 src/gui/query/metadata_query.h:95 +#: src/gui/query/metadata_query.h:95 src/gui/query/metadata_query.h:96 msgid "Unknown: Git repository missing" msgstr "" -#: src/gui/query/metadata_query.h:118 +#: src/gui/query/metadata_query.h:120 msgid "" "A global message contains a condition that could not be evaluated. Details: " "%1%" msgstr "" -#: src/gui/query/metadata_query.h:144 src/backend/plugin/plugin_sorter.cpp:219 +#: src/gui/query/metadata_query.h:146 src/backend/plugin/plugin_sorter.cpp:219 msgid "\"%1%\" contains a condition that could not be evaluated. Details: %2%" msgstr "" -#: src/gui/query/metadata_query.h:175 src/gui/query/metadata_query.h:176 +#: src/gui/query/metadata_query.h:178 src/gui/query/metadata_query.h:179 msgid "(edited)" msgstr "" diff --git a/src/backend/metadata/condition_evaluator.cpp b/src/backend/metadata/condition_evaluator.cpp index 9edd695b..6a6e0655 100644 --- a/src/backend/metadata/condition_evaluator.cpp +++ b/src/backend/metadata/condition_evaluator.cpp @@ -29,6 +29,7 @@ #include #include "backend/helpers/crc.h" +#include "backend/metadata/condition_grammar.h" #include "loot/exception/condition_syntax_error.h" using boost::format; @@ -36,6 +37,117 @@ using boost::format; namespace loot { ConditionEvaluator::ConditionEvaluator(Game * game) : game_(game) {} +bool ConditionEvaluator::evaluate(const std::string& condition) { + if (game_ == nullptr) { + // Still check that the syntax is valid. + parseCondition(condition); + return false; + } + + if (condition.empty()) + return true; + + BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << condition; + + auto cachedValue = game_->GetCachedCondition(condition); + if (cachedValue.second) + return cachedValue.first; + + bool result = parseCondition(condition); + + game_->CacheCondition(condition, result); + + return result; +} + +bool ConditionEvaluator::evaluate(const PluginCleaningData& cleaningData, const std::string& pluginName) { + if (game_ == nullptr || pluginName.empty()) + return false; + + // First need to get plugin's CRC. + uint32_t crc = 0; + + // Get the CRC from the game plugin cache if possible. + try { + crc = game_->GetPlugin(pluginName).Crc(); + } catch (...) {} + + // Otherwise calculate it from the file. + if (crc == 0) { + if (boost::filesystem::exists(game_->DataPath() / pluginName)) { + crc = GetCrc32(game_->DataPath() / pluginName); + } else if (boost::filesystem::exists(game_->DataPath() / (pluginName + ".ghost"))) { + crc = GetCrc32(game_->DataPath() / (pluginName + ".ghost")); + } + } + + return cleaningData.CRC() == crc; +} + +PluginMetadata ConditionEvaluator::evaluateAll(const PluginMetadata& pluginMetadata) { + if (game_ == nullptr) + return pluginMetadata; + + PluginMetadata evaluatedMetadata(pluginMetadata.Name()); + evaluatedMetadata.Enabled(pluginMetadata.Enabled()); + evaluatedMetadata.LocalPriority(pluginMetadata.LocalPriority()); + evaluatedMetadata.GlobalPriority(pluginMetadata.GlobalPriority()); + evaluatedMetadata.Locations(pluginMetadata.Locations()); + + std::set fileSet; + for (const auto& file : pluginMetadata.LoadAfter()) { + if (evaluate(file.Condition())) + fileSet.insert(file); + } + evaluatedMetadata.LoadAfter(fileSet); + + fileSet.clear(); + for (const auto& file : pluginMetadata.Reqs()) { + if (evaluate(file.Condition())) + fileSet.insert(file); + } + evaluatedMetadata.Reqs(fileSet); + + fileSet.clear(); + for (const auto& file : pluginMetadata.Incs()) { + if (evaluate(file.Condition())) + fileSet.insert(file); + } + evaluatedMetadata.Incs(fileSet); + + std::vector messages; + for (const auto& message : pluginMetadata.Messages()) { + if (evaluate(message.Condition())) + messages.push_back(message); + } + evaluatedMetadata.Messages(messages); + + std::set tagSet; + for (const auto& tag : pluginMetadata.Tags()) { + if (evaluate(tag.Condition())) + tagSet.insert(tag); + } + evaluatedMetadata.Tags(tagSet); + + if (!evaluatedMetadata.IsRegexPlugin()) { + std::set infoSet; + for (const auto& info : pluginMetadata.DirtyInfo()) { + if (evaluate(info, pluginMetadata.Name())) + infoSet.insert(info); + } + evaluatedMetadata.DirtyInfo(infoSet); + + infoSet.clear(); + for (const auto& info : pluginMetadata.CleanInfo()) { + if (evaluate(info, pluginMetadata.Name())) + infoSet.insert(info); + } + evaluatedMetadata.CleanInfo(infoSet); + } + + return evaluatedMetadata; +} + bool ConditionEvaluator::fileExists(const std::string& filePath) const { validatePath(filePath); @@ -264,6 +376,24 @@ bool ConditionEvaluator::areRegexMatchesInDataDirectory(const std::pair grammar(*this); + boost::spirit::qi::space_type skipper; + std::string::const_iterator begin = condition.begin(); + std::string::const_iterator end = condition.end(); + bool evaluation; + + bool parseResult = boost::spirit::qi::phrase_parse(begin, end, grammar, skipper, evaluation); + + if (!parseResult || begin != end) { + throw ConditionSyntaxError((boost::format("Failed to parse condition \"%1%\": only partially matched expected syntax.") % condition).str()); + } + + return evaluation; +} Version ConditionEvaluator::getVersion(const std::string& filePath) const { if (filePath == "LOOT") return Version(boost::filesystem::absolute("LOOT.exe")); diff --git a/src/backend/metadata/condition_evaluator.h b/src/backend/metadata/condition_evaluator.h index 673d2913..589eb9aa 100644 --- a/src/backend/metadata/condition_evaluator.h +++ b/src/backend/metadata/condition_evaluator.h @@ -32,12 +32,18 @@ #include "backend/game/game.h" #include "backend/helpers/version.h" +#include "loot/metadata/plugin_cleaning_data.h" +#include "loot/metadata/plugin_metadata.h" namespace loot { class ConditionEvaluator { public: ConditionEvaluator(Game * game); + bool evaluate(const std::string& condition); + bool evaluate(const PluginCleaningData& cleaningData, const std::string& pluginName); + PluginMetadata evaluateAll(const PluginMetadata& pluginMetadata); + bool fileExists(const std::string& filePath) const; bool regexMatchExists(const std::string& regexString) const; bool regexMatchesExist(const std::string& regexString) const; @@ -68,6 +74,8 @@ private: bool areRegexMatchesInDataDirectory(const std::pair& pathRegex, const std::function condition) const; + bool parseCondition(const std::string& condition); + Version getVersion(const std::string& filePath) const; Game * game_; diff --git a/src/backend/metadata/condition_grammar.h b/src/backend/metadata/condition_grammar.h index 33cff7dd..eb30465f 100644 --- a/src/backend/metadata/condition_grammar.h +++ b/src/backend/metadata/condition_grammar.h @@ -56,7 +56,7 @@ template class ConditionGrammar : public boost::spirit::qi::grammar < Iterator, bool(), Skipper > { public: ConditionGrammar() : ConditionGrammar(nullptr) {} - ConditionGrammar(Game * game) : ConditionGrammar::base_type(expression_, "condition grammar"), evaluator(game) { + ConditionGrammar(ConditionEvaluator& evaluator) : ConditionGrammar::base_type(expression_, "condition grammar"), evaluator_(evaluator) { using boost::spirit::unicode::char_; using boost::spirit::unicode::string; namespace phoenix = boost::phoenix; @@ -144,9 +144,9 @@ private: result = false; if (IsRegex(file)) - result = evaluator.regexMatchExists(file); + result = evaluator_.regexMatchExists(file); else - result = evaluator.fileExists(file); + result = evaluator_.fileExists(file); BOOST_LOG_TRIVIAL(trace) << "File check result: " << result; } @@ -155,21 +155,21 @@ private: BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist."; result = false; - result = evaluator.regexMatchesExist(regexStr); + result = evaluator_.regexMatchesExist(regexStr); } void CheckSum(bool& result, const std::string& file, const uint32_t checksum) { BOOST_LOG_TRIVIAL(trace) << "Checking the CRC of the file \"" << file << "\"."; result = false; - result = evaluator.checksumMatches(file, checksum); + result = evaluator_.checksumMatches(file, checksum); } void CheckVersion(bool& result, const std::string& file, const std::string& version, const std::string& comparator) const { BOOST_LOG_TRIVIAL(trace) << "Checking version of file \"" << file << "\"."; result = false; - result = evaluator.compareVersions(file, version, comparator); + result = evaluator_.compareVersions(file, version, comparator); BOOST_LOG_TRIVIAL(trace) << "Version check result: " << result; } @@ -177,9 +177,9 @@ private: void CheckActive(bool& result, const std::string& file) const { result = false; if (IsRegex(file)) - result = evaluator.isPluginMatchingRegexActive(file); + result = evaluator_.isPluginMatchingRegexActive(file); else - result = evaluator.isPluginActive(file); + result = evaluator_.isPluginActive(file); BOOST_LOG_TRIVIAL(trace) << "Active check result: " << result; } @@ -188,7 +188,7 @@ private: BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist."; result = false; - result = evaluator.arePluginsActive(regexStr); + result = evaluator_.arePluginsActive(regexStr); } void SyntaxError(Iterator const& first, Iterator const& last, Iterator const& errorpos, boost::spirit::info const& what) { @@ -203,7 +203,7 @@ private: boost::spirit::qi::rule quotedStr_, filePath_, comparator_; boost::spirit::qi::rule invalidPathChars_; - ConditionEvaluator evaluator; + ConditionEvaluator& evaluator_; }; } #endif diff --git a/src/backend/metadata/conditional_metadata.cpp b/src/backend/metadata/conditional_metadata.cpp index d801933c..d62e5996 100644 --- a/src/backend/metadata/conditional_metadata.cpp +++ b/src/backend/metadata/conditional_metadata.cpp @@ -28,7 +28,7 @@ #include #include "backend/game/game.h" -#include "backend/metadata/condition_grammar.h" +#include "backend/metadata/condition_evaluator.h" using std::string; @@ -45,46 +45,10 @@ std::string ConditionalMetadata::Condition() const { return condition_; } -bool ConditionalMetadata::EvalCondition(Game& game) const { - if (condition_.empty()) - return true; - - BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << condition_; - - auto cachedValue = game.GetCachedCondition(condition_); - if (cachedValue.second) - return cachedValue.first; - - bool result = ParseCondition(&game); - - game.CacheCondition(condition_, result); - - return result; -} - void ConditionalMetadata::ParseCondition() const { BOOST_LOG_TRIVIAL(trace) << "Testing condition syntax: " << condition_; - ParseCondition(nullptr); -} + ConditionEvaluator evaluator(nullptr); -bool ConditionalMetadata::ParseCondition(Game * game) const { - if (condition_.empty()) - return true; - - BOOST_LOG_TRIVIAL(trace) << "Testing condition syntax: " << condition_; - - ConditionGrammar grammar(game); - boost::spirit::qi::space_type skipper; - string::const_iterator begin = condition_.begin(); - string::const_iterator end = condition_.end(); - bool evaluation; - - bool parseResult = boost::spirit::qi::phrase_parse(begin, end, grammar, skipper, evaluation); - - if (!parseResult || begin != end) { - throw ConditionSyntaxError((boost::format("Failed to parse condition \"%1%\": only partially matched expected syntax.") % condition_).str()); - } - - return evaluation; + evaluator.evaluate(condition_); } } diff --git a/src/backend/metadata/plugin_cleaning_data.cpp b/src/backend/metadata/plugin_cleaning_data.cpp index ac4750a5..59dec880 100644 --- a/src/backend/metadata/plugin_cleaning_data.cpp +++ b/src/backend/metadata/plugin_cleaning_data.cpp @@ -122,28 +122,4 @@ Message PluginCleaningData::AsMessage() const { return Message(MessageType::warn, info); } - -bool PluginCleaningData::EvalCondition(Game& game, const std::string& pluginName) const { - if (pluginName.empty()) - return false; - - // First need to get plugin's CRC. - uint32_t crc = 0; - - // Get the CRC from the game plugin cache if possible. - try { - crc = game.GetPlugin(pluginName).Crc(); - } catch (...) {} - - // Otherwise calculate it from the file. - if (crc == 0) { - if (boost::filesystem::exists(game.DataPath() / pluginName)) { - crc = GetCrc32(game.DataPath() / pluginName); - } else if (boost::filesystem::exists(game.DataPath() / (pluginName + ".ghost"))) { - crc = GetCrc32(game.DataPath() / (pluginName + ".ghost")); - } - } - - return crc_ == crc; -} } diff --git a/src/backend/metadata/plugin_metadata.cpp b/src/backend/metadata/plugin_metadata.cpp index 4184899a..0127c867 100644 --- a/src/backend/metadata/plugin_metadata.cpp +++ b/src/backend/metadata/plugin_metadata.cpp @@ -272,63 +272,6 @@ void PluginMetadata::Locations(const std::set& locations) { locations_ = locations; } -PluginMetadata& PluginMetadata::EvalAllConditions(Game& game) { - for (auto it = loadAfter_.begin(); it != loadAfter_.end();) { - if (!it->EvalCondition(game)) - loadAfter_.erase(it++); - else - ++it; - } - - for (auto it = requirements_.begin(); it != requirements_.end();) { - if (!it->EvalCondition(game)) - requirements_.erase(it++); - else - ++it; - } - - for (auto it = incompatibilities_.begin(); it != incompatibilities_.end();) { - if (!it->EvalCondition(game)) - incompatibilities_.erase(it++); - else - ++it; - } - - for (auto it = messages_.begin(); it != messages_.end();) { - if (!it->EvalCondition(game)) - it = messages_.erase(it); - else - ++it; - } - - for (auto it = tags_.begin(); it != tags_.end();) { - if (!it->EvalCondition(game)) - tags_.erase(it++); - else - ++it; - } - - if (IsRegexPlugin()) { // Remove any dirty metadata from a regex plugin. - dirtyInfo_.clear(); - cleanInfo_.clear(); - } else { - for (auto it = dirtyInfo_.begin(); it != dirtyInfo_.end();) { - if (!it->EvalCondition(game, name_)) - dirtyInfo_.erase(it++); - else - ++it; - } - for (auto it = cleanInfo_.begin(); it != cleanInfo_.end();) { - if (!it->EvalCondition(game, name_)) - cleanInfo_.erase(it++); - else - ++it; - } - } - - return *this; -} - bool PluginMetadata::HasNameOnly() const { return !localPriority_.isExplicit() && !globalPriority_.isExplicit() diff --git a/src/backend/metadata_list.cpp b/src/backend/metadata_list.cpp index 1455f764..25a69e8b 100644 --- a/src/backend/metadata_list.cpp +++ b/src/backend/metadata_list.cpp @@ -31,6 +31,7 @@ #include "loot/exception/file_access_error.h" #include "loot/yaml/plugin_metadata.h" #include "backend/game/game.h" +#include "backend/metadata/condition_evaluator.h" namespace loot { void MetadataList::Load(const boost::filesystem::path& filepath) { @@ -149,18 +150,21 @@ void MetadataList::AppendMessage(const Message& message) { } void MetadataList::EvalAllConditions(Game& game) { + ConditionEvaluator evaluator(&game); std::unordered_set replacementSet; for (auto &plugin : plugins_) { - PluginMetadata p(plugin); - p.EvalAllConditions(game); - replacementSet.insert(p); + replacementSet.insert(evaluator.evaluateAll(plugin)); } plugins_ = replacementSet; for (auto &plugin : regexPlugins_) { - plugin.EvalAllConditions(game); + plugin = evaluator.evaluateAll(plugin); } - for (auto &message : messages_) { - message.EvalCondition(game); + + for (auto it = std::begin(messages_); it != std::end(messages_);) { + if (!evaluator.evaluate(it->Condition())) + it = messages_.erase(it); + else + ++it; } } } diff --git a/src/backend/plugin/plugin_sorter.cpp b/src/backend/plugin/plugin_sorter.cpp index 44e537a1..e0b0057b 100644 --- a/src/backend/plugin/plugin_sorter.cpp +++ b/src/backend/plugin/plugin_sorter.cpp @@ -36,6 +36,7 @@ #include "loot/exception/cyclic_interaction_error.h" #include "backend/game/game.h" +#include "backend/metadata/condition_evaluator.h" using std::list; using std::string; @@ -190,26 +191,28 @@ void PluginSorter::AddPluginVertices(Game& game, const LanguageCode language) { // Using a set of plugin names followed by finding the matching key // in the unordered map, as it's probably faster than copying the // full plugin objects then sorting them. + ConditionEvaluator evaluator(&game); for (const auto &plugin : game.GetPlugins()) { vertex_t v = boost::add_vertex(plugin, graph_); BOOST_LOG_TRIVIAL(trace) << "Merging for plugin \"" << graph_[v].Name() << "\""; - //Check if there is a plugin entry in the masterlist. This will also find matching regex entries. - BOOST_LOG_TRIVIAL(trace) << "Merging masterlist data down to plugin list data."; - graph_[v].MergeMetadata(game.GetMasterlist().FindPlugin(graph_[v])); - - //Check if there is a plugin entry in the userlist. This will also find matching regex entries. - PluginMetadata ulistPlugin = game.GetUserlist().FindPlugin(graph_[v]); - - if (!ulistPlugin.HasNameOnly() && ulistPlugin.Enabled()) { - BOOST_LOG_TRIVIAL(trace) << "Merging userlist data down to plugin list data."; - graph_[v].MergeMetadata(ulistPlugin); - } - - //Now that items are merged, evaluate any conditions they have. - BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data."; try { - graph_[v].EvalAllConditions(game); + //Check if there is a plugin entry in the masterlist. This will also find matching regex entries. + BOOST_LOG_TRIVIAL(trace) << "Evaluating conditions for any masterlist metadata."; + auto metadata = game.GetMasterlist().FindPlugin(graph_[v]); + metadata = evaluator.evaluateAll(metadata); + BOOST_LOG_TRIVIAL(trace) << "Merging masterlist metadata down to plugin list data."; + graph_[v].MergeMetadata(metadata); + + //Check if there is a plugin entry in the userlist. This will also find matching regex entries. + metadata = game.GetUserlist().FindPlugin(graph_[v]); + + if (!metadata.HasNameOnly() && metadata.Enabled()) { + BOOST_LOG_TRIVIAL(trace) << "Evaluating conditions for userlist metadata."; + metadata = evaluator.evaluateAll(metadata); + BOOST_LOG_TRIVIAL(trace) << "Merging userlist metadata down to plugin list data."; + graph_[v].MergeMetadata(metadata); + } } catch (std::exception& e) { BOOST_LOG_TRIVIAL(error) << "\"" << graph_[v].Name() << "\" contains a condition that could not be evaluated. Details: " << e.what(); vector messages(graph_[v].Messages()); diff --git a/src/gui/query/metadata_query.h b/src/gui/query/metadata_query.h index 46e0eac1..d0702ab3 100644 --- a/src/gui/query/metadata_query.h +++ b/src/gui/query/metadata_query.h @@ -29,6 +29,7 @@ along with LOOT. If not, see #include #include "backend/plugin/plugin.h" +#include "backend/metadata/condition_evaluator.h" #include "gui/query/query.h" #include "loot/exception/file_access_error.h" #include "loot/exception/git_state_error.h" @@ -54,10 +55,10 @@ protected: const PluginMetadata& userlistEntry) { Plugin plugin(file); - plugin.MergeMetadata(masterlistEntry); - plugin.MergeMetadata(userlistEntry); + plugin.MergeMetadata(evaluateMetadata(masterlistEntry)); + plugin.MergeMetadata(evaluateMetadata(userlistEntry)); - evaluatePlugin(plugin); + plugin.CheckInstallValidity(state_.getCurrentGame()); return toYaml(plugin); } @@ -106,9 +107,10 @@ private: void evaluateMessageConditions(std::vector& messages) { try { + ConditionEvaluator evaluator(&state_.getCurrentGame()); auto it = begin(messages); while (it != end(messages)) { - if (!it->EvalCondition(state_.getCurrentGame())) + if (!evaluator.evaluate(it->Condition())) it = messages.erase(it); else ++it; @@ -133,20 +135,21 @@ private: return simpleMessages; } - void evaluatePlugin(Plugin& plugin) { - //Evaluate any conditions + PluginMetadata evaluateMetadata(const PluginMetadata& pluginMetadata) { BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data."; try { - plugin.EvalAllConditions(state_.getCurrentGame()); + ConditionEvaluator evaluator(&state_.getCurrentGame()); + return evaluator.evaluateAll(pluginMetadata); } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "\"" << plugin.Name() << "\" contains a condition that could not be evaluated. Details: " << e.what(); - std::vector messages(plugin.Messages()); - messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("\"%1%\" contains a condition that could not be evaluated. Details: %2%")) % plugin.Name() % e.what()).str())); - plugin.Messages(messages); + BOOST_LOG_TRIVIAL(error) << "\"" << pluginMetadata.Name() << "\" contains a condition that could not be evaluated. Details: " << e.what(); + std::vector messages(pluginMetadata.Messages()); + messages.push_back(Message(MessageType::error, (boost::format(boost::locale::translate("\"%1%\" contains a condition that could not be evaluated. Details: %2%")) % pluginMetadata.Name() % e.what()).str())); + + PluginMetadata newMetadata(pluginMetadata); + newMetadata.Messages(messages); + + return newMetadata; } - - //Also check install validity. - plugin.CheckInstallValidity(state_.getCurrentGame()); } YAML::Node toYaml(const Plugin& plugin) { diff --git a/src/tests/backend/main.cpp b/src/tests/backend/main.cpp index efc9c281..d4bc3577 100644 --- a/src/tests/backend/main.cpp +++ b/src/tests/backend/main.cpp @@ -32,6 +32,7 @@ #include "tests/backend/helpers/language_test.h" #include "tests/backend/helpers/version_test.h" #include "tests/backend/helpers/yaml_set_helpers_test.h" +#include "tests/backend/metadata/condition_evaluator_test.h" #include "tests/backend/metadata/condition_grammar_test.h" #include "tests/backend/metadata/conditional_metadata_test.h" #include "tests/backend/metadata/file_test.h" diff --git a/src/tests/backend/metadata/condition_evaluator_test.h b/src/tests/backend/metadata/condition_evaluator_test.h new file mode 100644 index 00000000..5937fe0d --- /dev/null +++ b/src/tests/backend/metadata/condition_evaluator_test.h @@ -0,0 +1,132 @@ +/* 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_METADATA_CONDITION_EVALUATOR_TEST +#define LOOT_TESTS_BACKEND_METADATA_CONDITION_EVALUATOR_TEST + +#include "backend/metadata/condition_evaluator.h" + +#include "loot/exception/condition_syntax_error.h" +#include "tests/common_game_test_fixture.h" + +namespace loot { +namespace test { +class ConditionEvaluatorTest : public CommonGameTestFixture { +protected: + ConditionEvaluatorTest() : + info_(std::vector({ + MessageContent("info", LanguageCode::english), + })), + game_(GetParam(), dataPath.parent_path(), localPath), + evaluator_(&game_) {} + + const std::vector info_; + + Game game_; + ConditionEvaluator evaluator_; +}; + +// 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(, + ConditionEvaluatorTest, + ::testing::Values( + GameType::tes4, + GameType::tes5, + GameType::fo3, + GameType::fonv, + GameType::fo4, + GameType::tes5se)); + +TEST_P(ConditionEvaluatorTest, evaluateShouldReturnTrueForAnEmptyConditionString) { + EXPECT_TRUE(evaluator_.evaluate("")); +} + +TEST_P(ConditionEvaluatorTest, evaluateShouldThrowForAnInvalidConditionString) { + EXPECT_THROW(evaluator_.evaluate("condition"), ConditionSyntaxError); +} + +TEST_P(ConditionEvaluatorTest, evaluateShouldReturnTrueForAConditionThatIsTrue) { + EXPECT_TRUE(evaluator_.evaluate("file(\"" + blankEsm + "\")")); +} + +TEST_P(ConditionEvaluatorTest, evaluateShouldReturnFalseForAConditionThatIsFalse) { + EXPECT_FALSE(evaluator_.evaluate("file(\"" + missingEsp + "\")")); +} + +TEST_P(ConditionEvaluatorTest, evaluateConditionShouldBeTrueIfTheCrcInThePluginCleaningDataGivenMatchesTheRealPluginCrc) { + PluginCleaningData dirtyInfo(blankEsmCrc, "cleaner", info_, 2, 10, 30); + + EXPECT_TRUE(evaluator_.evaluate(dirtyInfo, blankEsm)); +} + +TEST_P(ConditionEvaluatorTest, evaluateShouldBeFalseIfTheCrcInThePluginCleaningDataGivenDoesNotMatchTheRealPluginCrc) { + PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", info_, 2, 10, 30); + + EXPECT_FALSE(evaluator_.evaluate(dirtyInfo, blankEsm)); +} + +TEST_P(ConditionEvaluatorTest, evaluateShouldBeFalseIfAnEmptyPluginFilenameIsGiven) { + PluginCleaningData dirtyInfo(blankEsmCrc, "cleaner", info_, 2, 10, 30); + + EXPECT_FALSE(evaluator_.evaluate(dirtyInfo, "")); +} + +TEST_P(ConditionEvaluatorTest, evaluateAllShouldEvaluateAllMetadataConditions) { + PluginMetadata plugin(blankEsm); + + File file1(blankEsp); + File file2(blankDifferentEsm, "", "file(\"" + missingEsp + "\")"); + plugin.LoadAfter({file1, file2}); + plugin.Reqs({file1, file2}); + plugin.Incs({file1, file2}); + + Message message1(MessageType::say, "content"); + Message message2(MessageType::say, "content", "file(\"" + missingEsp + "\")"); + plugin.Messages({message1, message2}); + + Tag tag1("Relev"); + Tag tag2("Relev", true, "file(\"" + missingEsp + "\")"); + plugin.Tags({tag1, tag2}); + + PluginCleaningData info1(blankEsmCrc, "utility", info_, 1, 2, 3); + PluginCleaningData info2(0xDEADBEEF, "utility", info_, 1, 2, 3); + plugin.DirtyInfo({info1, info2}); + plugin.CleanInfo({info1, info2}); + + EXPECT_NO_THROW(plugin = evaluator_.evaluateAll(plugin)); + + std::set expectedFiles({file1}); + EXPECT_EQ(expectedFiles, plugin.LoadAfter()); + EXPECT_EQ(expectedFiles, plugin.Reqs()); + EXPECT_EQ(expectedFiles, plugin.Incs()); + EXPECT_EQ(std::vector({message1}), plugin.Messages()); + EXPECT_EQ(std::set({tag1}), plugin.Tags()); + EXPECT_EQ(std::set({info1}), plugin.DirtyInfo()); + EXPECT_EQ(std::set({info1}), plugin.CleanInfo()); +} +} +} + +#endif diff --git a/src/tests/backend/metadata/condition_grammar_test.h b/src/tests/backend/metadata/condition_grammar_test.h index 49b2d1f9..5e82e8e7 100644 --- a/src/tests/backend/metadata/condition_grammar_test.h +++ b/src/tests/backend/metadata/condition_grammar_test.h @@ -38,6 +38,7 @@ protected: ConditionGrammarTest() : resourcePath(dataPath / "resource" / "detail" / "resource.txt"), game_(GetParam(), dataPath.parent_path(), localPath), + evaluator_(&game_), result_(false), success_(false) {} @@ -83,6 +84,7 @@ protected: const boost::filesystem::path resourcePath; Game game_; + ConditionEvaluator evaluator_; boost::spirit::qi::space_type skipper_; bool result_; bool success_; @@ -101,7 +103,8 @@ INSTANTIATE_TEST_CASE_P(, GameType::tes5se)); TEST_P(ConditionGrammarTest, parsingInvalidSyntaxShouldThrow) { - Grammar grammar(nullptr); + ConditionEvaluator evaluator(nullptr); + Grammar grammar(evaluator); std::string condition("file(foo)"); EXPECT_THROW(boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -112,7 +115,7 @@ TEST_P(ConditionGrammarTest, parsingInvalidSyntaxShouldThrow) { } TEST_P(ConditionGrammarTest, evaluatingInvalidSyntaxShouldThrow) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(foo)"); EXPECT_THROW(boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -123,7 +126,8 @@ TEST_P(ConditionGrammarTest, evaluatingInvalidSyntaxShouldThrow) { } TEST_P(ConditionGrammarTest, parsingAnEmptyConditionShouldThrow) { - Grammar grammar(nullptr); + ConditionEvaluator evaluator(nullptr); + Grammar grammar(evaluator); std::string condition(""); EXPECT_THROW(boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -134,7 +138,7 @@ TEST_P(ConditionGrammarTest, parsingAnEmptyConditionShouldThrow) { } TEST_P(ConditionGrammarTest, evaluatingAnEmptyConditionShouldThrow) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition(""); EXPECT_THROW(boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -145,7 +149,7 @@ TEST_P(ConditionGrammarTest, evaluatingAnEmptyConditionShouldThrow) { } TEST_P(ConditionGrammarTest, aFileConditionWithAPluginThatExistsShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + blankEsm + "\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -158,7 +162,7 @@ TEST_P(ConditionGrammarTest, aFileConditionWithAPluginThatExistsShouldEvaluateTo } TEST_P(ConditionGrammarTest, aFileConditionWithAPluginThatDoesNotExistShouldEvaluateToFalse) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + missingEsp + "\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -171,7 +175,7 @@ TEST_P(ConditionGrammarTest, aFileConditionWithAPluginThatDoesNotExistShouldEval } TEST_P(ConditionGrammarTest, evaluatingAFileConditionForAnUnsafePathShouldThrow) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"../../" + blankEsm + "\")"); EXPECT_THROW(boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -182,7 +186,7 @@ TEST_P(ConditionGrammarTest, evaluatingAFileConditionForAnUnsafePathShouldThrow) } TEST_P(ConditionGrammarTest, aFileConditionWithAnInvalidRegexShouldThrow) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"RagnvaldBook(Farengar(+Ragnvald)?)?\\.esp\")"); EXPECT_THROW(boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -193,7 +197,7 @@ TEST_P(ConditionGrammarTest, aFileConditionWithAnInvalidRegexShouldThrow) { } TEST_P(ConditionGrammarTest, aFileConditionWithARegexMatchingAPluginThatExistsShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"Blank.+\\.esm\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -206,7 +210,7 @@ TEST_P(ConditionGrammarTest, aFileConditionWithARegexMatchingAPluginThatExistsSh } TEST_P(ConditionGrammarTest, aFileConditionWithARegexMatchingAPluginThatDoesNotExistShouldEvaluateToFalse) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"Blank\\.m.+\\.esm\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -219,7 +223,7 @@ TEST_P(ConditionGrammarTest, aFileConditionWithARegexMatchingAPluginThatDoesNotE } TEST_P(ConditionGrammarTest, aFileConditionWithARegexMatchingAFileInASubfolderThatExistsShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"resource/detail/resource\\.txt\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -232,7 +236,7 @@ TEST_P(ConditionGrammarTest, aFileConditionWithARegexMatchingAFileInASubfolderTh } TEST_P(ConditionGrammarTest, aManyConditionWithARegexMatchingMoreThanOnePluginShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("many(\"Blank.+\\.esm\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -245,7 +249,7 @@ TEST_P(ConditionGrammarTest, aManyConditionWithARegexMatchingMoreThanOnePluginSh } TEST_P(ConditionGrammarTest, aManyConditionWithARegexMatchingOnlyOnePluginShouldEvaluateToFalse) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("many(\"Blank\\.esm\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -258,7 +262,7 @@ TEST_P(ConditionGrammarTest, aManyConditionWithARegexMatchingOnlyOnePluginShould } TEST_P(ConditionGrammarTest, aChecksumConditionWithACrcThatMatchesTheActualPluginCrcShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("checksum(\"" + blankEsm + "\", " + IntToHexString(blankEsmCrc) + ")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -274,7 +278,7 @@ TEST_P(ConditionGrammarTest, aChecksumConditionWithACrcThatMatchesTheActualCache ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, false)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("checksum(\"" + blankEsm + "\", " + IntToHexString(blankEsmCrc) + ")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -287,7 +291,7 @@ TEST_P(ConditionGrammarTest, aChecksumConditionWithACrcThatMatchesTheActualCache } TEST_P(ConditionGrammarTest, aChecksumConditionWithACrcThatDoesNotMatchTheActualPluginCrcShouldEvaluateToFalse) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("checksum(\"" + blankEsm + "\", DEADBEEF)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -303,7 +307,7 @@ TEST_P(ConditionGrammarTest, aVersionEqualityConditionWithAVersionThatEqualsTheA ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", ==)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -319,7 +323,7 @@ TEST_P(ConditionGrammarTest, aVersionEqualityConditionWithAVersionThatDoesNotEqu ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", ==)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -335,7 +339,7 @@ TEST_P(ConditionGrammarTest, aVersionEqualityConditionForAPluginWithNoVersionSho ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsp + "\", \"6.0\", ==)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -351,7 +355,7 @@ TEST_P(ConditionGrammarTest, aVersionInequalityConditionWithAVersionThatDoesNotE ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", !=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -367,7 +371,7 @@ TEST_P(ConditionGrammarTest, aVersionInequalityConditionWithAVersionThatEqualsTh ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", !=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -383,7 +387,7 @@ TEST_P(ConditionGrammarTest, aVersionInequalityConditionForAPluginWithNoVersionS ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsp + "\", \"6.0\", !=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -399,7 +403,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanConditionWithAnActualPluginVersionL ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", <)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -415,7 +419,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanConditionWithAnActualPluginVersionE ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", <)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -431,7 +435,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanConditionForAPluginWithNoVersionSho ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", <)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -447,7 +451,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionWithAnActualPluginVersi ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"4.0\", >)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -463,7 +467,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionWithAnActualPluginVersi ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", >)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -479,7 +483,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanConditionForAPluginWithNoVersion ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", >)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -495,7 +499,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionWithAnActualPlugi ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", <=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -511,7 +515,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionWithAnActualPlugi ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"4.0\", <=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -527,7 +531,7 @@ TEST_P(ConditionGrammarTest, aVersionLessThanOrEqualToConditionForAPluginWithNoV ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", <=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -543,7 +547,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionWithAnActualPl ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"5.0\", >=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -559,7 +563,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionWithAnActualPl ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsm + "\", \"6.0\", >=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -575,7 +579,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionForAPluginWith ASSERT_NO_THROW(game_.Init()); ASSERT_NO_THROW(loadInstalledPlugins(game_, true)); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("version(\"" + blankEsp + "\", \"5.0\", >=)"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -590,7 +594,7 @@ TEST_P(ConditionGrammarTest, aVersionGreaterThanOrEqualToConditionForAPluginWith TEST_P(ConditionGrammarTest, anActiveConditionWithAPluginThatIsActiveShouldEvaluateToTrue) { ASSERT_NO_THROW(game_.Init()); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("active(\"" + blankEsm + "\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -605,7 +609,7 @@ TEST_P(ConditionGrammarTest, anActiveConditionWithAPluginThatIsActiveShouldEvalu TEST_P(ConditionGrammarTest, anActiveConditionWithAPluginThatIsNotActiveShouldEvaluateToFalse) { ASSERT_NO_THROW(game_.Init()); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("active(\"" + blankEsp + "\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -620,7 +624,7 @@ TEST_P(ConditionGrammarTest, anActiveConditionWithAPluginThatIsNotActiveShouldEv TEST_P(ConditionGrammarTest, anActiveConditionWithARegexMatchingAnActivePluginShouldEvaluateToTrue) { ASSERT_NO_THROW(game_.Init()); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("active(\"Blank\\.esm\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -635,7 +639,7 @@ TEST_P(ConditionGrammarTest, anActiveConditionWithARegexMatchingAnActivePluginSh TEST_P(ConditionGrammarTest, anActiveConditionWithARegexMatchingNoActivePluginsShouldEvaluateToFalse) { ASSERT_NO_THROW(game_.Init()); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("active(\"Blank\\.esp\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -650,7 +654,7 @@ TEST_P(ConditionGrammarTest, anActiveConditionWithARegexMatchingNoActivePluginsS TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingMoreThanOnePluginThatIsActiveShouldEvaluateToTrue) { ASSERT_NO_THROW(game_.Init()); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("many_active(\"Blank( - Different Master Dependent)?\\.es(m|p)\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -665,7 +669,7 @@ TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingMoreThanOnePl TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingOnlyOnePluginThatIsActiveShouldEvaluateToFalse) { ASSERT_NO_THROW(game_.Init()); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("many_active(\"Blank\\.esm\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -680,7 +684,7 @@ TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingOnlyOnePlugin TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingNoPluginsThatAreActiveShouldEvaluateToFalse) { ASSERT_NO_THROW(game_.Init()); - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("many_active(\"Blank\\.esp\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -693,7 +697,7 @@ TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingNoPluginsThat } TEST_P(ConditionGrammarTest, aFalseConditionPrecededByANegatorShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("not file(\"" + missingEsp + "\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -706,7 +710,7 @@ TEST_P(ConditionGrammarTest, aFalseConditionPrecededByANegatorShouldEvaluateToTr } TEST_P(ConditionGrammarTest, aTrueConditionPrecededByANegatorShouldEvaluateToFalse) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("not file(\"" + blankEsm + "\")"); success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition), @@ -719,7 +723,7 @@ TEST_P(ConditionGrammarTest, aTrueConditionPrecededByANegatorShouldEvaluateToFal } TEST_P(ConditionGrammarTest, twoTrueConditionsJoinedByAnAndShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + blankEsm + "\")"); std::string compound(condition + " and " + condition); @@ -733,7 +737,7 @@ TEST_P(ConditionGrammarTest, twoTrueConditionsJoinedByAnAndShouldEvaluateToTrue) } TEST_P(ConditionGrammarTest, aTrueAndAFalseConditionJoinedByAnAndShouldEvaluateToFalse) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + blankEsm + "\")"); std::string compound(condition + " and not " + condition); @@ -747,7 +751,7 @@ TEST_P(ConditionGrammarTest, aTrueAndAFalseConditionJoinedByAnAndShouldEvaluateT } TEST_P(ConditionGrammarTest, aFalseAndATrueConditionJoinedByAnOrShouldEvaluateToTrue) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + blankEsm + "\")"); std::string compound("not " + condition + " or " + condition); @@ -761,7 +765,7 @@ TEST_P(ConditionGrammarTest, aFalseAndATrueConditionJoinedByAnOrShouldEvaluateTo } TEST_P(ConditionGrammarTest, twoFalseConditionsJoinedByAnOrShouldEvaluateToFalse) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + blankEsm + "\")"); std::string compound("not " + condition + " or not " + condition); @@ -775,7 +779,7 @@ TEST_P(ConditionGrammarTest, twoFalseConditionsJoinedByAnOrShouldEvaluateToFalse } TEST_P(ConditionGrammarTest, andOperatorsShouldTakePrecedenceOverOrOperators) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + blankEsm + "\")"); std::string compound("not " + condition + " and " + condition + " or " + condition); @@ -789,7 +793,7 @@ TEST_P(ConditionGrammarTest, andOperatorsShouldTakePrecedenceOverOrOperators) { } TEST_P(ConditionGrammarTest, parenthesesShouldTakePrecedenceOverAndOperators) { - Grammar grammar(&game_); + Grammar grammar(evaluator_); std::string condition("file(\"" + blankEsm + "\")"); std::string compound("not " + condition + " and ( " + condition + " or " + condition + " )"); diff --git a/src/tests/backend/metadata/conditional_metadata_test.h b/src/tests/backend/metadata/conditional_metadata_test.h index 49b2f4c1..7894abf2 100644 --- a/src/tests/backend/metadata/conditional_metadata_test.h +++ b/src/tests/backend/metadata/conditional_metadata_test.h @@ -68,33 +68,6 @@ TEST_P(ConditionalMetadataTest, isConditionalShouldBeTrueForANonEmptyConditionSt EXPECT_TRUE(conditionalMetadata_.IsConditional()); } -TEST_P(ConditionalMetadataTest, evalConditionShouldReturnTrueForAnEmptyCondition) { - Game game(GetParam(), dataPath.parent_path(), localPath); - - EXPECT_TRUE(conditionalMetadata_.EvalCondition(game)); -} - -TEST_P(ConditionalMetadataTest, evalConditionShouldThrowForAnInvalidCondition) { - Game game(GetParam(), dataPath.parent_path(), localPath); - - conditionalMetadata_ = ConditionalMetadata("condition"); - EXPECT_THROW(conditionalMetadata_.EvalCondition(game), ConditionSyntaxError); -} - -TEST_P(ConditionalMetadataTest, evalConditionShouldReturnTrueForAConditionThatIsTrue) { - Game game(GetParam(), dataPath.parent_path(), localPath); - - conditionalMetadata_ = ConditionalMetadata("file(\"" + blankEsm + "\")"); - EXPECT_TRUE(conditionalMetadata_.EvalCondition(game)); -} - -TEST_P(ConditionalMetadataTest, evalConditionShouldReturnFalseForAConditionThatIsFalse) { - Game game(GetParam(), dataPath.parent_path(), localPath); - - conditionalMetadata_ = ConditionalMetadata("file(\"" + missingEsp + "\")"); - EXPECT_FALSE(conditionalMetadata_.EvalCondition(game)); -} - TEST_P(ConditionalMetadataTest, parseConditionShouldNotThrowForAnEmptyCondition) { EXPECT_NO_THROW(conditionalMetadata_.ParseCondition()); } diff --git a/src/tests/backend/metadata/plugin_cleaning_data_test.h b/src/tests/backend/metadata/plugin_cleaning_data_test.h index 8f4a378b..b59476a7 100644 --- a/src/tests/backend/metadata/plugin_cleaning_data_test.h +++ b/src/tests/backend/metadata/plugin_cleaning_data_test.h @@ -145,27 +145,6 @@ TEST_P(PluginCleaningDataTest, LessThanOperatorShouldCompareCrcValues) { EXPECT_FALSE(info2 < info1); } -TEST_P(PluginCleaningDataTest, evalConditionShouldBeTrueIfTheCrcGivenMatchesTheRealPluginCrc) { - 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(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(GetParam(), dataPath.parent_path(), localPath); - - PluginCleaningData dirtyInfo; - EXPECT_FALSE(dirtyInfo.EvalCondition(game, "")); -} - TEST_P(PluginCleaningDataTest, chooseInfoShouldCreateADefaultContentObjectIfNoneExists) { PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", std::vector(), 2, 10, 30); EXPECT_EQ(MessageContent(), dirtyInfo.ChooseInfo(LanguageCode::english)); diff --git a/src/tests/backend/metadata/plugin_metadata_test.h b/src/tests/backend/metadata/plugin_metadata_test.h index 2ac67ac4..c231b87d 100644 --- a/src/tests/backend/metadata/plugin_metadata_test.h +++ b/src/tests/backend/metadata/plugin_metadata_test.h @@ -476,42 +476,6 @@ TEST_P(PluginMetadataTest, simpleMessagesShouldReturnMessagesAsSimpleMessages) { EXPECT_EQ("content3", simpleMessages.back().text); } -TEST_P(PluginMetadataTest, evalAllConditionsShouldEvaluateAllMetadataConditions) { - Game game(GetParam(), dataPath.parent_path(), localPath); - - PluginMetadata plugin(blankEsm); - - File file1(blankEsp); - File file2(blankDifferentEsm, "", "file(\"" + missingEsp + "\")"); - plugin.LoadAfter({file1, file2}); - plugin.Reqs({file1, file2}); - plugin.Incs({file1, file2}); - - Message message1(MessageType::say, "content"); - Message message2(MessageType::say, "content", "file(\"" + missingEsp + "\")"); - plugin.Messages({message1, message2}); - - Tag tag1("Relev"); - Tag tag2("Relev", true, "file(\"" + missingEsp + "\")"); - plugin.Tags({tag1, tag2}); - - PluginCleaningData info1(blankEsmCrc, "utility", info_, 1, 2, 3); - PluginCleaningData info2(0xDEADBEEF, "utility", info_, 1, 2, 3); - plugin.DirtyInfo({info1, info2}); - plugin.CleanInfo({info1, info2}); - - EXPECT_NO_THROW(plugin.EvalAllConditions(game)); - - std::set expectedFiles({file1}); - EXPECT_EQ(expectedFiles, plugin.LoadAfter()); - EXPECT_EQ(expectedFiles, plugin.Reqs()); - EXPECT_EQ(expectedFiles, plugin.Incs()); - EXPECT_EQ(std::vector({message1}), plugin.Messages()); - EXPECT_EQ(std::set({tag1}), plugin.Tags()); - EXPECT_EQ(std::set({info1}), plugin.DirtyInfo()); - EXPECT_EQ(std::set({info1}), plugin.CleanInfo()); -} - TEST_P(PluginMetadataTest, hasNameOnlyShouldBeTrueForADefaultConstructedPluginMetadataObject) { PluginMetadata plugin;