Test: Add unit test for PSU plug out and in
Split the function into two, one for handling the sdbus::message, the
other for handling the changed properties, so that it's easier to write
unit test cases.
Added the test cases:
* On a system with a PSU present, remove the PSU;
* On a system without PSU, plug the PSU in;
* On a system with a PSU, remove the PSU and add it back, while the
propertiesChanged callback is invoked with both Present and Version
properties.
* On a system with two PSUs with same version, remove them one-by-one,
and add back one-by-one, while PSU1 has a different version.
Tested: Verify ItemUpdater correctly handles the above cases:
* Remove the activation and version object if PSU is removed;
* Create activation and version object if PSU is added;
* When there are two PSUs with same version, removing one only
update the associations, removing the other shall result in
the objects to be removed;
Adding one back will create the objects, and adding the other
one with different version will create new objects.
Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I14c7ae9f03ec91bb1c85bb5a18d69f20dc1efd53
diff --git a/src/item_updater.cpp b/src/item_updater.cpp
index 6c55062..89bd388 100644
--- a/src/item_updater.cpp
+++ b/src/item_updater.cpp
@@ -286,20 +286,22 @@
return version;
}
-void ItemUpdater::onPsuInventoryChanged(sdbusplus::message::message& msg)
+void ItemUpdater::onPsuInventoryChangedMsg(sdbusplus::message::message& msg)
{
using Interface = std::string;
- using Property = std::string;
- using Properties =
- std::map<Property, sdbusplus::message::variant<bool, std::string>>;
-
Interface interface;
Properties properties;
- std::optional<bool> present;
- std::optional<std::string> version;
std::string psuPath = msg.get_path();
msg.read(interface, properties);
+ onPsuInventoryChanged(psuPath, properties);
+}
+
+void ItemUpdater::onPsuInventoryChanged(const std::string& psuPath,
+ const Properties& properties)
+{
+ std::optional<bool> present;
+ std::optional<std::string> version;
// The code was expecting to get callback on mutliple properties changed.
// But in practice, the callback is received one-by-one for each property.
@@ -370,7 +372,7 @@
MatchRules::type::signal() + MatchRules::path(p) +
MatchRules::member("PropertiesChanged") +
MatchRules::interface("org.freedesktop.DBus.Properties"),
- std::bind(&ItemUpdater::onPsuInventoryChanged, this,
+ std::bind(&ItemUpdater::onPsuInventoryChangedMsg, this,
std::placeholders::_1));
}
}
diff --git a/src/item_updater.hpp b/src/item_updater.hpp
index eeacdf8..f6fefff 100644
--- a/src/item_updater.hpp
+++ b/src/item_updater.hpp
@@ -91,12 +91,24 @@
*/
void createActivation(sdbusplus::message::message& msg);
+ using Properties =
+ std::map<std::string, utils::UtilsInterface::PropertyType>;
+
/** @brief Callback function for PSU inventory match.
* @details Update an Activation D-Bus object for PSU inventory.
*
* @param[in] msg - Data associated with subscribed signal
*/
- void onPsuInventoryChanged(sdbusplus::message::message& msg);
+ void onPsuInventoryChangedMsg(sdbusplus::message::message& msg);
+
+ /** @brief Callback function for PSU inventory match.
+ * @details Update an Activation D-Bus object for PSU inventory.
+ *
+ * @param[in] psuPath - The PSU inventory path
+ * @param[in] properties - The updated properties
+ */
+ void onPsuInventoryChanged(const std::string& psuPath,
+ const Properties& properties);
/** @brief Create Activation object */
std::unique_ptr<Activation> createActivationObject(
diff --git a/test/test_item_updater.cpp b/test/test_item_updater.cpp
index 2b9af43..b362803 100644
--- a/test/test_item_updater.cpp
+++ b/test/test_item_updater.cpp
@@ -13,11 +13,13 @@
using ::testing::StrEq;
using std::experimental::any;
-using PropertyType = utils::UtilsInterface::PropertyType;
class TestItemUpdater : public ::testing::Test
{
public:
+ using Properties = ItemUpdater::Properties;
+ using PropertyType = utils::UtilsInterface::PropertyType;
+
TestItemUpdater() :
mockedUtils(
reinterpret_cast<const utils::MockedUtils&>(utils::getUtils()))
@@ -39,6 +41,12 @@
return std::string(dBusPath) + "/" + versionId;
}
+ void onPsuInventoryChanged(const std::string& psuPath,
+ const Properties& properties)
+ {
+ itemUpdater->onPsuInventoryChanged(psuPath, properties);
+ }
+
static constexpr auto dBusPath = SOFTWARE_OBJPATH;
sdbusplus::SdBusMock sdbusMock;
sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
@@ -206,3 +214,203 @@
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, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
+ _, StrEq(VERSION)))
+ .WillOnce(Return(any(PropertyType(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, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
+ _, StrEq(VERSION)))
+ .WillOnce(Return(any(PropertyType(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 propAdded{{PRESENT, PropertyType(true)},
+ {VERSION, PropertyType(std::string(version))}};
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+ .Times(2);
+ onPsuInventoryChanged(psuPath, propAdded);
+}
+
+TEST_F(TestItemUpdater, OnOnePSURemovedAndAdded)
+{
+ 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, getPropertyImpl(_, StrEq(service), StrEq(psuPath),
+ _, StrEq(VERSION)))
+ .WillOnce(Return(any(PropertyType(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 propRemoved{{PRESENT, PropertyType(false)}};
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
+ .Times(2);
+ onPsuInventoryChanged(psuPath, propRemoved);
+
+ Properties propAdded{{PRESENT, PropertyType(true)}};
+ Properties propVersion{{VERSION, PropertyType(std::string(version))}};
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+ .Times(2);
+ onPsuInventoryChanged(psuPath, propAdded);
+ onPsuInventoryChanged(psuPath, propVersion);
+
+ // 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, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
+ StrEq(VERSION)))
+ .WillOnce(Return(any(PropertyType(version0))));
+ EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu0), _,
+ StrEq(PRESENT)))
+ .WillOnce(Return(any(PropertyType(true)))); // present
+ EXPECT_CALL(mockedUtils, getPropertyImpl(_, StrEq(service), StrEq(psu1), _,
+ StrEq(VERSION)))
+ .WillOnce(Return(any(PropertyType(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
+ Properties propRemoved{{PRESENT, PropertyType(false)}};
+ 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);
+ Properties propAdded0{{PRESENT, PropertyType(true)},
+ {VERSION, PropertyType(std::string(version0))}};
+ Properties propAdded1{{PRESENT, PropertyType(true)},
+ {VERSION, PropertyType(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, propAdded0);
+ onPsuInventoryChanged(psu1, propAdded1);
+
+ // 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);
+}