feat: add integration for sd-event

This commit is contained in:
Stanislav Angelovic
2023-01-09 00:05:20 +01:00
parent f332f46087
commit 3acd20c2fd
18 changed files with 502 additions and 271 deletions

View File

@ -34,6 +34,8 @@
#include <cstdint>
#include <optional>
struct sd_event;
namespace sdbus {
/********************************************//**
@ -127,7 +129,7 @@ namespace sdbus {
virtual void releaseName(const std::string& name) = 0;
/*!
* @brief Retrieve the unique name of a connection. E.g. ":1.xx"
* @brief Retrieves the unique name of a connection. E.g. ":1.xx"
*
* @throws sdbus::Error in case of failure
*/
@ -305,6 +307,32 @@ namespace sdbus {
*/
virtual void addMatch(const std::string& match, message_handler callback, floating_slot_t) = 0;
/*!
* @brief Attaches the bus connection to an sd-event event loop
*
* @param[in] event sd-event event loop object
* @param[in] priority Specified priority
*
* @throws sdbus::Error in case of failure
*
* See `man sd_bus_attach_event'.
*/
virtual void attachSdEventLoop(sd_event *event, int priority = 0) = 0;
/*!
* @brief Detaches the bus connection from an sd-event event loop
*
* @throws sdbus::Error in case of failure
*/
virtual void detachSdEventLoop() = 0;
/*!
* @brief Gets current sd-event event loop for the bus connection
*
* @return Pointer to event loop object if attached, nullptr otherwise
*/
virtual sd_event *getSdEventLoop() = 0;
/*!
* @copydoc IConnection::enterEventLoop()
*

View File

@ -32,6 +32,7 @@
#include <sdbus-c++/Error.h>
#include "ScopeGuard.h"
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include <unistd.h>
#include <poll.h>
#include <sys/eventfd.h>
@ -223,6 +224,102 @@ void Connection::addMatch(const std::string& match, message_handler callback, fl
floatingMatchRules_.push_back(addMatch(match, std::move(callback)));
}
void Connection::attachSdEventLoop(sd_event *event, int priority)
{
// Get default event if no event is provided by the caller
if (event != nullptr)
event = sd_event_ref(event);
else
(void)sd_event_default(&event);
SDBUS_THROW_ERROR_IF(!event, "Invalid sd_event handle", EINVAL);
Slot sdEvent{event, [](void* event){ sd_event_unref((sd_event*)event); }};
// TODO: auto sdEvent = createSdEventSlot(...);
sd_event_source *timeEventSource{};
auto r = sd_event_add_time(event, &timeEventSource, CLOCK_MONOTONIC, 0, 0, onSdTimerEvent, this);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add timer event", -r);
Slot sdTimeEventSource{timeEventSource, [](void* source){ sd_event_source_disable_unref((sd_event_source*)source); }};
r = sd_event_source_set_priority(timeEventSource, priority); // TODO maybe use custom EventSourceSlot with custom type? Then we can use slot.get() here
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set time event priority", -r);
r = sd_event_source_set_description(timeEventSource, "bus-time");
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set time event description", -r);
// TODO: auto sdTimeEventSource = createSdTimeEventSourceSlot(...);
sd_event_source *ioEventSource{};
auto pollData = getEventLoopPollData();
r = sd_event_add_io(event, &ioEventSource, pollData.fd, 0, onSdIoEvent, this);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add io event", -r);
Slot sdIoEventSource{ioEventSource, [](void* source){ sd_event_source_disable_unref((sd_event_source*)source); }};
r = sd_event_source_set_prepare(ioEventSource, onSdEventPrepare);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set prepare callback for IO event", -r);
r = sd_event_source_set_priority(ioEventSource, priority);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set priority for IO event", -r);
r = sd_event_source_set_description(ioEventSource, "bus-input");
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set priority for IO event", -r);
// TODO: auto sdIoEventSource = createSdIoEventSourceSlot(...);
sdEvent_ = std::make_unique<SdEvent>(SdEvent{std::move(sdEvent), std::move(sdTimeEventSource), std::move(sdIoEventSource)});
}
void Connection::detachSdEventLoop()
{
sdEvent_.reset();
}
sd_event *Connection::getSdEventLoop()
{
return iface_->sd_bus_get_event(bus_.get());
}
int Connection::onSdTimerEvent(sd_event_source */*s*/, uint64_t /*usec*/, void *userdata)
{
auto connection = static_cast<Connection*>(userdata);
assert(connection != nullptr);
connection->processPendingRequest();
return 1;
}
int Connection::onSdIoEvent(sd_event_source */*s*/, int /*fd*/, uint32_t /*revents*/, void *userdata)
{
auto connection = static_cast<Connection*>(userdata);
assert(connection != nullptr);
connection->processPendingRequest();
return 1;
}
int Connection::onSdEventPrepare(sd_event_source */*s*/, void *userdata)
{
auto connection = static_cast<Connection*>(userdata);
assert(connection != nullptr);
auto sdbusPollData = connection->getEventLoopPollData();
// Set poll events to watch out for
auto* sdIoEventSource = static_cast<sd_event_source*>(connection->sdEvent_->sdIoEventSource_.get());
auto r = sd_event_source_set_io_events(sdIoEventSource, sdbusPollData.events);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set poll events for IO event source", -r);
// Set current timeout to the time event source (may be zero if there are messages in the sd-bus queues to be processed)
auto* sdTimeEventSource = static_cast<sd_event_source*>(connection->sdEvent_->sdTimeEventSource_.get());
r = sd_event_source_set_time(sdTimeEventSource, sdbusPollData.timeout_usec);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set timeout for time event source", -r);
r = sd_event_source_set_enabled(sdTimeEventSource, SD_EVENT_ON);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to enable time event source", -r);
// TODO: Continue with integration tests
return 1;
}
Slot Connection::addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable

View File

@ -87,6 +87,10 @@ namespace sdbus::internal {
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback) override;
void addMatch(const std::string& match, message_handler callback, floating_slot_t) override;
void attachSdEventLoop(sd_event *event, int priority) override;
void detachSdEventLoop() override;
sd_event *getSdEventLoop() override;
const ISdBus& getSdBusInterface() const override;
ISdBus& getSdBusInterface() override;
@ -141,10 +145,13 @@ namespace sdbus::internal {
void clearEventLoopNotification(int fd) const;
void notifyEventLoopNewTimeout() const override;
private:
void joinWithEventLoop();
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
static int onSdTimerEvent(sd_event_source *s, uint64_t usec, void *userdata);
static int onSdIoEvent(sd_event_source *s, int fd, uint32_t revents, void *userdata);
static int onSdEventPrepare(sd_event_source *s, void *userdata);
struct EventFd
{
EventFd();
@ -169,6 +176,15 @@ namespace sdbus::internal {
EventFd eventFd_;
std::atomic<uint64_t> activeTimeout_{};
std::vector<Slot> floatingMatchRules_;
// sd-event integration
struct SdEvent
{
Slot sdEvent_;
Slot sdTimeEventSource_;
Slot sdIoEventSource_{};
};
std::unique_ptr<SdEvent> sdEvent_;
};
}

View File

@ -101,6 +101,8 @@ namespace sdbus::internal {
virtual int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) = 0;
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) = 0;
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) = 0;
virtual sd_event *sd_bus_get_event(sd_bus *bus) = 0;
};
}

