feat: introduce sd-event integration

This commit is contained in:
Stanislav Angelovič
2023-01-25 00:02:51 +01:00
parent 2efb6909b5
commit b9aa770f58
14 changed files with 654 additions and 289 deletions

View File

@ -441,7 +441,7 @@ On the **server** side, we generally need to create D-Bus objects and publish th
* its internal event loop
* either in a blocking way, through `enterEventLoop()`,
* or in a non-blocking async way, through `enterEventLoopAsync()`,
* or an external event loop. This is suitable if we use in our application an event loop implementation of our choice (e.g., GLib Event Loop, boost::asio, ...) and we want to hook up our sdbus-c++ connections with it. See [Using sdbus-c++ in external event loops](#using-sdbus-c-in-external-event-loops) section for more information.
* or an external event loop. This is suitable if we use in our application an event loop implementation of our choice (e.g., sd-event, GLib Event Loop, boost::asio, ...) and we want to hook up our sdbus-c++ connections with it. See [Using sdbus-c++ in external event loops](#using-sdbus-c-in-external-event-loops) section for more information.
The object takes the D-Bus connection as a reference in its constructor. This is the only way to wire the connection and the object together. We must make sure the connection exists as long as objects using it exist.
@ -1723,7 +1723,13 @@ Note that the returned timeout should be considered only a maximum sleeping time
`PollData::fd` is a bus I/O fd. `PollData::eventFd` is an sdbus-c++ internal fd for communicating important changes from other threads to the event loop thread, so the event loop retrieves new poll data (with updated timeout, for example) and, potentially, processes pending D-Bus messages (like signals that came in during a blocking synchronous call from other thread, or queued outgoing messages that are very big to be able to have been sent in one shot from another thread), before the next poll.
Consult `IConnection::PollData` and `IConnection::getEventLoopPollData()` documentation for more potentially more information.
Consult `IConnection::PollData` and `IConnection::getEventLoopPollData()` documentation for potentially more information.
### Integration of sd-event event loop
sdbus-c++ provides built-in integration of sd-event, which makes it very convenient to hook sdbus-c++ connection up with an sd-event event loop.
See documentation of `IConnection::attachSdEventLoop()`, `IConnection::detachSdEventLoop()`, and `IConnection::getSdEventLoop()` methods, or sdbus-c++ integration tests for an example of use. These methods are sdbus-c++ counterparts to and mimic the behavior of these underlying sd-bus functions: `sd_bus_attach_event()`, `sd_bus_detach_event()`, and `sd_bus_get_event()`. Their manual pages provide much more details about their behavior.
Conclusion
----------

View File

@ -35,6 +35,7 @@
#include <optional>
struct sd_bus;
struct sd_event;
namespace sdbus {
@ -107,6 +108,32 @@ namespace sdbus {
*/
virtual void leaveEventLoop() = 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;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
*
@ -150,6 +177,10 @@ namespace sdbus {
* Use PollData::getPollTimeout() to have the timeout value converted
* in a form that can be passed to poll(2).
*
* The bus connection conveniently integrates sd-event event loop.
* To attach the bus connection to an sd-event event loop, use
* attachSdEventLoop() function.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual PollData getEventLoopPollData() const = 0;
@ -170,7 +201,8 @@ namespace sdbus {
*
* You don't need to directly call this method or getEventLoopPollData() method
* when using convenient, internal bus connection event loops through
* enterEventLoop() or enterEventLoopAsync() calls.
* enterEventLoop() or enterEventLoopAsync() calls, or when the bus is
* connected to an sd-event event loop through attachSdEventLoop().
* It is invoked automatically when necessary.
*
* @throws sdbus::Error in case of failure

View File

@ -32,6 +32,9 @@
#include <sdbus-c++/Error.h>
#include "ScopeGuard.h"
#include SDBUS_HEADER
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
#include <systemd/sd-event.h>
#endif
#include <unistd.h>
#include <poll.h>
#include <sys/eventfd.h>
@ -274,6 +277,189 @@ void Connection::addMatchAsync(const std::string& match, message_handler callbac
floatingMatchRules_.push_back(addMatchAsync(match, std::move(callback), std::move(installCallback)));
}
void Connection::attachSdEventLoop(sd_event *event, int priority)
{
#ifndef SDBUS_basu
auto pollData = getEventLoopPollData();
auto sdEvent = createSdEventSlot(event);
auto sdTimeEventSource = createSdTimeEventSourceSlot(event, priority);
auto sdIoEventSource = createSdIoEventSourceSlot(event, pollData.fd, priority);
auto sdInternalEventSource = createSdInternalEventSourceSlot(event, pollData.eventFd, priority);
sdEvent_ = std::make_unique<SdEvent>(SdEvent{ std::move(sdEvent)
, std::move(sdTimeEventSource)
, std::move(sdIoEventSource)
, std::move(sdInternalEventSource) });
#else
(void)event;
(void)priority;
SDBUS_THROW_ERROR("sd_event integration is not supported on this platform", EOPNOTSUPP);
#endif
}
void Connection::detachSdEventLoop()
{
sdEvent_.reset();
}
sd_event *Connection::getSdEventLoop()
{
return sdEvent_ ? static_cast<sd_event*>(sdEvent_->sdEvent.get()) : nullptr;
}
#ifndef SDBUS_basu
Slot Connection::createSdEventSlot(sd_event *event)
{
// 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);
return Slot{event, [](void* event){ sd_event_unref((sd_event*)event); }};
}
Slot Connection::createSdTimeEventSourceSlot(sd_event *event, int priority)
{
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){ deleteSdEventSource((sd_event_source*)source); }};
r = sd_event_source_set_priority(timeEventSource, priority);
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);
return sdTimeEventSource;
}
Slot Connection::createSdIoEventSourceSlot(sd_event *event, int fd, int priority)
{
sd_event_source *ioEventSource{};
auto r = sd_event_add_io(event, &ioEventSource, fd, 0, onSdIoEvent, this);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add io event", -r);
Slot sdIoEventSource{ioEventSource, [](void* source){ deleteSdEventSource((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);
return sdIoEventSource;
}
Slot Connection::createSdInternalEventSourceSlot(sd_event *event, int fd, int priority)
{
sd_event_source *internalEventSource{};
auto r = sd_event_add_io(event, &internalEventSource, fd, 0, onSdInternalEvent, this);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add internal event", -r);
Slot sdInternalEventSource{internalEventSource, [](void* source){ deleteSdEventSource((sd_event_source*)source); }};
// sd-event loop calls prepare callbacks for all event sources, not just for the one that fired now.
// So since onSdEventPrepare is already registered on ioEventSource, we don't need to duplicate it here.
//r = sd_event_source_set_prepare(internalEventSource, onSdEventPrepare);
//SDBUS_THROW_ERROR_IF(r < 0, "Failed to set prepare callback for internal event", -r);
r = sd_event_source_set_priority(internalEventSource, priority);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set priority for internal event", -r);
r = sd_event_source_set_description(internalEventSource, "internal-event");
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set priority for IO event", -r);
return sdInternalEventSource;
}
int Connection::onSdTimerEvent(sd_event_source */*s*/, uint64_t /*usec*/, void *userdata)
{
auto connection = static_cast<Connection*>(userdata);
assert(connection != nullptr);
(void)connection->processPendingEvent();
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);
(void)connection->processPendingEvent();
return 1;
}
int Connection::onSdInternalEvent(sd_event_source */*s*/, int /*fd*/, uint32_t /*revents*/, void *userdata)
{
auto connection = static_cast<Connection*>(userdata);
assert(connection != nullptr);
// It's not really necessary to processPendingEvent() here. We just clear the event fd.
// The sd-event loop will before the next poll call prepare callbacks for all event sources,
// including I/O bus fd. This will get up-to-date poll timeout, which will be zero if there
// are pending D-Bus messages in the read queue, which will immediately wake up next poll
// and go to onSdIoEvent() handler, which calls processPendingEvent(). Viola.
// For external event loops that only have access to public sdbus-c++ API, processPendingEvent()
// is the only option to clear event fd (it comes at a little extra cost but on the other hand
// the solution is simpler for clients -- we don't provide an extra method for just clearing
// the event fd. There is one method for both fd's -- and that's processPendingEvent().
// Kept here so that potential readers know what to do in their custom external event loops.
//(void)connection->processPendingEvent();
connection->eventFd_.clear();
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 on I/O fd
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 poll events to watch out for on internal event fd
auto* sdInternalEventSource = static_cast<sd_event_source*>(connection->sdEvent_->sdInternalEventSource.get());
r = sd_event_source_set_io_events(sdInternalEventSource, POLLIN);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set poll events for internal event source", -r);
// Set current timeout to the time event source (it 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, static_cast<uint64_t>(sdbusPollData.timeout.count()));
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);
return 1;
}
void Connection::deleteSdEventSource(sd_event_source *s)
{
#if LIBSYSTEMD_VERSION>=243
sd_event_source_disable_unref(s);
#else
sd_event_source_set_enabled(s, SD_EVENT_OFF);
sd_event_source_unref(s);
#endif
}
#endif // SDBUS_basu
Slot Connection::addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable

View File

@ -38,6 +38,8 @@
#include <string>
#include <vector>
struct sd_event_source;
namespace sdbus::internal {
class Connection final
@ -97,6 +99,10 @@ namespace sdbus::internal {
[[nodiscard]] Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override;
void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, 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;
@ -162,6 +168,19 @@ namespace sdbus::internal {
static int sdbus_match_install_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
private:
#ifndef SDBUS_basu // sd_event integration is not supported if instead of libsystemd we are based on basu
Slot createSdEventSlot(sd_event *event);
Slot createSdTimeEventSourceSlot(sd_event *event, int priority);
Slot createSdIoEventSourceSlot(sd_event *event, int fd, int priority);
Slot createSdInternalEventSourceSlot(sd_event *event, int fd, int priority);
static void deleteSdEventSource(sd_event_source *s);
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 onSdInternalEvent(sd_event_source *s, int fd, uint32_t revents, void *userdata);
static int onSdEventPrepare(sd_event_source *s, void *userdata);
#endif
struct EventFd
{
EventFd();
@ -180,6 +199,15 @@ namespace sdbus::internal {
sd_bus_slot *slot;
};
// sd-event integration
struct SdEvent
{
Slot sdEvent;
Slot sdTimeEventSource;
Slot sdIoEventSource;
Slot sdInternalEventSource;
};
private:
std::unique_ptr<ISdBus> sdbus_;
BusPtr bus_;
@ -187,6 +215,7 @@ namespace sdbus::internal {
EventFd loopExitFd_; // To wake up event loop I/O polling to exit
EventFd eventFd_; // To wake up event loop I/O polling to re-enter poll with fresh PollData values
std::vector<Slot> floatingMatchRules_;
std::unique_ptr<SdEvent> sdEvent_; // Integration of systemd sd-event event loop implementation
};
}

View File

@ -300,11 +300,13 @@ int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
{
#ifdef SDBUS_basu
#ifndef SDBUS_basu
return ::sd_bus_open_system_remote(ret, host);
#else
(void)ret;
(void)host;
// https://git.sr.ht/~emersion/basu/commit/01d33b244eb6
return -EOPNOTSUPP;
#else
return ::sd_bus_open_system_remote(ret, host);
#endif
}

View File

@ -107,12 +107,21 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
target_compile_definitions(sdbus-c++-unit-tests PRIVATE
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
SDBUS_${LIBSYSTEMD_IMPL}
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
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)
target_compile_definitions(sdbus-c++-integration-tests PRIVATE
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
SDBUS_${LIBSYSTEMD})
if(NOT LIBSYSTEMD STREQUAL "basu")
# 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)
else()
# sd-event implementation is not part of basu, so its integration tests will be skipped
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
endif()
# 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, (1s).count()); // 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,21 +154,21 @@ 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, InvokesMethodAsynchronouslyOnClientSideWithFuture)
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
{
auto future = m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
auto future = this->m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
{
auto future = m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
auto future = this->m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
auto methodReply = future.get();
uint32_t returnValue{};
@ -179,72 +177,72 @@ TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasic
ASSERT_THAT(returnValue, 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(); }));
}
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, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
TYPED_TEST(AsyncSdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
{
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);
@ -252,14 +250,14 @@ TEST_F(SdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doErroneousOperationClientSideAsync();
this->m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}
TEST_F(SdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
TYPED_TEST(AsyncSdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
{
auto future = m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
auto future = this->m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
ASSERT_THROW(future.get(), sdbus::Error);
}

View File

@ -45,7 +45,6 @@ using ::testing::Eq;
using namespace std::chrono_literals;
using namespace sdbus::test;
using AConnection = TestFixture;
using ADirectConnection = TestFixtureWithDirectConnection;
/*-------------------------------------*/
@ -73,61 +72,61 @@ 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));
}
TEST_F(AConnection, CanInstallMatchRuleAsynchronously)
TYPED_TEST(AConnection, CanInstallMatchRuleAsynchronously)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
std::atomic<bool> matchRuleInstalled{false};
auto slot = s_proxyConnection->addMatchAsync( matchRule
, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}
, [&](sdbus::Message& /*msg*/)
{
matchRuleInstalled = true;
} );
auto slot = this->s_proxyConnection->addMatchAsync( matchRule
, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}
, [&](sdbus::Message& /*msg*/)
{
matchRuleInstalled = true;
} );
EXPECT_TRUE(waitUntil(matchRuleInstalled));
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(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(waitUntil(matchingMessageReceived, 1s));
}
TEST_F(AConnection, CanAddFloatingMatchRule)
TYPED_TEST(AConnection, CanAddFloatingMatchRule)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
@ -139,31 +138,31 @@ TEST_F(AConnection, CanAddFloatingMatchRule)
matchingMessageReceived = true;
};
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
[[maybe_unused]] auto gotMessage = waitUntil(matchingMessageReceived, 2s);
assert(gotMessage);
matchingMessageReceived = false;
con.reset();
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
}
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));

