Emit adding/removing interfaces for object server
The object server currently either creats the objects and interfaces, or
defer the signal by not adding objects.
In practice, we have situations that the code would like to add
interfaces to an existing object, and it's not supported, or needs
tricky code to workaround.
Exmaples:
https://gerrit.openbmc-project.xyz/c/openbmc/phosphor-bmc-code-mgmt/+/5820
https://gerrit.openbmc-project.xyz/c/openbmc/openpower-pnor-code-mgmt/+/5346
This commit adds the support by:
1. Adding emit_added() in interface.hpp and the generated server.hpp
2. Adding a enum class in object's constructor to indicate which action
to do, to create the object, or adding the interface, or defer signal
as before.
So the user of object<> could pass `action::emit_interface_added` to the
constructor to tell the object server *only* emit interface added to
DBus, without emitting object added.
The previous code stays the same behavior:
* If `true` is passed in object's constructor, it defers emitting object
added signal;
* If no extra parameter is passed in object's constructor, it emits
object added signal as before.
Tested: 1. Make sure the openbmc builds fine with
https://gerrit.openbmc-project.xyz/c/openbmc/phosphor-logging/+/25089
because phosphor-logging uses its own server.hpp for interface, the
above patch removes that.
2. Manually write a small service to verify the interfaces are
added and removed by using the `emit_interface_added` action.
3. Added the unit test cases for object.hpp to check the
ctor/dtor with different actions.
Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I178c5bed3c9ff39ee2ac8d143fbe9131b0753dfa
diff --git a/test/server/Test.interface.yaml b/test/server/Test.interface.yaml
new file mode 100644
index 0000000..d015c99
--- /dev/null
+++ b/test/server/Test.interface.yaml
@@ -0,0 +1,5 @@
+description: >
+ A test interface
+properties:
+ - name: SomeValue
+ type: int64
diff --git a/test/server/object.cpp b/test/server/object.cpp
new file mode 100644
index 0000000..8faa668
--- /dev/null
+++ b/test/server/object.cpp
@@ -0,0 +1,105 @@
+#include "Test/server.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/manager.hpp>
+#include <sdbusplus/test/sdbus_mock.hpp>
+
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::StrEq;
+
+using TestInherit =
+ sdbusplus::server::object::object<sdbusplus::server::server::Test>;
+
+class Object : public ::testing::Test
+{
+ protected:
+ sdbusplus::SdBusMock sdbusMock;
+ sdbusplus::bus::bus bus = sdbusplus::get_mocked_new(&sdbusMock);
+
+ static constexpr auto busName = "xyz.openbmc_project.sdbusplus.test.Object";
+ static constexpr auto objPath = "/xyz/openbmc_project/sdbusplus/test";
+};
+
+TEST_F(Object, InterfaceAddedOnly)
+{
+ // Simulate the typical usage of a service
+ sdbusplus::server::manager::manager objManager(bus, objPath);
+ bus.request_name(busName);
+
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+ .Times(0);
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_interfaces_added_strv(_, StrEq(objPath), _))
+ .Times(1);
+
+ // This only add interface to the existing object
+ auto test = std::make_unique<TestInherit>(
+ bus, objPath, TestInherit::action::emit_interface_added);
+
+ // After destruction, the interface shall be removed
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
+ .Times(0);
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_interfaces_removed_strv(_, StrEq(objPath), _))
+ .Times(1);
+}
+
+TEST_F(Object, DeferAddInterface)
+{
+ // Simulate the typical usage of a service
+ sdbusplus::server::manager::manager objManager(bus, objPath);
+ bus.request_name(busName);
+
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+ .Times(0);
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_interfaces_added_strv(_, StrEq(objPath), _))
+ .Times(0);
+
+ // It defers emit_object_added()
+ auto test = std::make_unique<TestInherit>(bus, objPath,
+ TestInherit::action::defer_emit);
+
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+ .Times(1);
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_interfaces_added_strv(_, StrEq(objPath), _))
+ .Times(0);
+
+ // Now invoke emit_object_added()
+ test->emit_object_added();
+
+ // After destruction, the object will be removed
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
+ .Times(1);
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_interfaces_removed_strv(_, StrEq(objPath), _))
+ .Times(0);
+}
+
+TEST_F(Object, ObjectAdded)
+{
+ // Simulate the typical usage of a service
+ sdbusplus::server::manager::manager objManager(bus, objPath);
+ bus.request_name(busName);
+
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(_, StrEq(objPath)))
+ .Times(1);
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_interfaces_added_strv(_, StrEq(objPath), _))
+ .Times(0);
+
+ // Note: this is the wrong usage!
+ // With the below code, the interface is added as expected, but after
+ // destruction of TestInherit, the whole object will be removed from DBus.
+ // Refer to InterfaceAddedOnly case for the correct usage.
+ auto test = std::make_unique<TestInherit>(bus, objPath);
+
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(_, StrEq(objPath)))
+ .Times(1);
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_interfaces_removed_strv(_, StrEq(objPath), _))
+ .Times(0);
+}