View File

@ -401,4 +401,11 @@ int SdBus::sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label)
return ::sd_bus_creds_get_selinux_context(c, label);
}
sd_event *SdBus::sd_bus_get_event(sd_bus *bus)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_get_event(bus);
}
}

View File

@ -94,6 +94,8 @@ public:
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) override;
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) override;
virtual sd_event *sd_bus_get_event(sd_bus *bus) override;
private:
std::recursive_mutex sdbusMutex_;
};

View File

@ -109,7 +109,8 @@ target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib GTest::gmock)
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
# Systemd::Libsystemd is included because integration tests use sd-event. Otherwise sdbus-c++ encapsulates and hides libsystemd.
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ Systemd::Libsystemd GTest::gmock)
# Manual performance and stress tests
option(ENABLE_PERF_TESTS "Build and install manual performance tests (default OFF)" OFF)

View File

@ -49,20 +49,18 @@ using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
{
std::chrono::time_point<std::chrono::steady_clock> start;
try
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
@ -71,7 +69,7 @@ TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
});
start = std::chrono::steady_clock::now();
m_proxy->doOperationClientSideAsyncWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
this->m_proxy->doOperationClientSideAsyncWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 1us, so we should time out
future.get();
FAIL() << "Expected sdbus::Error exception";
@ -89,7 +87,7 @@ TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
}
}
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
{
// Yeah, this is kinda timing-dependent test, but times should be safe...
std::mutex mtx;
@ -114,7 +112,7 @@ TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
}
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
TYPED_TEST(AsyncSdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
{
std::atomic<size_t> resultCount{};
std::atomic<bool> invoke{};
@ -144,11 +142,11 @@ TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
ASSERT_THAT(resultCount, Eq(1500));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
@ -156,77 +154,77 @@ TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doOperationClientSideAsync(100);
this->m_proxy->doOperationClientSideAsync(100);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
{
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
auto call = m_proxy->doOperationClientSideAsync(100);
auto call = this->m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TEST_F(SdbusTestObject, CancelsPendingAsyncCallOnClientSide)
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = this->m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = this->m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_FALSE(call.isPending());
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(0);
auto call = this->m_proxy->doOperationClientSideAsync(0);
(void) future.get(); // Wait for the call to finish
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
ASSERT_TRUE(this->waitUntil([&call](){ return !call.isPending(); }));
}
TEST_F(SdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
TYPED_TEST(AsyncSdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
{
sdbus::PendingAsyncCall call;
ASSERT_FALSE(call.isPending());
}
TEST_F(SdbusTestObject, SupportsAsyncCallCopyAssignment)
TYPED_TEST(AsyncSdbusTestObject, SupportsAsyncCallCopyAssignment)
{
sdbus::PendingAsyncCall call;
call = m_proxy->doOperationClientSideAsync(100);
call = this->m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
TYPED_TEST(AsyncSdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
@ -234,7 +232,7 @@ TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doErroneousOperationClientSideAsync();
this->m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}

View File

@ -45,8 +45,6 @@ using ::testing::Eq;
using namespace std::chrono_literals;
using namespace sdbus::test;
using AConnection = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
@ -72,38 +70,38 @@ TEST(AnAdaptor, SupportsMoveSemantics)
static_assert(std::is_move_assignable_v<DummyTestAdaptor>);
}
TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
TYPED_TEST(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
});
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(matchingMessageReceived));
ASSERT_TRUE(this->waitUntil(matchingMessageReceived));
}
TEST_F(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
TYPED_TEST(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
});
slot.reset();
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
ASSERT_FALSE(this->waitUntil(matchingMessageReceived, 2s));
}
TEST_F(AConnection, CanAddFloatingMatchRule)
TYPED_TEST(AConnection, CanAddFloatingMatchRule)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
@ -115,32 +113,32 @@ TEST_F(AConnection, CanAddFloatingMatchRule)
matchingMessageReceived = true;
};
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
m_adaptor->emitSimpleSignal();
[[maybe_unused]] auto gotMessage = waitUntil(matchingMessageReceived, 2s);
this->m_adaptor->emitSimpleSignal();
[[maybe_unused]] auto gotMessage = this->waitUntil(matchingMessageReceived, 2s);
assert(gotMessage);
matchingMessageReceived = false;
con.reset();
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
ASSERT_FALSE(this->waitUntil(matchingMessageReceived, 2s));
}
TEST_F(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
TYPED_TEST(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
{
auto matchRule = "type='signal',interface='" + INTERFACE_NAME + "',member='simpleSignal'";
std::atomic<size_t> numberOfMatchingMessages{};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getMemberName() == "simpleSignal")
numberOfMatchingMessages++;
});
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
m_adaptor->emitSignalWithMap({});
this->m_adaptor->emitSignalWithMap({});
adaptor2->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
ASSERT_TRUE(this->waitUntil([&](){ return numberOfMatchingMessages == 2; }));
ASSERT_FALSE(this->waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
}

