#include "activation.hpp" #include "mocked_activation_listener.hpp" #include "mocked_association_interface.hpp" #include "mocked_utils.hpp" #include #include #include using namespace phosphor::software::updater; using ::testing::_; using ::testing::Return; using ::testing::StrEq; using std::any; class TestActivation : public ::testing::Test { public: using PropertyType = utils::UtilsInterface::PropertyType; using Status = Activation::Status; using RequestedStatus = Activation::RequestedActivations; TestActivation(const TestActivation&) = delete; TestActivation& operator=(const TestActivation&) = delete; TestActivation(TestActivation&&) = delete; TestActivation& operator=(TestActivation&&) = delete; TestActivation() : mockedUtils( reinterpret_cast(utils::getUtils())) { // By default make it compatible with the test software ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) .WillByDefault(Return(any(PropertyType(std::string("TestManu"))))); ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MODEL))) .WillByDefault(Return(any(PropertyType(std::string("TestModel"))))); ON_CALL(mockedUtils, isAssociated(_, _)).WillByDefault(Return(false)); } ~TestActivation() override { utils::freeUtils(); } void onUpdateDone() const { activation->onUpdateDone(); } void onUpdateFailed() const { activation->onUpdateFailed(); } int getProgress() const { return activation->activationProgress->progress(); } const auto& getPsuQueue() const { return activation->psuQueue; } std::string getUpdateService(const std::string& psuInventoryPath) const { return activation->getUpdateService(psuInventoryPath); } sdbusplus::SdBusMock sdbusMock; sdbusplus::bus_t mockedBus = sdbusplus::get_mocked_new(&sdbusMock); const utils::MockedUtils& mockedUtils; MockedAssociationInterface mockedAssociationInterface; MockedActivationListener mockedActivationListener; std::unique_ptr activation; std::string versionId = "abcdefgh"; std::string extVersion = "manufacturer=TestManu,model=TestModel"; std::string filePath = "/tmp/images/abcdefgh"; std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId; Status status = Status::Ready; AssociationList associations; }; TEST_F(TestActivation, ctordtor) { activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); } TEST_F(TestActivation, ctorWithInvalidExtVersion) { extVersion = "invalid text"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); } TEST_F(TestActivation, getUpdateService) { std::string psuInventoryPath = "/com/example/inventory/powersupply1"; std::string toCompare = "psu-update@-com-example-inventory-" "powersupply1\\x20-tmp-images-12345678.service"; versionId = "12345678"; filePath = "/tmp/images/12345678"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); auto service = getUpdateService(psuInventoryPath); EXPECT_EQ(toCompare, service); } TEST_F(TestActivation, doUpdateWhenNoPSU) { activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault( Return(std::vector({}))); // No PSU inventory activation->requestedActivation(RequestedStatus::Active); EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) .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()); } TEST_F(TestActivation, doUpdateOnePSUOK) { constexpr auto psu0 = "/com/example/inventory/psu0"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault( Return(std::vector({psu0}))); // One PSU inventory activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) .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); 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( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault(Return( std::vector({psu0, psu1, psu2, psu3}))); // 4 PSUs activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(10, getProgress()); EXPECT_CALL(mockedActivationListener, onUpdateDone(StrEq(versionId), StrEq(psu0))) .Times(1); onUpdateDone(); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(30, getProgress()); EXPECT_CALL(mockedActivationListener, onUpdateDone(StrEq(versionId), StrEq(psu1))) .Times(1); onUpdateDone(); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(50, getProgress()); EXPECT_CALL(mockedActivationListener, onUpdateDone(StrEq(versionId), StrEq(psu2))) .Times(1); onUpdateDone(); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(70, getProgress()); EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) .Times(1); EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) .Times(1); EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) .Times(1); EXPECT_CALL(mockedActivationListener, onUpdateDone(StrEq(versionId), StrEq(psu3))) .Times(1); 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( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault(Return( std::vector({psu0, psu1, psu2, psu3}))); // 4 PSUs activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(10, getProgress()); EXPECT_CALL(mockedActivationListener, onUpdateDone(StrEq(versionId), StrEq(psu0))) .Times(1); onUpdateDone(); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(30, getProgress()); EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) .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()); } TEST_F(TestActivation, doUpdateOnExceptionFromDbus) { constexpr auto psu0 = "/com/example/inventory/psu0"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault( Return(std::vector({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()); } TEST_F(TestActivation, doUpdateOnePSUModelNotCompatible) { constexpr auto psu0 = "/com/example/inventory/psu0"; extVersion = "manufacturer=TestManu,model=DifferentModel"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault(Return(std::vector({psu0}))); activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Ready, activation->activation()); } TEST_F(TestActivation, doUpdateOnePSUManufactureNotCompatible) { constexpr auto psu0 = "/com/example/inventory/psu0"; extVersion = "manufacturer=DifferentManu,model=TestModel"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault(Return(std::vector({psu0}))); activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Ready, activation->activation()); } TEST_F(TestActivation, doUpdateOnePSUSelfManufactureIsEmpty) { ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(MANUFACTURER))) .WillByDefault(Return(any(PropertyType(std::string(""))))); extVersion = "manufacturer=AnyManu,model=TestModel"; // Below is the same as doUpdateOnePSUOK case constexpr auto psu0 = "/com/example/inventory/psu0"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault( Return(std::vector({psu0}))); // One PSU inventory activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) .Times(1); EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) .Times(1); EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) .Times(1); onUpdateDone(); EXPECT_EQ(Status::Active, activation->activation()); } TEST_F(TestActivation, doUpdateFourPSUsSecondPSUNotCompatible) { 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"; ON_CALL(mockedUtils, getPropertyImpl(_, _, StrEq(psu1), _, StrEq(MODEL))) .WillByDefault( Return(any(PropertyType(std::string("DifferentModel"))))); activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault(Return( std::vector({psu0, psu1, psu2, psu3}))); // 4 PSUs activation->requestedActivation(RequestedStatus::Active); const auto& psuQueue = getPsuQueue(); EXPECT_EQ(3U, psuQueue.size()); // Only 3 PSUs shall be updated, and psu1 shall be skipped EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(10, getProgress()); onUpdateDone(); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(36, getProgress()); onUpdateDone(); EXPECT_EQ(Status::Activating, activation->activation()); EXPECT_EQ(62, getProgress()); EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath)) .Times(1); EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath)) .Times(1); EXPECT_CALL(mockedAssociationInterface, addUpdateableAssociation(dBusPath)) .Times(1); onUpdateDone(); EXPECT_EQ(Status::Active, activation->activation()); } TEST_F(TestActivation, doUpdateWhenNoFilePathInActiveState) { filePath = ""; status = Status::Active; // Typically, a running PSU software is active // without file path constexpr auto psu0 = "/com/example/inventory/psu0"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault( Return(std::vector({psu0}))); // One PSU inventory // There shall be no DBus call to start update service EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, StrEq("StartUnit"))) .Times(0); activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Active, activation->activation()); } TEST_F(TestActivation, doUpdateWhenNoFilePathInReadyState) { filePath = ""; status = Status::Ready; // Usually a Ready activation should have file path, // but we are testing this case as well constexpr auto psu0 = "/com/example/inventory/psu0"; activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault( Return(std::vector({psu0}))); // One PSU inventory // There shall be no DBus call to start update service EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, StrEq("StartUnit"))) .Times(0); activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Ready, activation->activation()); } TEST_F(TestActivation, doUpdateWhenPSUIsAssociated) { constexpr auto psu0 = "/com/example/inventory/psu0"; status = Status::Active; // Typically, a running PSU software is associated activation = std::make_unique( mockedBus, dBusPath, versionId, extVersion, status, associations, filePath, &mockedAssociationInterface, &mockedActivationListener); ON_CALL(mockedUtils, getPSUInventoryPath(_)) .WillByDefault( Return(std::vector({psu0}))); // One PSU inventory // When PSU is already associated, there shall be no DBus call to start // update service EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _)) .WillOnce(Return(true)); EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, StrEq("StartUnit"))) .Times(0); activation->requestedActivation(RequestedStatus::Active); EXPECT_EQ(Status::Active, activation->activation()); }