#include "item_updater.hpp" #include "mocked_utils.hpp" #include #include #include 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(const TestItemUpdater&) = delete; TestItemUpdater& operator=(const TestItemUpdater&) = delete; TestItemUpdater(TestItemUpdater&&) = delete; TestItemUpdater& operator=(TestItemUpdater&&) = delete; TestItemUpdater() : mockedUtils( reinterpret_cast(utils::getUtils())) { ON_CALL(mockedUtils, getVersionId(_)).WillByDefault(ReturnArg<0>()); ON_CALL(mockedUtils, getPropertyImpl(_, _, _, _, StrEq(PRESENT))) .WillByDefault(Return(any(PropertyType(true)))); } ~TestItemUpdater() override { utils::freeUtils(); } auto& GetActivations() const { return itemUpdater->activations; } static std::string getObjPath(const std::string& versionId) { return std::string(dBusPath) + "/" + versionId; } void onPsuInventoryChanged(const std::string& psuPath, const Properties& properties) const { itemUpdater->onPsuInventoryChanged(psuPath, properties); } void scanDirectory(const fs::path& p) const { 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; 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(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({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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string(""))))); // 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(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({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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel"))))); // 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(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({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, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel0"))))); 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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel1"))))); // 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(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({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, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel0"))))); 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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel1"))))); // 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(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({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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel"))))); // 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(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({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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string(""))))); // 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(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({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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel"))))); // 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(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 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({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, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel0"))))); 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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel1"))))); // 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(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({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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string(""))))); // 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(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(0); 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({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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel"))))); // 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(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(0); 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({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, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel0"))))); 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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel1"))))); itemUpdater = std::make_unique(mockedBus, dBusPath); std::string newVersionId = "NewVersionId"; AssociationList associations; auto dummyActivation = std::make_unique( 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 association 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({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, getPropertyImpl(_, StrEq(service), StrEq(psu0), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel0"))))); 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 EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _, StrEq(MODEL))) .WillOnce(Return(any(PropertyType(std::string("dummyModel1"))))); itemUpdater = std::make_unique(mockedBus, dBusPath); std::string newVersionId = "NewVersionId"; AssociationList associations; auto dummyActivation = std::make_unique( 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({psuPath}))); EXPECT_CALL(mockedUtils, getService(_, StrEq(psuPath), _)) .WillOnce(Return(service)) .WillOnce(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 ON_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psuPath), _, StrEq(MODEL))) .WillByDefault(Return(any(PropertyType(std::string("dummyModel"))))); itemUpdater = std::make_unique(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 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); }