diff --git a/include/sdbus-c++/StandardInterfaces.h b/include/sdbus-c++/StandardInterfaces.h index 7a89446..8f50016 100644 --- a/include/sdbus-c++/StandardInterfaces.h +++ b/include/sdbus-c++/StandardInterfaces.h @@ -187,8 +187,8 @@ namespace sdbus { }; // Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality - // is provided by underlying libsystemd implementation. The exception is Properties_adaptor and - // ObjectManager_adaptor, which provide convenience functionality to emit signals. + // is provided by underlying libsystemd implementation. The exception is Properties_adaptor, + // ObjectManager_adaptor and ManagedObject_adaptor, which provide convenience functionality to emit signals. // Adaptor for properties class Properties_adaptor @@ -218,13 +218,22 @@ namespace sdbus { sdbus::IObject& object_; }; - // Adaptor for object manager + /*! + * @brief Object Manager Convenience Adaptor + * + * Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces + * implements the *GetManagedObjects()* method of the [org.freedesktop.DBus.ObjectManager.GetManagedObjects](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) + * interface. + * + * Note that there can be multiple object managers in a path hierarchy. InterfacesAdded/InterfacesRemoved + * signals are sent from the closest object manager at either the same path or the closest parent path of an object. + */ class ObjectManager_adaptor { static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager"; protected: - ObjectManager_adaptor(sdbus::IObject& object) + explicit ObjectManager_adaptor(sdbus::IObject& object) : object_(object) { object_.addObjectManager(); @@ -232,22 +241,66 @@ namespace sdbus { ~ObjectManager_adaptor() = default; + private: + sdbus::IObject& object_; + }; + + /*! + * @brief Managed Object Convenience Adaptor + * + * Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces + * will extend the resulting object adaptor with emitInterfacesAddedSignal()/emitInterfacesRemovedSignal() + * according to org.freedesktop.DBus.ObjectManager.InterfacesAdded/.InterfacesRemoved. + * + * Note that objects which implement this adaptor require an object manager (e.g via ObjectManager_adaptor) to be + * instantiated on one of it's parent object paths or the same path. InterfacesAdded/InterfacesRemoved + * signals are sent from the closest object manager at either the same path or the closest parent path of an object. + */ + class ManagedObject_adaptor + { + protected: + explicit ManagedObject_adaptor(sdbus::IObject& object) : object_(object) + { + } + + ~ManagedObject_adaptor() = default; + public: + /*! + * @brief Emits InterfacesAdded signal for this object path + * + * See IObject::emitInterfacesAddedSignal(). + */ void emitInterfacesAddedSignal() { object_.emitInterfacesAddedSignal(); } + /*! + * @brief Emits InterfacesAdded signal for this object path + * + * See IObject::emitInterfacesAddedSignal(). + */ void emitInterfacesAddedSignal(const std::vector& interfaces) { object_.emitInterfacesAddedSignal(interfaces); } + /*! + * @brief Emits InterfacesRemoved signal for this object path + * + * See IObject::emitInterfacesRemovedSignal(). + */ void emitInterfacesRemovedSignal() { object_.emitInterfacesRemovedSignal(); } + /*! + * @brief Emits InterfacesRemoved signal for this object path + * + * See IObject::emitInterfacesRemovedSignal(). + */ void emitInterfacesRemovedSignal(const std::vector& interfaces) { object_.emitInterfacesRemovedSignal(interfaces); diff --git a/tests/integrationtests/DBusGeneralTests.cpp b/tests/integrationtests/DBusGeneralTests.cpp index c0a9060..2ee1f02 100644 --- a/tests/integrationtests/DBusGeneralTests.cpp +++ b/tests/integrationtests/DBusGeneralTests.cpp @@ -49,6 +49,6 @@ TEST(AdaptorAndProxy, CanBeConstructedSuccesfully) auto connection = sdbus::createConnection(); connection->requestName(INTERFACE_NAME); - ASSERT_NO_THROW(TestAdaptor adaptor(*connection)); + ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH)); ASSERT_NO_THROW(TestProxy proxy(INTERFACE_NAME, OBJECT_PATH)); } diff --git a/tests/integrationtests/DBusMethodsTests.cpp b/tests/integrationtests/DBusMethodsTests.cpp index f2123dd..2d9a006 100644 --- a/tests/integrationtests/DBusMethodsTests.cpp +++ b/tests/integrationtests/DBusMethodsTests.cpp @@ -258,13 +258,13 @@ TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHan #if LIBSYSTEMD_VERSION>=240 TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239) { - s_connection->setMethodCallTimeout(5000000); - ASSERT_THAT(s_connection->getMethodCallTimeout(), Eq(5000000)); + s_adaptorConnection->setMethodCallTimeout(5000000); + ASSERT_THAT(s_adaptorConnection->getMethodCallTimeout(), Eq(5000000)); } #else TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240) { - ASSERT_THROW(s_connection->setMethodCallTimeout(5000000), sdbus::Error); - ASSERT_THROW(s_connection->getMethodCallTimeout(), sdbus::Error); + ASSERT_THROW(s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error); + ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error); } #endif diff --git a/tests/integrationtests/DBusSignalsTests.cpp b/tests/integrationtests/DBusSignalsTests.cpp index 703dfe3..801e7bc 100644 --- a/tests/integrationtests/DBusSignalsTests.cpp +++ b/tests/integrationtests/DBusSignalsTests.cpp @@ -58,8 +58,8 @@ TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully) TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully) { - auto proxy1 = std::make_unique(*s_connection, INTERFACE_NAME, OBJECT_PATH); - auto proxy2 = std::make_unique(*s_connection, INTERFACE_NAME, OBJECT_PATH); + auto proxy1 = std::make_unique(*s_adaptorConnection, INTERFACE_NAME, OBJECT_PATH); + auto proxy2 = std::make_unique(*s_adaptorConnection, INTERFACE_NAME, OBJECT_PATH); m_adaptor->emitSimpleSignal(); @@ -72,7 +72,7 @@ TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName) { auto otherBusName = INTERFACE_NAME + "2"; auto connection2 = sdbus::createConnection(otherBusName); - auto adaptor2 = std::make_unique(*connection2); + auto adaptor2 = std::make_unique(*connection2, OBJECT_PATH); adaptor2->emitSimpleSignal(); diff --git a/tests/integrationtests/DBusStandardInterfacesTests.cpp b/tests/integrationtests/DBusStandardInterfacesTests.cpp index bef754e..ba0bb78 100644 --- a/tests/integrationtests/DBusStandardInterfacesTests.cpp +++ b/tests/integrationtests/DBusStandardInterfacesTests.cpp @@ -143,32 +143,31 @@ TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties) TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects) { - const auto objectsInterfacesAndProperties = m_proxy->GetManagedObjects(); + m_adaptor.reset(); + const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects(); ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0)); } TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully) { - auto subObject1 = sdbus::createObject(*s_connection, "/sub/path1"); - subObject1->registerProperty("aProperty1").onInterface("org.sdbuscpp.integrationtests.iface1").withGetter([]{return uint8_t{123};}); - subObject1->finishRegistration(); - auto subObject2 = sdbus::createObject(*s_connection, "/sub/path2"); - subObject2->registerProperty("aProperty2").onInterface("org.sdbuscpp.integrationtests.iface2").withGetter([]{return "hi";}); - subObject2->finishRegistration(); - - const auto objectsInterfacesAndProperties = m_proxy->GetManagedObjects(); + auto adaptor2 = std::make_unique(*s_adaptorConnection, OBJECT_PATH_2); + const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects(); ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2)); - EXPECT_THAT(objectsInterfacesAndProperties.at("/sub/path1").at("org.sdbuscpp.integrationtests.iface1").at("aProperty1").get(), Eq(123)); - EXPECT_THAT(objectsInterfacesAndProperties.at("/sub/path2").at("org.sdbuscpp.integrationtests.iface2").at("aProperty2").get(), Eq("hi")); + EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH) + .at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME) + .at("action").get(), Eq(DEFAULT_ACTION_VALUE)); + EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2) + .at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME) + .at("action").get(), Eq(DEFAULT_ACTION_VALUE)); } TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces) { std::atomic signalReceived{false}; - m_proxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath - , const std::map>& interfacesAndProperties ) + m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath + , const std::map>& interfacesAndProperties ) { EXPECT_THAT(objectPath, Eq(OBJECT_PATH)); EXPECT_THAT(interfacesAndProperties, SizeIs(1)); @@ -198,8 +197,8 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces) TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces) { std::atomic signalReceived{false}; - m_proxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath - , const std::map>& interfacesAndProperties ) + m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath + , const std::map>& interfacesAndProperties ) { EXPECT_THAT(objectPath, Eq(OBJECT_PATH)); EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces @@ -228,8 +227,8 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces) TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces) { std::atomic signalReceived{false}; - m_proxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath - , const std::vector& interfaces ) + m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath + , const std::vector& interfaces ) { EXPECT_THAT(objectPath, Eq(OBJECT_PATH)); ASSERT_THAT(interfaces, SizeIs(1)); @@ -245,8 +244,8 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces) TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces) { std::atomic signalReceived{false}; - m_proxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath - , const std::vector& interfaces ) + m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath + , const std::vector& interfaces ) { EXPECT_THAT(objectPath, Eq(OBJECT_PATH)); ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces diff --git a/tests/integrationtests/Defs.h b/tests/integrationtests/Defs.h index 7dcc4d9..5419290 100644 --- a/tests/integrationtests/Defs.h +++ b/tests/integrationtests/Defs.h @@ -32,7 +32,9 @@ namespace sdbus { namespace test { const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"}; -const std::string OBJECT_PATH{"/"}; +const std::string MANAGER_PATH {"/org/sdbuscpp/integrationtests"}; +const std::string OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"}; +const std::string OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"}; constexpr const uint8_t UINT8_VALUE{1}; constexpr const int16_t INT16_VALUE{21}; diff --git a/tests/integrationtests/TestAdaptor.cpp b/tests/integrationtests/TestAdaptor.cpp index c15b713..6bc92dd 100644 --- a/tests/integrationtests/TestAdaptor.cpp +++ b/tests/integrationtests/TestAdaptor.cpp @@ -31,8 +31,8 @@ namespace sdbus { namespace test { -TestAdaptor::TestAdaptor(sdbus::IConnection& connection) : - AdaptorInterfaces(connection, OBJECT_PATH) +TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) : + AdaptorInterfaces(connection, path) { registerAdaptor(); } diff --git a/tests/integrationtests/TestAdaptor.h b/tests/integrationtests/TestAdaptor.h index a9fe2be..4b49612 100644 --- a/tests/integrationtests/TestAdaptor.h +++ b/tests/integrationtests/TestAdaptor.h @@ -32,15 +32,31 @@ #include #include #include +#include namespace sdbus { namespace test { -class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integrationtests_adaptor - , sdbus::Properties_adaptor - , sdbus::ObjectManager_adaptor > +class ObjectManagerTestAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor > { public: - TestAdaptor(sdbus::IConnection& connection); + ObjectManagerTestAdaptor(sdbus::IConnection& connection, std::string path) : + AdaptorInterfaces(connection, std::move(path)) + { + registerAdaptor(); + } + + ~ObjectManagerTestAdaptor() + { + unregisterAdaptor(); + } +}; + +class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integrationtests_adaptor + , sdbus::Properties_adaptor + , sdbus::ManagedObject_adaptor > +{ +public: + TestAdaptor(sdbus::IConnection& connection, const std::string& path); ~TestAdaptor(); protected: diff --git a/tests/integrationtests/TestFixture.cpp b/tests/integrationtests/TestFixture.cpp index e49b395..1b2987b 100644 --- a/tests/integrationtests/TestFixture.cpp +++ b/tests/integrationtests/TestFixture.cpp @@ -28,6 +28,7 @@ namespace sdbus { namespace test { -std::unique_ptr TestFixture::s_connection = sdbus::createSystemBusConnection(); +std::unique_ptr TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection(); +std::unique_ptr TestFixture::s_proxyConnection = sdbus::createSystemBusConnection(); }} diff --git a/tests/integrationtests/TestFixture.h b/tests/integrationtests/TestFixture.h index 88cc32f..69c2094 100644 --- a/tests/integrationtests/TestFixture.h +++ b/tests/integrationtests/TestFixture.h @@ -46,14 +46,17 @@ class TestFixture : public ::testing::Test public: static void SetUpTestCase() { - s_connection->requestName(INTERFACE_NAME); - s_connection->enterEventLoopAsync(); + s_proxyConnection->enterEventLoopAsync(); + s_adaptorConnection->requestName(INTERFACE_NAME); + s_adaptorConnection->enterEventLoopAsync(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals } static void TearDownTestCase() { - s_connection->leaveEventLoop(); - s_connection->releaseName(INTERFACE_NAME); + s_adaptorConnection->releaseName(INTERFACE_NAME); + s_adaptorConnection->leaveEventLoop(); + s_proxyConnection->leaveEventLoop(); } template @@ -81,9 +84,11 @@ public: private: void SetUp() override { - m_adaptor = std::make_unique(*s_connection); - m_proxy = std::make_unique(INTERFACE_NAME, OBJECT_PATH); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy to start listening to signals + m_objectManagerProxy = std::make_unique(*s_proxyConnection, INTERFACE_NAME, MANAGER_PATH); + m_proxy = std::make_unique(*s_proxyConnection, INTERFACE_NAME, OBJECT_PATH); + + m_objectManagerAdaptor = std::make_unique(*s_adaptorConnection, MANAGER_PATH); + m_adaptor = std::make_unique(*s_adaptorConnection, OBJECT_PATH); } void TearDown() override @@ -93,8 +98,10 @@ private: } public: - static std::unique_ptr s_connection; - + static std::unique_ptr s_adaptorConnection; + static std::unique_ptr s_proxyConnection; + std::unique_ptr m_objectManagerAdaptor; + std::unique_ptr m_objectManagerProxy; std::unique_ptr m_adaptor; std::unique_ptr m_proxy; }; diff --git a/tests/integrationtests/TestProxy.cpp b/tests/integrationtests/TestProxy.cpp index 9eca329..c5f9571 100644 --- a/tests/integrationtests/TestProxy.cpp +++ b/tests/integrationtests/TestProxy.cpp @@ -92,18 +92,6 @@ void TestProxy::onPropertiesChanged( const std::string& interfaceName m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties); } -void TestProxy::onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map>& interfacesAndProperties) -{ - if (m_onInterfacesAddedHandler) - m_onInterfacesAddedHandler(objectPath, interfacesAndProperties); -} - -void TestProxy::onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector& interfaces) -{ - if (m_onInterfacesRemovedHandler) - m_onInterfacesRemovedHandler(objectPath, interfaces); -} - void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function handler) { m_DoOperationClientSideAsyncReplyHandler = std::move(handler); diff --git a/tests/integrationtests/TestProxy.h b/tests/integrationtests/TestProxy.h index 723b08a..876f355 100644 --- a/tests/integrationtests/TestProxy.h +++ b/tests/integrationtests/TestProxy.h @@ -35,11 +35,41 @@ namespace sdbus { namespace test { +class ObjectManagerTestProxy : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy > +{ +public: + ObjectManagerTestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath) + : ProxyInterfaces(connection, std::move(destination), std::move(objectPath)) + { + registerProxy(); + } + + ~ObjectManagerTestProxy() + { + unregisterProxy(); + } +protected: + void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map>& interfacesAndProperties) override + { + if (m_onInterfacesAddedHandler) + m_onInterfacesAddedHandler(objectPath, interfacesAndProperties); + } + + void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector& interfaces) override + { + if (m_onInterfacesRemovedHandler) + m_onInterfacesRemovedHandler(objectPath, interfaces); + } + +public: // for tests + std::function>&)> m_onInterfacesAddedHandler; + std::function&)> m_onInterfacesRemovedHandler; +}; + class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy , sdbus::Peer_proxy , sdbus::Introspectable_proxy - , sdbus::Properties_proxy - , sdbus::ObjectManager_proxy > + , sdbus::Properties_proxy > { public: TestProxy(std::string destination, std::string objectPath); @@ -58,9 +88,6 @@ protected: void onPropertiesChanged( const std::string& interfaceName , const std::map& changedProperties , const std::vector& invalidatedProperties ) override; - void onInterfacesAdded( const sdbus::ObjectPath& objectPath - , const std::map>& interfacesAndProperties) override; - void onInterfacesRemoved( const sdbus::ObjectPath& objectPath, const std::vector& interfaces) override; public: void installDoOperationClientSideAsyncReplyHandler(std::function handler); @@ -85,8 +112,6 @@ public: // for tests std::function m_DoOperationClientSideAsyncReplyHandler; std::function&, const std::vector&)> m_onPropertiesChangedHandler; - std::function>&)> m_onInterfacesAddedHandler; - std::function&)> m_onInterfacesRemovedHandler; const Message* m_signalMsg{}; std::string m_signalMemberName;