View File

@ -50,66 +50,64 @@ 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, CallsMethodWithStdVariantSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithStdVariantSuccesfully)
{
std::variant<int32_t, double, std::string> v{DOUBLE_VALUE};
auto variantRes = m_proxy->processVariant(v);
auto variantRes = this->m_proxy->processVariant(v);
ASSERT_THAT(std::get<int32_t>(variantRes), 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{ {sdbus::Variant{-2}, sdbus::Variant{false}}
, {sdbus::Variant{0}, sdbus::Variant{false}}
, {sdbus::Variant{2}, sdbus::Variant{true}}};
@ -119,69 +117,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->sumArrayItems({1, 7}, {2, 3, 4});
auto val = this->m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
}
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(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, (20ms).count()); // 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, (1s).count()); // 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)
@ -197,11 +195,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)
@ -215,74 +213,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(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));
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
EXPECT_TRUE(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)
{
#if defined(__clang__) && defined(__FreeBSD__)
GTEST_SKIP() << "https://github.com/Kistler-Group/sdbus-cpp/issues/359";

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(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(this->m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(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,94 @@ TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
adaptor2->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
}
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(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(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(sdbus::Variant{d});
this->m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
this->m_adaptor->emitSignalWithVariant(d);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
ASSERT_TRUE(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(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);
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(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
}
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_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
}
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(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
// 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(waitUntil(this->m_proxy->m_gotSimpleSignal));
}

View File

@ -48,43 +48,41 @@ 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 (::access("/etc/machine-id", F_OK) == -1 &&
::access("/var/lib/dbus/machine-id", F_OK) == -1)
GTEST_SKIP() << "/etc/machine-id and /var/lib/dbus/machine-id files do 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, GetsPropertyAsynchronouslyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
{
std::promise<std::string> promise;
auto future = promise.get_future();
m_proxy->GetAsync(INTERFACE_NAME, "state", [&](const sdbus::Error* err, sdbus::Variant value)
this->m_proxy->GetAsync(INTERFACE_NAME, "state", [&](const sdbus::Error* err, sdbus::Variant value)
{
if (err == nullptr)
promise.set_value(value.get<std::string>());
@ -95,29 +93,29 @@ TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
ASSERT_THAT(future.get(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
{
auto future = m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
auto future = this->m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
ASSERT_THAT(future.get().get<std::string>(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(future.get().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", sdbus::Variant{newActionValue});
this->m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
{
uint32_t newActionValue = 2346;
std::promise<void> promise;
auto future = promise.get_future();
m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](const sdbus::Error* err)
this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value();
@ -126,35 +124,35 @@ TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
});
ASSERT_NO_THROW(future.get());
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
{
uint32_t newActionValue = 2347;
auto future = m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
auto future = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
ASSERT_NO_THROW(future.get());
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, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
{
std::promise<std::map<std::string, sdbus::Variant>> promise;
auto future = promise.get_future();
m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map<std::string, sdbus::Variant> value)
this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map<std::string, sdbus::Variant> value)
{
if (err == nullptr)
promise.set_value(std::move(value));
@ -169,24 +167,24 @@ TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
{
auto future = m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
auto future = this->m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
auto properties = future.get();
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));
@ -194,19 +192,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));
}
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));
@ -216,38 +214,38 @@ TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
signalReceived = true;
};
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
ASSERT_TRUE(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));
@ -269,16 +267,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
this->m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
ASSERT_TRUE(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
@ -305,16 +303,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal();
this->m_adaptor->emitInterfacesAddedSignal();
ASSERT_TRUE(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));
@ -322,16 +320,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
this->m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
ASSERT_TRUE(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
@ -344,7 +342,7 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal();
this->m_adaptor->emitInterfacesRemovedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}

View File

@ -2,7 +2,7 @@
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
* @file TestFixture.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
@ -28,7 +28,17 @@
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();
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
std::thread TestFixture<SdEventLoop>::s_adaptorEventLoopThread{};
std::thread TestFixture<SdEventLoop>::s_proxyEventLoopThread{};
sd_event *TestFixture<SdEventLoop>::s_adaptorSdEvent{};
sd_event *TestFixture<SdEventLoop>::s_proxySdEvent{};
int TestFixture<SdEventLoop>::s_eventExitFd{-1};
#endif // SDBUS_basu
}}

View File

@ -2,7 +2,7 @@
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
* @file TestFixture.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
@ -33,6 +33,10 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
#include <systemd/sd-event.h>
#endif // SDBUS_basu
#include <sys/eventfd.h>
#include <thread>
#include <chrono>
@ -46,22 +50,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();
}
private:
@ -89,6 +88,108 @@ 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();
}
};
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
// Fixture working upon attached external sd-event loop
template <>
class TestFixture<SdEventLoop> : public BaseTestFixture
{
public:
static void SetUpTestCase()
{
sd_event_new(&s_adaptorSdEvent);
sd_event_new(&s_proxySdEvent);
s_adaptorConnection->attachSdEventLoop(s_adaptorSdEvent);
s_proxyConnection->attachSdEventLoop(s_proxySdEvent);
s_eventExitFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
auto exitHandler = [](sd_event_source *s, auto...){ return sd_event_exit(sd_event_source_get_event(s), 0); };
sd_event_add_io(s_adaptorSdEvent, nullptr, s_eventExitFd, EPOLLIN, exitHandler, nullptr);
sd_event_add_io(s_proxySdEvent, nullptr, s_eventExitFd, EPOLLIN, exitHandler, nullptr);
s_adaptorEventLoopThread = std::thread([]()
{
sd_event_loop(s_adaptorSdEvent);
});
s_proxyEventLoopThread = std::thread([]()
{
sd_event_loop(s_proxySdEvent);
});
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()
{
(void)eventfd_write(s_eventExitFd, 1);
s_adaptorEventLoopThread.join();
s_proxyEventLoopThread.join();
sd_event_unref(s_adaptorSdEvent);
sd_event_unref(s_proxySdEvent);
close(s_eventExitFd);
BaseTestFixture::TearDownTestCase();
}
private:
static std::thread s_adaptorEventLoopThread;
static std::thread s_proxyEventLoopThread;
static sd_event *s_adaptorSdEvent;
static sd_event *s_proxySdEvent;
static int s_eventExitFd;
};
typedef ::testing::Types<SdBusCppLoop, SdEventLoop> EventLoopTags;
#else // SDBUS_basu
typedef ::testing::Types<SdBusCppLoop> EventLoopTags;
#endif // SDBUS_basu
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);
class TestFixtureWithDirectConnection : public ::testing::Test
{
private: