forked from Kistler-Group/sdbus-cpp
Add support for PropertyChanged signal on server side
This commit is contained in:
committed by
GitHub
parent
38552483ca
commit
01e2a7a570
@ -187,6 +187,25 @@ namespace sdbus {
|
||||
*/
|
||||
virtual void emitSignal(const sdbus::Signal& message) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits PropertyChanged signal for specified properties under a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that properties belong to
|
||||
* @param[in] propNames Names of properties that will be included in the PropertiesChanged signal
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits PropertyChanged signal for all properties of a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const std::string& interfaceName) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides D-Bus connection used by the object
|
||||
*
|
||||
|
@ -178,8 +178,34 @@ namespace sdbus {
|
||||
};
|
||||
|
||||
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
|
||||
// is provided automatically for each D-Bus object by underlying libsystemd (with the exception of
|
||||
// object manager which is provided but needs to be added explicitly, see IObject::addObjectManager).
|
||||
// is provided by underlying libsystemd implementation. The exception is Properties_adaptor and
|
||||
// ObjectManager_adaptor, which provide convenience functionality to emit signals.
|
||||
|
||||
class Properties_adaptor
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
|
||||
{
|
||||
object_.emitPropertiesChangedSignal(interfaceName, properties);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName)
|
||||
{
|
||||
object_.emitPropertiesChangedSignal(interfaceName);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_STANDARDINTERFACES_H_ */
|
||||
|
@ -33,6 +33,19 @@
|
||||
#include <poll.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector</*const */char*> to_strv(const std::vector<std::string>& strings)
|
||||
{
|
||||
std::vector</*const */char*> strv;
|
||||
for (auto& str : strings)
|
||||
strv.push_back(const_cast<char*>(str.c_str()));
|
||||
strv.push_back(nullptr);
|
||||
return strv;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
Connection::Connection(Connection::BusType type, std::unique_ptr<ISdBus>&& interface)
|
||||
@ -176,6 +189,20 @@ Signal Connection::createSignal( const std::string& objectPath
|
||||
return Signal{sdbusSignal, iface_.get(), adopt_message};
|
||||
}
|
||||
|
||||
void Connection::emitPropertiesChangedSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::vector<std::string>& propNames )
|
||||
{
|
||||
auto names = to_strv(propNames);
|
||||
|
||||
auto r = iface_->sd_bus_emit_properties_changed_strv( bus_.get()
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, propNames.empty() ? nullptr : &names[0] );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit PropertiesChanged signal", -r);
|
||||
}
|
||||
|
||||
SlotPtr Connection::registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
|
@ -76,6 +76,9 @@ namespace sdbus { namespace internal {
|
||||
Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const override;
|
||||
void emitPropertiesChangedSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::vector<std::string>& propNames ) override;
|
||||
|
||||
SlotPtr registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
|
@ -68,6 +68,9 @@ namespace internal {
|
||||
virtual Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
virtual void emitPropertiesChangedSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::vector<std::string>& propNames ) = 0;
|
||||
|
||||
virtual SlotPtr registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
|
@ -53,6 +53,8 @@ namespace sdbus { namespace internal {
|
||||
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) = 0;
|
||||
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) = 0;
|
||||
|
||||
virtual int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) = 0;
|
||||
|
||||
virtual int sd_bus_open_user(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_open_system(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0;
|
||||
|
@ -135,6 +135,16 @@ void Object::emitSignal(const sdbus::Signal& message)
|
||||
message.send();
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames)
|
||||
{
|
||||
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const std::string& interfaceName)
|
||||
{
|
||||
Object::emitPropertiesChangedSignal(interfaceName, {});
|
||||
}
|
||||
|
||||
sdbus::IConnection& Object::getConnection() const
|
||||
{
|
||||
return dynamic_cast<sdbus::IConnection&>(connection_);
|
||||
|
@ -77,6 +77,8 @@ namespace internal {
|
||||
|
||||
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Signal& message) override;
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) override;
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName) override;
|
||||
|
||||
sdbus::IConnection& getConnection() const override;
|
||||
|
||||
|
@ -91,6 +91,13 @@ int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message
|
||||
return ::sd_bus_message_new_method_error(call, m, e);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_properties_changed_strv(bus, path, interface, names);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_user(sd_bus **ret)
|
||||
{
|
||||
return ::sd_bus_open_user(ret);
|
||||
|
@ -47,6 +47,8 @@ public:
|
||||
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) override;
|
||||
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) override;
|
||||
|
||||
virtual int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) override;
|
||||
|
||||
virtual int sd_bus_open_user(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_system(sd_bus **ret) override;
|
||||
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
|
||||
|
@ -469,22 +469,39 @@ TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
||||
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
// TODO: Uncomment once we have support for PropertiesChanged signals
|
||||
//TEST_F(SdbusTestObject, GetsSignalOnChangedPropertiesViaPropertiesInterface)
|
||||
//{
|
||||
// std::atomic<bool> signalReceived{false};
|
||||
// m_proxy->m_onPropertiesChangedHandler = [&signalReceived](const std::string& interfaceName, const std::map<std::string, sdbus::Variant>& changedProperties, const std::vector<std::string>& invalidatedProperties)
|
||||
// {
|
||||
// EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
// EXPECT_THAT(changedProperties, SizeIs(2));
|
||||
// EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(false));
|
||||
// EXPECT_THAT(invalidatedProperties, SizeIs(0));
|
||||
// signalReceived = true;
|
||||
// };
|
||||
//
|
||||
// m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
|
||||
// waitUntil(signalReceived);
|
||||
//}
|
||||
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_proxy->m_onPropertiesChangedHandler = [&signalReceived](const std::string& interfaceName, const std::map<std::string, sdbus::Variant>& changedProperties, const std::vector<std::string>& invalidatedProperties)
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
|
||||
m_proxy->action(DEFAULT_ACTION_VALUE*2);
|
||||
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
|
||||
waitUntil(signalReceived);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_proxy->m_onPropertiesChangedHandler = [&signalReceived](const std::string& interfaceName, const std::map<std::string, sdbus::Variant>& changedProperties, const std::vector<std::string>& invalidatedProperties)
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
ASSERT_THAT(invalidatedProperties, SizeIs(1));
|
||||
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
|
||||
waitUntil(signalReceived);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, DoesNotProvideObjectManagerInterfaceByDefault)
|
||||
{
|
||||
|
@ -31,7 +31,8 @@
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
class TestingAdaptor : public sdbus::AdaptorInterfaces<testing_adaptor>
|
||||
class TestingAdaptor : public sdbus::AdaptorInterfaces< testing_adaptor
|
||||
, sdbus::Properties_adaptor >
|
||||
{
|
||||
public:
|
||||
TestingAdaptor(sdbus::IConnection& connection) :
|
||||
|
@ -46,6 +46,8 @@ public:
|
||||
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **m));
|
||||
MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e));
|
||||
|
||||
MOCK_METHOD4(sd_bus_emit_properties_changed_strv, int(sd_bus *bus, const char *path, const char *interface, char **names));
|
||||
|
||||
MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret));
|
||||
MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret));
|
||||
MOCK_METHOD3(sd_bus_request_name, int(sd_bus *bus, const char *name, uint64_t flags));
|
||||
|
Reference in New Issue
Block a user