View File

@ -50,59 +50,57 @@ using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
TYPED_TEST(SdbusTestObject, CallsEmptyMethodSuccesfully)
{
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
ASSERT_NO_THROW(this->m_proxy->noArgNoReturn());
}
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
{
auto resInt = m_proxy->getInt();
auto resInt = this->m_proxy->getInt();
ASSERT_THAT(resInt, Eq(INT32_VALUE));
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
auto multiplyRes = this->m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
{
auto resTuple = m_proxy->getTuple();
auto resTuple = this->m_proxy->getTuple();
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithStructSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccesfully)
{
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
auto vectorRes = m_proxy->getInts16FromStruct(a);
auto vectorRes = this->m_proxy->getInts16FromStruct(a);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{0})); // because second item is by default initialized to 0
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> b{
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
};
vectorRes = m_proxy->getInts16FromStruct(b);
vectorRes = this->m_proxy->getInts16FromStruct(b);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
}
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithVariantSuccesfully)
{
sdbus::Variant v{DOUBLE_VALUE};
auto variantRes = m_proxy->processVariant(v);
sdbus::Variant variantRes = this->m_proxy->processVariant(v);
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
}
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
{
std::vector<int32_t> x{-2, 0, 2};
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
std::map<int32_t, sdbus::Variant> mapOfVariants = this->m_proxy->getMapOfVariants(x, y);
decltype(mapOfVariants) res{{-2, false}, {0, false}, {2, true}};
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
@ -110,69 +108,69 @@ TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
}
TEST_F(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
{
auto val = m_proxy->getStructInStruct();
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
auto val = this->m_proxy->getStructInStruct();
ASSERT_THAT(val.template get<0>(), Eq(STRING_VALUE));
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
{
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
auto val = this->m_proxy->sumStructItems({1, 2}, {3, 4});
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
{
auto val = m_proxy->sumVectorItems({1, 7}, {2, 3});
auto val = this->m_proxy->sumVectorItems({1, 7}, {2, 3});
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3));
}
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
{
auto resSignature = m_proxy->getSignature();
auto resSignature = this->m_proxy->getSignature();
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
{
auto resObjectPath = m_proxy->getObjPath();
auto resObjectPath = this->m_proxy->getObjPath();
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
{
auto resUnixFd = m_proxy->getUnixFd();
auto resUnixFd = this->m_proxy->getUnixFd();
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
{
auto resComplex = m_proxy->getComplex();
auto resComplex = this->m_proxy->getComplex();
ASSERT_THAT(resComplex.count(0), Eq(1));
}
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
TYPED_TEST(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
{
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
this->m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_TRUE(waitUntil(m_adaptor->m_wasMultiplyCalled));
ASSERT_THAT(m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
ASSERT_TRUE(this->waitUntil(this->m_adaptor->m_wasMultiplyCalled));
ASSERT_THAT(this->m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
{
auto res = m_proxy->doOperationWithTimeout(500ms, 20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
auto res = this->m_proxy->doOperationWithTimeout(500ms, 20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
ASSERT_THAT(res, Eq(20));
}
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
TYPED_TEST(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
{
auto start = std::chrono::steady_clock::now();
try
{
m_proxy->doOperationWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 1us, so we should time out
this->m_proxy->doOperationWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 1us, so we should time out
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
@ -188,11 +186,11 @@ TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
}
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
TYPED_TEST(SdbusTestObject, CallsMethodThatThrowsError)
{
try
{
m_proxy->throwError();
this->m_proxy->throwError();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
@ -206,74 +204,74 @@ TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
}
}
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
TYPED_TEST(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
ASSERT_NO_THROW(this->m_proxy->throwErrorWithNoReply());
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
ASSERT_TRUE(this->waitUntil(this->m_adaptor->m_wasThrowErrorCalled));
}
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
TYPED_TEST(SdbusTestObject, FailsCallingNonexistentMethod)
{
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
ASSERT_THROW(this->m_proxy->callNonexistentMethod(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
{
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
ASSERT_THROW(this->m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
{
TestProxy proxy("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
{
TestProxy proxy(BUS_NAME, "/sdbuscpp/path/that/does/not/exist");
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
TYPED_TEST(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
{
m_proxy->emitTwoSimpleSignals();
this->m_proxy->emitTwoSimpleSignals();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSignalWithMap));
}
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
{
m_proxy->doOperation(10); // This will save pointer to method call message on server side
this->m_proxy->doOperation(10); // This will save pointer to method call message on server side
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperation"));
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_methodCallMemberName, Eq("doOperation"));
}
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
{
m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
this->m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperationAsync"));
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_methodCallMemberName, Eq("doOperationAsync"));
}
#if LIBSYSTEMD_VERSION>=240
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
TYPED_TEST(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
{
s_adaptorConnection->setMethodCallTimeout(5000000);
ASSERT_THAT(s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
this->s_adaptorConnection->setMethodCallTimeout(5000000);
ASSERT_THAT(this->s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
}
#else
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
TYPED_TEST(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
{
ASSERT_THROW(s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
ASSERT_THROW(this->s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(this->s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
}
#endif
TEST_F(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
TYPED_TEST(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
{
auto proxy = std::make_unique<TestProxy>(BUS_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);

View File

@ -51,35 +51,33 @@ using ::testing::IsEmpty;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
TYPED_TEST(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
{
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(this->m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
TYPED_TEST(SdbusTestObject, FailsWritingToReadOnlyProperty)
{
ASSERT_THROW(m_proxy->setStateProperty("new_value"), sdbus::Error);
ASSERT_THROW(this->m_proxy->setStateProperty("new_value"), sdbus::Error);
}
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
{
uint32_t newActionValue = 5678;
m_proxy->action(newActionValue);
this->m_proxy->action(newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
TYPED_TEST(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
{
m_proxy->blocking(true); // This will save pointer to property get message on server side
this->m_proxy->blocking(true); // This will save pointer to property get message on server side
ASSERT_THAT(m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(m_adaptor->m_propertySetSender, Not(IsEmpty()));
ASSERT_THAT(this->m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_propertySetSender, Not(IsEmpty()));
}

View File

@ -44,32 +44,30 @@ using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalSuccesfully)
{
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
{
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, BUS_NAME, OBJECT_PATH);
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(this->waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(this->waitUntil(proxy2->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
TYPED_TEST(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
{
auto otherBusName = BUS_NAME + "2";
auto connection2 = sdbus::createConnection(otherBusName);
@ -77,93 +75,93 @@ TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
adaptor2->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_FALSE(this->waitUntil(this->m_proxy->m_gotSimpleSignal, 2s));
}
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccesfully)
{
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
this->m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("zero"));
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("one"));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSignalWithMap));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("zero"));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("one"));
}
TEST_F(SdbusTestObject, EmitsSignalWithLargeMapSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithLargeMapSuccesfully)
{
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 20'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
m_adaptor->emitSignalWithMap(largeMap);
this->m_adaptor->emitSignalWithMap(largeMap);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSignalWithMap));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
}
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
{
double d = 3.14;
m_adaptor->emitSignalWithVariant(d);
this->m_adaptor->emitSignalWithVariant(d);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(this->m_proxy->m_variantFromSignal, DoubleEq(d));
}
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
{
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
this->m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(this->m_proxy->m_signatureFromSignal["platform"], Eq("av"));
}
TEST_F(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
TYPED_TEST(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
{
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
waitUntil(m_proxy->m_gotSimpleSignal);
this->waitUntil(this->m_proxy->m_gotSimpleSignal);
ASSERT_THAT(m_proxy->m_signalMsg, NotNull());
ASSERT_THAT(m_proxy->m_signalMemberName, Eq("simpleSignal"));
ASSERT_THAT(this->m_proxy->m_signalMsg, NotNull());
ASSERT_THAT(this->m_proxy->m_signalMemberName, Eq("simpleSignal"));
}
TEST_F(SdbusTestObject, UnregistersSignalHandler)
TYPED_TEST(SdbusTestObject, UnregistersSignalHandler)
{
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_FALSE(this->waitUntil(this->m_proxy->m_gotSimpleSignal, 2s));
}
TEST_F(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
TYPED_TEST(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
{
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, BUS_NAME, OBJECT_PATH);
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_TRUE(this->waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(this->waitUntil(proxy2->m_gotSimpleSignal));
ASSERT_FALSE(this->waitUntil(this->m_proxy->m_gotSimpleSignal, 2s));
}
TEST_F(SdbusTestObject, ReRegistersSignalHandler)
TYPED_TEST(SdbusTestObject, ReRegistersSignalHandler)
{
// unregister simple-signal handler
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_FALSE(this->waitUntil(this->m_proxy->m_gotSimpleSignal, 2s));
// re-register simple-signal handler
ASSERT_NO_THROW(m_proxy->reRegisterSimpleSignalHandler());
ASSERT_NO_THROW(this->m_proxy->reRegisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(this->waitUntil(this->m_proxy->m_gotSimpleSignal));
}

View File

@ -48,18 +48,16 @@ using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, PingsViaPeerInterface)
TYPED_TEST(SdbusTestObject, PingsViaPeerInterface)
{
ASSERT_NO_THROW(m_proxy->Ping());
ASSERT_NO_THROW(this->m_proxy->Ping());
}
TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
TYPED_TEST(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
{
// If /etc/machine-id does not exist in your system (which is very likely because you have
// a non-systemd Linux), org.freedesktop.DBus.Peer.GetMachineId() will not work. To solve
@ -68,45 +66,45 @@ TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
if (::access("/etc/machine-id", F_OK) == -1)
GTEST_SKIP() << "/etc/machine-id file does not exist, GetMachineId() will not work";
ASSERT_NO_THROW(m_proxy->GetMachineId());
ASSERT_NO_THROW(this->m_proxy->GetMachineId());
}
// TODO: Adjust expected xml and uncomment this test
//TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
//TYPED_TEST(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
//{
// ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
// ASSERT_THAT(this->m_proxy->Introspect(), Eq(this->m_adaptor->getExpectedXmlApiDescription()));
//}
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, GetsPropertyViaPropertiesInterface)
{
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(this->m_proxy->Get(INTERFACE_NAME, "state").template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, SetsPropertyViaPropertiesInterface)
{
uint32_t newActionValue = 2345;
m_proxy->Set(INTERFACE_NAME, "action", newActionValue);
this->m_proxy->Set(INTERFACE_NAME, "action", newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
const auto properties = this->m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
EXPECT_THAT(properties.at("state").template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
TYPED_TEST(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*/ )
this->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));
@ -114,19 +112,19 @@ TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
signalReceived = true;
};
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
m_proxy->action(DEFAULT_ACTION_VALUE*2);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
this->m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
this->m_proxy->action(DEFAULT_ACTION_VALUE*2);
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
ASSERT_TRUE(waitUntil(signalReceived));
ASSERT_TRUE(this->waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
TYPED_TEST(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 )
this->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));
@ -136,38 +134,38 @@ TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
signalReceived = true;
};
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
ASSERT_TRUE(waitUntil(signalReceived));
ASSERT_TRUE(this->waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
TYPED_TEST(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
{
m_adaptor.reset();
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
this->m_adaptor.reset();
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
}
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
TYPED_TEST(SdbusTestObject, GetsManagedObjectsSuccessfully)
{
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH)
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
.at("action").template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2)
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
.at("action").template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
@ -189,16 +187,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
this->m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
ASSERT_TRUE(this->waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
@ -225,16 +223,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal();
this->m_adaptor->emitInterfacesAddedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
ASSERT_TRUE(this->waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(1));
@ -242,16 +240,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
this->m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
ASSERT_TRUE(this->waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
@ -264,7 +262,7 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal();
this->m_adaptor->emitInterfacesRemovedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
ASSERT_TRUE(this->waitUntil(signalReceived));
}

View File

@ -2,7 +2,7 @@
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
* @file TestFixture.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
@ -27,8 +27,12 @@
#include "TestFixture.h"
namespace sdbus { namespace test {
std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection();
std::unique_ptr<sdbus::IConnection> BaseTestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
std::unique_ptr<sdbus::IConnection> BaseTestFixture::s_proxyConnection = sdbus::createSystemBusConnection();
std::thread TestFixture<SdEventLoop>::s_eventLoopThread{};
sd_event *TestFixture<SdEventLoop>::s_sdEvent{};
int TestFixture<SdEventLoop>::s_eventExitFd{-1};
}}

View File

@ -2,7 +2,7 @@
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
* @file TestFixture.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
@ -33,6 +33,8 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <systemd/sd-event.h>
#include <sys/eventfd.h>
#include <thread>
#include <chrono>
@ -41,22 +43,17 @@
namespace sdbus { namespace test {
class TestFixture : public ::testing::Test
class BaseTestFixture : public ::testing::Test
{
public:
static void SetUpTestCase()
{
s_proxyConnection->enterEventLoopAsync();
s_adaptorConnection->requestName(BUS_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_adaptorConnection->releaseName(BUS_NAME);
s_adaptorConnection->leaveEventLoop();
s_proxyConnection->leaveEventLoop();
}
template <typename _Fnc>
@ -84,17 +81,17 @@ public:
private:
void SetUp() override
{
m_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, BUS_NAME, MANAGER_PATH);
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, BUS_NAME, OBJECT_PATH);
m_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, BUS_NAME, MANAGER_PATH);
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, BUS_NAME, OBJECT_PATH);
m_objectManagerAdaptor = std::make_unique<ObjectManagerTestAdaptor>(*s_adaptorConnection, MANAGER_PATH);
m_adaptor = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH);
m_objectManagerAdaptor = std::make_unique<ObjectManagerTestAdaptor>(*s_adaptorConnection, MANAGER_PATH);
m_adaptor = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH);
}
void TearDown() override
{
m_proxy.reset();
m_adaptor.reset();
m_proxy.reset();
m_adaptor.reset();
}
public:
@ -106,6 +103,93 @@ public:
std::unique_ptr<TestProxy> m_proxy;
};
struct SdBusCppLoop{};
struct SdEventLoop{};
template <typename _EventLoop>
class TestFixture : public BaseTestFixture{};
// Fixture working upon internal sdbus-c++ event loop
template <>
class TestFixture<SdBusCppLoop> : public BaseTestFixture
{
public:
static void SetUpTestCase()
{
BaseTestFixture::SetUpTestCase();
s_proxyConnection->enterEventLoopAsync();
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()
{
BaseTestFixture::TearDownTestCase();
s_adaptorConnection->leaveEventLoop();
s_proxyConnection->leaveEventLoop();
}
};
// Fixture working upon attached external sd-event loop
template <>
class TestFixture<SdEventLoop> : public BaseTestFixture
{
public:
static void SetUpTestCase()
{
sd_event_new(&s_sdEvent);
s_proxyConnection->attachSdEventLoop(s_sdEvent);
s_adaptorConnection->attachSdEventLoop(s_sdEvent);
s_eventExitFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
// No callback means that the event loop will exit when this event source triggers
sd_event_add_io(s_sdEvent, nullptr, s_eventExitFd, EPOLLIN, nullptr, NULL);
s_eventLoopThread = std::thread([]()
{
sd_event_loop(s_sdEvent);
});
BaseTestFixture::SetUpTestCase();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
}
static void TearDownTestCase()
{
uint64_t value = 1;
write(s_eventExitFd, &value, sizeof(value));
s_eventLoopThread.join();
sd_event_unref(s_sdEvent);
close(s_eventExitFd);
BaseTestFixture::TearDownTestCase();
}
private:
static std::thread s_eventLoopThread;
static sd_event *s_sdEvent;
static int s_eventExitFd;
};
typedef ::testing::Types<SdBusCppLoop, SdEventLoop> EventLoopTags;
TYPED_TEST_SUITE(TestFixture, EventLoopTags);
template <typename _EventLoop>
using SdbusTestObject = TestFixture<_EventLoop>;
TYPED_TEST_SUITE(SdbusTestObject, EventLoopTags);
template <typename _EventLoop>
using AsyncSdbusTestObject = TestFixture<_EventLoop>;
TYPED_TEST_SUITE(AsyncSdbusTestObject, EventLoopTags);
template <typename _EventLoop>
using AConnection = TestFixture<_EventLoop>;
TYPED_TEST_SUITE(AConnection, EventLoopTags);
}}
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_ */

View File

@ -2,7 +2,7 @@
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
* @file TestProxy.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++

View File

@ -2,7 +2,7 @@
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
* @file TestProxy.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++

View File

@ -92,6 +92,8 @@ public:
MOCK_METHOD2(sd_bus_creds_get_egid, int(sd_bus_creds *, gid_t *));
MOCK_METHOD2(sd_bus_creds_get_supplementary_gids, int(sd_bus_creds *, const gid_t **));
MOCK_METHOD2(sd_bus_creds_get_selinux_context, int(sd_bus_creds *, const char **));
MOCK_METHOD1(sd_bus_get_event, sd_event *(sd_bus *bus));
};
#endif //SDBUS_CXX_SDBUS_MOCK_H