| #include "item_updater.hpp" | 
 | #include "mocked_utils.hpp" | 
 |  | 
 | #include <sdbusplus/test/sdbus_mock.hpp> | 
 |  | 
 | #include <gmock/gmock.h> | 
 | #include <gtest/gtest.h> | 
 |  | 
 | using namespace phosphor::software::updater; | 
 | using ::testing::_; | 
 | using ::testing::ContainerEq; | 
 | using ::testing::Pointee; | 
 | using ::testing::Return; | 
 | using ::testing::ReturnArg; | 
 | using ::testing::StrEq; | 
 |  | 
 | using std::any; | 
 |  | 
 | class TestItemUpdater : public ::testing::Test | 
 | { | 
 |   public: | 
 |     using Properties = ItemUpdater::Properties; | 
 |     using PropertyType = utils::UtilsInterface::PropertyType; | 
 |  | 
 |     TestItemUpdater() : | 
 |         mockedUtils( | 
 |             reinterpret_cast<const utils::MockedUtils&>(utils::getUtils())) | 
 |     { | 
 |         ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>()); | 
 |         ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT))) | 
 |             .WillByDefault(Return(any(PropertyType(true)))); | 
 |     } | 
 |  | 
 |     ~TestItemUpdater() | 
 |     { | 
 |         utils::freeUtils(); | 
 |     } | 
 |  | 
 |     auto& GetActivations() | 
 |     { | 
 |         return itemUpdater->activations; | 
 |     } | 
 |  | 
 |     std::string getObjPath(const std::string& versionId) | 
 |     { | 
 |         return std::string(dBusPath) + "/" + versionId; | 
 |     } | 
 |  | 
 |     void onPsuInventoryChanged(const std::string& psuPath, | 
 |                                const Properties& properties) | 
 |     { | 
 |         itemUpdater->onPsuInventoryChanged(psuPath, properties); | 
 |     } | 
 |  | 
 |     void scanDirectory(const fs::path& p) | 
 |     { | 
 |         itemUpdater->scanDirectory(p); | 
 |     } | 
 |  | 
 |     static constexpr auto dBusPath = SOFTWARE_OBJPATH; | 
 |     sdbusplus::SdBusMock sdbusMock; | 
 |     sdbusplus::bus_t mockedBus = sdbusplus::get_mocked_new(&sdbusMock); | 
 |     const utils::MockedUtils& mockedUtils; | 
 |     std::unique_ptr<ItemUpdater> itemUpdater; | 
 |     Properties propAdded{{PRESENT, PropertyType(true)}}; | 
 |     Properties propRemoved{{PRESENT, PropertyType(false)}}; | 
 |     Properties propModel{{MODEL, PropertyType(std::string("dummyModel"))}}; | 
 | }; | 
 |  | 
 | TEST_F(TestItemUpdater, ctordtor) | 
 | { | 
 |     EXPECT_CALL(mockedUtils, getLatestVersion(_)).Times(1); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, NotCreateObjectOnNotPresent) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string objPath = getObjPath(version); | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psuPath}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(false)))); // not present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // No activation/version objects are created | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(0); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, CreateOnePSUOnPresent) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string objPath = getObjPath(version); | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psuPath}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // activation and version object will be added | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, CreateTwoPSUsWithSameVersion) | 
 | { | 
 |     constexpr auto psu0 = "/com/example/inventory/psu0"; | 
 |     constexpr auto psu1 = "/com/example/inventory/psu1"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     auto version0 = std::string("version0"); | 
 |     auto version1 = std::string("version0"); | 
 |     auto objPath0 = getObjPath(version0); | 
 |     auto objPath1 = getObjPath(version1); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) | 
 |         .WillOnce(Return(std::string(version0))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) | 
 |         .WillOnce(Return(std::string(version1))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // activation and version object will be added | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) | 
 |         .Times(2); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // Verify there is only one activation and it has two associations | 
 |     const auto& activations = GetActivations(); | 
 |     EXPECT_EQ(1u, activations.size()); | 
 |     const auto& activation = activations.find(version0)->second; | 
 |     const auto& assocs = activation->associations(); | 
 |     EXPECT_EQ(2u, assocs.size()); | 
 |     EXPECT_EQ(psu0, std::get<2>(assocs[0])); | 
 |     EXPECT_EQ(psu1, std::get<2>(assocs[1])); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, CreateTwoPSUsWithDifferentVersion) | 
 | { | 
 |     constexpr auto psu0 = "/com/example/inventory/psu0"; | 
 |     constexpr auto psu1 = "/com/example/inventory/psu1"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     auto version0 = std::string("version0"); | 
 |     auto version1 = std::string("version1"); | 
 |     auto objPath0 = getObjPath(version0); | 
 |     auto objPath1 = getObjPath(version1); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) | 
 |         .WillOnce(Return(std::string(version0))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) | 
 |         .WillOnce(Return(std::string(version1))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // two new activation and version objects will be added | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) | 
 |         .Times(2); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1))) | 
 |         .Times(2); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // Verify there are two activations and each with one association | 
 |     const auto& activations = GetActivations(); | 
 |     EXPECT_EQ(2u, activations.size()); | 
 |     const auto& activation0 = activations.find(version0)->second; | 
 |     const auto& assocs0 = activation0->associations(); | 
 |     EXPECT_EQ(1u, assocs0.size()); | 
 |     EXPECT_EQ(psu0, std::get<2>(assocs0[0])); | 
 |  | 
 |     const auto& activation1 = activations.find(version1)->second; | 
 |     const auto& assocs1 = activation1->associations(); | 
 |     EXPECT_EQ(1u, assocs1.size()); | 
 |     EXPECT_EQ(psu1, std::get<2>(assocs1[0])); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, OnOnePSURemoved) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string objPath = getObjPath(version); | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psuPath}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // activation and version object will be added | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // the activation and version object will be removed | 
 |     Properties p{{PRESENT, PropertyType(false)}}; | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     onPsuInventoryChanged(psuPath, p); | 
 |  | 
 |     // on exit, item updater is removed | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, OnOnePSUAdded) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string objPath = getObjPath(version); | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psuPath}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(false)))); // not present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // No activation/version objects are created | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(0); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // The PSU is present and version is added in a single call | 
 |     Properties propAddedAndModel{ | 
 |         {PRESENT, PropertyType(true)}, | 
 |         {MODEL, PropertyType(std::string("testModel"))}}; | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     onPsuInventoryChanged(psuPath, propAddedAndModel); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithLatestVersion) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string objPath = getObjPath(version); | 
 |     ON_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillByDefault(Return(std::vector<std::string>({psuPath}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // activation and version object will be added | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // the activation and version object will be removed | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     onPsuInventoryChanged(psuPath, propRemoved); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |  | 
 |     // On PSU inserted, it shall check if it's the latest version | 
 |     std::set<std::string> expectedVersions = {version}; | 
 |     EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions))) | 
 |         .WillOnce(Return(version)); | 
 |     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psuPath), _)) | 
 |         .WillOnce(Return(true)); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, | 
 |                                                           StrEq("StartUnit"))) | 
 |         .Times(0); | 
 |     onPsuInventoryChanged(psuPath, propAdded); | 
 |     onPsuInventoryChanged(psuPath, propModel); | 
 |  | 
 |     // on exit, objects are removed | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, | 
 |        TwoPSUsWithSameVersionRemovedAndAddedWithDifferntVersion) | 
 | { | 
 |     constexpr auto psu0 = "/com/example/inventory/psu0"; | 
 |     constexpr auto psu1 = "/com/example/inventory/psu1"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     auto version0 = std::string("version0"); | 
 |     auto version1 = std::string("version0"); | 
 |     auto objPath0 = getObjPath(version0); | 
 |     auto objPath1 = getObjPath(version1); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) | 
 |         .WillOnce(Return(std::string(version0))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) | 
 |         .WillOnce(Return(std::string(version1))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // activation and version object will be added | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) | 
 |         .Times(2); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // Verify there is only one activation and it has two associations | 
 |     const auto& activations = GetActivations(); | 
 |     EXPECT_EQ(1u, activations.size()); | 
 |     const auto& activation = activations.find(version0)->second; | 
 |     auto assocs = activation->associations(); | 
 |     EXPECT_EQ(2u, assocs.size()); | 
 |     EXPECT_EQ(psu0, std::get<2>(assocs[0])); | 
 |     EXPECT_EQ(psu1, std::get<2>(assocs[1])); | 
 |  | 
 |     // PSU0 is removed, only associations shall be updated | 
 |     onPsuInventoryChanged(psu0, propRemoved); | 
 |     assocs = activation->associations(); | 
 |     EXPECT_EQ(1u, assocs.size()); | 
 |     EXPECT_EQ(psu1, std::get<2>(assocs[0])); | 
 |  | 
 |     // PSU1 is removed, the activation and version object shall be removed | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0))) | 
 |         .Times(2); | 
 |     onPsuInventoryChanged(psu1, propRemoved); | 
 |  | 
 |     // Add PSU0 and PSU1 back, but PSU1 with a different version | 
 |     version1 = "version1"; | 
 |     objPath1 = getObjPath(version1); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) | 
 |         .WillOnce(Return(std::string(version0))); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) | 
 |         .WillOnce(Return(std::string(version1))); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath0))) | 
 |         .Times(2); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath1))) | 
 |         .Times(2); | 
 |     onPsuInventoryChanged(psu0, propAdded); | 
 |     onPsuInventoryChanged(psu1, propModel); | 
 |     onPsuInventoryChanged(psu1, propAdded); | 
 |     onPsuInventoryChanged(psu0, propModel); | 
 |  | 
 |     // on exit, objects are removed | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath0))) | 
 |         .Times(2); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath1))) | 
 |         .Times(2); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, scanDirOnNoPSU) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string objPath = getObjPath(version); | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psuPath}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(false)))); // not present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // No activation/version objects are created | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(0); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // The valid image in test/psu-images-one-valid-one-invalid/model-1/ | 
 |     auto objPathValid = getObjPath("psu-test.v0.4"); | 
 |     auto objPathInvalid = getObjPath("psu-test.v0.5"); | 
 |     // activation and version object will be added on scan dir | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathValid))) | 
 |         .Times(2); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPathInvalid))) | 
 |         .Times(0); | 
 |     scanDirectory("./psu-images-one-valid-one-invalid"); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, scanDirOnSamePSUVersion) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string objPath = getObjPath(version); | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psuPath}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     // The item updater itself | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(dBusPath))) | 
 |         .Times(1); | 
 |  | 
 |     // activation and version object will be added | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(2); | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // The valid image in test/psu-images-valid-version0/model-3/ has the same | 
 |     // version as the running PSU, so no objects will be created, but only the | 
 |     // path will be set to the version object | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath))) | 
 |         .Times(0); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_emit_properties_changed_strv( | 
 |                                _, StrEq(objPath), | 
 |                                StrEq("xyz.openbmc_project.Common.FilePath"), | 
 |                                Pointee(StrEq("Path")))) | 
 |         .Times(1); | 
 |     scanDirectory("./psu-images-valid-version0"); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithSameVersion) | 
 | { | 
 |     // Simulate there are two PSUs with same version, and updated to a new | 
 |     // version | 
 |     constexpr auto psu0 = "/com/example/inventory/psu0"; | 
 |     constexpr auto psu1 = "/com/example/inventory/psu1"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     auto version0 = std::string("version0"); | 
 |     auto version1 = std::string("version0"); | 
 |     auto objPath0 = getObjPath(version0); | 
 |     auto objPath1 = getObjPath(version1); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) | 
 |         .WillOnce(Return(std::string(version0))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) | 
 |         .WillOnce(Return(std::string(version1))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     std::string newVersionId = "NewVersionId"; | 
 |     AssociationList associations; | 
 |     auto dummyActivation = std::make_unique<Activation>( | 
 |         mockedBus, dBusPath, newVersionId, "", Activation::Status::Active, | 
 |         associations, "", itemUpdater.get(), itemUpdater.get()); | 
 |  | 
 |     // Now there is one activation and it has two associations | 
 |     auto& activations = GetActivations(); | 
 |     activations.emplace(newVersionId, std::move(dummyActivation)); | 
 |     auto& activation = activations.find(version0)->second; | 
 |     auto assocs = activation->associations(); | 
 |     EXPECT_EQ(2u, assocs.size()); | 
 |     EXPECT_EQ(psu0, std::get<2>(assocs[0])); | 
 |     EXPECT_EQ(psu1, std::get<2>(assocs[1])); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _)) | 
 |         .WillOnce(Return(true)); | 
 |     itemUpdater->onUpdateDone(newVersionId, psu0); | 
 |  | 
 |     // Now the activation should have one assoiation | 
 |     assocs = activation->associations(); | 
 |     EXPECT_EQ(1u, assocs.size()); | 
 |     EXPECT_EQ(psu1, std::get<2>(assocs[0])); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _)) | 
 |         .WillOnce(Return(true)); | 
 |     itemUpdater->onUpdateDone(newVersionId, psu1); | 
 |  | 
 |     // Now the activation shall be erased and only the dummy one is left | 
 |     EXPECT_EQ(1u, activations.size()); | 
 |     EXPECT_NE(activations.find(newVersionId), activations.end()); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, OnUpdateDoneOnTwoPSUsWithDifferentVersion) | 
 | { | 
 |     // Simulate there are two PSUs with different version, and updated to a new | 
 |     // version | 
 |     constexpr auto psu0 = "/com/example/inventory/psu0"; | 
 |     constexpr auto psu1 = "/com/example/inventory/psu1"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     auto version0 = std::string("version0"); | 
 |     auto version1 = std::string("version1"); | 
 |     auto objPath0 = getObjPath(version0); | 
 |     auto objPath1 = getObjPath(version1); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillOnce(Return(std::vector<std::string>({psu0, psu1}))); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu0), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getService(_, StrEq(psu1), _)) | 
 |         .WillOnce(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu0))) | 
 |         .WillOnce(Return(std::string(version0))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psu1))) | 
 |         .WillOnce(Return(std::string(version1))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, | 
 |                                              StrEq(PRESENT))) | 
 |         .WillOnce(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     std::string newVersionId = "NewVersionId"; | 
 |     AssociationList associations; | 
 |     auto dummyActivation = std::make_unique<Activation>( | 
 |         mockedBus, dBusPath, newVersionId, "", Activation::Status::Active, | 
 |         associations, "", itemUpdater.get(), itemUpdater.get()); | 
 |  | 
 |     auto& activations = GetActivations(); | 
 |     activations.emplace(newVersionId, std::move(dummyActivation)); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu0), _)) | 
 |         .WillOnce(Return(true)); | 
 |  | 
 |     // After psu0 is done, two activations should be left | 
 |     itemUpdater->onUpdateDone(newVersionId, psu0); | 
 |     EXPECT_EQ(2u, activations.size()); | 
 |     const auto& activation1 = activations.find(version1)->second; | 
 |     const auto& assocs1 = activation1->associations(); | 
 |     EXPECT_EQ(1u, assocs1.size()); | 
 |     EXPECT_EQ(psu1, std::get<2>(assocs1[0])); | 
 |  | 
 |     EXPECT_CALL(mockedUtils, isAssociated(StrEq(psu1), _)) | 
 |         .WillOnce(Return(true)); | 
 |     // After psu1 is done, only the dummy activation should be left | 
 |     itemUpdater->onUpdateDone(newVersionId, psu1); | 
 |     EXPECT_EQ(1u, activations.size()); | 
 |     EXPECT_NE(activations.find(newVersionId), activations.end()); | 
 | } | 
 |  | 
 | TEST_F(TestItemUpdater, OnOnePSURemovedAndAddedWithOldVersion) | 
 | { | 
 |     constexpr auto psuPath = "/com/example/inventory/psu0"; | 
 |     constexpr auto service = "com.example.Software.Psu"; | 
 |     constexpr auto version = "version0"; | 
 |     std::string versionId = | 
 |         version; // In testing versionId is the same as version | 
 |     std::string objPath = getObjPath(version); | 
 |     ON_CALL(mockedUtils, getPSUInventoryPath(_)) | 
 |         .WillByDefault(Return(std::vector<std::string>({psuPath}))); | 
 |     ON_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) | 
 |         .WillByDefault(Return(service)); | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(version))); | 
 |     ON_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, | 
 |                                          StrEq(PRESENT))) | 
 |         .WillByDefault(Return(any(PropertyType(true)))); // present | 
 |  | 
 |     itemUpdater = std::make_unique<ItemUpdater>(mockedBus, dBusPath); | 
 |  | 
 |     // Add an association to simulate that it has image in BMC filesystem | 
 |     auto& activation = GetActivations().find(versionId)->second; | 
 |     auto assocs = activation->associations(); | 
 |     assocs.emplace_back(ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, | 
 |                         "SomePath"); | 
 |     activation->associations(assocs); | 
 |     activation->path("SomeFilePath"); | 
 |  | 
 |     onPsuInventoryChanged(psuPath, propRemoved); | 
 |  | 
 |     // On PSU inserted, it checks and finds a newer version | 
 |     auto oldVersion = "old-version"; | 
 |     EXPECT_CALL(mockedUtils, getVersion(StrEq(psuPath))) | 
 |         .WillOnce(Return(std::string(oldVersion))); | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(MANUFACTURER))) | 
 |         .WillOnce( | 
 |             Return(any(PropertyType(std::string(""))))); // Checking compatible | 
 |     EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), | 
 |                                              _, StrEq(MODEL))) | 
 |         .WillOnce( | 
 |             Return(any(PropertyType(std::string(""))))); // Checking compatible | 
 |     std::set<std::string> expectedVersions = {version, oldVersion}; | 
 |     EXPECT_CALL(mockedUtils, getLatestVersion(ContainerEq(expectedVersions))) | 
 |         .WillOnce(Return(version)); | 
 |     ON_CALL(mockedUtils, isAssociated(StrEq(psuPath), _)) | 
 |         .WillByDefault(Return(false)); | 
 |     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _, | 
 |                                                           StrEq("StartUnit"))) | 
 |         .Times(3); // There are 3 systemd units are started, enable bmc reboot | 
 |                    // guard, start activation, and disable bmc reboot guard | 
 |     onPsuInventoryChanged(psuPath, propAdded); | 
 |     onPsuInventoryChanged(psuPath, propModel); | 
 | } |