Activation: Support to update multiple PSUs

Queue the update on multiple PSUs, and do the update one-by-one.

Tested: Write unit test cases and verify the cases pass.
        On witherspoon, verify the dummy update services are run on all
        PSUs.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: Icfe0f6e74623e54840df8d731d852d53d6071768
diff --git a/test/test_activation.cpp b/test/test_activation.cpp
index 0ea3640..338dc60 100644
--- a/test/test_activation.cpp
+++ b/test/test_activation.cpp
@@ -1,4 +1,5 @@
 #include "activation.hpp"
+#include "mocked_utils.hpp"
 
 #include <sdbusplus/test/sdbus_mock.hpp>
 
@@ -7,24 +8,43 @@
 
 using namespace phosphor::software::updater;
 
+using ::testing::_;
+using ::testing::Return;
+
 class TestActivation : public ::testing::Test
 {
   public:
-    using ActivationStatus = sdbusplus::xyz::openbmc_project::Software::server::
-        Activation::Activations;
-    TestActivation()
+    using Status = Activation::Status;
+    using RequestedStatus = Activation::RequestedActivations;
+    TestActivation() :
+        mockedUtils(
+            reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
     {
     }
     ~TestActivation()
     {
     }
+
+    void onUpdateDone()
+    {
+        activation->onUpdateDone();
+    }
+    void onUpdateFailed()
+    {
+        activation->onUpdateFailed();
+    }
+    int getProgress()
+    {
+        return activation->activationProgress->progress();
+    }
     static constexpr auto dBusPath = SOFTWARE_OBJPATH;
     sdbusplus::SdBusMock sdbusMock;
     sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
+    const utils::MockedUtils& mockedUtils;
     std::unique_ptr<Activation> activation;
     std::string versionId = "abcdefgh";
     std::string extVersion = "Some Ext Version";
-    ActivationStatus status = ActivationStatus::Active;
+    Status status = Status::Ready;
     AssociationList associations;
 };
 
@@ -51,3 +71,102 @@
         psuInventoryPath, versionId);
     EXPECT_EQ(toCompare, service);
 }
+
+TEST_F(TestActivation, doUpdateWhenNoPSU)
+{
+    activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
+                                              extVersion, status, associations);
+    ON_CALL(mockedUtils, getPSUInventoryPath(_))
+        .WillByDefault(
+            Return(std::vector<std::string>({}))); // No PSU inventory
+    activation->requestedActivation(RequestedStatus::Active);
+
+    EXPECT_EQ(Status::Failed, activation->activation());
+}
+
+TEST_F(TestActivation, doUpdateOnePSUOK)
+{
+    constexpr auto psu0 = "/com/example/inventory/psu0";
+    activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
+                                              extVersion, status, associations);
+    ON_CALL(mockedUtils, getPSUInventoryPath(_))
+        .WillByDefault(
+            Return(std::vector<std::string>({psu0}))); // One PSU inventory
+    activation->requestedActivation(RequestedStatus::Active);
+
+    EXPECT_EQ(Status::Activating, activation->activation());
+
+    onUpdateDone();
+    EXPECT_EQ(Status::Active, activation->activation());
+}
+
+TEST_F(TestActivation, doUpdateFourPSUsOK)
+{
+    constexpr auto psu0 = "/com/example/inventory/psu0";
+    constexpr auto psu1 = "/com/example/inventory/psu1";
+    constexpr auto psu2 = "/com/example/inventory/psu2";
+    constexpr auto psu3 = "/com/example/inventory/psu3";
+    activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
+                                              extVersion, status, associations);
+    ON_CALL(mockedUtils, getPSUInventoryPath(_))
+        .WillByDefault(Return(
+            std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
+    activation->requestedActivation(RequestedStatus::Active);
+
+    EXPECT_EQ(Status::Activating, activation->activation());
+    EXPECT_EQ(10, getProgress());
+
+    onUpdateDone();
+    EXPECT_EQ(Status::Activating, activation->activation());
+    EXPECT_EQ(30, getProgress());
+
+    onUpdateDone();
+    EXPECT_EQ(Status::Activating, activation->activation());
+    EXPECT_EQ(50, getProgress());
+
+    onUpdateDone();
+    EXPECT_EQ(Status::Activating, activation->activation());
+    EXPECT_EQ(70, getProgress());
+
+    onUpdateDone();
+    EXPECT_EQ(Status::Active, activation->activation());
+}
+
+TEST_F(TestActivation, doUpdateFourPSUsFailonSecond)
+{
+    constexpr auto psu0 = "/com/example/inventory/psu0";
+    constexpr auto psu1 = "/com/example/inventory/psu1";
+    constexpr auto psu2 = "/com/example/inventory/psu2";
+    constexpr auto psu3 = "/com/example/inventory/psu3";
+    activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
+                                              extVersion, status, associations);
+    ON_CALL(mockedUtils, getPSUInventoryPath(_))
+        .WillByDefault(Return(
+            std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
+    activation->requestedActivation(RequestedStatus::Active);
+
+    EXPECT_EQ(Status::Activating, activation->activation());
+    EXPECT_EQ(10, getProgress());
+
+    onUpdateDone();
+    EXPECT_EQ(Status::Activating, activation->activation());
+    EXPECT_EQ(30, getProgress());
+
+    onUpdateFailed();
+    EXPECT_EQ(Status::Failed, activation->activation());
+}
+
+TEST_F(TestActivation, doUpdateOnExceptionFromDbus)
+{
+    constexpr auto psu0 = "/com/example/inventory/psu0";
+    activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
+                                              extVersion, status, associations);
+    ON_CALL(mockedUtils, getPSUInventoryPath(_))
+        .WillByDefault(
+            Return(std::vector<std::string>({psu0}))); // One PSU inventory
+    ON_CALL(sdbusMock, sd_bus_call(_, _, _, _, nullptr))
+        .WillByDefault(Return(-1)); // Make sdbus call failure
+    activation->requestedActivation(RequestedStatus::Active);
+
+    EXPECT_EQ(Status::Failed, activation->activation());
+}