forked from Kistler-Group/sdbus-cpp
Introduce support for some common D-Bus annotations (#30)
* Add ability to declare property behavior on PropertyChanged signal * Add support for Method.NoReply annotation (WIP) * Add support for common annotations/flags
This commit is contained in:
committed by
Lukáš Ďurfina
parent
2c78e08d19
commit
9c0e98c580
@@ -41,6 +41,7 @@
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
@@ -200,6 +201,51 @@ TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
|
||||
{
|
||||
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
|
||||
|
||||
for (auto i = 0; i < 100; ++i)
|
||||
{
|
||||
if (m_adaptor->wasMultiplyCalled())
|
||||
break;
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
ASSERT_TRUE(m_adaptor->wasMultiplyCalled());
|
||||
ASSERT_THAT(m_adaptor->getMultiplyResult(), Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_proxy->throwError();
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
ASSERT_THAT(e.getName(), Eq("org.freedesktop.DBus.Error.AccessDenied"));
|
||||
ASSERT_THAT(e.getMessage(), Eq("A test error occurred (Operation not permitted)"));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
|
||||
|
||||
for (auto i = 0; i < 100; ++i)
|
||||
{
|
||||
if (m_adaptor->wasThrowErrorCalled())
|
||||
break;
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
ASSERT_TRUE(m_adaptor->wasThrowErrorCalled());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, DoesServerSideAsynchoronousMethodInParallel)
|
||||
{
|
||||
// Yeah, this is kinda timing-dependent test, but times should be safe...
|
||||
@@ -227,7 +273,6 @@ TEST_F(SdbusTestObject, DoesServerSideAsynchoronousMethodInParallel)
|
||||
|
||||
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
{
|
||||
std::mutex mtx;
|
||||
std::atomic<size_t> resultCount{};
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
@@ -331,7 +376,7 @@ TEST_F(SdbusTestObject, ReadsReadPropertySuccesfully)
|
||||
|
||||
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
|
||||
{
|
||||
auto x = 42;
|
||||
uint32_t x = 42;
|
||||
ASSERT_NO_THROW(m_proxy->action(x));
|
||||
ASSERT_THAT(m_proxy->action(), Eq(x));
|
||||
}
|
||||
@@ -346,3 +391,8 @@ TEST_F(SdbusTestObject, CannotReadFromWriteProperty)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->blocking(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersXmlApiDescriptionOnIntrospection)
|
||||
{
|
||||
ASSERT_THAT(m_proxy->Introspect(), Eq(testing_adaptor::expectedXmlApiDescription));
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "adaptor-glue.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
class TestingAdaptor : public sdbus::Interfaces<testing_adaptor>
|
||||
{
|
||||
@@ -38,6 +39,10 @@ public:
|
||||
|
||||
virtual ~TestingAdaptor() { }
|
||||
|
||||
bool wasMultiplyCalled() const { return m_multiplyCalled; }
|
||||
double getMultiplyResult() const { return m_multiplyResult; }
|
||||
bool wasThrowErrorCalled() const { return m_throwErrorCalled; }
|
||||
|
||||
protected:
|
||||
|
||||
void noArgNoReturn() const { }
|
||||
@@ -48,6 +53,12 @@ protected:
|
||||
|
||||
double multiply(const int64_t& a, const double& b) const { return a * b; }
|
||||
|
||||
void multiplyWithNoReply(const int64_t& a, const double& b) const
|
||||
{
|
||||
m_multiplyResult = a * b;
|
||||
m_multiplyCalled = true;
|
||||
}
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const
|
||||
{
|
||||
std::vector<int16_t> res{x.get<1>()};
|
||||
@@ -154,6 +165,12 @@ protected:
|
||||
};
|
||||
}
|
||||
|
||||
void throwError() const
|
||||
{
|
||||
m_throwErrorCalled = true;
|
||||
throw sdbus::createError(1, "A test error occurred");
|
||||
}
|
||||
|
||||
std::string state() { return STRING_VALUE; }
|
||||
uint32_t action() { return m_action; }
|
||||
void action(const uint32_t& value) { m_action = value; }
|
||||
@@ -164,6 +181,10 @@ private:
|
||||
uint32_t m_action;
|
||||
bool m_blocking;
|
||||
|
||||
// For dont-expect-reply method call verifications
|
||||
mutable std::atomic<bool> m_multiplyCalled{};
|
||||
mutable double m_multiplyResult{};
|
||||
mutable std::atomic<bool> m_throwErrorCalled{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
|
||||
#include "proxy-glue.h"
|
||||
|
||||
class TestingProxy : public sdbus::ProxyInterfaces<::testing_proxy>
|
||||
class TestingProxy : public sdbus::ProxyInterfaces<::testing_proxy, sdbus::introspectable_proxy>
|
||||
{
|
||||
public:
|
||||
using sdbus::ProxyInterfaces<::testing_proxy>::ProxyInterfaces;
|
||||
using sdbus::ProxyInterfaces<::testing_proxy, sdbus::introspectable_proxy>::ProxyInterfaces;
|
||||
|
||||
int getSimpleCallCount() const { return m_simpleCallCounter; }
|
||||
std::map<int32_t, std::string> getMap() const { return m_map; }
|
||||
|
||||
@@ -57,11 +57,14 @@ protected:
|
||||
testing_adaptor(sdbus::IObject& object) :
|
||||
object_(object)
|
||||
{
|
||||
object_.setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
|
||||
|
||||
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
|
||||
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getInt(); });
|
||||
object_.registerMethod("getTuple").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getTuple(); });
|
||||
|
||||
object_.registerMethod("multiply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
|
||||
object_.registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
|
||||
object_.registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x){ return this->getInts16FromStruct(x); });
|
||||
|
||||
@@ -95,15 +98,20 @@ protected:
|
||||
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
|
||||
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
|
||||
|
||||
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); });
|
||||
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
|
||||
|
||||
object_.registerMethod("throwError").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwError(); });
|
||||
object_.registerMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](){ this->throwError(); }).withNoReply();
|
||||
|
||||
object_.registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([](){}).markAsPrivileged();
|
||||
|
||||
// registration of signals is optional, it is useful because of introspection
|
||||
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
|
||||
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>();
|
||||
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>();
|
||||
|
||||
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); });
|
||||
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); });
|
||||
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
|
||||
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
|
||||
object_.registerProperty("blocking").onInterface(INTERFACE_NAME)./*withGetter([this](){ return this->blocking(); }).*/withSetter([this](const bool& value){ this->blocking(value); });
|
||||
|
||||
}
|
||||
@@ -143,6 +151,7 @@ protected:
|
||||
virtual int32_t getInt() const = 0;
|
||||
virtual std::tuple<uint32_t, std::string> getTuple() const = 0;
|
||||
virtual double multiply(const int64_t& a, const double& b) const = 0;
|
||||
virtual void multiplyWithNoReply(const int64_t& a, const double& b) const = 0;
|
||||
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const = 0;
|
||||
virtual sdbus::Variant processVariant(sdbus::Variant& v) = 0;
|
||||
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const = 0;
|
||||
@@ -154,6 +163,7 @@ protected:
|
||||
virtual sdbus::Signature getSignature() const = 0;
|
||||
virtual sdbus::ObjectPath getObjectPath() const = 0;
|
||||
virtual ComplexType getComplex() const = 0;
|
||||
virtual void throwError() const = 0;
|
||||
|
||||
virtual std::string state() = 0;
|
||||
virtual uint32_t action() = 0;
|
||||
@@ -161,6 +171,139 @@ protected:
|
||||
virtual bool blocking() = 0;
|
||||
virtual void blocking(const bool& value) = 0;
|
||||
|
||||
public: // For testing purposes
|
||||
static constexpr char expectedXmlApiDescription[] =
|
||||
R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.freedesktop.DBus.Peer">
|
||||
<method name="Ping"/>
|
||||
<method name="GetMachineId">
|
||||
<arg type="s" name="machine_uuid" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="org.freedesktop.DBus.Introspectable">
|
||||
<method name="Introspect">
|
||||
<arg name="data" type="s" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="org.freedesktop.DBus.Properties">
|
||||
<method name="Get">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="out" type="v"/>
|
||||
</method>
|
||||
<method name="GetAll">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="properties" direction="out" type="a{sv}"/>
|
||||
</method>
|
||||
<method name="Set">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="in" type="v"/>
|
||||
</method>
|
||||
<signal name="PropertiesChanged">
|
||||
<arg type="s" name="interface"/>
|
||||
<arg type="a{sv}" name="changed_properties"/>
|
||||
<arg type="as" name="invalidated_properties"/>
|
||||
</signal>
|
||||
</interface>
|
||||
<interface name="com.kistler.testsdbuscpp">
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
<method name="doOperationAsync">
|
||||
<arg type="u" direction="in"/>
|
||||
<arg type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="doOperationSync">
|
||||
<arg type="u" direction="in"/>
|
||||
<arg type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="doPrivilegedStuff">
|
||||
<annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
|
||||
</method>
|
||||
<method name="getComplex">
|
||||
<arg type="a{t(a{ya(obva{is})}gs)}" direction="out"/>
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
</method>
|
||||
<method name="getInt">
|
||||
<arg type="i" direction="out"/>
|
||||
</method>
|
||||
<method name="getInts16FromStruct">
|
||||
<arg type="(yndsan)" direction="in"/>
|
||||
<arg type="an" direction="out"/>
|
||||
</method>
|
||||
<method name="getMapOfVariants">
|
||||
<arg type="ai" direction="in"/>
|
||||
<arg type="(vv)" direction="in"/>
|
||||
<arg type="a{iv}" direction="out"/>
|
||||
</method>
|
||||
<method name="getObjectPath">
|
||||
<arg type="o" direction="out"/>
|
||||
</method>
|
||||
<method name="getSignature">
|
||||
<arg type="g" direction="out"/>
|
||||
</method>
|
||||
<method name="getStructInStruct">
|
||||
<arg type="(s(a{ii}))" direction="out"/>
|
||||
</method>
|
||||
<method name="getTuple">
|
||||
<arg type="u" direction="out"/>
|
||||
<arg type="s" direction="out"/>
|
||||
</method>
|
||||
<method name="multiply">
|
||||
<arg type="x" direction="in"/>
|
||||
<arg type="d" direction="in"/>
|
||||
<arg type="d" direction="out"/>
|
||||
</method>
|
||||
<method name="multiplyWithNoReply">
|
||||
<arg type="x" direction="in"/>
|
||||
<arg type="d" direction="in"/>
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||
</method>
|
||||
<method name="noArgNoReturn">
|
||||
</method>
|
||||
<method name="processVariant">
|
||||
<arg type="v" direction="in"/>
|
||||
<arg type="v" direction="out"/>
|
||||
</method>
|
||||
<method name="sumStructItems">
|
||||
<arg type="(yq)" direction="in"/>
|
||||
<arg type="(ix)" direction="in"/>
|
||||
<arg type="i" direction="out"/>
|
||||
</method>
|
||||
<method name="sumVectorItems">
|
||||
<arg type="aq" direction="in"/>
|
||||
<arg type="at" direction="in"/>
|
||||
<arg type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="throwError">
|
||||
</method>
|
||||
<method name="throwErrorWithNoReply">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||
</method>
|
||||
<signal name="signalWithMap">
|
||||
<arg type="a{is}"/>
|
||||
</signal>
|
||||
<signal name="signalWithVariant">
|
||||
<arg type="v"/>
|
||||
</signal>
|
||||
<signal name="simpleSignal">
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
</signal>
|
||||
<property name="action" type="u" access="readwrite">
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
|
||||
</property>
|
||||
<property name="blocking" type="b" access="readwrite">
|
||||
</property>
|
||||
<property name="state" type="s" access="read">
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
|
||||
</property>
|
||||
</interface>
|
||||
</node>
|
||||
)delimiter";
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ protected:
|
||||
{ this->onSignalWithoutRegistration(s); });
|
||||
}
|
||||
|
||||
|
||||
virtual void onSimpleSignal() = 0;
|
||||
virtual void onSignalWithMap(const std::map<int32_t, std::string>& map) = 0;
|
||||
virtual void onSignalWithVariant(const sdbus::Variant& v) = 0;
|
||||
@@ -78,6 +77,11 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
void multiplyWithNoReply(const int64_t& a, const double& b)
|
||||
{
|
||||
object_.callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
|
||||
}
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x)
|
||||
{
|
||||
std::vector<int16_t> result;
|
||||
@@ -155,6 +159,16 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
void throwError()
|
||||
{
|
||||
object_.callMethod("throwError").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void throwErrorWithNoReply()
|
||||
{
|
||||
object_.callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
|
||||
}
|
||||
|
||||
int32_t callNonexistentMethod()
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
Reference in New Issue
Block a user