Support the new association defs interface

An xyz.openbmc_project version of the org.openbmc_project.Associations
interface was just created:
    xyz.openbmc_project.Association.Definitions
        property: Associations

Support this interface as well as the original org.openbmc one.

Change-Id: Idc5a0e5afab51ec96f18a2759446707146b077d1
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/src/main.cpp b/src/main.cpp
index 5dd4698..e6ea2bb 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,6 +5,7 @@
 #include <tinyxml2.h>
 
 #include <atomic>
+#include <boost/algorithm/string/case_conv.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/container/flat_map.hpp>
 #include <chrono>
@@ -128,7 +129,8 @@
 
 void do_associations(sdbusplus::asio::connection* system_bus,
                      sdbusplus::asio::object_server& objectServer,
-                     const std::string& processName, const std::string& path)
+                     const std::string& processName, const std::string& path,
+                     const std::string& assocDefIface)
 {
     system_bus->async_method_call(
         [&objectServer, path, processName](
@@ -146,7 +148,7 @@
                                associationOwners, associationInterfaces);
         },
         processName, path, "org.freedesktop.DBus.Properties", "Get",
-        ASSOCIATIONS_INTERFACE, "associations");
+        assocDefIface, getAssocDefPropName(assocDefIface));
 }
 
 void do_introspect(sdbusplus::asio::connection* system_bus,
@@ -194,14 +196,13 @@
                     continue;
                 }
 
-                std::string iface{iface_name};
-
                 thisPathMap[transaction->process_name].emplace(iface_name);
 
-                if (std::strcmp(iface_name, ASSOCIATIONS_INTERFACE) == 0)
+                if (isAssocDefIface(iface_name))
                 {
                     do_associations(system_bus, objectServer,
-                                    transaction->process_name, path);
+                                    transaction->process_name, path,
+                                    iface_name);
                 }
 
                 pElement = pElement->NextSiblingElement("interface");
@@ -543,7 +544,7 @@
                     continue;
                 }
 
-                if (interface == ASSOCIATIONS_INTERFACE)
+                if (isAssocDefIface(interface))
                 {
                     removeAssociation(obj_path.str, sender, server,
                                       associationOwners, associationInterfaces);
@@ -573,38 +574,52 @@
         interfacesRemovedHandler);
 
     std::function<void(sdbusplus::message::message & message)>
