Skip to content

Commit

Permalink
Add support for PropertyChanged signal on server side
Browse files Browse the repository at this point in the history
  • Loading branch information
Stanislav Angelovič authored Jun 3, 2019
1 parent 3855248 commit 01e2a7a
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 19 deletions.
19 changes: 19 additions & 0 deletions include/sdbus-c++/IObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
30 changes: 28 additions & 2 deletions include/sdbus-c++/StandardInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
27 changes: 27 additions & 0 deletions src/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/Connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/IConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/ISdBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions src/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
Expand Down
2 changes: 2 additions & 0 deletions src/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
7 changes: 7 additions & 0 deletions src/SdBus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/SdBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class SdBus final : public ISdBus
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;
Expand Down
49 changes: 33 additions & 16 deletions tests/integrationtests/AdaptorAndProxy_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
3 changes: 2 additions & 1 deletion tests/integrationtests/TestingAdaptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) :
Expand Down
2 changes: 2 additions & 0 deletions tests/unittests/mocks/SdBusMock.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class SdBusMock : public sdbus::internal::ISdBus
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));
Expand Down

0 comments on commit 01e2a7a

Please sign in to comment.