utils: Add function to check if PSU is associated

Add a helper function to check if a PSU is in an association list, which
will be used in future commits to check if a PSU is running a software
image.

Tested: added unit test case and verify it passes.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I99b5d3d8d09f8e09a1eb42ba104a4804b7214cf1
diff --git a/src/utils.cpp b/src/utils.cpp
index 171de29..8d782ab 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -4,6 +4,7 @@
 
 #include <openssl/sha.h>
 
+#include <algorithm>
 #include <fstream>
 #include <phosphor-logging/log.hpp>
 #include <sstream>
@@ -169,6 +170,15 @@
     return (rc == 0) ? r : "";
 }
 
+bool Utils::isAssociated(const std::string& psuInventoryPath,
+                         const AssociationList& assocs) const
+{
+    return std::find_if(assocs.begin(), assocs.end(),
+                        [&psuInventoryPath](const auto& assoc) {
+                            return psuInventoryPath == std::get<2>(assoc);
+                        }) != assocs.end();
+}
+
 any Utils::getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
                            const char* path, const char* interface,
                            const char* propertyName) const
diff --git a/src/utils.hpp b/src/utils.hpp
index bb5f13f..ae08156 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "types.hpp"
+
 #include <experimental/any>
 #include <sdbusplus/bus.hpp>
 #include <set>
@@ -11,6 +13,7 @@
 
 class UtilsInterface;
 
+using AssociationList = phosphor::software::updater::AssociationList;
 // Due to a libstdc++ bug, we got compile error using std::any with gmock.
 // A temporary workaround is to use std::experimental::any.
 // See details in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90415
@@ -91,6 +94,16 @@
  */
 std::string getLatestVersion(const std::set<std::string>& versions);
 
+/** @brief Check if the PSU is associated
+ *
+ * @param[in] psuInventoryPath - The PSU inventory path
+ * @param[in] assocs - The list of associations
+ *
+ * @return true if the psu is in the association list
+ */
+bool isAssociated(const std::string& psuInventoryPath,
+                  const AssociationList& assocs);
+
 /**
  * @brief The interface for utils
  */
@@ -119,6 +132,9 @@
     virtual std::string
         getLatestVersion(const std::set<std::string>& versions) const = 0;
 
+    virtual bool isAssociated(const std::string& psuInventoryPath,
+                              const AssociationList& assocs) const = 0;
+
     virtual any getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
                                 const char* path, const char* interface,
                                 const char* propertyName) const = 0;
@@ -155,6 +171,9 @@
     std::string
         getLatestVersion(const std::set<std::string>& versions) const override;
 
+    bool isAssociated(const std::string& psuInventoryPath,
+                      const AssociationList& assocs) const override;
+
     any getPropertyImpl(sdbusplus::bus::bus& bus, const char* service,
                         const char* path, const char* interface,
                         const char* propertyName) const override;
@@ -193,6 +212,12 @@
     return getUtils().getLatestVersion(versions);
 }
 
+inline bool isAssociated(const std::string& psuInventoryPath,
+                         const AssociationList& assocs)
+{
+    return getUtils().isAssociated(psuInventoryPath, assocs);
+}
+
 template <typename T>
 T getProperty(sdbusplus::bus::bus& bus, const char* service, const char* path,
               const char* interface, const char* propertyName)
diff --git a/test/mocked_utils.hpp b/test/mocked_utils.hpp
index cd85c3c..71aac99 100644
--- a/test/mocked_utils.hpp
+++ b/test/mocked_utils.hpp
@@ -30,6 +30,9 @@
     MOCK_CONST_METHOD1(getLatestVersion,
                        std::string(const std::set<std::string>& versions));
 
+    MOCK_CONST_METHOD2(isAssociated, bool(const std::string& psuInventoryPath,
+                                          const AssociationList& assocs));
+
     MOCK_CONST_METHOD5(getPropertyImpl,
                        any(sdbusplus::bus::bus& bus, const char* service,
                            const char* path, const char* interface,
diff --git a/test/test_utils.cpp b/test/test_utils.cpp
index a623cf5..afcbb47 100644
--- a/test/test_utils.cpp
+++ b/test/test_utils.cpp
@@ -1,3 +1,5 @@
+#include "config.h"
+
 #include "utils.hpp"
 
 #include <sdbusplus/test/sdbus_mock.hpp>
@@ -63,10 +65,23 @@
 
 TEST(Utils, GetVersionID)
 {
-
     auto ret = utils::getVersionId("");
     EXPECT_EQ("", ret);
 
     ret = utils::getVersionId("some version");
     EXPECT_EQ(8u, ret.size());
 }
+
+TEST(Utils, IsAssociated)
+{
+    std::string path = "/com/example/chassis/powersupply0";
+    utils::AssociationList assocs = {{ACTIVATION_FWD_ASSOCIATION,
+                                      ACTIVATION_REV_ASSOCIATION,
+                                      "a-different-path"}};
+
+    EXPECT_FALSE(utils::isAssociated(path, assocs));
+
+    assocs.emplace_back(ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
+                        path);
+    EXPECT_TRUE(utils::isAssociated(path, assocs));
+}