mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Overhaul Group APIs
- Group equality now compares all fields - All other comparison operators are now overridden for Group - Group "after" metadata is now stored as a vector of strings instead of an unordered set, to preserve source metadata ordering and simplify implementing the comparison operator overloads - std::hash is no longer specialised for Group - DatabaseInterface::GetGroups() and DatabaseInterface::GetUserGroups() now return vectors of Groups instead of unordered sets. - DatabaseInterface::SetUserGroups() now takes a vector of Groups instead of an unordered set.
This commit is contained in:
@@ -190,16 +190,18 @@ public:
|
||||
* If true, any group metadata present in the userlist is included in
|
||||
* the returned metadata, otherwise the metadata returned only includes
|
||||
* metadata from the masterlist.
|
||||
* @returns An unordered set of Group objects.
|
||||
* @returns An vector of Group objects. Each Group's name is unique, if a
|
||||
* group has masterlist and user metadata the two are merged into a
|
||||
* single group object.
|
||||
*/
|
||||
virtual std::unordered_set<Group> GetGroups(
|
||||
virtual std::vector<Group> GetGroups(
|
||||
bool includeUserMetadata = true) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets the groups that are defined or extended in the loaded userlist.
|
||||
* @returns An unordered set of Group objects.
|
||||
*/
|
||||
virtual std::unordered_set<Group> GetUserGroups() const = 0;
|
||||
virtual std::vector<Group> GetUserGroups() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Sets the group definitions to store in the userlist, overwriting any
|
||||
@@ -207,7 +209,7 @@ public:
|
||||
* @param groups
|
||||
* The unordered set of Group objects to set.
|
||||
*/
|
||||
virtual void SetUserGroups(const std::unordered_set<Group>& groups) = 0;
|
||||
virtual void SetUserGroups(const std::vector<Group>& groups) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the "shortest" path between the two given groups according to
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
#ifndef LOOT_METADATA_GROUP
|
||||
#define LOOT_METADATA_GROUP
|
||||
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "loot/api_decorator.h"
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
* @return A Group object.
|
||||
*/
|
||||
LOOT_API explicit Group(const std::string& name,
|
||||
const std::unordered_set<std::string>& afterGroups = {},
|
||||
const std::vector<std::string>& afterGroups = {},
|
||||
const std::string& description = "");
|
||||
|
||||
/**
|
||||
@@ -63,6 +63,14 @@ public:
|
||||
*/
|
||||
LOOT_API bool operator==(const Group& rhs) const;
|
||||
|
||||
/**
|
||||
* A less-than operator implemented with no semantics so that Group objects
|
||||
* can be stored in sets.
|
||||
* @returns True if this Group is less than the given Group, false
|
||||
* otherwise.
|
||||
*/
|
||||
LOOT_API bool operator<(const Group& rhs) const;
|
||||
|
||||
/**
|
||||
* Get the name of the group.
|
||||
* @return The group's name.
|
||||
@@ -79,29 +87,43 @@ public:
|
||||
* Get the set of groups this group loads after.
|
||||
* @return A set of group names.
|
||||
*/
|
||||
LOOT_API std::unordered_set<std::string> GetAfterGroups() const;
|
||||
LOOT_API std::vector<std::string> GetAfterGroups() const;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string description_;
|
||||
std::unordered_set<std::string> afterGroups_;
|
||||
std::vector<std::string> afterGroups_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
/**
|
||||
* A specialisation of std::hash for loot::Group.
|
||||
* Check if two Group objects are not equal.
|
||||
* @returns True if the Group objects are not equal, false otherwise.
|
||||
*/
|
||||
template<>
|
||||
struct hash<loot::Group> {
|
||||
/**
|
||||
* Calculate a hash value for a loot::Group object.
|
||||
* @return The hash generated from the group's name.
|
||||
*/
|
||||
size_t operator()(const loot::Group& group) const {
|
||||
return hash<string>()(group.GetName());
|
||||
}
|
||||
};
|
||||
LOOT_API bool operator!=(const Group& lhs, const Group& rhs);
|
||||
|
||||
/**
|
||||
* Check if the first Group object is greater than the second Group
|
||||
* object.
|
||||
* @returns True if the second Group object is less than the first Group
|
||||
* object, false otherwise.
|
||||
*/
|
||||
LOOT_API bool operator>(const Group& lhs, const Group& rhs);
|
||||
|
||||
/**
|
||||
* Check if the first Group object is less than or equal to the second
|
||||
* Group object.
|
||||
* @returns True if the first Group object is not greater than the second
|
||||
* Group object, false otherwise.
|
||||
*/
|
||||
LOOT_API bool operator<=(const Group& lhs, const Group& rhs);
|
||||
|
||||
/**
|
||||
* Check if the first Group object is greater than or equal to the second
|
||||
* Group object.
|
||||
* @returns True if the first Group object is not less than the second
|
||||
* Group object, false otherwise.
|
||||
*/
|
||||
LOOT_API bool operator>=(const Group& lhs, const Group& rhs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+44
-39
@@ -30,14 +30,15 @@
|
||||
#include "api/game/game.h"
|
||||
#include "api/metadata/condition_evaluator.h"
|
||||
#include "api/metadata/yaml/plugin_metadata.h"
|
||||
#include "api/sorting/plugin_sort.h"
|
||||
#include "api/sorting/group_sort.h"
|
||||
#include "loot/metadata/group.h"
|
||||
#include "api/sorting/plugin_sort.h"
|
||||
#include "loot/exception/file_access_error.h"
|
||||
#include "loot/metadata/group.h"
|
||||
|
||||
namespace loot {
|
||||
ApiDatabase::ApiDatabase(std::shared_ptr<ConditionEvaluator> conditionEvaluator) :
|
||||
conditionEvaluator_(conditionEvaluator) {}
|
||||
ApiDatabase::ApiDatabase(
|
||||
std::shared_ptr<ConditionEvaluator> conditionEvaluator) :
|
||||
conditionEvaluator_(conditionEvaluator) {}
|
||||
|
||||
///////////////////////////////////
|
||||
// Database Loading Functions
|
||||
@@ -90,7 +91,8 @@ bool ApiDatabase::UpdateMasterlist(const std::filesystem::path& masterlistPath,
|
||||
const std::string& remoteURL,
|
||||
const std::string& remoteBranch) {
|
||||
if (!std::filesystem::is_directory(masterlistPath.parent_path()))
|
||||
throw std::invalid_argument("Given masterlist path \"" + masterlistPath.u8string() +
|
||||
throw std::invalid_argument("Given masterlist path \"" +
|
||||
masterlistPath.u8string() +
|
||||
"\" does not have a valid parent directory.");
|
||||
|
||||
Masterlist masterlist;
|
||||
@@ -110,7 +112,7 @@ MasterlistInfo ApiDatabase::GetMasterlistRevision(
|
||||
|
||||
bool ApiDatabase::IsLatestMasterlist(
|
||||
const std::filesystem::path& masterlist_path,
|
||||
const std::string& branch) const {
|
||||
const std::string& branch) const {
|
||||
return Masterlist::IsLatest(masterlist_path, branch);
|
||||
}
|
||||
|
||||
@@ -155,59 +157,62 @@ std::vector<Message> ApiDatabase::GetGeneralMessages(
|
||||
return masterlistMessages;
|
||||
}
|
||||
|
||||
std::unordered_set<Group> ApiDatabase::GetGroups(bool includeUserMetadata) const {
|
||||
if (!includeUserMetadata) {
|
||||
auto groups = masterlist_.Groups();
|
||||
std::vector<Group> ApiDatabase::GetGroups(
|
||||
bool includeUserMetadata) const {
|
||||
auto groups = masterlist_.Groups();
|
||||
|
||||
//Insert the default group in case the masterlist hasn't been loaded.
|
||||
groups.insert(Group());
|
||||
if (includeUserMetadata) {
|
||||
std::vector<Group> newGroups;
|
||||
for (const auto& userlistGroup : userlist_.Groups()) {
|
||||
auto groupIt = std::find_if(groups.begin(), groups.end(), [&](const Group& existingGroup) {
|
||||
return existingGroup.GetName() == userlistGroup.GetName();
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
if (groupIt == groups.end()) {
|
||||
newGroups.push_back(userlistGroup);
|
||||
} else {
|
||||
// Replace the masterlist group description with the userlist group
|
||||
// description if the latter is not empty.
|
||||
auto description = userlistGroup.GetDescription().empty()
|
||||
? groupIt->GetDescription()
|
||||
: userlistGroup.GetDescription();
|
||||
|
||||
std::unordered_set<Group> mergedGroups;
|
||||
auto afterGroups = groupIt->GetAfterGroups();
|
||||
auto userAfterGroups = userlistGroup.GetAfterGroups();
|
||||
afterGroups.insert(afterGroups.end(), userAfterGroups.begin(), userAfterGroups.end());
|
||||
|
||||
auto userlistGroups = userlist_.Groups();
|
||||
for (const auto& group : masterlist_.Groups()) {
|
||||
auto userlistGroup = userlistGroups.find(group);
|
||||
if (userlistGroup != userlistGroups.end()) {
|
||||
auto afterGroups = group.GetAfterGroups();
|
||||
auto userlistAfterGroups = userlistGroup->GetAfterGroups();
|
||||
|
||||
afterGroups.insert(userlistAfterGroups.begin(), userlistAfterGroups.end());
|
||||
mergedGroups.insert(Group(group.GetName(), afterGroups));
|
||||
} else {
|
||||
mergedGroups.insert(group);
|
||||
*groupIt = Group(userlistGroup.GetName(), afterGroups, description);
|
||||
}
|
||||
}
|
||||
|
||||
groups.insert(groups.end(), newGroups.cbegin(), newGroups.cend());
|
||||
}
|
||||
mergedGroups.insert(userlistGroups.begin(), userlistGroups.end());
|
||||
|
||||
// Insert the default group if it's not already present.
|
||||
mergedGroups.insert(Group());
|
||||
|
||||
return mergedGroups;
|
||||
return groups;
|
||||
}
|
||||
|
||||
std::unordered_set<Group> ApiDatabase::GetUserGroups() const {
|
||||
std::vector<Group> ApiDatabase::GetUserGroups() const {
|
||||
return userlist_.Groups();
|
||||
}
|
||||
|
||||
void ApiDatabase::SetUserGroups(const std::unordered_set<Group>& groups) {
|
||||
void ApiDatabase::SetUserGroups(const std::vector<Group>& groups) {
|
||||
userlist_.SetGroups(groups);
|
||||
}
|
||||
|
||||
|
||||
std::vector<Vertex> ApiDatabase::GetGroupsPath(const std::string& fromGroupName,
|
||||
const std::string& toGroupName) const {
|
||||
std::vector<Vertex> ApiDatabase::GetGroupsPath(
|
||||
const std::string& fromGroupName,
|
||||
const std::string& toGroupName) const {
|
||||
auto masterlistGroups = GetGroups(false);
|
||||
auto userGroups = GetUserGroups();
|
||||
|
||||
return loot::GetGroupsPath(masterlistGroups, userGroups, fromGroupName, toGroupName);
|
||||
return loot::GetGroupsPath(
|
||||
masterlistGroups, userGroups, fromGroupName, toGroupName);
|
||||
}
|
||||
|
||||
std::optional<PluginMetadata> ApiDatabase::GetPluginMetadata(const std::string& plugin,
|
||||
bool includeUserMetadata,
|
||||
bool evaluateConditions) const {
|
||||
std::optional<PluginMetadata> ApiDatabase::GetPluginMetadata(
|
||||
const std::string& plugin,
|
||||
bool includeUserMetadata,
|
||||
bool evaluateConditions) const {
|
||||
auto metadata = masterlist_.FindPlugin(plugin);
|
||||
|
||||
if (includeUserMetadata) {
|
||||
|
||||
@@ -67,9 +67,9 @@ struct ApiDatabase : public DatabaseInterface {
|
||||
std::vector<Message> GetGeneralMessages(
|
||||
bool evaluateConditions = false) const;
|
||||
|
||||
std::unordered_set<Group> GetGroups(bool includeUserMetadata = true) const;
|
||||
std::unordered_set<Group> GetUserGroups() const;
|
||||
void SetUserGroups(const std::unordered_set<Group>& groups);
|
||||
std::vector<Group> GetGroups(bool includeUserMetadata = true) const;
|
||||
std::vector<Group> GetUserGroups() const;
|
||||
void SetUserGroups(const std::vector<Group>& groups);
|
||||
std::vector<Vertex> GetGroupsPath(const std::string& fromGroupName,
|
||||
const std::string& toGroupName) const;
|
||||
|
||||
|
||||
@@ -30,21 +30,52 @@ namespace loot {
|
||||
Group::Group() : name_("default") {}
|
||||
|
||||
Group::Group(const std::string& name,
|
||||
const std::unordered_set<std::string>& afterGroups,
|
||||
const std::vector<std::string>& afterGroups,
|
||||
const std::string& description) :
|
||||
name_(name),
|
||||
afterGroups_(afterGroups),
|
||||
description_(description) {}
|
||||
|
||||
bool Group::operator==(const Group& rhs) const {
|
||||
return name_ == rhs.name_;
|
||||
return name_ == rhs.name_ && description_ == rhs.description_ &&
|
||||
afterGroups_ == rhs.afterGroups_;
|
||||
}
|
||||
|
||||
bool Group::operator<(const Group& rhs) const {
|
||||
if (name_ < rhs.name_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rhs.name_ < name_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (description_ < rhs.description_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rhs.description_ < description_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return afterGroups_ < rhs.afterGroups_;
|
||||
}
|
||||
|
||||
std::string Group::GetName() const { return name_; }
|
||||
|
||||
std::string Group::GetDescription() const { return description_; }
|
||||
|
||||
std::unordered_set<std::string> Group::GetAfterGroups() const {
|
||||
std::vector<std::string> Group::GetAfterGroups() const {
|
||||
return afterGroups_;
|
||||
}
|
||||
|
||||
bool operator!=(const Group& lhs, const Group& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator>(const Group& lhs, const Group& rhs) { return rhs < lhs; }
|
||||
|
||||
bool operator<=(const Group& lhs, const Group& rhs) { return !(lhs > rhs); }
|
||||
|
||||
bool operator>=(const Group& lhs, const Group& rhs) { return !(lhs < rhs); }
|
||||
}
|
||||
|
||||
@@ -64,14 +64,14 @@ struct convert<loot::Group> {
|
||||
|
||||
std::string name = node["name"].as<std::string>();
|
||||
std::string description;
|
||||
std::unordered_set<std::string> afterGroups;
|
||||
std::vector<std::string> afterGroups;
|
||||
|
||||
if (node["description"]) {
|
||||
description = node["description"].as<std::string>();
|
||||
}
|
||||
|
||||
if (node["after"]) {
|
||||
afterGroups = node["after"].as<std::unordered_set<std::string>>();
|
||||
afterGroups = node["after"].as<std::vector<std::string>>();
|
||||
}
|
||||
|
||||
rhs = loot::Group(name, afterGroups, description);
|
||||
|
||||
+54
-16
@@ -52,8 +52,8 @@ void MetadataList::Load(const std::filesystem::path& filepath) {
|
||||
in.close();
|
||||
|
||||
if (!metadataList.IsMap())
|
||||
throw FileAccessError("The root of the metadata file " + filepath.u8string() +
|
||||
" is not a YAML map.");
|
||||
throw FileAccessError("The root of the metadata file " +
|
||||
filepath.u8string() + " is not a YAML map.");
|
||||
|
||||
if (metadataList["plugins"]) {
|
||||
for (const auto& node : metadataList["plugins"]) {
|
||||
@@ -61,7 +61,7 @@ void MetadataList::Load(const std::filesystem::path& filepath) {
|
||||
if (plugin.IsRegexPlugin())
|
||||
regexPlugins_.push_back(plugin);
|
||||
else if (!plugins_.insert(plugin).second)
|
||||
throw FileAccessError("More than one entry exists for \"" +
|
||||
throw FileAccessError("More than one entry exists for plugin \"" +
|
||||
plugin.GetName() + "\"");
|
||||
}
|
||||
}
|
||||
@@ -71,10 +71,23 @@ void MetadataList::Load(const std::filesystem::path& filepath) {
|
||||
if (metadataList["bash_tags"])
|
||||
bashTags_ = metadataList["bash_tags"].as<std::set<std::string>>();
|
||||
|
||||
if (metadataList["groups"])
|
||||
groups_ = metadataList["groups"].as<std::unordered_set<Group>>();
|
||||
std::unordered_set<std::string> groupNames;
|
||||
if (metadataList["groups"]) {
|
||||
for (const auto& node : metadataList["groups"]) {
|
||||
auto group = node.as<Group>();
|
||||
if (groupNames.count(group.GetName()) != 0) {
|
||||
throw FileAccessError("More than one entry exists for group \"" +
|
||||
group.GetName() + "\"");
|
||||
}
|
||||
groups_.push_back(group);
|
||||
groupNames.insert(group.GetName());
|
||||
}
|
||||
}
|
||||
|
||||
groups_.insert(Group());
|
||||
auto defaultGroup = Group();
|
||||
if (groupNames.count(defaultGroup.GetName()) == 0) {
|
||||
groups_.insert(groups_.cbegin(), Group());
|
||||
}
|
||||
|
||||
if (logger) {
|
||||
logger->debug("File loaded successfully.");
|
||||
@@ -93,16 +106,19 @@ void MetadataList::Save(const std::filesystem::path& filepath) const {
|
||||
if (!bashTags_.empty())
|
||||
emitter << YAML::Key << "bash_tags" << YAML::Value << bashTags_;
|
||||
|
||||
if (!groups_.empty())
|
||||
if (!groups_.empty()) {
|
||||
emitter << YAML::Key << "groups" << YAML::Value << groups_;
|
||||
}
|
||||
|
||||
if (!messages_.empty())
|
||||
emitter << YAML::Key << "globals" << YAML::Value << messages_;
|
||||
|
||||
auto plugins = Plugins();
|
||||
std::sort(plugins.begin(), plugins.end(), [](const PluginMetadata& p1, const PluginMetadata& p2) {
|
||||
return CompareFilenames(p1.GetName(), p2.GetName()) < 0;
|
||||
});
|
||||
std::sort(plugins.begin(),
|
||||
plugins.end(),
|
||||
[](const PluginMetadata& p1, const PluginMetadata& p2) {
|
||||
return CompareFilenames(p1.GetName(), p2.GetName()) < 0;
|
||||
});
|
||||
|
||||
if (!plugins.empty())
|
||||
emitter << YAML::Key << "plugins" << YAML::Value << plugins;
|
||||
@@ -118,10 +134,15 @@ void MetadataList::Save(const std::filesystem::path& filepath) const {
|
||||
}
|
||||
|
||||
void MetadataList::Clear() {
|
||||
groups_.clear();
|
||||
bashTags_.clear();
|
||||
plugins_.clear();
|
||||
regexPlugins_.clear();
|
||||
messages_.clear();
|
||||
|
||||
unevaluatedPlugins_.clear();
|
||||
unevaluatedRegexPlugins_.clear();
|
||||
unevaluatedMessages_.clear();
|
||||
}
|
||||
|
||||
std::vector<PluginMetadata> MetadataList::Plugins() const {
|
||||
@@ -137,11 +158,29 @@ std::vector<Message> MetadataList::Messages() const { return messages_; }
|
||||
|
||||
std::set<std::string> MetadataList::BashTags() const { return bashTags_; }
|
||||
|
||||
std::unordered_set<Group> MetadataList::Groups() const { return groups_; }
|
||||
std::vector<Group> MetadataList::Groups() const {
|
||||
if (groups_.empty()) {
|
||||
return {Group()};
|
||||
}
|
||||
|
||||
void MetadataList::SetGroups(const std::unordered_set<Group>& groups) {
|
||||
groups_ = groups;
|
||||
groups_.insert(Group());
|
||||
return groups_;
|
||||
}
|
||||
|
||||
void MetadataList::SetGroups(const std::vector<Group>& groups) {
|
||||
// In case the default group is missing.
|
||||
auto defaultGroupName = Group().GetName();
|
||||
auto defaultGroupsExists =
|
||||
std::any_of(groups.cbegin(), groups.cend(), [&](const Group& group) {
|
||||
return group.GetName() == defaultGroupName;
|
||||
});
|
||||
|
||||
if (!defaultGroupsExists) {
|
||||
groups_.clear();
|
||||
groups_.push_back(Group());
|
||||
groups_.insert(groups_.end(), groups.begin(), groups.end());
|
||||
} else {
|
||||
groups_ = groups;
|
||||
}
|
||||
}
|
||||
|
||||
// Merges multiple matching regex entries if any are found.
|
||||
@@ -195,8 +234,7 @@ void MetadataList::AppendMessage(const Message& message) {
|
||||
messages_.push_back(message);
|
||||
}
|
||||
|
||||
void MetadataList::EvalAllConditions(
|
||||
ConditionEvaluator& conditionEvaluator) {
|
||||
void MetadataList::EvalAllConditions(ConditionEvaluator& conditionEvaluator) {
|
||||
if (unevaluatedPlugins_.empty())
|
||||
unevaluatedPlugins_.swap(plugins_);
|
||||
else
|
||||
|
||||
@@ -63,9 +63,9 @@ public:
|
||||
std::vector<PluginMetadata> Plugins() const;
|
||||
std::vector<Message> Messages() const;
|
||||
std::set<std::string> BashTags() const;
|
||||
std::unordered_set<Group> Groups() const;
|
||||
std::vector<Group> Groups() const;
|
||||
|
||||
void SetGroups(const std::unordered_set<Group>& groups);
|
||||
void SetGroups(const std::vector<Group>& groups);
|
||||
|
||||
// Merges multiple matching regex entries if any are found.
|
||||
std::optional<PluginMetadata> FindPlugin(const std::string& pluginName) const;
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
void EvalAllConditions(ConditionEvaluator& conditionEvaluator);
|
||||
|
||||
protected:
|
||||
std::unordered_set<Group> groups_;
|
||||
std::vector<Group> groups_;
|
||||
std::set<std::string> bashTags_;
|
||||
std::unordered_set<PluginMetadata> plugins_;
|
||||
std::vector<PluginMetadata> regexPlugins_;
|
||||
|
||||
@@ -102,7 +102,7 @@ private:
|
||||
std::vector<Vertex> trail;
|
||||
};
|
||||
|
||||
std::string join(const std::unordered_set<std::string>& set) {
|
||||
std::string joinVector(const std::vector<std::string>& set) {
|
||||
std::string output;
|
||||
for (const auto& element : set) {
|
||||
output += element + ", ";
|
||||
@@ -111,8 +111,17 @@ std::string join(const std::unordered_set<std::string>& set) {
|
||||
return output.substr(0, output.length() - 2);
|
||||
}
|
||||
|
||||
GroupGraph BuildGraph(const std::unordered_set<Group>& masterlistGroups,
|
||||
const std::unordered_set<Group>& userGroups) {
|
||||
std::string joinUnorderedSet(const std::unordered_set<std::string>& set) {
|
||||
std::string output;
|
||||
for (const auto& element : set) {
|
||||
output += element + ", ";
|
||||
}
|
||||
|
||||
return output.substr(0, output.length() - 2);
|
||||
}
|
||||
|
||||
GroupGraph BuildGraph(const std::vector<Group>& masterlistGroups,
|
||||
const std::vector<Group>& userGroups) {
|
||||
GroupGraph graph;
|
||||
|
||||
std::unordered_map<std::string, vertex_t> groupVertices;
|
||||
@@ -127,7 +136,7 @@ GroupGraph BuildGraph(const std::unordered_set<Group>& masterlistGroups,
|
||||
logger->trace(
|
||||
"Masterlist group \"{}\" directly loads after groups \"{}\"",
|
||||
group.GetName(),
|
||||
join(group.GetAfterGroups()));
|
||||
joinVector(group.GetAfterGroups()));
|
||||
}
|
||||
|
||||
auto vertex = groupVertices.at(group.GetName());
|
||||
@@ -153,7 +162,7 @@ GroupGraph BuildGraph(const std::unordered_set<Group>& masterlistGroups,
|
||||
if (logger) {
|
||||
logger->trace("Userlist group \"{}\" directly loads after groups \"{}\"",
|
||||
group.GetName(),
|
||||
join(group.GetAfterGroups()));
|
||||
joinVector(group.GetAfterGroups()));
|
||||
}
|
||||
|
||||
auto vertex = groupVertices.at(group.GetName());
|
||||
@@ -172,8 +181,8 @@ GroupGraph BuildGraph(const std::unordered_set<Group>& masterlistGroups,
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::unordered_set<std::string>>
|
||||
GetTransitiveAfterGroups(const std::unordered_set<Group>& masterlistGroups,
|
||||
const std::unordered_set<Group>& userGroups) {
|
||||
GetTransitiveAfterGroups(const std::vector<Group>& masterlistGroups,
|
||||
const std::vector<Group>& userGroups) {
|
||||
GroupGraph graph = BuildGraph(masterlistGroups, userGroups);
|
||||
|
||||
auto logger = getLogger();
|
||||
@@ -205,7 +214,7 @@ GetTransitiveAfterGroups(const std::unordered_set<Group>& masterlistGroups,
|
||||
if (logger) {
|
||||
logger->trace("Group \"{}\" transitively loads after groups \"{}\"",
|
||||
graph[vertex],
|
||||
join(visitedGroups));
|
||||
joinUnorderedSet(visitedGroups));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +238,8 @@ vertex_t GetVertexByName(const GroupGraph& graph, const std::string& name) {
|
||||
}
|
||||
|
||||
std::vector<Vertex> GetGroupsPath(
|
||||
const std::unordered_set<Group>& masterlistGroups,
|
||||
const std::unordered_set<Group>& userGroups,
|
||||
const std::vector<Group>& masterlistGroups,
|
||||
const std::vector<Group>& userGroups,
|
||||
const std::string& fromGroupName,
|
||||
const std::string& toGroupName) {
|
||||
GroupGraph graph = BuildGraph(masterlistGroups, userGroups);
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
namespace loot {
|
||||
// Map entries are a group name and names of transitive load after groups.
|
||||
std::unordered_map<std::string, std::unordered_set<std::string>>
|
||||
GetTransitiveAfterGroups(const std::unordered_set<Group>& masterlistGroups,
|
||||
const std::unordered_set<Group>& userGroups);
|
||||
GetTransitiveAfterGroups(const std::vector<Group>& masterlistGroups,
|
||||
const std::vector<Group>& userGroups);
|
||||
|
||||
std::vector<Vertex> GetGroupsPath(
|
||||
const std::unordered_set<Group>& masterlistGroups,
|
||||
const std::unordered_set<Group>& userGroups,
|
||||
const std::vector<Group>& masterlistGroups,
|
||||
const std::vector<Group>& userGroups,
|
||||
const std::string& fromGroupName,
|
||||
const std::string& toGroupName);
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ void ignorePlugin(const std::string& pluginName,
|
||||
std::unordered_set<std::string> pathfinder(
|
||||
const Group& group,
|
||||
const std::string& targetGroupName,
|
||||
const std::unordered_set<Group>& groups,
|
||||
const std::unordered_map<std::string, Group>& groups,
|
||||
std::unordered_set<std::string> visitedGroups) {
|
||||
// If the current group is the target group, return the set of groups in the
|
||||
// path leading to it.
|
||||
@@ -552,10 +552,14 @@ std::unordered_set<std::string> pathfinder(
|
||||
// all return values.
|
||||
std::unordered_set<std::string> mergedVisitedGroups;
|
||||
for (const auto& afterGroupName : group.GetAfterGroups()) {
|
||||
auto afterGroup = *groups.find(Group(afterGroupName));
|
||||
auto groupIt = groups.find(afterGroupName);
|
||||
if (groupIt == groups.end()) {
|
||||
throw std::runtime_error("Cannot find group \"" + afterGroupName +
|
||||
"\" during sorting.");
|
||||
}
|
||||
|
||||
auto recursedVisitedGroups =
|
||||
pathfinder(afterGroup, targetGroupName, groups, visitedGroups);
|
||||
pathfinder(groupIt->second, targetGroupName, groups, visitedGroups);
|
||||
|
||||
mergedVisitedGroups.insert(recursedVisitedGroups.begin(),
|
||||
recursedVisitedGroups.end());
|
||||
@@ -576,22 +580,26 @@ std::unordered_set<std::string> pathfinder(
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> getGroupsInPaths(
|
||||
const std::unordered_set<Group>& groups,
|
||||
const std::unordered_map<std::string, Group>& groups,
|
||||
const std::string& firstGroupName,
|
||||
const std::string& lastGroupName) {
|
||||
// Groups are linked in reverse order, i.e. firstGroup can be found from
|
||||
// lastGroup, but not the other way around.
|
||||
auto lastGroup = *groups.find(Group(lastGroupName));
|
||||
auto groupIt = groups.find(lastGroupName);
|
||||
if (groupIt == groups.end()) {
|
||||
throw std::runtime_error("Cannot find group \"" + lastGroupName +
|
||||
"\" during sorting.");
|
||||
}
|
||||
|
||||
auto groupsInPaths = pathfinder(
|
||||
lastGroup, firstGroupName, groups, std::unordered_set<std::string>());
|
||||
groupIt->second, firstGroupName, groups, std::unordered_set<std::string>());
|
||||
|
||||
groupsInPaths.erase(lastGroupName);
|
||||
|
||||
return groupsInPaths;
|
||||
}
|
||||
|
||||
void PluginGraph::AddGroupEdges(const std::unordered_set<Group>& groups) {
|
||||
void PluginGraph::AddGroupEdges(const std::unordered_map<std::string, Group>& groups) {
|
||||
std::vector<std::pair<vertex_t, vertex_t>> acyclicEdgePairs;
|
||||
std::map<std::string, std::unordered_set<std::string>> groupPluginsToIgnore;
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
void AddPluginVertices(Game& game, const std::vector<std::string>& loadOrder);
|
||||
void AddSpecificEdges();
|
||||
void AddHardcodedPluginEdges(Game& game);
|
||||
void AddGroupEdges(const std::unordered_set<Group>& groups);
|
||||
void AddGroupEdges(const std::unordered_map<std::string, Group>& groups);
|
||||
void AddOverlapEdges();
|
||||
void AddTieBreakEdges();
|
||||
|
||||
|
||||
@@ -51,7 +51,12 @@ std::vector<std::string> SortPlugins(
|
||||
// Now add the interactions between plugins to the graph as edges.
|
||||
graph.AddSpecificEdges();
|
||||
graph.AddHardcodedPluginEdges(game);
|
||||
graph.AddGroupEdges(game.GetDatabase()->GetGroups());
|
||||
|
||||
std::unordered_map<std::string, Group> groups;
|
||||
for (const auto& group : game.GetDatabase()->GetGroups()) {
|
||||
groups.emplace(group.GetName(), group);
|
||||
}
|
||||
graph.AddGroupEdges(groups);
|
||||
graph.AddOverlapEdges();
|
||||
graph.AddTieBreakEdges();
|
||||
|
||||
|
||||
@@ -380,31 +380,30 @@ TEST_P(DatabaseInterfaceTest,
|
||||
|
||||
auto groups = db_->GetGroups();
|
||||
|
||||
EXPECT_EQ(4, groups.size());
|
||||
ASSERT_EQ(4, groups.size());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("default")));
|
||||
EXPECT_TRUE(groups.find(Group("default"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_TRUE(groups[0].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group1")));
|
||||
EXPECT_TRUE(groups.find(Group("group1"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("group1", groups[1].GetName());
|
||||
EXPECT_TRUE(groups[1].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group2")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"group1", "default"}),
|
||||
groups.find(Group("group2"))->GetAfterGroups());
|
||||
EXPECT_EQ("group2", groups[2].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"group1", "default"}),
|
||||
groups[2].GetAfterGroups());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group3")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"group1"}),
|
||||
groups.find(Group("group3"))->GetAfterGroups());
|
||||
EXPECT_EQ("group3", groups[3].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"group1"}), groups[3].GetAfterGroups());
|
||||
}
|
||||
|
||||
TEST_P(DatabaseInterfaceTest,
|
||||
getGroupsShouldReturnDefaultGroupEvenIfNoMetadataIsLoaded) {
|
||||
auto groups = db_->GetGroups();
|
||||
|
||||
EXPECT_EQ(1, groups.size());
|
||||
ASSERT_EQ(1, groups.size());
|
||||
|
||||
EXPECT_EQ("default", groups.begin()->GetName());
|
||||
EXPECT_TRUE(groups.begin()->GetAfterGroups().empty());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_TRUE(groups[0].GetAfterGroups().empty());
|
||||
}
|
||||
|
||||
TEST_P(DatabaseInterfaceTest,
|
||||
@@ -416,17 +415,17 @@ TEST_P(DatabaseInterfaceTest,
|
||||
|
||||
auto groups = db_->GetGroups(false);
|
||||
|
||||
EXPECT_EQ(3, groups.size());
|
||||
ASSERT_EQ(3, groups.size());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("default")));
|
||||
EXPECT_TRUE(groups.find(Group("default"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_TRUE(groups[0].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group1")));
|
||||
EXPECT_TRUE(groups.find(Group("group1"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("group1", groups[1].GetName());
|
||||
EXPECT_TRUE(groups[1].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group2")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"group1"}),
|
||||
groups.find(Group("group2"))->GetAfterGroups());
|
||||
EXPECT_EQ("group2", groups[2].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"group1"}),
|
||||
groups[2].GetAfterGroups());
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
@@ -449,18 +448,16 @@ TEST_P(DatabaseInterfaceTest,
|
||||
|
||||
auto groups = db_->GetUserGroups();
|
||||
|
||||
EXPECT_EQ(3, groups.size());
|
||||
ASSERT_EQ(3, groups.size());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("default")));
|
||||
EXPECT_TRUE(groups.find(Group("default"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_TRUE(groups[0].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group2")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"default"}),
|
||||
groups.find(Group("group2"))->GetAfterGroups());
|
||||
EXPECT_EQ("group2", groups[1].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"default"}), groups[1].GetAfterGroups());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group3")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"group1"}),
|
||||
groups.find(Group("group3"))->GetAfterGroups());
|
||||
EXPECT_EQ("group3", groups[2].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"group1"}), groups[2].GetAfterGroups());
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
@@ -471,19 +468,19 @@ TEST_P(
|
||||
|
||||
ASSERT_NO_THROW(db_->LoadLists(masterlistPath, userlistPath_));
|
||||
|
||||
db_->SetUserGroups(std::unordered_set<Group>({
|
||||
db_->SetUserGroups(std::vector<Group>({
|
||||
Group("group4"),
|
||||
}));
|
||||
|
||||
auto groups = db_->GetUserGroups();
|
||||
|
||||
EXPECT_EQ(2, groups.size());
|
||||
ASSERT_EQ(2, groups.size());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("default")));
|
||||
EXPECT_TRUE(groups.find(Group("default"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_TRUE(groups[0].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group4")));
|
||||
EXPECT_TRUE(groups.find(Group("group4"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("group4", groups[1].GetName());
|
||||
EXPECT_TRUE(groups[1].GetAfterGroups().empty());
|
||||
}
|
||||
|
||||
TEST_P(DatabaseInterfaceTest,
|
||||
|
||||
@@ -50,34 +50,306 @@ TEST(Group,
|
||||
}
|
||||
|
||||
TEST(Group, allArgsConstructorShouldStoreGivenValues) {
|
||||
Group group(
|
||||
"group1", std::unordered_set<std::string>({"other_group"}), "test");
|
||||
Group group("group1", {"other_group"}, "test");
|
||||
|
||||
EXPECT_EQ("group1", group.GetName());
|
||||
EXPECT_EQ("test", group.GetDescription());
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"other_group"}),
|
||||
EXPECT_EQ(std::vector<std::string>({"other_group"}),
|
||||
group.GetAfterGroups());
|
||||
}
|
||||
|
||||
TEST(Group, groupsWithCaseInsensitiveEqualNameStringsShouldNotBeEqual) {
|
||||
Group group1("name");
|
||||
Group group2("Name");
|
||||
|
||||
EXPECT_FALSE(group1 == group2);
|
||||
}
|
||||
|
||||
TEST(Group, groupsWithCaseSensitiveEqualNameStringsShouldBeEqual) {
|
||||
Group group1("name");
|
||||
Group group2("name");
|
||||
TEST(Group, equalityShouldBeCaseSensitiveOnNameAndDescription) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 == group2);
|
||||
}
|
||||
|
||||
TEST(Group, groupsWithDifferentNamesShouldBeUnequal) {
|
||||
Group group1("name1");
|
||||
Group group2("name2");
|
||||
group1 = Group("name");
|
||||
group2 = Group("Name");
|
||||
|
||||
EXPECT_FALSE(group1 == group2);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {}, "Description");
|
||||
|
||||
EXPECT_FALSE(group1 == group2);
|
||||
|
||||
group1 = Group("name1");
|
||||
group2 = Group("name2");
|
||||
|
||||
EXPECT_FALSE(group1 == group2);
|
||||
|
||||
group1 = Group("name", {}, "description1");
|
||||
group2 = Group("name", {}, "description2");
|
||||
|
||||
EXPECT_FALSE(group1 == group2);
|
||||
}
|
||||
|
||||
TEST(Group, equalityShouldRequireEqualAfterGroups) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 == group2);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {"after1"}, "Description");
|
||||
|
||||
EXPECT_FALSE(group1 == group2);
|
||||
}
|
||||
|
||||
TEST(Group, inequalityShouldBeTheInverseOfEquality) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 != group2);
|
||||
|
||||
group1 = Group("name");
|
||||
group2 = Group("Name");
|
||||
|
||||
EXPECT_TRUE(group1 != group2);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {}, "Description");
|
||||
|
||||
EXPECT_TRUE(group1 != group2);
|
||||
|
||||
group1 = Group("name1");
|
||||
group2 = Group("name2");
|
||||
|
||||
EXPECT_TRUE(group1 != group2);
|
||||
|
||||
group1 = Group("name", {}, "description1");
|
||||
group2 = Group("name", {}, "description2");
|
||||
|
||||
EXPECT_TRUE(group1 != group2);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {"after1"}, "Description");
|
||||
|
||||
EXPECT_TRUE(group1 != group2);
|
||||
}
|
||||
|
||||
TEST(Group,
|
||||
lessThanOperatorShouldUseCaseSensitiveLexicographicalComparisonForNames) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
|
||||
group1 = Group("Name", {}, "description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
|
||||
group1 = Group("name1", {}, "description");
|
||||
group2 = Group("name2", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
}
|
||||
|
||||
TEST(
|
||||
Group,
|
||||
lessThanOperatorShouldUseCaseSensitiveLexicographicalComparisonForDescriptions) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
|
||||
group1 = Group("name", {}, "Description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
|
||||
group1 = Group("name", {}, "description1");
|
||||
group2 = Group("name", {}, "description2");
|
||||
|
||||
EXPECT_TRUE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
}
|
||||
|
||||
TEST(Group, lessThanOperatorShouldCompareAfterGroups) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
|
||||
group1 = Group("name", {"Group"}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
|
||||
group1 = Group("name", {"group1"}, "description");
|
||||
group2 = Group("name", {"group2"}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 < group2);
|
||||
EXPECT_FALSE(group2 < group1);
|
||||
}
|
||||
|
||||
TEST(
|
||||
Group,
|
||||
greaterThanOperatorShouldReturnTrueIfTheSecondGroupIsLessThanTheFirst) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_FALSE(group2 > group1);
|
||||
|
||||
group1 = Group("Name", {}, "description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_TRUE(group2 > group1);
|
||||
|
||||
group1 = Group("name1", {}, "description");
|
||||
group2 = Group("name2", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_TRUE(group2 > group1);
|
||||
|
||||
group1 = Group("name", {}, "Description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_TRUE(group2 > group1);
|
||||
|
||||
group1 = Group("name", {}, "description1");
|
||||
group2 = Group("name", {}, "description2");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_TRUE(group2 > group1);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_TRUE(group2 > group1);
|
||||
|
||||
group1 = Group("name", {"Group"}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_TRUE(group2 > group1);
|
||||
|
||||
group1 = Group("name", {"group1"}, "description");
|
||||
group2 = Group("name", {"group2"}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 > group2);
|
||||
EXPECT_TRUE(group2 > group1);
|
||||
}
|
||||
|
||||
TEST(Group,
|
||||
lessThanOrEqualToOperatorShouldReturnTrueIfTheFirstGroupIsNotGreaterThanTheSecond) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_TRUE(group2 <= group1);
|
||||
|
||||
group1 = Group("Name", {}, "description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_FALSE(group2 <= group1);
|
||||
|
||||
group1 = Group("name1", {}, "description");
|
||||
group2 = Group("name2", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_FALSE(group2 <= group1);
|
||||
|
||||
group1 = Group("name", {}, "Description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_FALSE(group2 <= group1);
|
||||
|
||||
group1 = Group("name", {}, "description1");
|
||||
group2 = Group("name", {}, "description2");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_FALSE(group2 <= group1);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_FALSE(group2 <= group1);
|
||||
|
||||
group1 = Group("name", {"Group"}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_FALSE(group2 <= group1);
|
||||
|
||||
group1 = Group("name", {"group1"}, "description");
|
||||
group2 = Group("name", {"group2"}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 <= group2);
|
||||
EXPECT_FALSE(group2 <= group1);
|
||||
}
|
||||
|
||||
TEST(Group,
|
||||
greaterThanOrEqualToOperatorShouldReturnTrueIfTheFirstGroupIsNotLessThanTheSecond) {
|
||||
Group group1("name", {}, "description");
|
||||
Group group2("name", {}, "description");
|
||||
|
||||
EXPECT_TRUE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
|
||||
group1 = Group("Name", {}, "description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
|
||||
group1 = Group("name1", {}, "description");
|
||||
group2 = Group("name2", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
|
||||
group1 = Group("name", {}, "Description");
|
||||
group2 = Group("name", {}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
|
||||
group1 = Group("name", {}, "description1");
|
||||
group2 = Group("name", {}, "description2");
|
||||
|
||||
EXPECT_FALSE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
|
||||
group1 = Group("name", {}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
|
||||
group1 = Group("name", {"Group"}, "description");
|
||||
group2 = Group("name", {"group"}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
|
||||
group1 = Group("name", {"group1"}, "description");
|
||||
group2 = Group("name", {"group2"}, "description");
|
||||
|
||||
EXPECT_FALSE(group1 >= group2);
|
||||
EXPECT_TRUE(group2 >= group1);
|
||||
}
|
||||
|
||||
TEST(Group, emittingAsYamlShouldOmitAfterKeyIfAfterGroupsIsEmpty) {
|
||||
@@ -102,7 +374,7 @@ TEST(Group, emittingAsYamlShouldIncludeDescriptionKeyIfDescriptionIsNotEmpty) {
|
||||
}
|
||||
|
||||
TEST(Group, emittingAsYamlShouldIncludeAfterKeyIfAfterGroupsIsNotEmpty) {
|
||||
Group group("group1", std::unordered_set<std::string>({"other_group"}));
|
||||
Group group("group1", {"other_group"});
|
||||
|
||||
YAML::Emitter emitter;
|
||||
emitter << group;
|
||||
@@ -142,14 +414,13 @@ TEST(Group, encodingAsYamlShouldOmitAfterKeyIfAfterGroupsIsEmpty) {
|
||||
}
|
||||
|
||||
TEST(Group, encodingAsYamlShouldIncludeAfterKeyIfAfterGroupsIsNotEmpty) {
|
||||
Group group("group1", std::unordered_set<std::string>({"other_group"}));
|
||||
Group group("group1", {"other_group"});
|
||||
YAML::Node node;
|
||||
node = group;
|
||||
|
||||
std::unordered_set<std::string> expectedAfterGroups = {"other_group"};
|
||||
std::vector<std::string> expectedAfterGroups = {"other_group"};
|
||||
EXPECT_EQ("group1", node["name"].as<std::string>());
|
||||
EXPECT_EQ(expectedAfterGroups,
|
||||
node["after"].as<std::unordered_set<std::string>>());
|
||||
EXPECT_EQ(expectedAfterGroups, node["after"].as<std::vector<std::string>>());
|
||||
}
|
||||
|
||||
TEST(Group, decodingFromYamlShouldSetGivenName) {
|
||||
@@ -172,7 +443,7 @@ TEST(Group, decodingFromYamlShouldSetAfterGroupsIfAnyAreGiven) {
|
||||
YAML::Node node = YAML::Load("{name: group1, after: [ other_group ]}");
|
||||
Group group = node.as<Group>();
|
||||
|
||||
std::unordered_set<std::string> expectedAfterGroups = {"other_group"};
|
||||
std::vector<std::string> expectedAfterGroups = {"other_group"};
|
||||
EXPECT_EQ("group1", group.GetName());
|
||||
EXPECT_EQ(expectedAfterGroups, group.GetAfterGroups());
|
||||
}
|
||||
|
||||
@@ -130,18 +130,16 @@ TEST_P(MetadataListTest, loadShouldLoadGroups) {
|
||||
|
||||
auto groups = metadataList.Groups();
|
||||
|
||||
EXPECT_EQ(3, groups.size());
|
||||
ASSERT_EQ(3, groups.size());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("default")));
|
||||
EXPECT_TRUE(groups.find(Group("default"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_TRUE(groups[0].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group1")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"group2"}),
|
||||
groups.find(Group("group1"))->GetAfterGroups());
|
||||
EXPECT_EQ("group1", groups[1].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"group2"}), groups[1].GetAfterGroups());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group2")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"default"}),
|
||||
groups.find(Group("group2"))->GetAfterGroups());
|
||||
EXPECT_EQ("group2", groups[2].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"default"}), groups[2].GetAfterGroups());
|
||||
}
|
||||
|
||||
TEST_P(MetadataListTest, loadYamlParsingShouldSupportMergeKeys) {
|
||||
@@ -164,11 +162,10 @@ TEST_P(MetadataListTest, loadYamlParsingShouldSupportMergeKeys) {
|
||||
|
||||
auto groups = metadataList.Groups();
|
||||
|
||||
EXPECT_EQ(1, groups.size());
|
||||
ASSERT_EQ(1, groups.size());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("default")));
|
||||
EXPECT_EQ(std::unordered_set<std::string>({"earliest"}),
|
||||
groups.find(Group("default"))->GetAfterGroups());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_EQ(std::vector<std::string>({"earliest"}), groups[0].GetAfterGroups());
|
||||
}
|
||||
|
||||
TEST_P(MetadataListTest, loadShouldThrowIfAnInvalidMetadataFileIsGiven) {
|
||||
@@ -222,9 +219,9 @@ TEST_P(MetadataListTest, saveShouldWriteTheLoadedMetadataToTheGivenFilePath) {
|
||||
EXPECT_EQ(std::set<std::string>({"C.Climate", "Relev"}),
|
||||
metadataList.BashTags());
|
||||
|
||||
EXPECT_EQ(std::unordered_set<Group>(
|
||||
{Group("default"), Group("group1"), Group("group2")}),
|
||||
metadataList.Groups());
|
||||
auto expectedGroups =
|
||||
std::vector<Group>({Group("default"), Group("group1", {"group2"}), Group("group2", {"default"})});
|
||||
EXPECT_EQ(expectedGroups, metadataList.Groups());
|
||||
|
||||
EXPECT_EQ(std::vector<Message>({
|
||||
Message(MessageType::say, "A global message."),
|
||||
@@ -271,13 +268,13 @@ TEST_P(MetadataListTest, setGroupsShouldReplaceExistingGroups) {
|
||||
|
||||
auto groups = metadataList.Groups();
|
||||
|
||||
EXPECT_EQ(2, groups.size());
|
||||
ASSERT_EQ(2, groups.size());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("default")));
|
||||
EXPECT_TRUE(groups.find(Group("default"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("default", groups[0].GetName());
|
||||
EXPECT_TRUE(groups[0].GetAfterGroups().empty());
|
||||
|
||||
EXPECT_EQ(1, groups.count(Group("group4")));
|
||||
EXPECT_TRUE(groups.find(Group("group4"))->GetAfterGroups().empty());
|
||||
EXPECT_EQ("group4", groups[1].GetName());
|
||||
EXPECT_TRUE(groups[1].GetAfterGroups().empty());
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
|
||||
@@ -35,7 +35,7 @@ along with LOOT. If not, see
|
||||
namespace loot {
|
||||
namespace test {
|
||||
TEST(GetTransitiveAfterGroups, shouldMapGroupsToTheirTransitiveAfterGroups) {
|
||||
std::unordered_set<Group> groups(
|
||||
std::vector<Group> groups(
|
||||
{Group("a"), Group("b", {"a"}), Group("c", {"b"})});
|
||||
|
||||
auto mapped = GetTransitiveAfterGroups(groups, {});
|
||||
@@ -46,14 +46,14 @@ TEST(GetTransitiveAfterGroups, shouldMapGroupsToTheirTransitiveAfterGroups) {
|
||||
}
|
||||
|
||||
TEST(GetTransitiveAfterGroups, shouldThrowIfAnAfterGroupDoesNotExist) {
|
||||
std::unordered_set<Group> groups({Group("b", {"a"})});
|
||||
std::vector<Group> groups({Group("b", {"a"})});
|
||||
|
||||
EXPECT_THROW(GetTransitiveAfterGroups(groups, {}), UndefinedGroupError);
|
||||
}
|
||||
|
||||
TEST(GetTransitiveAfterGroups, shouldThrowIfAfterGroupsAreCyclic) {
|
||||
std::unordered_set<Group> groups({Group("a"), Group("b", {"a"})});
|
||||
std::unordered_set<Group> userGroups({Group("a", {"c"}), Group("c", {"b"})});
|
||||
std::vector<Group> groups({Group("a"), Group("b", {"a"})});
|
||||
std::vector<Group> userGroups({Group("a", {"c"}), Group("c", {"b"})});
|
||||
|
||||
try {
|
||||
GetTransitiveAfterGroups(groups, userGroups);
|
||||
@@ -101,16 +101,16 @@ TEST(GetTransitiveAfterGroups, shouldThrowIfAfterGroupsAreCyclic) {
|
||||
}
|
||||
|
||||
TEST(GetGroupsPath, shouldThrowIfTheFromGroupDoesNotExist) {
|
||||
std::unordered_set<Group> groups({Group("a"), Group("b", {"a"})});
|
||||
std::unordered_set<Group> userGroups({Group("a", {"c"}), Group("c", {"b"})});
|
||||
std::vector<Group> groups({Group("a"), Group("b", {"a"})});
|
||||
std::vector<Group> userGroups({Group("a", {"c"}), Group("c", {"b"})});
|
||||
|
||||
EXPECT_THROW(GetGroupsPath(groups, userGroups, "d", "a"),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(GetGroupsPath, shouldThrowIfTheToGroupDoesNotExist) {
|
||||
std::unordered_set<Group> groups({Group("a"), Group("b", {"a"})});
|
||||
std::unordered_set<Group> userGroups({Group("a", {"c"}), Group("c", {"b"})});
|
||||
std::vector<Group> groups({Group("a"), Group("b", {"a"})});
|
||||
std::vector<Group> userGroups({Group("a", {"c"}), Group("c", {"b"})});
|
||||
|
||||
EXPECT_THROW(GetGroupsPath(groups, userGroups, "a", "d"),
|
||||
std::invalid_argument);
|
||||
@@ -118,7 +118,7 @@ TEST(GetGroupsPath, shouldThrowIfTheToGroupDoesNotExist) {
|
||||
|
||||
TEST(GetGroupsPath,
|
||||
shouldReturnAnEmptyVectorIfThereIsNoPathBetweenTheTwoGroups) {
|
||||
std::unordered_set<Group> groups({Group("a", {}),
|
||||
std::vector<Group> groups({Group("a", {}),
|
||||
Group("b", {"a"}),
|
||||
Group("c", {"a"}),
|
||||
Group("d", {"c"}),
|
||||
@@ -131,7 +131,7 @@ TEST(GetGroupsPath,
|
||||
|
||||
TEST(GetGroupsPath,
|
||||
shouldFindThePathWithTheLeastNumberOfEdgesInAMasterlistOnlyGraph) {
|
||||
std::unordered_set<Group> groups({Group("a", {}),
|
||||
std::vector<Group> groups({Group("a", {}),
|
||||
Group("b", {"a"}),
|
||||
Group("c", {"a"}),
|
||||
Group("d", {"c"}),
|
||||
@@ -152,11 +152,11 @@ TEST(GetGroupsPath,
|
||||
|
||||
TEST(GetGroupsPath,
|
||||
shouldFindThePathWithTheLeastNumberOfEdgesThatContainsUserMetadata) {
|
||||
std::unordered_set<Group> groups({Group("a", {}),
|
||||
std::vector<Group> groups({Group("a", {}),
|
||||
Group("b", {"a"}),
|
||||
Group("c", {"a"}),
|
||||
Group("e", {"b"})});
|
||||
std::unordered_set<Group> userGroups({Group("d", {"c"}), Group("e", {"d"})});
|
||||
std::vector<Group> userGroups({Group("d", {"c"}), Group("e", {"d"})});
|
||||
|
||||
auto path = GetGroupsPath(groups, userGroups, "a", "e");
|
||||
|
||||
@@ -175,11 +175,11 @@ TEST(GetGroupsPath,
|
||||
}
|
||||
|
||||
TEST(GetGroupsPath, shouldThrowIfMasterlistGroupLoadsAfterAUserlistGroup) {
|
||||
std::unordered_set<Group> groups({Group("a", {}),
|
||||
std::vector<Group> groups({Group("a", {}),
|
||||
Group("b", {"a"}),
|
||||
Group("c", {"a"}),
|
||||
Group("e", {"b", "d"})});
|
||||
std::unordered_set<Group> userGroups({Group("d", {"c"})});
|
||||
std::vector<Group> userGroups({Group("d", {"c"})});
|
||||
|
||||
EXPECT_THROW(GetGroupsPath(groups, userGroups, "a", "e"),
|
||||
UndefinedGroupError);
|
||||
|
||||
Reference in New Issue
Block a user