-        associationChangedHandler =
-            [&server, &name_owners](sdbusplus::message::message& message) {
-                std::string objectName;
-                boost::container::flat_map<
-                    std::string,
-                    sdbusplus::message::variant<std::vector<Association>>>
-                    values;
-                message.read(objectName, values);
-                auto findAssociations = values.find("associations");
-                if (findAssociations != values.end())
-                {
-                    std::vector<Association> associations =
-                        sdbusplus::message::variant_ns::get<
-                            std::vector<Association>>(findAssociations->second);
+        associationChangedHandler = [&server, &name_owners](
+                                        sdbusplus::message::message& message) {
+            std::string objectName;
+            boost::container::flat_map<
+                std::string,
+                sdbusplus::message::variant<std::vector<Association>>>
+                values;
+            message.read(objectName, values);
 
-                    std::string well_known;
-                    if (!getWellKnown(name_owners, message.get_sender(),
-                                      well_known))
-                    {
-                        return;
-                    }
-                    associationChanged(server, associations, message.get_path(),
-                                       well_known, associationOwners,
-                                       associationInterfaces);
+            auto prop =
+                std::find_if(values.begin(), values.end(), [](const auto& v) {
+                    using namespace boost::algorithm;
+                    return to_lower_copy(v.first) == "associations";
+                });
+
+            if (prop != values.end())
+            {
+                std::vector<Association> associations =
+                    sdbusplus::message::variant_ns::get<
+                        std::vector<Association>>(prop->second);
+
+                std::string well_known;
+                if (!getWellKnown(name_owners, message.get_sender(),
+                                  well_known))
+                {
+                    return;
                 }
-            };
-    sdbusplus::bus::match::match associationChanged(
+                associationChanged(server, associations, message.get_path(),
+                                   well_known, associationOwners,
+                                   associationInterfaces);
+            }
+        };
+    sdbusplus::bus::match::match assocChangedMatch(
         static_cast<sdbusplus::bus::bus&>(*system_bus),
         sdbusplus::bus::match::rules::interface(
             "org.freedesktop.DBus.Properties") +
             sdbusplus::bus::match::rules::member("PropertiesChanged") +
-            sdbusplus::bus::match::rules::argN(0, ASSOCIATIONS_INTERFACE),
+            sdbusplus::bus::match::rules::argN(0, assocDefsInterface),
+        associationChangedHandler);
+
+    sdbusplus::bus::match::match orgOpenbmcAssocChangedMatch(
+        static_cast<sdbusplus::bus::bus&>(*system_bus),
+        sdbusplus::bus::match::rules::interface(
+            "org.freedesktop.DBus.Properties") +
+            sdbusplus::bus::match::rules::member("PropertiesChanged") +
+            sdbusplus::bus::match::rules::argN(0, orgOpenBMCAssocDefsInterface),
         associationChangedHandler);
 
     std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
diff --git a/src/processing.cpp b/src/processing.cpp
index 6dd6493..3c7e7fb 100644
--- a/src/processing.cpp
+++ b/src/processing.cpp
@@ -64,8 +64,9 @@
         auto ifaces = pathIt->second.find(wellKnown);
         if (ifaces != pathIt->second.end())
         {
-            auto assoc = std::find(ifaces->second.begin(), ifaces->second.end(),
-                                   ASSOCIATIONS_INTERFACE);
+            auto assoc = std::find_if(
+                ifaces->second.begin(), ifaces->second.end(),
+                [](const auto& iface) { return isAssocDefIface(iface); });
             if (assoc != ifaces->second.end())
             {
                 removeAssociation(pathIt->first, wellKnown, server, assocOwners,
@@ -98,13 +99,13 @@
     {
         ifaceList[wellKnown].emplace(interfacePair.first);
 
-        if (interfacePair.first == ASSOCIATIONS_INTERFACE)
+        if (isAssocDefIface(interfacePair.first))
         {
             const sdbusplus::message::variant<std::vector<Association>>*
                 variantAssociations = nullptr;
             for (const auto& interface : interfacePair.second)
             {
-                if (interface.first == "associations")
+                if (interface.first == getAssocDefPropName(interfacePair.first))
                 {
                     variantAssociations = &(interface.second);
                 }
diff --git a/src/processing.hpp b/src/processing.hpp
index d5ed2b4..475133d 100644
--- a/src/processing.hpp
+++ b/src/processing.hpp
@@ -4,13 +4,44 @@
 
 #include <boost/container/flat_map.hpp>
 #include <boost/container/flat_set.hpp>
+#include <cassert>
 #include <string>
 
 /** @brief Define white list and black list data structure */
 using WhiteBlackList = boost::container::flat_set<std::string>;
 
-/** @brief Dbus interface which contains org.openbmc Associations */
-constexpr const char* ASSOCIATIONS_INTERFACE = "org.openbmc.Associations";
+/** @brief The old associations definitions interface */
+constexpr const char* orgOpenBMCAssocDefsInterface = "org.openbmc.Associations";
+/** @brief The new associations definitions interface */
+constexpr const char* assocDefsInterface =
+    "xyz.openbmc_project.Association.Definitions";
+
+/** @brief Says if the interface is the association definition interface.
+ * Supports either the new or old interface.
+ *
+ * @param[in] iface - the interface to check
+ * @return bool - if the interface is one of the association definition
+ *                ones.
+ */
+inline bool isAssocDefIface(std::string_view iface)
+{
+    return (iface == assocDefsInterface) ||
+           (iface == orgOpenBMCAssocDefsInterface);
+}
+
+/** @brief Returns the property name used by the defs iface.
+ *
+ * The old interface broke convention and used a lower case property
+ * name.  This was resolved with the new interface.
+ *
+ * @param[in] iface - the interface to check
+ * @return std::string - the property name
+ */
+inline std::string getAssocDefPropName(std::string_view iface)
+{
+    assert(isAssocDefIface(iface));
+    return (iface == assocDefsInterface) ? "Associations" : "associations";
+}
 
 /** @brief interface_map_type is the underlying datastructure the mapper uses.
  *
diff --git a/src/test/interfaces_added.cpp b/src/test/interfaces_added.cpp
index 6fbeead..a48c254 100644
--- a/src/test/interfaces_added.cpp
+++ b/src/test/interfaces_added.cpp
@@ -16,7 +16,8 @@
 
 // This is the data structure that comes in via the InterfacesAdded
 // signal
-InterfacesAdded createInterfacesAdded()
+InterfacesAdded createInterfacesAdded(const std::string& interface,
+                                      const std::string& property)
 {
     std::vector<Association> associations = {
         {"inventory", "error",
@@ -25,8 +26,8 @@
         associations};
     std::vector<std::pair<
         std::string, sdbusplus::message::variant<std::vector<Association>>>>
-        vecMethToAssoc = {{"associations", sdbVecAssoc}};
-    InterfacesAdded intfAdded = {{ASSOCIATIONS_INTERFACE, vecMethToAssoc}};
+        vecMethToAssoc = {{property, sdbVecAssoc}};
+    InterfacesAdded intfAdded = {{interface, vecMethToAssoc}};
     return intfAdded;
 }
 
@@ -36,7 +37,35 @@
     interface_map_type interfaceMap;
     AssociationOwnersType assocOwners;
     AssociationInterfaces assocInterfaces;
-    auto intfAdded = createInterfacesAdded();
+    auto intfAdded = createInterfacesAdded(
+        assocDefsInterface, getAssocDefPropName(assocDefsInterface));
+
+    processInterfaceAdded(interfaceMap, DEFAULT_SOURCE_PATH, intfAdded,
+                          DEFAULT_DBUS_SVC, assocOwners, assocInterfaces,
+                          *server);
+
+    // Interface map will get the following:
+    // /logging/entry/1 /logging/entry /logging/ /
+    // dump_InterfaceMapType(interfaceMap);
+    EXPECT_EQ(interfaceMap.size(), 4);
+
+    // New association ower created so ensure it now contains a single entry
+    // dump_AssociationOwnersType(assocOwners);
+    EXPECT_EQ(assocOwners.size(), 1);
+
+    // Ensure the 2 association interfaces were created
+    // dump_AssociationInterfaces(assocInterfaces);
+    EXPECT_EQ(assocInterfaces.size(), 2);
+}
+
+TEST_F(TestInterfacesAdded, OrgOpenBmcInterfacesAddedGoodPath)
+{
+    interface_map_type interfaceMap;
+    AssociationOwnersType assocOwners;
+    AssociationInterfaces assocInterfaces;
+    auto intfAdded = createInterfacesAdded(
+        orgOpenBMCAssocDefsInterface,
+        getAssocDefPropName(orgOpenBMCAssocDefsInterface));
 
     processInterfaceAdded(interfaceMap, DEFAULT_SOURCE_PATH, intfAdded,
                           DEFAULT_DBUS_SVC, assocOwners, assocInterfaces,
diff --git a/src/test/name_change.cpp b/src/test/name_change.cpp
index f4a9662..a6d18e4 100644
--- a/src/test/name_change.cpp
+++ b/src/test/name_change.cpp
@@ -33,7 +33,7 @@
         {":1.99", DEFAULT_DBUS_SVC}};
     std::string oldOwner = {":1.99"};
     boost::container::flat_set<std::string> assocInterfacesSet = {
-        ASSOCIATIONS_INTERFACE};
+        assocDefsInterface};
 
     // Build up these objects so that an associated interface will match
     // with the associated owner being removed