Tweak cleaning data metadata structure

Rename `util` to `utility`, and add an `info` key that acts like the
message structure's `content` key. Also change the generated message
structures slightly to reflect the changes. Closes #608.
This commit is contained in:
Oliver Hamlet
2016-08-06 16:58:42 +01:00
parent bdd1cabb72
commit 01836966b4
19 changed files with 349 additions and 232 deletions
+1 -1
View File
@@ -89,7 +89,7 @@ set(PSEUDOSEM_INCLUDE_DIRS "${SOURCE_DIR}/include")
ExternalProject_Add(testing-metadata
PREFIX "external"
URL "https://github.com/loot/testing-metadata/archive/1.1.0.tar.gz"
URL "https://github.com/loot/testing-metadata/archive/2.0.0.tar.gz"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
+2 -1
View File
@@ -433,7 +433,8 @@ name: 'Unofficial Skyrim Patch on Steam Workshop'
<thead><tr><th>Key Name<th>Value Type<th>Required<th>Notes
<tbody>
<tr><td><code>crc</code><td>hexadecimal integer<td>&#x2713;<td>The CRC-32 checksum of the plugin. If the plugin is dirty, this needs to be the CRC of the plugin before before cleaning. LOOT displays the CRCs of installed plugins in its report. The 8-character CRC should be preceded by <code>0x</code> so that it is correctly interpreted.
<tr><td><code>util</code><td>string<td>&#x2713;<td>The utility that should be or that was used to clean the plugin.
<tr><td><code>utility</code><td>string<td>&#x2713;<td>The utility that was used to check the plugin for dirty edits. If available, the version of the utility used should also be included (e.g. <code>TES5Edit v3.11</code>).
<tr><td><code>info</code><td>string <em>or</em> localised content list<td>&#x2717;<td>A message that will be displayed to the user. If a localised content list is provided, one of the structures must be for English. This is only used if the plugin is dirty, and is intended for providing cleaning instructions to the user.
<tr><td><code>itm</code><td>integer<td>&#x2717;<td>The number of identical-to-master records reported for the dirty plugin. If the number is unknown or zero, this field should not be supplied.
<tr><td><code>udr</code><td>integer<td>&#x2717;<td>The number of undeleted records reported for the dirty plugin. If the number is unknown or zero, this field should not be supplied.
<tr><td><code>nav</code><td>integer<td>&#x2717;<td>The number of deleted navmeshes reported for the dirty plugin. If the number is unknown or zero, this field should not be supplied.
+81 -94
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: LOOT 0.9.2\n"
"Report-Msgid-Bugs-To: https://github.com/loot/loot/issues\n"
"POT-Creation-Date: 2016-08-06 16:32+0100\n"
"POT-Creation-Date: 2016-08-06 16:58+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -131,132 +131,127 @@ msgstr ""
#: src/gui/html/js/translateStaticText.js:33
#: src/gui/html/js/translateStaticText.js:88
msgid "Global Priority"
msgid "Priority"
msgstr ""
#: src/gui/html/js/translateStaticText.js:34
msgid ""
"Global priorities are compared against all other plugins. Normal priorities "
"are compared against only conflicting plugins."
#: src/gui/html/js/translateStaticText.js:87
msgid "Global Priority"
msgstr ""
#: src/gui/html/js/translateStaticText.js:35
msgid "Priority Value"
msgstr ""
#: src/gui/html/js/translateStaticText.js:37
#: src/gui/html/js/translateStaticText.js:36
msgid "Main"
msgstr ""
#: src/gui/html/js/translateStaticText.js:38
#: src/gui/html/js/translateStaticText.js:37
msgid "Load After"
msgstr ""
#: src/gui/html/js/translateStaticText.js:39
#: src/gui/html/js/translateStaticText.js:38
msgid "Requirements"
msgstr ""
#: src/gui/html/js/translateStaticText.js:40
#: src/gui/html/js/translateStaticText.js:39
msgid "Incompatibilities"
msgstr ""
#: src/gui/html/js/translateStaticText.js:41
#: src/gui/html/js/translateStaticText.js:40
msgid "Messages"
msgstr ""
#: src/gui/html/js/translateStaticText.js:42
#: src/gui/html/js/translateStaticText.js:41
msgid "Bash Tags"
msgstr ""
#: src/gui/html/js/translateStaticText.js:43
#: src/gui/html/js/translateStaticText.js:42
msgid "Dirty Plugin Info"
msgstr ""
#: src/gui/html/js/translateStaticText.js:44
#: src/gui/html/js/translateStaticText.js:43
msgid "Clean Plugin Info"
msgstr ""
#: src/gui/html/js/translateStaticText.js:45
#: src/gui/html/js/translateStaticText.js:44
msgid "Locations"
msgstr ""
#: src/gui/html/js/translateStaticText.js:46
#: src/gui/html/js/translateStaticText.js:50
#: src/gui/html/js/translateStaticText.js:54
msgid "Filename"
msgstr ""
#: src/gui/html/js/translateStaticText.js:47
#: src/gui/html/js/translateStaticText.js:51
#: src/gui/html/js/translateStaticText.js:55
msgid "Filename"
msgid "Display Name"
msgstr ""
#: src/gui/html/js/translateStaticText.js:48
#: src/gui/html/js/translateStaticText.js:52
#: src/gui/html/js/translateStaticText.js:56
msgid "Display Name"
msgstr ""
#: src/gui/html/js/translateStaticText.js:49
#: src/gui/html/js/translateStaticText.js:53
#: src/gui/html/js/translateStaticText.js:57
#: src/gui/html/js/translateStaticText.js:61
#: src/gui/html/js/translateStaticText.js:66
#: src/gui/html/js/translateStaticText.js:60
#: src/gui/html/js/translateStaticText.js:65
msgid "Condition"
msgstr ""
#: src/gui/html/js/translateStaticText.js:59
#: src/gui/html/js/translateStaticText.js:58
msgid "Type"
msgstr ""
#: src/gui/html/js/translateStaticText.js:60
#: src/gui/html/js/translateStaticText.js:59
msgid "Content"
msgstr ""
#: src/gui/html/js/translateStaticText.js:62
#: src/gui/html/js/translateStaticText.js:61
#: src/gui/html/js/translateStaticText.js:252
msgid "Language"
msgstr ""
#: src/gui/html/js/translateStaticText.js:64
#: src/gui/html/js/translateStaticText.js:63
msgid "Add/Remove"
msgstr ""
#: src/gui/html/js/translateStaticText.js:65
#: src/gui/html/js/translateStaticText.js:64
msgid "Bash Tag"
msgstr ""
#: src/gui/html/js/translateStaticText.js:68
#: src/gui/html/js/translateStaticText.js:74
#: src/gui/html/js/translateStaticText.js:67
#: src/gui/html/js/translateStaticText.js:73
msgid "CRC"
msgstr ""
#: src/gui/html/js/translateStaticText.js:69
#: src/gui/html/js/translateStaticText.js:68
msgid "ITM Count"
msgstr ""
#: src/gui/html/js/translateStaticText.js:70
#: src/gui/html/js/translateStaticText.js:69
msgid "Deleted References"
msgstr ""
#: src/gui/html/js/translateStaticText.js:71
#: src/gui/html/js/translateStaticText.js:70
msgid "Deleted Navmeshes"
msgstr ""
#: src/gui/html/js/translateStaticText.js:72
#: src/gui/html/js/translateStaticText.js:75
#: src/gui/html/js/translateStaticText.js:71
#: src/gui/html/js/translateStaticText.js:74
msgid "Cleaning Utility"
msgstr ""
#: src/gui/html/js/translateStaticText.js:77
#: src/gui/html/js/translateStaticText.js:76
msgid "URL"
msgstr ""
#: src/gui/html/js/translateStaticText.js:78
#: src/gui/html/js/translateStaticText.js:77
#: src/gui/html/js/translateStaticText.js:261
msgid "Name"
msgstr ""
#: src/gui/html/js/translateStaticText.js:80
#: src/gui/html/js/translateStaticText.js:79
msgid "Save Metadata"
msgstr ""
#: src/gui/html/js/translateStaticText.js:81
#: src/gui/html/js/translateStaticText.js:80
#: src/gui/html/js/translateStaticText.js:98
#: src/gui/html/js/translateStaticText.js:182
#: src/gui/html/js/translateStaticText.js:275
@@ -607,14 +602,14 @@ msgstr ""
msgid "Failed to apply plugin metadata. Details: %1%"
msgstr ""
#: src/gui/query_handler.cpp:598
#: src/gui/query_handler.cpp:601
msgid ""
"An error occurred while parsing the masterlist: %1%. This probably happened "
"because an update to LOOT changed its metadata syntax support. Try updating "
"your masterlist to resolve the error."
msgstr ""
#: src/gui/query_handler.cpp:613
#: src/gui/query_handler.cpp:616
msgid ""
"An error occurred while parsing the userlist: %1%. This probably happened "
"because an update to LOOT changed its metadata syntax support. Your user "
@@ -630,33 +625,33 @@ msgid ""
"[LOOT's website](https://loot.github.io/)."
msgstr ""
#: src/gui/query_handler.cpp:723 src/gui/query_handler.cpp:726
#: src/gui/query_handler.cpp:721 src/gui/query_handler.cpp:724
msgid "Failed to get game data. Details: %1%"
msgstr ""
#: src/gui/query_handler.cpp:808 src/gui/query_handler.cpp:811
#: src/gui/query_handler.cpp:806 src/gui/query_handler.cpp:809
msgid "Failed to update the masterlist. Details: %1%"
msgstr ""
#: src/gui/query_handler.cpp:850
#: src/gui/query_handler.cpp:848
msgid "Loading plugin contents..."
msgstr ""
#: src/gui/query_handler.cpp:854
#: src/gui/query_handler.cpp:852
msgid "Sorting load order..."
msgstr ""
#: src/gui/query_handler.cpp:911
#: src/gui/query_handler.cpp:909
msgid "Failed to sort plugins. Details: %1%"
msgstr ""
#: src/gui/query_handler.cpp:941
#: src/gui/query_handler.cpp:939
msgid ""
"A global message contains a condition that could not be evaluated. Details: "
"%1%"
msgstr ""
#: src/gui/query_handler.cpp:963 src/backend/plugin/plugin_sorter.cpp:216
#: src/gui/query_handler.cpp:961 src/backend/plugin/plugin_sorter.cpp:216
msgid "\"%1%\" contains a condition that could not be evaluated. Details: %2%"
msgstr ""
@@ -764,22 +759,16 @@ msgstr ""
msgid "Failed to open file in its default application."
msgstr ""
#: src/backend/metadata/condition_grammar.h:146
#: src/backend/metadata/condition_grammar.h:281
#: src/backend/metadata/condition_grammar.h:365
#: src/backend/metadata/condition_evaluator.cpp:173
msgid "Invalid file path:"
msgstr ""
#: src/backend/metadata/condition_grammar.h:187
#: src/backend/metadata/condition_grammar.h:213
#: src/backend/metadata/condition_evaluator.cpp:183
#: src/backend/metadata/condition_evaluator.cpp:223
msgid "Invalid regex string \"%1%\": %2%"
msgstr ""
#: src/backend/metadata/condition_grammar.h:205
msgid "Invalid folder path:"
msgstr ""
#: src/backend/metadata/condition_grammar.h:385
#: src/backend/metadata/condition_grammar.h:202
msgid "Expected \"%1%\" at \"%2%\"."
msgstr ""
@@ -793,39 +782,37 @@ msgstr ""
msgid "Failed to parse condition \"%1%\"."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:71
msgid ""
"Contains %1% ITM records, %2% deleted references and %3% deleted navmeshes. "
"Clean with %4%."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:73
msgid "Clean with %1%."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:76
msgid ""
"Contains %1% deleted references and %2% deleted navmeshes. Clean with %3%."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:78
msgid "Contains %1% deleted navmeshes. Clean with %2%."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:80
msgid "Contains %1% deleted references. Clean with %2%."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:83
msgid "Contains %1% ITM records and %2% deleted navmeshes. Clean with %3%."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:85
msgid "Contains %1% ITM records. Clean with %2%."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:88
msgid "Contains %1% ITM records and %2% deleted references. Clean with %3%."
msgid ""
"%1% found %2% ITM records, %3% deleted references and %4% deleted navmeshes."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:90
msgid "%1% found dirty edits."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:93
msgid "%1% found %2% deleted references and %3% deleted navmeshes."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:95
msgid "%1% found %2% deleted navmeshes."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:97
msgid "%1% found %2% deleted references."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:100
msgid "%1% found %2% ITM records and %3% deleted navmeshes."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:102
msgid "%1% found %2% ITM records."
msgstr ""
#: src/backend/metadata/plugin_cleaning_data.cpp:105
msgid "%1% found %2% ITM records and %3% deleted references."
msgstr ""
#: src/backend/plugin/plugin.cpp:115
+1 -14
View File
@@ -73,20 +73,7 @@ bool Message::EvalCondition(loot::Game& game, const Language::Code language) {
MessageContent Message::ChooseContent(const Language::Code language) const {
BOOST_LOG_TRIVIAL(trace) << "Choosing message content.";
if (content_.empty())
return MessageContent();
else if (content_.size() == 1)
return content_[0];
else {
MessageContent english;
for (const auto &mc : content_) {
if (mc.GetLanguage() == language) {
return mc;
} else if (mc.GetLanguage() == Language::Code::english)
english = mc;
}
return english;
}
return MessageContent::Choose(content_, language);
}
Message::Type Message::GetType() const {
+17
View File
@@ -48,6 +48,23 @@ bool MessageContent::operator < (const MessageContent& rhs) const {
bool MessageContent::operator == (const MessageContent& rhs) const {
return (boost::iequals(text_, rhs.GetText()));
}
MessageContent MessageContent::Choose(const std::vector<MessageContent> content,
const Language::Code language) {
if (content.empty())
return MessageContent();
else if (content.size() == 1)
return content[0];
else {
MessageContent english;
for (const auto &mc : content) {
if (mc.GetLanguage() == language) {
return mc;
} else if (mc.GetLanguage() == Language::Code::english)
english = mc;
}
return english;
}
}
}
namespace YAML {
+3
View File
@@ -41,6 +41,9 @@ public:
bool operator < (const MessageContent& rhs) const;
bool operator == (const MessageContent& rhs) const;
static MessageContent Choose(const std::vector<MessageContent> content,
const Language::Code language);
private:
std::string text_;
Language::Code language_;
+50 -20
View File
@@ -26,6 +26,7 @@
#include <boost/format.hpp>
#include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
#include "backend/game/game.h"
#include "backend/helpers/helpers.h"
@@ -33,9 +34,16 @@
namespace loot {
PluginCleaningData::PluginCleaningData() : crc_(0), itm_(0), ref_(0), nav_(0) {}
PluginCleaningData::PluginCleaningData(uint32_t crc, const std::string& utility) : crc_(crc), utility_(utility), itm_(0), ref_(0), nav_(0) {}
PluginCleaningData::PluginCleaningData(uint32_t crc, const std::string& utility)
: crc_(crc), utility_(utility), itm_(0), ref_(0), nav_(0) {}
PluginCleaningData::PluginCleaningData(uint32_t crc, unsigned int itm, unsigned int ref, unsigned int nav, const std::string& utility) : crc_(crc), itm_(itm), ref_(ref), nav_(nav), utility_(utility) {}
PluginCleaningData::PluginCleaningData(uint32_t crc,
const std::string& utility,
const std::vector<MessageContent>& info,
unsigned int itm,
unsigned int ref,
unsigned int nav)
: crc_(crc), itm_(itm), ref_(ref), nav_(nav), utility_(utility), info_(info) {}
bool PluginCleaningData::operator < (const PluginCleaningData& rhs) const {
return crc_ < rhs.CRC();
@@ -65,29 +73,44 @@ std::string PluginCleaningData::CleaningUtility() const {
return utility_;
}
std::vector<MessageContent> PluginCleaningData::Info() const {
return info_;
}
MessageContent PluginCleaningData::ChooseInfo(const Language::Code language) const {
BOOST_LOG_TRIVIAL(trace) << "Choosing dirty info content.";
return MessageContent::Choose(info_, language);
}
Message PluginCleaningData::AsMessage() const {
boost::format f;
if (this->itm_ > 0 && this->ref_ > 0 && this->nav_ > 0)
f = boost::format(boost::locale::translate("Contains %1% ITM records, %2% deleted references and %3% deleted navmeshes. Clean with %4%.")) % this->itm_ % this->ref_ % this->nav_ % this->utility_;
else if (this->itm_ == 0 && this->ref_ == 0 && this->nav_ == 0)
f = boost::format(boost::locale::translate("Clean with %1%.")) % this->utility_;
if (itm_ > 0 && ref_ > 0 && nav_ > 0)
f = boost::format(boost::locale::translate("%1% found %2% ITM records, %3% deleted references and %4% deleted navmeshes.")) % utility_ % itm_ % ref_ % nav_;
else if (itm_ == 0 && ref_ == 0 && nav_ == 0)
f = boost::format(boost::locale::translate("%1% found dirty edits.")) % utility_;
else if (this->itm_ == 0 && this->ref_ > 0 && this->nav_ > 0)
f = boost::format(boost::locale::translate("Contains %1% deleted references and %2% deleted navmeshes. Clean with %3%.")) % this->ref_ % this->nav_ % this->utility_;
else if (this->itm_ == 0 && this->ref_ == 0 && this->nav_ > 0)
f = boost::format(boost::locale::translate("Contains %1% deleted navmeshes. Clean with %2%.")) % this->nav_ % this->utility_;
else if (this->itm_ == 0 && this->ref_ > 0 && this->nav_ == 0)
f = boost::format(boost::locale::translate("Contains %1% deleted references. Clean with %2%.")) % this->ref_ % this->utility_;
else if (itm_ == 0 && ref_ > 0 && nav_ > 0)
f = boost::format(boost::locale::translate("%1% found %2% deleted references and %3% deleted navmeshes.")) % utility_ % ref_ % nav_;
else if (itm_ == 0 && ref_ == 0 && nav_ > 0)
f = boost::format(boost::locale::translate("%1% found %2% deleted navmeshes.")) % utility_ % nav_;
else if (itm_ == 0 && ref_ > 0 && nav_ == 0)
f = boost::format(boost::locale::translate("%1% found %2% deleted references.")) % utility_ % ref_;
else if (this->itm_ > 0 && this->ref_ == 0 && this->nav_ > 0)
f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% deleted navmeshes. Clean with %3%.")) % this->itm_ % this->nav_ % this->utility_;
else if (this->itm_ > 0 && this->ref_ == 0 && this->nav_ == 0)
f = boost::format(boost::locale::translate("Contains %1% ITM records. Clean with %2%.")) % this->itm_ % this->utility_;
else if (itm_ > 0 && ref_ == 0 && nav_ > 0)
f = boost::format(boost::locale::translate("%1% found %2% ITM records and %3% deleted navmeshes.")) % utility_ % itm_ % nav_;
else if (itm_ > 0 && ref_ == 0 && nav_ == 0)
f = boost::format(boost::locale::translate("%1% found %2% ITM records.")) % utility_ % itm_;
else if (this->itm_ > 0 && this->ref_ > 0 && this->nav_ == 0)
f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% deleted references. Clean with %3%.")) % this->itm_ % this->ref_ % this->utility_;
else if (itm_ > 0 && ref_ > 0 && nav_ == 0)
f = boost::format(boost::locale::translate("%1% found %2% ITM records and %3% deleted references.")) % utility_ % itm_ % ref_;
return Message(Message::Type::warn, f.str());
std::string message = f.str();
auto info = info_;
for (auto& content : info) {
content = MessageContent(message + " " + content.GetText(), content.GetLanguage());
}
return Message(Message::Type::warn, info);
}
bool PluginCleaningData::EvalCondition(Game& game, const std::string& pluginName) const {
@@ -119,7 +142,14 @@ namespace YAML {
Emitter& operator << (Emitter& out, const loot::PluginCleaningData& rhs) {
out << BeginMap
<< Key << "crc" << Value << Hex << rhs.CRC() << Dec
<< Key << "util" << Value << YAML::SingleQuoted << rhs.CleaningUtility();
<< Key << "utility" << Value << YAML::SingleQuoted << rhs.CleaningUtility();
if (!rhs.Info().empty()) {
if (rhs.Info().size() == 1)
out << Key << "info" << Value << YAML::SingleQuoted << rhs.Info().front().GetText();
else
out << Key << "info" << Value << rhs.Info();
}
if (rhs.ITMs() > 0)
out << Key << "itm" << Value << rhs.ITMs();
+34 -5
View File
@@ -39,7 +39,12 @@ class PluginCleaningData {
public:
PluginCleaningData();
PluginCleaningData(uint32_t crc, const std::string& utility);
PluginCleaningData(uint32_t crc, unsigned int itm, unsigned int ref, unsigned int nav, const std::string& utility);
PluginCleaningData(uint32_t crc,
const std::string& utility,
const std::vector<MessageContent>& info,
unsigned int itm,
unsigned int ref,
unsigned int nav);
bool operator < (const PluginCleaningData& rhs) const;
bool operator == (const PluginCleaningData& rhs) const;
@@ -49,7 +54,9 @@ public:
unsigned int DeletedRefs() const;
unsigned int DeletedNavmeshes() const;
std::string CleaningUtility() const;
std::vector<MessageContent> Info() const;
MessageContent ChooseInfo(const Language::Code language) const;
Message AsMessage() const;
bool EvalCondition(Game& game, const std::string& pluginName) const;
@@ -59,6 +66,7 @@ private:
unsigned int ref_;
unsigned int nav_;
std::string utility_;
std::vector<MessageContent> info_;
};
}
@@ -68,7 +76,8 @@ struct convert<loot::PluginCleaningData> {
static Node encode(const loot::PluginCleaningData& rhs) {
Node node;
node["crc"] = rhs.CRC();
node["util"] = rhs.CleaningUtility();
node["utility"] = rhs.CleaningUtility();
node["info"] = rhs.Info();
if (rhs.ITMs() > 0)
node["itm"] = rhs.ITMs();
@@ -85,7 +94,7 @@ struct convert<loot::PluginCleaningData> {
throw RepresentationException(node.Mark(), "bad conversion: 'cleaning data' object must be a map");
if (!node["crc"])
throw RepresentationException(node.Mark(), "bad conversion: 'crc' key missing from 'cleaning data' object");
if (!node["util"])
if (!node["utility"])
throw RepresentationException(node.Mark(), "bad conversion: 'util' key missing from 'cleaning data' object");
uint32_t crc = node["crc"].as<uint32_t>();
@@ -98,9 +107,29 @@ struct convert<loot::PluginCleaningData> {
if (node["nav"])
nav = node["nav"].as<unsigned int>();
std::string utility = node["util"].as<std::string>();
std::string utility = node["utility"].as<std::string>();
rhs = loot::PluginCleaningData(crc, itm, ref, nav, utility);
std::vector<loot::MessageContent> info;
if (node["info"]) {
if (node["info"].IsSequence())
info = node["info"].as<std::vector<loot::MessageContent>>();
else {
info.push_back(loot::MessageContent(node["info"].as<std::string>(), loot::Language::Code::english));
}
}
//Check now that at least one item in info is English if there are multiple items.
if (info.size() > 1) {
bool found = false;
for (const auto &mc : info) {
if (mc.GetLanguage() == loot::Language::Code::english)
found = true;
}
if (!found)
throw RepresentationException(node.Mark(), "bad conversion: multilingual messages must contain an English info string");
}
rhs = loot::PluginCleaningData(crc, utility, info, itm, ref, nav);
return true;
}
+3 -5
View File
@@ -171,11 +171,11 @@ bool Plugin::IsValid(const std::string& filename, const Game& game) {
else
name = filename;
// Check that the file has a valid extension.
// Check that the file has a valid extension.
if (!boost::iends_with(name, ".esm") && !boost::iends_with(name, ".esp"))
return false;
// Add the ".ghost" file extension if the plugin is ghosted.
// Add the ".ghost" file extension if the plugin is ghosted.
boost::filesystem::path filepath = game.DataPath() / name;
if (!boost::filesystem::exists(filepath) && boost::filesystem::exists(filepath.string() + ".ghost"))
filepath += ".ghost";
@@ -199,7 +199,7 @@ uint32_t Plugin::Crc() const {
return crc_;
}
bool Plugin::CheckInstallValidity(const Game& game) {
void Plugin::CheckInstallValidity(const Game& game) {
BOOST_LOG_TRIVIAL(trace) << "Checking that the current install is valid according to " << Name() << "'s data.";
if (IsActive()) {
auto pluginExists = [](const Game& game, const std::string& file) {
@@ -236,8 +236,6 @@ bool Plugin::CheckInstallValidity(const Game& game) {
for (const auto &element : DirtyInfo()) {
messages_.push_back(element.AsMessage());
}
return !DirtyInfo().empty();
}
bool Plugin::LoadsArchive() const {
+3 -2
View File
@@ -58,8 +58,9 @@ public:
bool DoFormIDsOverlap(const Plugin& plugin) const;
std::set<libespm::FormId> OverlapFormIDs(const Plugin& plugin) const;
//Validity checks.
bool CheckInstallValidity(const Game& game); //Checks that reqs and masters are all present, and that no incs are present. Returns true if the plugin is dirty.
// Validity checks.
// Checks that reqs and masters are all present, and that no incs are present.
void CheckInstallValidity(const Game& game);
static bool IsValid(const std::string& filename, const Game& game);
bool operator < (const Plugin& rhs) const;
+2 -2
View File
@@ -963,7 +963,7 @@ YAML::Node QueryHandler::GenerateDerivedMetadata(const Plugin& file, const Plugi
}
//Also check install validity.
bool isDirty = tempPlugin.CheckInstallValidity(lootState_.getCurrentGame());
tempPlugin.CheckInstallValidity(lootState_.getCurrentGame());
// Now add to pluginNode.
YAML::Node pluginNode;
@@ -972,7 +972,7 @@ YAML::Node QueryHandler::GenerateDerivedMetadata(const Plugin& file, const Plugi
pluginNode["globalPriority"] = tempPlugin.GlobalPriority().getValue();
pluginNode["messages"] = tempPlugin.Messages();
pluginNode["tags"] = tempPlugin.Tags();
pluginNode["isDirty"] = isDirty;
pluginNode["isDirty"] = !tempPlugin.DirtyInfo().empty();
pluginNode["loadOrderIndex"] = lootState_.getCurrentGame().GetActiveLoadOrderIndex(tempPlugin.Name());
if (!tempPlugin.CleanInfo().empty()) {
+1 -1
View File
@@ -84,7 +84,7 @@ protected:
<< " content: '" << warningMessage << "'" << endl
<< " dirty:" << endl
<< " - crc: 0x7d22f9df" << endl
<< " util: TES4Edit" << endl
<< " utility: TES4Edit" << endl
<< " udr: 4" << endl
<< " - name: " << blankDifferentEsp << endl
<< " after:" << endl
@@ -34,11 +34,13 @@ namespace test {
class loot_get_masterlist_revision_test : public ApiGameOperationsTest {
protected:
loot_get_masterlist_revision_test() :
branch_("2.x"),
revisionId_("foo"),
revisionDate_("bar"),
isModified_(true),
updated_(false) {}
const std::string branch_;
const char * revisionId_;
const char * revisionDate_;
bool isModified_;
@@ -80,7 +82,7 @@ TEST_P(loot_get_masterlist_revision_test, shouldSucceedIfANonVersionControlledMa
}
TEST_P(loot_get_masterlist_revision_test, shouldOutputLongStringsAndBooleanFalseIfAVersionControlledMasterlistIsPresentAndGetShortIdParameterIsFalse) {
ASSERT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", &updated_));
ASSERT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
EXPECT_EQ(loot_ok, loot_get_masterlist_revision(db_, masterlistPath.string().c_str(), false, &revisionId_, &revisionDate_, &isModified_));
EXPECT_STRNE(NULL, revisionId_);
@@ -91,7 +93,7 @@ TEST_P(loot_get_masterlist_revision_test, shouldOutputLongStringsAndBooleanFalse
}
TEST_P(loot_get_masterlist_revision_test, shouldOutputShortStringsAndBooleanFalseIfAVersionControlledMasterlistIsPresentAndGetShortIdParameterIsTrue) {
ASSERT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", &updated_));
ASSERT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
EXPECT_EQ(loot_ok, loot_get_masterlist_revision(db_, masterlistPath.string().c_str(), false, &revisionId_, &revisionDate_, &isModified_));
EXPECT_STRNE(NULL, revisionId_);
@@ -103,7 +105,7 @@ TEST_P(loot_get_masterlist_revision_test, shouldOutputShortStringsAndBooleanFals
}
TEST_P(loot_get_masterlist_revision_test, shouldSucceedIfAnEditedVersionControlledMasterlistIsPresent) {
ASSERT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", &updated_));
ASSERT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
ASSERT_NO_THROW(GenerateMasterlist());
EXPECT_EQ(loot_ok, loot_get_masterlist_revision(db_, masterlistPath.string().c_str(), false, &revisionId_, &revisionDate_, &isModified_));
+13 -11
View File
@@ -34,6 +34,7 @@ namespace test {
class loot_update_masterlist_test : public ApiGameOperationsTest {
protected:
loot_update_masterlist_test() :
branch_("2.x"),
updated_(false) {}
inline void TearDown() {
@@ -43,6 +44,7 @@ protected:
ASSERT_NO_THROW(boost::filesystem::remove_all(masterlistPath.parent_path() / ".git"));
}
const std::string branch_;
bool updated_;
};
@@ -58,27 +60,27 @@ INSTANTIATE_TEST_CASE_P(,
loot_game_fo4));
TEST_P(loot_update_masterlist_test, shouldReturnAnInvalidArgsErrorIfAnyOfTheArgumentsAreNull) {
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(NULL, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, NULL, "https://github.com/loot/testing-metadata.git", "master", &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, masterlistPath.string().c_str(), NULL, "master", &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(NULL, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, NULL, "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, masterlistPath.string().c_str(), NULL, branch_.c_str(), &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", NULL, &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", NULL));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), NULL));
}
TEST_P(loot_update_masterlist_test, shouldReturnAnInvalidArgsErrorIfTheMasterlistPathGivenIsInvalid) {
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, ";//\?", "https://github.com/loot/testing-metadata.git", "master", &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, ";//\?", "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
}
TEST_P(loot_update_masterlist_test, shouldReturnAnInvalidArgsErrorIfTheMasterlistPathGivenIsEmpty) {
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, "", "https://github.com/loot/testing-metadata.git", "master", &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, "", "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
}
TEST_P(loot_update_masterlist_test, shouldReturnAGitErrorIfTheRepositoryUrlGivenCannotBeFound) {
EXPECT_EQ(loot_error_git_error, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/oblivion-does-not-exist.git", "master", &updated_));
EXPECT_EQ(loot_error_git_error, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/oblivion-does-not-exist.git", branch_.c_str(), &updated_));
}
TEST_P(loot_update_masterlist_test, shouldReturnAnInvalidArgsErrorIfTheRepositoryUrlGivenIsEmpty) {
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, masterlistPath.string().c_str(), "", "master", &updated_));
EXPECT_EQ(loot_error_invalid_args, loot_update_masterlist(db_, masterlistPath.string().c_str(), "", branch_.c_str(), &updated_));
}
TEST_P(loot_update_masterlist_test, shouldReturnAGitErrorIfTheRepositoryBranchGivenCannotBeFound) {
@@ -90,16 +92,16 @@ TEST_P(loot_update_masterlist_test, shouldReturnAnInvalidArgsErrorIfTheRepositor
}
TEST_P(loot_update_masterlist_test, shouldSucceedIfPassedValidParametersAndOutputTrueIfTheMasterlistWasUpdated) {
EXPECT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", &updated_));
EXPECT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
EXPECT_TRUE(updated_);
EXPECT_TRUE(boost::filesystem::exists(masterlistPath));
}
TEST_P(loot_update_masterlist_test, shouldSucceedIfCalledRepeatedlyButOnlyOutputTrueForTheFirstCall) {
EXPECT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", &updated_));
EXPECT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
EXPECT_TRUE(updated_);
EXPECT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", "master", &updated_));
EXPECT_EQ(loot_ok, loot_update_masterlist(db_, masterlistPath.string().c_str(), "https://github.com/loot/testing-metadata.git", branch_.c_str(), &updated_));
EXPECT_FALSE(updated_);
EXPECT_TRUE(boost::filesystem::exists(masterlistPath));
}
+1 -1
View File
@@ -68,7 +68,7 @@ protected:
<< " - name: '" << blankDifferentEsm << "'" << endl
<< " dirty:" << endl
<< " - crc: 0x7d22f9df" << endl
<< " util: 'TES4Edit'" << endl
<< " utility: 'TES4Edit'" << endl
<< " udr: 4";
return expectedContent.str();
+1 -1
View File
@@ -35,7 +35,7 @@ namespace test {
class MasterlistTest : public BaseGameTest {
protected:
MasterlistTest() :
repoBranch("master"),
repoBranch("2.x"),
repoUrl("https://github.com/loot/testing-metadata.git"),
masterlistPath(localPath / "masterlist.yaml") {}
@@ -32,7 +32,14 @@ along with LOOT. If not, see
namespace loot {
namespace test {
class PluginCleaningDataTest : public BaseGameTest {};
class PluginCleaningDataTest : public BaseGameTest {
protected:
PluginCleaningDataTest() : info_(std::vector<MessageContent>({
MessageContent("info", Language::Code::english),
})) {}
const std::vector<MessageContent> info_;
};
// 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.
@@ -48,69 +55,71 @@ TEST_P(PluginCleaningDataTest, defaultConstructorShouldLeaveAllCountsAtZeroAndTh
EXPECT_EQ(0, info.DeletedRefs());
EXPECT_EQ(0, info.DeletedNavmeshes());
EXPECT_TRUE(info.CleaningUtility().empty());
EXPECT_TRUE(info.Info().empty());
}
TEST_P(PluginCleaningDataTest, contentConstructorShouldStoreAllGivenData) {
PluginCleaningData info(0x12345678, 2, 10, 30, "cleaner");
PluginCleaningData info(0x12345678, "cleaner", info_, 2, 10, 30);
EXPECT_EQ(0x12345678, info.CRC());
EXPECT_EQ(2, info.ITMs());
EXPECT_EQ(10, info.DeletedRefs());
EXPECT_EQ(30, info.DeletedNavmeshes());
EXPECT_EQ("cleaner", info.CleaningUtility());
EXPECT_EQ(info_, info.Info());
}
TEST_P(PluginCleaningDataTest, asMessageShouldOutputAllNonZeroCounts) {
Message message = PluginCleaningData(0x12345678, 2, 10, 30, "cleaner").AsMessage();
Message message = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Contains 2 ITM records, 10 deleted references and 30 deleted navmeshes. Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found 2 ITM records, 10 deleted references and 30 deleted navmeshes. info", message.ChooseContent(Language::Code::english).GetText());
message = PluginCleaningData(0x12345678, 0, 0, 0, "cleaner").AsMessage();
message = PluginCleaningData(0x12345678, "cleaner", info_, 0, 0, 0).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found dirty edits. info", message.ChooseContent(Language::Code::english).GetText());
message = PluginCleaningData(0x12345678, 0, 10, 30, "cleaner").AsMessage();
message = PluginCleaningData(0x12345678, "cleaner", info_, 0, 10, 30).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Contains 10 deleted references and 30 deleted navmeshes. Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found 10 deleted references and 30 deleted navmeshes. info", message.ChooseContent(Language::Code::english).GetText());
message = PluginCleaningData(0x12345678, 0, 0, 30, "cleaner").AsMessage();
message = PluginCleaningData(0x12345678, "cleaner", info_, 0, 0, 30).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Contains 30 deleted navmeshes. Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found 30 deleted navmeshes. info", message.ChooseContent(Language::Code::english).GetText());
message = PluginCleaningData(0x12345678, 0, 10, 0, "cleaner").AsMessage();
message = PluginCleaningData(0x12345678, "cleaner", info_, 0, 10, 0).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Contains 10 deleted references. Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found 10 deleted references. info", message.ChooseContent(Language::Code::english).GetText());
message = PluginCleaningData(0x12345678, 2, 0, 30, "cleaner").AsMessage();
message = PluginCleaningData(0x12345678, "cleaner", info_, 2, 0, 30).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Contains 2 ITM records and 30 deleted navmeshes. Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found 2 ITM records and 30 deleted navmeshes. info", message.ChooseContent(Language::Code::english).GetText());
message = PluginCleaningData(0x12345678, 2, 0, 0, "cleaner").AsMessage();
message = PluginCleaningData(0x12345678, "cleaner", info_, 2, 0, 0).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Contains 2 ITM records. Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found 2 ITM records. info", message.ChooseContent(Language::Code::english).GetText());
message = PluginCleaningData(0x12345678, 2, 10, 0, "cleaner").AsMessage();
message = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 0).AsMessage();
EXPECT_EQ(Message::Type::warn, message.GetType());
EXPECT_EQ("Contains 2 ITM records and 10 deleted references. Clean with cleaner.", message.ChooseContent(Language::Code::english).GetText());
EXPECT_EQ("cleaner found 2 ITM records and 10 deleted references. info", message.ChooseContent(Language::Code::english).GetText());
}
TEST_P(PluginCleaningDataTest, dirtyInfoShouldBeEqualIfCrcValuesAreEqual) {
PluginCleaningData info1(0x12345678, 2, 10, 30, "cleaner1");
PluginCleaningData info2(0x12345678, 4, 20, 60, "cleaner2");
PluginCleaningData info1(0x12345678, "cleaner1", info_, 2, 10, 30);
PluginCleaningData info2(0x12345678, "cleaner2", info_, 4, 20, 60);
EXPECT_TRUE(info1 == info2);
info1 = PluginCleaningData(0x12345678, 2, 10, 30, "cleaner");
info2 = PluginCleaningData(0x87654321, 2, 10, 30, "cleaner");
info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30);
info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30);
EXPECT_FALSE(info1 == info2);
}
TEST_P(PluginCleaningDataTest, LessThanOperatorShouldCompareCrcValues) {
PluginCleaningData info1(0x12345678, 2, 10, 30, "cleaner1");
PluginCleaningData info2(0x12345678, 4, 20, 60, "cleaner2");
PluginCleaningData info1(0x12345678, "cleaner1", info_, 2, 10, 30);
PluginCleaningData info2(0x12345678, "cleaner2", info_, 4, 20, 60);
EXPECT_FALSE(info1 < info2);
EXPECT_FALSE(info2 < info1);
info1 = PluginCleaningData(0x12345678, 2, 10, 30, "cleaner");
info2 = PluginCleaningData(0x87654321, 2, 10, 30, "cleaner");
info1 = PluginCleaningData(0x12345678, "cleaner", info_, 2, 10, 30);
info2 = PluginCleaningData(0x87654321, "cleaner", info_, 2, 10, 30);
EXPECT_TRUE(info1 < info2);
EXPECT_FALSE(info2 < info1);
}
@@ -119,7 +128,7 @@ TEST_P(PluginCleaningDataTest, evalConditionShouldBeTrueIfTheCrcGivenMatchesTheR
Game game(GetParam());
game.SetGamePath(dataPath.parent_path());
PluginCleaningData dirtyInfo(blankEsmCrc, 2, 10, 30, "cleaner");
PluginCleaningData dirtyInfo(blankEsmCrc, "cleaner", info_, 2, 10, 30);
EXPECT_TRUE(dirtyInfo.EvalCondition(game, blankEsm));
}
@@ -127,7 +136,7 @@ TEST_P(PluginCleaningDataTest, evalConditionShouldBeFalseIfTheCrcGivenDoesNotMat
Game game(GetParam());
game.SetGamePath(dataPath.parent_path());
PluginCleaningData dirtyInfo(0xDEADBEEF, 2, 10, 30, "cleaner");
PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", info_, 2, 10, 30);
EXPECT_FALSE(dirtyInfo.EvalCondition(game, blankEsm));
}
@@ -139,51 +148,89 @@ TEST_P(PluginCleaningDataTest, evalConditionShouldBeFalseIfAnEmptyPluginFilename
EXPECT_FALSE(dirtyInfo.EvalCondition(game, ""));
}
TEST_P(PluginCleaningDataTest, chooseInfoShouldCreateADefaultContentObjectIfNoneExists) {
PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", std::vector<MessageContent>(), 2, 10, 30);
EXPECT_EQ(MessageContent(), dirtyInfo.ChooseInfo(Language::Code::english));
}
TEST_P(PluginCleaningDataTest, chooseInfoShouldLeaveTheContentUnchangedIfOnlyOneStringExists) {
PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", info_, 2, 10, 30);
EXPECT_EQ(info_[0], dirtyInfo.ChooseInfo(Language::Code::french));
EXPECT_EQ(info_[0], dirtyInfo.ChooseInfo(Language::Code::english));
}
TEST_P(PluginCleaningDataTest, chooseInfoShouldSelectTheEnglishStringIfNoStringExistsForTheGivenLanguage) {
MessageContent content("content1", Language::Code::english);
std::vector<MessageContent> info({
content,
MessageContent("content1", Language::Code::german),
});
PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", info, 2, 10, 30);
EXPECT_EQ(content, dirtyInfo.ChooseInfo(Language::Code::french));
}
TEST_P(PluginCleaningDataTest, chooseInfoShouldSelectTheStringForTheGivenLanguageIfOneExists) {
MessageContent french("content3", Language::Code::french);
std::vector<MessageContent> info({
MessageContent("content1", Language::Code::german),
MessageContent("content2", Language::Code::english),
french,
});
PluginCleaningData dirtyInfo(0xDEADBEEF, "cleaner", info, 2, 10, 30);
EXPECT_EQ(french, dirtyInfo.ChooseInfo(Language::Code::french));
}
TEST_P(PluginCleaningDataTest, emittingAsYamlShouldOutputAllNonZeroCounts) {
PluginCleaningData info(0x12345678, 2, 10, 30, "cleaner");
PluginCleaningData info(0x12345678, "cleaner", info_, 2, 10, 30);
YAML::Emitter emitter;
emitter << info;
EXPECT_STREQ("crc: 0x12345678\nutil: 'cleaner'\nitm: 2\nudr: 10\nnav: 30", emitter.c_str());
EXPECT_STREQ("crc: 0x12345678\nutility: 'cleaner'\ninfo: 'info'\nitm: 2\nudr: 10\nnav: 30", emitter.c_str());
}
TEST_P(PluginCleaningDataTest, emittingAsYamlShouldOmitAllZeroCounts) {
PluginCleaningData info(0x12345678, 0, 0, 0, "cleaner");
PluginCleaningData info(0x12345678, "cleaner", info_, 0, 0, 0);
YAML::Emitter emitter;
emitter << info;
EXPECT_STREQ("crc: 0x12345678\nutil: 'cleaner'", emitter.c_str());
EXPECT_STREQ("crc: 0x12345678\nutility: 'cleaner'\ninfo: 'info'", emitter.c_str());
}
TEST_P(PluginCleaningDataTest, encodingAsYamlShouldOmitAllZeroCountFields) {
PluginCleaningData info(0x12345678, 0, 0, 0, "cleaner");
PluginCleaningData info(0x12345678, "cleaner", info_, 0, 0, 0);
YAML::Node node;
node = info;
EXPECT_EQ(0x12345678, node["crc"].as<uint32_t>());
EXPECT_EQ("cleaner", node["util"].as<std::string>());
EXPECT_EQ("cleaner", node["utility"].as<std::string>());
EXPECT_EQ(info_, node["info"].as<std::vector<MessageContent>>());
EXPECT_FALSE(node["itm"]);
EXPECT_FALSE(node["udr"]);
EXPECT_FALSE(node["nav"]);
}
TEST_P(PluginCleaningDataTest, encodingAsYamlShouldOutputAllNonZeroCountFields) {
PluginCleaningData info(0x12345678, 2, 10, 30, "cleaner");
PluginCleaningData info(0x12345678, "cleaner", info_, 2, 10, 30);
YAML::Node node;
node = info;
EXPECT_EQ(0x12345678, node["crc"].as<uint32_t>());
EXPECT_EQ("cleaner", node["util"].as<std::string>());
EXPECT_EQ("cleaner", node["utility"].as<std::string>());
EXPECT_EQ(info_, node["info"].as<std::vector<MessageContent>>());
EXPECT_EQ(2, node["itm"].as<unsigned int>());
EXPECT_EQ(10, node["udr"].as<unsigned int>());
EXPECT_EQ(30, node["nav"].as<unsigned int>());
}
TEST_P(PluginCleaningDataTest, decodingFromYamlShouldLeaveMissingFieldsWithZeroValues) {
YAML::Node node = YAML::Load("{crc: 0x12345678, util: cleaner}");
YAML::Node node = YAML::Load("{crc: 0x12345678, utility: cleaner}");
PluginCleaningData info = node.as<PluginCleaningData>();
EXPECT_EQ(0x12345678, info.CRC());
EXPECT_TRUE(info.Info().empty());
EXPECT_EQ(0, info.ITMs());
EXPECT_EQ(0, info.DeletedRefs());
EXPECT_EQ(0, info.DeletedNavmeshes());
@@ -191,10 +238,11 @@ TEST_P(PluginCleaningDataTest, decodingFromYamlShouldLeaveMissingFieldsWithZeroV
}
TEST_P(PluginCleaningDataTest, decodingFromYamlShouldStoreAllNonZeroCounts) {
YAML::Node node = YAML::Load("{crc: 0x12345678, util: cleaner, itm: 2, udr: 10, nav: 30}");
YAML::Node node = YAML::Load("{crc: 0x12345678, utility: cleaner, info: info, itm: 2, udr: 10, nav: 30}");
PluginCleaningData info = node.as<PluginCleaningData>();
EXPECT_EQ(0x12345678, info.CRC());
EXPECT_EQ(info_, info.Info());
EXPECT_EQ(2, info.ITMs());
EXPECT_EQ(10, info.DeletedRefs());
EXPECT_EQ(30, info.DeletedNavmeshes());
@@ -31,7 +31,14 @@ along with LOOT. If not, see
namespace loot {
namespace test {
class PluginMetadataTest : public BaseGameTest {};
class PluginMetadataTest : public BaseGameTest {
protected:
PluginMetadataTest() : info_(std::vector<MessageContent>({
MessageContent("info", Language::Code::english),
})) {}
const std::vector<MessageContent> info_;
};
// 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.
@@ -254,8 +261,8 @@ TEST_P(PluginMetadataTest, mergeMetadataShouldMergeTags) {
TEST_P(PluginMetadataTest, mergeMetadataShouldMergeDirtyInfoData) {
PluginMetadata plugin1;
PluginMetadata plugin2;
PluginCleaningData info1(0x5, 1, 2, 3, "utility");
PluginCleaningData info2(0xA, 1, 2, 3, "utility");
PluginCleaningData info1(0x5, "utility", info_, 1, 2, 3);
PluginCleaningData info2(0xA, "utility", info_, 1, 2, 3);
plugin1.DirtyInfo({info1});
plugin2.DirtyInfo({info1, info2});
@@ -432,9 +439,9 @@ TEST_P(PluginMetadataTest, diffMetadataShouldOutputTagsThatAreNotCommonToBothInp
TEST_P(PluginMetadataTest, diffMetadataShouldOutputDirtyInfoObjectsThatAreNotCommonToBothInputPlugins) {
PluginMetadata plugin1;
PluginMetadata plugin2;
PluginCleaningData info1(0x5, 1, 2, 3, "utility");
PluginCleaningData info2(0xA, 1, 2, 3, "utility");
PluginCleaningData info3(0x1, 1, 2, 3, "utility");
PluginCleaningData info1(0x5, "utility", info_, 1, 2, 3);
PluginCleaningData info2(0xA, "utility", info_, 1, 2, 3);
PluginCleaningData info3(0x1, "utility", info_, 1, 2, 3);
plugin1.DirtyInfo({info1, info2});
plugin2.DirtyInfo({info1, info3});
@@ -588,9 +595,9 @@ TEST_P(PluginMetadataTest, newMetadataShouldOutputTagsThatAreNotCommonToBothInpu
TEST_P(PluginMetadataTest, newMetadataShouldOutputDirtyInfoObjectsThatAreNotCommonToBothInputPlugins) {
PluginMetadata plugin1;
PluginMetadata plugin2;
PluginCleaningData info1(0x5, 1, 2, 3, "utility");
PluginCleaningData info2(0xA, 1, 2, 3, "utility");
PluginCleaningData info3(0x1, 1, 2, 3, "utility");
PluginCleaningData info1(0x5, "utility", info_, 1, 2, 3);
PluginCleaningData info2(0xA, "utility", info_, 1, 2, 3);
PluginCleaningData info3(0x1, "utility", info_, 1, 2, 3);
plugin1.DirtyInfo({info1, info2});
plugin2.DirtyInfo({info1, info3});
@@ -647,8 +654,8 @@ TEST_P(PluginMetadataTest, evalAllConditionsShouldEvaluateAllMetadataConditions)
Tag tag2("Relev", true, "file(\"" + missingEsp + "\")");
plugin.Tags({tag1, tag2});
PluginCleaningData info1(blankEsmCrc, 1, 2, 3, "utility");
PluginCleaningData info2(0xDEADBEEF, 1, 2, 3, "utility");
PluginCleaningData info1(blankEsmCrc, "utility", info_, 1, 2, 3);
PluginCleaningData info2(0xDEADBEEF, "utility", info_, 1, 2, 3);
plugin.DirtyInfo({info1, info2});
plugin.CleanInfo({info1, info2});
@@ -734,7 +741,7 @@ TEST_P(PluginMetadataTest, hasNameOnlyShouldBeFalseIfTagsExist) {
TEST_P(PluginMetadataTest, hasNameOnlyShouldBeFalseIfDirtyInfoExists) {
PluginMetadata plugin(blankEsp);
plugin.DirtyInfo({PluginCleaningData(5, 0, 1, 2, "utility")});
plugin.DirtyInfo({PluginCleaningData(5, "utility", info_, 0, 1, 2)});
EXPECT_FALSE(plugin.HasNameOnly());
}
@@ -911,7 +918,7 @@ TEST_P(PluginMetadataTest, emittingAsYamlShouldOutputAPluginWithTagsCorrectly) {
TEST_P(PluginMetadataTest, emittingAsYamlShouldOutputAPluginWithDirtyInfoCorrectly) {
PluginMetadata plugin(blankEsp);
plugin.DirtyInfo({PluginCleaningData(5, 0, 1, 2, "utility")});
plugin.DirtyInfo({PluginCleaningData(5, "utility", info_, 0, 1, 2)});
YAML::Emitter emitter;
emitter << plugin;
@@ -919,7 +926,8 @@ TEST_P(PluginMetadataTest, emittingAsYamlShouldOutputAPluginWithDirtyInfoCorrect
EXPECT_STREQ("name: 'Blank.esp'\n"
"dirty:\n"
" - crc: 0x5\n"
" util: 'utility'\n"
" utility: 'utility'\n"
" info: 'info'\n"
" udr: 1\n"
" nav: 2", emitter.c_str());
}
@@ -934,7 +942,7 @@ TEST_P(PluginMetadataTest, emittingAsYamlShouldOutputAPluginWithCleanInfoCorrect
EXPECT_STREQ("name: 'Blank.esp'\n"
"clean:\n"
" - crc: 0x5\n"
" util: 'utility'", emitter.c_str());
" utility: 'utility'", emitter.c_str());
}
TEST_P(PluginMetadataTest, emittingAsYamlShouldOutputAPluginWithLocationsCorrectly) {
@@ -1057,7 +1065,7 @@ TEST_P(PluginMetadataTest, encodingAsYamlShouldSetTagFieldIfTagsExist) {
TEST_P(PluginMetadataTest, encodingAsYamlShouldSetDirtyFieldIfDirtyInfoExists) {
PluginMetadata plugin(blankEsp);
plugin.DirtyInfo({PluginCleaningData(5, 0, 1, 2, "utility")});
plugin.DirtyInfo({PluginCleaningData(5, "utility", info_, 0, 1, 2)});
YAML::Node node;
node = plugin;
@@ -1111,12 +1119,12 @@ TEST_P(PluginMetadataTest, decodingFromYamlShouldStoreAllGivenData) {
" - Relev\n"
"dirty:\n"
" - crc: 0x5\n"
" util: 'utility'\n"
" utility: 'utility'\n"
" udr: 1\n"
" nav: 2\n"
"clean:\n"
" - crc: 0x6\n"
" util: 'utility'\n"
" utility: 'utility'\n"
"url:\n"
" - 'http://www.example.com'");
PluginMetadata plugin = node.as<PluginMetadata>();
@@ -1140,7 +1148,7 @@ TEST_P(PluginMetadataTest, decodingFromYamlShouldStoreAllGivenData) {
Tag("Relev")
}), plugin.Tags());
EXPECT_EQ(std::set<PluginCleaningData>({
PluginCleaningData(5, 0, 1, 2, "utility")
PluginCleaningData(5, "utility", info_, 0, 1, 2)
}), plugin.DirtyInfo());
EXPECT_EQ(std::set<PluginCleaningData>({
PluginCleaningData(6, "utility")
@@ -1154,7 +1162,7 @@ TEST_P(PluginMetadataTest, decodingFromYamlWithDirtyInfoInARegexPluginMetadataOb
YAML::Node node = YAML::Load("name: 'Blank\\.esp'\n"
"dirty:\n"
" - crc: 0x5\n"
" util: 'utility'\n"
" utility: 'utility'\n"
" udr: 1\n"
" nav: 2");
@@ -1165,7 +1173,7 @@ TEST_P(PluginMetadataTest, decodingFromYamlWithCleanInfoInARegexPluginMetadataOb
YAML::Node node = YAML::Load("name: 'Blank\\.esp'\n"
"clean:\n"
" - crc: 0x5\n"
" util: 'utility'");
" utility: 'utility'");
EXPECT_THROW(node.as<PluginMetadata>(), YAML::RepresentationException);
}
@@ -1174,7 +1182,7 @@ TEST_P(PluginMetadataTest, decodingFromYamlWithAnInvalidRegexNameShouldThrow) {
YAML::Node node = YAML::Load("name: 'RagnvaldBook(Farengar(+Ragnvald)?)?\\.esp'\n"
"dirty:\n"
" - crc: 0x5\n"
" util: 'utility'\n"
" utility: 'utility'\n"
" udr: 1\n"
" nav: 2");
+15 -11
View File
@@ -280,7 +280,7 @@ TEST_P(PluginTest, checkInstallValidityShouldCheckThatRequirementsArePresent) {
File(blankEsp),
});
EXPECT_FALSE(plugin.CheckInstallValidity(game_));
plugin.CheckInstallValidity(game_);
EXPECT_EQ(std::list<Message>({
Message(Message::Type::error, "This plugin requires \"" + missingEsp + "\" to be installed, but it is missing."),
}), plugin.Messages());
@@ -293,30 +293,34 @@ TEST_P(PluginTest, checkInstallValidityShouldCheckThatIncompatibilitiesAreAbsent
File(masterFile),
});
EXPECT_FALSE(plugin.CheckInstallValidity(game_));
plugin.CheckInstallValidity(game_);
EXPECT_EQ(std::list<Message>({
Message(Message::Type::error, "This plugin is incompatible with \"" + masterFile + "\", but both are present."),
}), plugin.Messages());
}
TEST_P(PluginTest, checkInstallValidityShouldGenerateMessagesFromDirtyInfo) {
Plugin plugin(game_, blankEsm, false);
plugin.DirtyInfo({
PluginCleaningData(blankEsmCrc, 0, 1, 2, "utility1"),
PluginCleaningData(0xDEADBEEF, 0, 5, 10, "utility2"),
const std::vector<MessageContent> info = std::vector<MessageContent>({
MessageContent("info", Language::Code::english),
});
EXPECT_TRUE(plugin.CheckInstallValidity(game_));
Plugin plugin(game_, blankEsm, false);
plugin.DirtyInfo({
PluginCleaningData(blankEsmCrc, "utility1", info, 0, 1, 2),
PluginCleaningData(0xDEADBEEF, "utility2", info, 0, 5, 10),
});
plugin.CheckInstallValidity(game_);
EXPECT_EQ(std::list<Message>({
PluginCleaningData(blankEsmCrc, 0, 1, 2, "utility1").AsMessage(),
PluginCleaningData(0xDEADBEEF, 0, 5, 10, "utility2").AsMessage(),
PluginCleaningData(blankEsmCrc, "utility1", info, 0, 1, 2).AsMessage(),
PluginCleaningData(0xDEADBEEF, "utility2", info, 0, 5, 10).AsMessage(),
}), plugin.Messages());
}
TEST_P(PluginTest, checkInstallValidityShouldCheckIfAPluginsMastersAreAllPresentAndActiveIfNoFilterTagIsPresent) {
Plugin plugin(game_, blankDifferentMasterDependentEsp, false);
EXPECT_FALSE(plugin.CheckInstallValidity(game_));
plugin.CheckInstallValidity(game_);
EXPECT_EQ(std::list<Message>({
Message(Message::Type::error, "This plugin requires \"" + blankDifferentEsm + "\" to be active, but it is inactive."),
}), plugin.Messages());
@@ -326,7 +330,7 @@ TEST_P(PluginTest, checkInstallValidityShouldNotCheckIfAPluginsMastersAreAllActi
Plugin plugin(game_, blankDifferentMasterDependentEsp, false);
plugin.Tags({Tag("Filter")});
EXPECT_FALSE(plugin.CheckInstallValidity(game_));
plugin.CheckInstallValidity(game_);
EXPECT_TRUE(plugin.Messages().empty());
}
}