From e85ec7b1ceee6f80bb0eaf568ff6d36b5892623b Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 19 Apr 2020 19:52:16 +0100 Subject: [PATCH] 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. --- include/loot/database_interface.h | 10 +- include/loot/metadata/group.h | 56 +++- src/api/api_database.cpp | 83 ++--- src/api/api_database.h | 6 +- src/api/metadata/group.cpp | 37 +- src/api/metadata/yaml/group.h | 4 +- src/api/metadata_list.cpp | 70 +++- src/api/metadata_list.h | 6 +- src/api/sorting/group_sort.cpp | 29 +- src/api/sorting/group_sort.h | 8 +- src/api/sorting/plugin_graph.cpp | 22 +- src/api/sorting/plugin_graph.h | 2 +- src/api/sorting/plugin_sort.cpp | 7 +- .../api/interface/database_interface_test.h | 71 ++-- src/tests/api/internals/metadata/group_test.h | 317 ++++++++++++++++-- src/tests/api/internals/metadata_list_test.h | 39 +-- .../api/internals/sorting/group_sort_test.h | 28 +- 17 files changed, 590 insertions(+), 205 deletions(-) diff --git a/include/loot/database_interface.h b/include/loot/database_interface.h index 4b08e23d..a868089f 100644 --- a/include/loot/database_interface.h +++ b/include/loot/database_interface.h @@ -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 GetGroups( + virtual std::vector 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 GetUserGroups() const = 0; + virtual std::vector 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& groups) = 0; + virtual void SetUserGroups(const std::vector& groups) = 0; /** * @brief Get the "shortest" path between the two given groups according to diff --git a/include/loot/metadata/group.h b/include/loot/metadata/group.h index 0e264c09..5373c845 100644 --- a/include/loot/metadata/group.h +++ b/include/loot/metadata/group.h @@ -24,8 +24,8 @@ #ifndef LOOT_METADATA_GROUP #define LOOT_METADATA_GROUP -#include #include +#include #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& afterGroups = {}, + const std::vector& 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 GetAfterGroups() const; + LOOT_API std::vector GetAfterGroups() const; private: std::string name_; std::string description_; - std::unordered_set afterGroups_; + std::vector 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 { - /** - * 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()(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 diff --git a/src/api/api_database.cpp b/src/api/api_database.cpp index 58457a47..c498a09a 100644 --- a/src/api/api_database.cpp +++ b/src/api/api_database.cpp @@ -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) {} +ApiDatabase::ApiDatabase( + std::shared_ptr 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 ApiDatabase::GetGeneralMessages( return masterlistMessages; } -std::unordered_set ApiDatabase::GetGroups(bool includeUserMetadata) const { - if (!includeUserMetadata) { - auto groups = masterlist_.Groups(); +std::vector 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 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 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 ApiDatabase::GetUserGroups() const { +std::vector ApiDatabase::GetUserGroups() const { return userlist_.Groups(); } -void ApiDatabase::SetUserGroups(const std::unordered_set& groups) { +void ApiDatabase::SetUserGroups(const std::vector& groups) { userlist_.SetGroups(groups); } - -std::vector ApiDatabase::GetGroupsPath(const std::string& fromGroupName, - const std::string& toGroupName) const { +std::vector 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 ApiDatabase::GetPluginMetadata(const std::string& plugin, - bool includeUserMetadata, - bool evaluateConditions) const { +std::optional ApiDatabase::GetPluginMetadata( + const std::string& plugin, + bool includeUserMetadata, + bool evaluateConditions) const { auto metadata = masterlist_.FindPlugin(plugin); if (includeUserMetadata) { diff --git a/src/api/api_database.h b/src/api/api_database.h index 680bdbbc..e0b64516 100644 --- a/src/api/api_database.h +++ b/src/api/api_database.h @@ -67,9 +67,9 @@ struct ApiDatabase : public DatabaseInterface { std::vector GetGeneralMessages( bool evaluateConditions = false) const; - std::unordered_set GetGroups(bool includeUserMetadata = true) const; - std::unordered_set GetUserGroups() const; - void SetUserGroups(const std::unordered_set& groups); + std::vector GetGroups(bool includeUserMetadata = true) const; + std::vector GetUserGroups() const; + void SetUserGroups(const std::vector& groups); std::vector GetGroupsPath(const std::string& fromGroupName, const std::string& toGroupName) const; diff --git a/src/api/metadata/group.cpp b/src/api/metadata/group.cpp index 21eb0a14..b5cf80b3 100644 --- a/src/api/metadata/group.cpp +++ b/src/api/metadata/group.cpp @@ -30,21 +30,52 @@ namespace loot { Group::Group() : name_("default") {} Group::Group(const std::string& name, - const std::unordered_set& afterGroups, + const std::vector& 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 Group::GetAfterGroups() const { +std::vector 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); } } diff --git a/src/api/metadata/yaml/group.h b/src/api/metadata/yaml/group.h index 8e695473..5808bab8 100644 --- a/src/api/metadata/yaml/group.h +++ b/src/api/metadata/yaml/group.h @@ -64,14 +64,14 @@ struct convert { std::string name = node["name"].as(); std::string description; - std::unordered_set afterGroups; + std::vector afterGroups; if (node["description"]) { description = node["description"].as(); } if (node["after"]) { - afterGroups = node["after"].as>(); + afterGroups = node["after"].as>(); } rhs = loot::Group(name, afterGroups, description); diff --git a/src/api/metadata_list.cpp b/src/api/metadata_list.cpp index 6ea5ac65..6932a3f5 100644 --- a/src/api/metadata_list.cpp +++ b/src/api/metadata_list.cpp @@ -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>(); - if (metadataList["groups"]) - groups_ = metadataList["groups"].as>(); + std::unordered_set groupNames; + if (metadataList["groups"]) { + for (const auto& node : metadataList["groups"]) { + auto group = node.as(); + 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 MetadataList::Plugins() const { @@ -137,11 +158,29 @@ std::vector MetadataList::Messages() const { return messages_; } std::set MetadataList::BashTags() const { return bashTags_; } -std::unordered_set MetadataList::Groups() const { return groups_; } +std::vector MetadataList::Groups() const { + if (groups_.empty()) { + return {Group()}; + } -void MetadataList::SetGroups(const std::unordered_set& groups) { - groups_ = groups; - groups_.insert(Group()); + return groups_; +} + +void MetadataList::SetGroups(const std::vector& 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 diff --git a/src/api/metadata_list.h b/src/api/metadata_list.h index 23b6938c..46f99bd4 100644 --- a/src/api/metadata_list.h +++ b/src/api/metadata_list.h @@ -63,9 +63,9 @@ public: std::vector Plugins() const; std::vector Messages() const; std::set BashTags() const; - std::unordered_set Groups() const; + std::vector Groups() const; - void SetGroups(const std::unordered_set& groups); + void SetGroups(const std::vector& groups); // Merges multiple matching regex entries if any are found. std::optional FindPlugin(const std::string& pluginName) const; @@ -81,7 +81,7 @@ public: void EvalAllConditions(ConditionEvaluator& conditionEvaluator); protected: - std::unordered_set groups_; + std::vector groups_; std::set bashTags_; std::unordered_set plugins_; std::vector regexPlugins_; diff --git a/src/api/sorting/group_sort.cpp b/src/api/sorting/group_sort.cpp index f2d32cb5..ed2106c2 100644 --- a/src/api/sorting/group_sort.cpp +++ b/src/api/sorting/group_sort.cpp @@ -102,7 +102,7 @@ private: std::vector trail; }; -std::string join(const std::unordered_set& set) { +std::string joinVector(const std::vector& set) { std::string output; for (const auto& element : set) { output += element + ", "; @@ -111,8 +111,17 @@ std::string join(const std::unordered_set& set) { return output.substr(0, output.length() - 2); } -GroupGraph BuildGraph(const std::unordered_set& masterlistGroups, - const std::unordered_set& userGroups) { +std::string joinUnorderedSet(const std::unordered_set& set) { + std::string output; + for (const auto& element : set) { + output += element + ", "; + } + + return output.substr(0, output.length() - 2); +} + +GroupGraph BuildGraph(const std::vector& masterlistGroups, + const std::vector& userGroups) { GroupGraph graph; std::unordered_map groupVertices; @@ -127,7 +136,7 @@ GroupGraph BuildGraph(const std::unordered_set& 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& 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& masterlistGroups, } std::unordered_map> -GetTransitiveAfterGroups(const std::unordered_set& masterlistGroups, - const std::unordered_set& userGroups) { +GetTransitiveAfterGroups(const std::vector& masterlistGroups, + const std::vector& userGroups) { GroupGraph graph = BuildGraph(masterlistGroups, userGroups); auto logger = getLogger(); @@ -205,7 +214,7 @@ GetTransitiveAfterGroups(const std::unordered_set& 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 GetGroupsPath( - const std::unordered_set& masterlistGroups, - const std::unordered_set& userGroups, + const std::vector& masterlistGroups, + const std::vector& userGroups, const std::string& fromGroupName, const std::string& toGroupName) { GroupGraph graph = BuildGraph(masterlistGroups, userGroups); diff --git a/src/api/sorting/group_sort.h b/src/api/sorting/group_sort.h index 0adb83c4..a22a0e50 100644 --- a/src/api/sorting/group_sort.h +++ b/src/api/sorting/group_sort.h @@ -36,12 +36,12 @@ namespace loot { // Map entries are a group name and names of transitive load after groups. std::unordered_map> -GetTransitiveAfterGroups(const std::unordered_set& masterlistGroups, - const std::unordered_set& userGroups); +GetTransitiveAfterGroups(const std::vector& masterlistGroups, + const std::vector& userGroups); std::vector GetGroupsPath( - const std::unordered_set& masterlistGroups, - const std::unordered_set& userGroups, + const std::vector& masterlistGroups, + const std::vector& userGroups, const std::string& fromGroupName, const std::string& toGroupName); } diff --git a/src/api/sorting/plugin_graph.cpp b/src/api/sorting/plugin_graph.cpp index 05bfd88a..8dad5565 100644 --- a/src/api/sorting/plugin_graph.cpp +++ b/src/api/sorting/plugin_graph.cpp @@ -534,7 +534,7 @@ void ignorePlugin(const std::string& pluginName, std::unordered_set pathfinder( const Group& group, const std::string& targetGroupName, - const std::unordered_set& groups, + const std::unordered_map& groups, std::unordered_set 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 pathfinder( // all return values. std::unordered_set 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 pathfinder( } std::unordered_set getGroupsInPaths( - const std::unordered_set& groups, + const std::unordered_map& 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()); + groupIt->second, firstGroupName, groups, std::unordered_set()); groupsInPaths.erase(lastGroupName); return groupsInPaths; } -void PluginGraph::AddGroupEdges(const std::unordered_set& groups) { +void PluginGraph::AddGroupEdges(const std::unordered_map& groups) { std::vector> acyclicEdgePairs; std::map> groupPluginsToIgnore; diff --git a/src/api/sorting/plugin_graph.h b/src/api/sorting/plugin_graph.h index f8c1f618..0cd4d54e 100644 --- a/src/api/sorting/plugin_graph.h +++ b/src/api/sorting/plugin_graph.h @@ -86,7 +86,7 @@ public: void AddPluginVertices(Game& game, const std::vector& loadOrder); void AddSpecificEdges(); void AddHardcodedPluginEdges(Game& game); - void AddGroupEdges(const std::unordered_set& groups); + void AddGroupEdges(const std::unordered_map& groups); void AddOverlapEdges(); void AddTieBreakEdges(); diff --git a/src/api/sorting/plugin_sort.cpp b/src/api/sorting/plugin_sort.cpp index ae27966f..46dbfcda 100644 --- a/src/api/sorting/plugin_sort.cpp +++ b/src/api/sorting/plugin_sort.cpp @@ -51,7 +51,12 @@ std::vector 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 groups; + for (const auto& group : game.GetDatabase()->GetGroups()) { + groups.emplace(group.GetName(), group); + } + graph.AddGroupEdges(groups); graph.AddOverlapEdges(); graph.AddTieBreakEdges(); diff --git a/src/tests/api/interface/database_interface_test.h b/src/tests/api/interface/database_interface_test.h index 01c0c50f..bc9fcaac 100644 --- a/src/tests/api/interface/database_interface_test.h +++ b/src/tests/api/interface/database_interface_test.h @@ -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({"group1", "default"}), - groups.find(Group("group2"))->GetAfterGroups()); + EXPECT_EQ("group2", groups[2].GetName()); + EXPECT_EQ(std::vector({"group1", "default"}), + groups[2].GetAfterGroups()); - EXPECT_EQ(1, groups.count(Group("group3"))); - EXPECT_EQ(std::unordered_set({"group1"}), - groups.find(Group("group3"))->GetAfterGroups()); + EXPECT_EQ("group3", groups[3].GetName()); + EXPECT_EQ(std::vector({"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({"group1"}), - groups.find(Group("group2"))->GetAfterGroups()); + EXPECT_EQ("group2", groups[2].GetName()); + EXPECT_EQ(std::vector({"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({"default"}), - groups.find(Group("group2"))->GetAfterGroups()); + EXPECT_EQ("group2", groups[1].GetName()); + EXPECT_EQ(std::vector({"default"}), groups[1].GetAfterGroups()); - EXPECT_EQ(1, groups.count(Group("group3"))); - EXPECT_EQ(std::unordered_set({"group1"}), - groups.find(Group("group3"))->GetAfterGroups()); + EXPECT_EQ("group3", groups[2].GetName()); + EXPECT_EQ(std::vector({"group1"}), groups[2].GetAfterGroups()); } TEST_P( @@ -471,19 +468,19 @@ TEST_P( ASSERT_NO_THROW(db_->LoadLists(masterlistPath, userlistPath_)); - db_->SetUserGroups(std::unordered_set({ + db_->SetUserGroups(std::vector({ 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, diff --git a/src/tests/api/internals/metadata/group_test.h b/src/tests/api/internals/metadata/group_test.h index 2c1e1319..708b351b 100644 --- a/src/tests/api/internals/metadata/group_test.h +++ b/src/tests/api/internals/metadata/group_test.h @@ -50,34 +50,306 @@ TEST(Group, } TEST(Group, allArgsConstructorShouldStoreGivenValues) { - Group group( - "group1", std::unordered_set({"other_group"}), "test"); + Group group("group1", {"other_group"}, "test"); EXPECT_EQ("group1", group.GetName()); EXPECT_EQ("test", group.GetDescription()); - EXPECT_EQ(std::unordered_set({"other_group"}), + EXPECT_EQ(std::vector({"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({"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({"other_group"})); + Group group("group1", {"other_group"}); YAML::Node node; node = group; - std::unordered_set expectedAfterGroups = {"other_group"}; + std::vector expectedAfterGroups = {"other_group"}; EXPECT_EQ("group1", node["name"].as()); - EXPECT_EQ(expectedAfterGroups, - node["after"].as>()); + EXPECT_EQ(expectedAfterGroups, node["after"].as>()); } TEST(Group, decodingFromYamlShouldSetGivenName) { @@ -172,7 +443,7 @@ TEST(Group, decodingFromYamlShouldSetAfterGroupsIfAnyAreGiven) { YAML::Node node = YAML::Load("{name: group1, after: [ other_group ]}"); Group group = node.as(); - std::unordered_set expectedAfterGroups = {"other_group"}; + std::vector expectedAfterGroups = {"other_group"}; EXPECT_EQ("group1", group.GetName()); EXPECT_EQ(expectedAfterGroups, group.GetAfterGroups()); } diff --git a/src/tests/api/internals/metadata_list_test.h b/src/tests/api/internals/metadata_list_test.h index d04c5f05..3ee4a325 100644 --- a/src/tests/api/internals/metadata_list_test.h +++ b/src/tests/api/internals/metadata_list_test.h @@ -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({"group2"}), - groups.find(Group("group1"))->GetAfterGroups()); + EXPECT_EQ("group1", groups[1].GetName()); + EXPECT_EQ(std::vector({"group2"}), groups[1].GetAfterGroups()); - EXPECT_EQ(1, groups.count(Group("group2"))); - EXPECT_EQ(std::unordered_set({"default"}), - groups.find(Group("group2"))->GetAfterGroups()); + EXPECT_EQ("group2", groups[2].GetName()); + EXPECT_EQ(std::vector({"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({"earliest"}), - groups.find(Group("default"))->GetAfterGroups()); + EXPECT_EQ("default", groups[0].GetName()); + EXPECT_EQ(std::vector({"earliest"}), groups[0].GetAfterGroups()); } TEST_P(MetadataListTest, loadShouldThrowIfAnInvalidMetadataFileIsGiven) { @@ -222,9 +219,9 @@ TEST_P(MetadataListTest, saveShouldWriteTheLoadedMetadataToTheGivenFilePath) { EXPECT_EQ(std::set({"C.Climate", "Relev"}), metadataList.BashTags()); - EXPECT_EQ(std::unordered_set( - {Group("default"), Group("group1"), Group("group2")}), - metadataList.Groups()); + auto expectedGroups = + std::vector({Group("default"), Group("group1", {"group2"}), Group("group2", {"default"})}); + EXPECT_EQ(expectedGroups, metadataList.Groups()); EXPECT_EQ(std::vector({ 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( diff --git a/src/tests/api/internals/sorting/group_sort_test.h b/src/tests/api/internals/sorting/group_sort_test.h index 06be32ed..ec377628 100644 --- a/src/tests/api/internals/sorting/group_sort_test.h +++ b/src/tests/api/internals/sorting/group_sort_test.h @@ -35,7 +35,7 @@ along with LOOT. If not, see namespace loot { namespace test { TEST(GetTransitiveAfterGroups, shouldMapGroupsToTheirTransitiveAfterGroups) { - std::unordered_set groups( + std::vector 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 groups({Group("b", {"a"})}); + std::vector groups({Group("b", {"a"})}); EXPECT_THROW(GetTransitiveAfterGroups(groups, {}), UndefinedGroupError); } TEST(GetTransitiveAfterGroups, shouldThrowIfAfterGroupsAreCyclic) { - std::unordered_set groups({Group("a"), Group("b", {"a"})}); - std::unordered_set userGroups({Group("a", {"c"}), Group("c", {"b"})}); + std::vector groups({Group("a"), Group("b", {"a"})}); + std::vector userGroups({Group("a", {"c"}), Group("c", {"b"})}); try { GetTransitiveAfterGroups(groups, userGroups); @@ -101,16 +101,16 @@ TEST(GetTransitiveAfterGroups, shouldThrowIfAfterGroupsAreCyclic) { } TEST(GetGroupsPath, shouldThrowIfTheFromGroupDoesNotExist) { - std::unordered_set groups({Group("a"), Group("b", {"a"})}); - std::unordered_set userGroups({Group("a", {"c"}), Group("c", {"b"})}); + std::vector groups({Group("a"), Group("b", {"a"})}); + std::vector userGroups({Group("a", {"c"}), Group("c", {"b"})}); EXPECT_THROW(GetGroupsPath(groups, userGroups, "d", "a"), std::invalid_argument); } TEST(GetGroupsPath, shouldThrowIfTheToGroupDoesNotExist) { - std::unordered_set groups({Group("a"), Group("b", {"a"})}); - std::unordered_set userGroups({Group("a", {"c"}), Group("c", {"b"})}); + std::vector groups({Group("a"), Group("b", {"a"})}); + std::vector 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 groups({Group("a", {}), + std::vector groups({Group("a", {}), Group("b", {"a"}), Group("c", {"a"}), Group("d", {"c"}), @@ -131,7 +131,7 @@ TEST(GetGroupsPath, TEST(GetGroupsPath, shouldFindThePathWithTheLeastNumberOfEdgesInAMasterlistOnlyGraph) { - std::unordered_set groups({Group("a", {}), + std::vector groups({Group("a", {}), Group("b", {"a"}), Group("c", {"a"}), Group("d", {"c"}), @@ -152,11 +152,11 @@ TEST(GetGroupsPath, TEST(GetGroupsPath, shouldFindThePathWithTheLeastNumberOfEdgesThatContainsUserMetadata) { - std::unordered_set groups({Group("a", {}), + std::vector groups({Group("a", {}), Group("b", {"a"}), Group("c", {"a"}), Group("e", {"b"})}); - std::unordered_set userGroups({Group("d", {"c"}), Group("e", {"d"})}); + std::vector 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 groups({Group("a", {}), + std::vector groups({Group("a", {}), Group("b", {"a"}), Group("c", {"a"}), Group("e", {"b", "d"})}); - std::unordered_set userGroups({Group("d", {"c"})}); + std::vector userGroups({Group("d", {"c"})}); EXPECT_THROW(GetGroupsPath(groups, userGroups, "a", "e"), UndefinedGroupError);