Add updateable association

Add 'updateable' association to the functional PSU firmware.
This 'updateable' association can be used to mark all the
firmware components which can be programmable from BMC
interfaces like Redfish.

Resolves openbmc/phosphor-psu-code-mgmt#2

Tested: Verify unit tests success;
        Verify the updateable associations are created for PSU firmware
        objects on Witherspoon, and the Redfish has "Updateable": true
        for the PSU images.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I579121587b0d2c14f08beceaa68e380eaf1eefd9
diff --git a/meson.build b/meson.build
index bec51a2..e46790b 100644
--- a/meson.build
+++ b/meson.build
@@ -25,6 +25,8 @@
 cdata.set_quoted('ACTIVE_REV_ASSOCIATION', 'software_version')
 cdata.set_quoted('FUNCTIONAL_FWD_ASSOCIATION', 'functional')
 cdata.set_quoted('FUNCTIONAL_REV_ASSOCIATION', 'software_version')
+cdata.set_quoted('UPDATEABLE_FWD_ASSOCIATION', 'updateable')
+cdata.set_quoted('UPDATEABLE_REV_ASSOCIATION', 'software_version')
 cdata.set_quoted('VERSION', 'Version')
 cdata.set_quoted('PRESENT', 'Present')
 cdata.set_quoted('MANUFACTURER', 'Manufacturer')
diff --git a/src/activation.cpp b/src/activation.cpp
index 37610c0..13b42cc 100644
--- a/src/activation.cpp
+++ b/src/activation.cpp
@@ -225,6 +225,7 @@
 
     associationInterface->createActiveAssociation(objPath);
     associationInterface->addFunctionalAssociation(objPath);
+    associationInterface->addUpdateableAssociation(objPath);
 
     // Reset RequestedActivations to none so that it could be activated in
     // future
diff --git a/src/association_interface.hpp b/src/association_interface.hpp
index e3d653c..2969719 100644
--- a/src/association_interface.hpp
+++ b/src/association_interface.hpp
@@ -21,6 +21,13 @@
      */
     virtual void addFunctionalAssociation(const std::string& path) = 0;
 
+    /** @brief Add the updateable association to the
+     *  "running" PSU software image
+     *
+     * @param[in]  path - The path to create the association.
+     */
+    virtual void addUpdateableAssociation(const std::string& path) = 0;
+
     /** @brief Remove the associations from the provided software image path
      *
      * @param[in]  path - The path to remove the association from.
diff --git a/src/item_updater.cpp b/src/item_updater.cpp
index 90ff62d..a5b5afb 100644
--- a/src/item_updater.cpp
+++ b/src/item_updater.cpp
@@ -163,6 +163,13 @@
     associations(assocs);
 }
 
+void ItemUpdater::addUpdateableAssociation(const std::string& path)
+{
+    assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION,
+                                        UPDATEABLE_REV_ASSOCIATION, path));
+    associations(assocs);
+}
+
 void ItemUpdater::removeAssociation(const std::string& path)
 {
     for (auto iter = assocs.begin(); iter != assocs.end();)
@@ -246,6 +253,7 @@
 
         createActiveAssociation(path);
         addFunctionalAssociation(path);
+        addUpdateableAssociation(path);
     }
 }
 
diff --git a/src/item_updater.hpp b/src/item_updater.hpp
index 0477e60..94d97f5 100644
--- a/src/item_updater.hpp
+++ b/src/item_updater.hpp
@@ -80,6 +80,13 @@
      */
     void addFunctionalAssociation(const std::string& path) override;
 
+    /** @brief Add the updateable association to the
+     *  "running" PSU software image
+     *
+     * @param[in]  path - The path to create the association.
+     */
+    void addUpdateableAssociation(const std::string& path) override;
+
     /** @brief Removes the associations from the provided software image path
      *
      * @param[in]  path - The path to remove the association from.
diff --git a/test/mocked_association_interface.hpp b/test/mocked_association_interface.hpp
index 731ea58..0ad4796 100644
--- a/test/mocked_association_interface.hpp
+++ b/test/mocked_association_interface.hpp
@@ -11,5 +11,6 @@
 
     MOCK_METHOD1(createActiveAssociation, void(const std::string& path));
     MOCK_METHOD1(addFunctionalAssociation, void(const std::string& path));
+    MOCK_METHOD1(addUpdateableAssociation, void(const std::string& path));
     MOCK_METHOD1(removeAssociation, void(const std::string& path));
 };
diff --git a/test/test_activation.cpp b/test/test_activation.cpp
index a7ce542..f2cc1ef 100644
--- a/test/test_activation.cpp
+++ b/test/test_activation.cpp
@@ -118,6 +118,8 @@
         .Times(0);
     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
         .Times(0);
+    EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
+        .Times(0);
     EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
     EXPECT_EQ(Status::Failed, activation->activation());
 }
@@ -139,6 +141,8 @@
         .Times(1);
     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
         .Times(1);
+    EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
+        .Times(1);
     EXPECT_CALL(mockedActivationListener,
                 onUpdateDone(StrEq(versionId), StrEq(psu0)))
         .Times(1);
@@ -188,6 +192,8 @@
         .Times(1);
     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
         .Times(1);
+    EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
+        .Times(1);
 
     EXPECT_CALL(mockedActivationListener,
                 onUpdateDone(StrEq(versionId), StrEq(psu3)))
@@ -224,6 +230,8 @@
         .Times(0);
     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
         .Times(0);
+    EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
+        .Times(0);
     EXPECT_CALL(mockedActivationListener, onUpdateDone(_, _)).Times(0);
     onUpdateFailed();
     EXPECT_EQ(Status::Failed, activation->activation());
@@ -294,6 +302,8 @@
         .Times(1);
     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
         .Times(1);
+    EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
+        .Times(1);
     onUpdateDone();
     EXPECT_EQ(Status::Active, activation->activation());
 }
@@ -334,6 +344,8 @@
         .Times(1);
     EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
         .Times(1);
+    EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath))
+        .Times(1);
 
     onUpdateDone();
     EXPECT_EQ(Status::Active, activation->activation());