From 2b83d7ca2d87084accca74b7ca581951230d853a Mon Sep 17 00:00:00 2001 From: ardazishvili Date: Sun, 17 Mar 2019 18:02:47 +0300 Subject: [PATCH] Mock sdbus lib, add unit tests of Connection class. Introduce mock of sdbus library through extracting its interface. Set up unit tests of Connection class through injection of sdbusMock to constructor. Clients of Connections class should use fabrics instead. --- CMakeLists.txt | 7 +- README.md | 1 + doc/systemd-dbus-config.md | 54 +++++++ src/Connection.cpp | 63 ++++---- src/Connection.h | 14 +- src/ISdBus.h | 55 +++++++ src/SdBus.cpp | 107 +++++++++++++ src/SdBus.h | 53 +++++++ test/CMakeLists.txt | 3 +- .../integrationtests/AdaptorAndProxy_test.cpp | 4 +- test/unittests/Connection_test.cpp | 148 ++++++++++++++++++ test/unittests/mocks/SdBusMock.h | 55 +++++++ 12 files changed, 529 insertions(+), 35 deletions(-) create mode 100644 doc/systemd-dbus-config.md create mode 100644 src/ISdBus.h create mode 100644 src/SdBus.cpp create mode 100644 src/SdBus.h create mode 100644 test/unittests/Connection_test.cpp create mode 100644 test/unittests/mocks/SdBusMock.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cc5d56f..6a9d6b5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,8 @@ set(SDBUSCPP_CPP_SRCS ${SDBUSCPP_SOURCE_DIR}/ObjectProxy.cpp ${SDBUSCPP_SOURCE_DIR}/Types.cpp ${SDBUSCPP_SOURCE_DIR}/Flags.cpp - ${SDBUSCPP_SOURCE_DIR}/VTableUtils.c) + ${SDBUSCPP_SOURCE_DIR}/VTableUtils.c + ${SDBUSCPP_SOURCE_DIR}/SdBus.cpp) set(SDBUSCPP_HDR_SRCS ${SDBUSCPP_SOURCE_DIR}/Connection.h @@ -42,7 +43,9 @@ set(SDBUSCPP_HDR_SRCS ${SDBUSCPP_SOURCE_DIR}/Object.h ${SDBUSCPP_SOURCE_DIR}/ObjectProxy.h ${SDBUSCPP_SOURCE_DIR}/ScopeGuard.h - ${SDBUSCPP_SOURCE_DIR}/VTableUtils.h) + ${SDBUSCPP_SOURCE_DIR}/VTableUtils.h + ${SDBUSCPP_SOURCE_DIR}/SdBus.h + ${SDBUSCPP_SOURCE_DIR}/ISdBus.h) set(SDBUSCPP_PUBLIC_HDRS ${SDBUSCPP_INCLUDE_DIR}/ConvenienceClasses.h diff --git a/README.md b/README.md index 582a036..4447f2e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ References/documentation * [D-Bus Specification](https://dbus.freedesktop.org/doc/dbus-specification.html) * [sd-bus Overview](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html) * [Tutorial: Using sdbus-c++](doc/using-sdbus-c++.md) +* [Systemd and dbus configuration](doc/systemd-dbus-config.md) Contributing ------------ diff --git a/doc/systemd-dbus-config.md b/doc/systemd-dbus-config.md new file mode 100644 index 0000000..b32a632 --- /dev/null +++ b/doc/systemd-dbus-config.md @@ -0,0 +1,54 @@ +Systemd and dbus configuration +======================= + +**Table of contents** + +1. [Introduction](#introduction) +2. [Systemd configuration](#systemd-configuration) +3. [Dbus configuration](#dbus-configuration) + +Introduction +------------ + +To run executable as a systemd service you may need some additional setup. For example, you may need explicitly allow +the usage of your service. Following chapters contain template configurations. + + +Systemd configuration +--------------------------------------- + +Filename should use `.service` extension. It also must be placed in configuration directory (/etc/systemd/system in +Ubuntu 18.04.1 LTS) + +``` +[Unit] +Description=nameOfService + +[Service] +ExecStart=/path/to/executable + +[Install] +WantedBy=multi-user.target +``` + +Dbus configuration +------------------ + +Typical default D-Bus configuration does not allow to register services except explicitly allowed. Filename should +contain name of your service, e.g `/etc/dbus-1/system.d/org.sdbuscpp.concatenator.conf`. So, here is template +configuration to use dbus interface under root: + +``` + + + + + + + + +``` + +If you need access from other user `root` should be substituted by desired username. For more refer to `man dbus-daemon`. \ No newline at end of file diff --git a/src/Connection.cpp b/src/Connection.cpp index e5815d7..cc9c3c4 100755 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -24,6 +24,7 @@ */ #include "Connection.h" +#include "SdBus.h" #include #include #include "ScopeGuard.h" @@ -34,8 +35,9 @@ namespace sdbus { namespace internal { -Connection::Connection(Connection::BusType type) - : busType_(type) +Connection::Connection(Connection::BusType type, std::unique_ptr&& interface) + : busType_(type), + iface_(std::move(interface)) { auto bus = openBus(busType_); bus_.reset(bus); @@ -53,13 +55,13 @@ Connection::~Connection() void Connection::requestName(const std::string& name) { - auto r = sd_bus_request_name(bus_.get(), name.c_str(), 0); + auto r = iface_->sd_bus_request_name(bus_.get(), name.c_str(), 0); SDBUS_THROW_ERROR_IF(r < 0, "Failed to request bus name", -r); } void Connection::releaseName(const std::string& name) { - auto r = sd_bus_release_name(bus_.get(), name.c_str()); + auto r = iface_->sd_bus_release_name(bus_.get(), name.c_str()); SDBUS_THROW_ERROR_IF(r < 0, "Failed to release bus name", -r); } @@ -97,7 +99,7 @@ void* Connection::addObjectVTable( const std::string& objectPath { sd_bus_slot *slot{}; - auto r = sd_bus_add_object_vtable( bus_.get() + auto r = iface_->sd_bus_add_object_vtable( bus_.get() , &slot , objectPath.c_str() , interfaceName.c_str() @@ -111,7 +113,7 @@ void* Connection::addObjectVTable( const std::string& objectPath void Connection::removeObjectVTable(void* vtableHandle) { - sd_bus_slot_unref((sd_bus_slot *)vtableHandle); + iface_->sd_bus_slot_unref((sd_bus_slot *)vtableHandle); } sdbus::MethodCall Connection::createMethodCall( const std::string& destination @@ -122,9 +124,9 @@ sdbus::MethodCall Connection::createMethodCall( const std::string& destination sd_bus_message *sdbusMsg{}; // Returned message will become an owner of sdbusMsg - SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); }; + SCOPE_EXIT{ iface_->sd_bus_message_unref(sdbusMsg); }; - auto r = sd_bus_message_new_method_call( bus_.get() + auto r = iface_->sd_bus_message_new_method_call( bus_.get() , &sdbusMsg , destination.c_str() , objectPath.c_str() @@ -145,7 +147,7 @@ sdbus::Signal Connection::createSignal( const std::string& objectPath // Returned message will become an owner of sdbusSignal SCOPE_EXIT{ sd_bus_message_unref(sdbusSignal); }; - auto r = sd_bus_message_new_signal( bus_.get() + auto r = iface_->sd_bus_message_new_signal( bus_.get() , &sdbusSignal , objectPath.c_str() , interfaceName.c_str() @@ -165,7 +167,7 @@ void* Connection::registerSignalHandler( const std::string& objectPath sd_bus_slot *slot{}; auto filter = composeSignalMatchFilter(objectPath, interfaceName, signalName); - auto r = sd_bus_add_match(bus_.get(), &slot, filter.c_str(), callback, userData); + auto r = iface_->sd_bus_add_match(bus_.get(), &slot, filter.c_str(), callback, userData); SDBUS_THROW_ERROR_IF(r < 0, "Failed to register signal handler", -r); @@ -174,7 +176,7 @@ void* Connection::registerSignalHandler( const std::string& objectPath void Connection::unregisterSignalHandler(void* handlerCookie) { - sd_bus_slot_unref((sd_bus_slot *)handlerCookie); + iface_->sd_bus_slot_unref((sd_bus_slot *)handlerCookie); } void Connection::sendReplyAsynchronously(const sdbus::MethodReply& reply) @@ -186,20 +188,21 @@ void Connection::sendReplyAsynchronously(const sdbus::MethodReply& reply) std::unique_ptr Connection::clone() const { - return std::make_unique(busType_); + auto interface = std::make_unique(SdBus()); + assert(interface != nullptr); + return std::make_unique(busType_, std::move(interface)); } sd_bus* Connection::openBus(Connection::BusType type) { - static std::map busTypeToFactory - { - {sdbus::internal::Connection::BusType::eSystem, &sd_bus_open_system}, - {sdbus::internal::Connection::BusType::eSession, &sd_bus_open_user} - }; - sd_bus* bus{}; - - auto r = busTypeToFactory[type](&bus); + int r = 0; + if (type == BusType::eSystem) + r = iface_->sd_bus_open_system(&bus); + else if (type == BusType::eSession) + r = iface_->sd_bus_open_user(&bus); + else + assert(false); SDBUS_THROW_ERROR_IF(r < 0, "Failed to open bus", -r); assert(bus != nullptr); @@ -215,7 +218,7 @@ void Connection::finishHandshake(sd_bus* bus) assert(bus != nullptr); - auto r = sd_bus_flush(bus); + auto r = iface_->sd_bus_flush(bus); SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r); } @@ -263,7 +266,7 @@ bool Connection::processPendingRequest() assert(bus != nullptr); - int r = sd_bus_process(bus, nullptr); + int r = iface_->sd_bus_process(bus, nullptr); SDBUS_THROW_ERROR_IF(r < 0, "Failed to process bus requests", -r); @@ -288,16 +291,16 @@ Connection::WaitResult Connection::waitForNextRequest() assert(bus != nullptr); assert(notificationFd_ != 0); - auto r = sd_bus_get_fd(bus); + auto r = iface_->sd_bus_get_fd(bus); SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r); auto sdbusFd = r; - r = sd_bus_get_events(bus); + r = iface_->sd_bus_get_events(bus); SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus events", -r); short int sdbusEvents = r; uint64_t usec; - sd_bus_get_timeout(bus, &usec); + iface_->sd_bus_get_timeout(bus, &usec); struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {notificationFd_, POLLIN, 0}}; auto fdsCount = sizeof(fds)/sizeof(fds[0]); @@ -356,7 +359,10 @@ std::unique_ptr createConnection(const std::string& name) std::unique_ptr createSystemBusConnection() { - return std::make_unique(sdbus::internal::Connection::BusType::eSystem); + auto interface = std::make_unique(SdBus()); + assert(interface != nullptr); + return std::make_unique(sdbus::internal::Connection::BusType::eSystem, + std::move(interface)); } std::unique_ptr createSystemBusConnection(const std::string& name) @@ -368,7 +374,10 @@ std::unique_ptr createSystemBusConnection(const std::string& std::unique_ptr createSessionBusConnection() { - return std::make_unique(sdbus::internal::Connection::BusType::eSession); + auto interface = std::make_unique(SdBus()); + assert(interface != nullptr); + return std::make_unique(sdbus::internal::Connection::BusType::eSession, + std::move(interface)); } std::unique_ptr createSessionBusConnection(const std::string& name) diff --git a/src/Connection.h b/src/Connection.h index 40abc12..68bd15e 100755 --- a/src/Connection.h +++ b/src/Connection.h @@ -29,6 +29,8 @@ #include #include #include "IConnection.h" +#include "ISdBus.h" + #include #include #include @@ -49,7 +51,7 @@ namespace sdbus { namespace internal { eSession }; - Connection(BusType type); + Connection(BusType type, std::unique_ptr&& interface); ~Connection(); void requestName(const std::string& name) override; @@ -93,8 +95,8 @@ namespace sdbus { namespace internal { return msgsToProcess || asyncMsgsToProcess; } }; - static sd_bus* openBus(Connection::BusType type); - static void finishHandshake(sd_bus* bus); + sd_bus* openBus(Connection::BusType type); + void finishHandshake(sd_bus* bus); static int createLoopNotificationDescriptor(); static void closeLoopNotificationDescriptor(int fd); bool processPendingRequest(); @@ -108,7 +110,11 @@ namespace sdbus { namespace internal { void joinWithProcessingLoop(); private: - std::unique_ptr bus_{nullptr, &sd_bus_flush_close_unref}; + std::unique_ptr iface_; + std::unique_ptr> bus_ {nullptr, [this](sd_bus* bus) + { + return iface_->sd_bus_flush_close_unref(bus); + }}; std::thread asyncLoopThread_; std::mutex mutex_; std::queue asyncReplies_; diff --git a/src/ISdBus.h b/src/ISdBus.h new file mode 100644 index 0000000..bcbb4b4 --- /dev/null +++ b/src/ISdBus.h @@ -0,0 +1,55 @@ +/** + * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * + * @file ISdBus.h + * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) + * + * Created on: Mar 12, 2019 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_ISDBUS_H +#define SDBUS_CXX_ISDBUS_H + +#include + +class ISdBus +{ +public: + virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0; + virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0; + virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) = 0; + virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0; + virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) = 0; + virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) = 0; + virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) = 0; + virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0; + virtual int sd_bus_open_user(sd_bus **ret) = 0; + virtual int sd_bus_open_system(sd_bus **ret) = 0; + virtual int sd_bus_flush(sd_bus *bus) = 0; + virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0; + virtual int sd_bus_get_fd(sd_bus *bus) = 0; + virtual int sd_bus_get_events(sd_bus *bus) = 0; + virtual int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) = 0; + virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0; + + virtual ~ISdBus() = default; +}; + +#endif //SDBUS_CXX_ISDBUS_H diff --git a/src/SdBus.cpp b/src/SdBus.cpp new file mode 100644 index 0000000..3e64721 --- /dev/null +++ b/src/SdBus.cpp @@ -0,0 +1,107 @@ +/** + * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * + * @file SdBus.cpp + * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) + * + * Created on: Mar 3, 2019 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#include "SdBus.h" + +int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) +{ + return ::sd_bus_request_name(bus, name, flags); +} + +int SdBus::sd_bus_release_name(sd_bus *bus, const char *name) +{ + return ::sd_bus_release_name(bus, name); +} + +int SdBus::sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) +{ + return ::sd_bus_add_object_vtable(bus, slot, path, interface, vtable, userdata); +} + +sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot) +{ + return ::sd_bus_slot_unref(slot); +} + +int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) +{ + return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member); +} + +sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m) +{ + return ::sd_bus_message_unref(m); +} + +int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) +{ + return ::sd_bus_message_new_signal(bus, m, path, interface, member); +} + +int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) +{ + return :: sd_bus_add_match(bus, slot, match, callback, userdata); +} + +int SdBus::sd_bus_open_user(sd_bus **ret) +{ + return ::sd_bus_open_user(ret); +} + +int SdBus::sd_bus_open_system(sd_bus **ret) +{ + return ::sd_bus_open_system(ret); +} + +int SdBus::sd_bus_flush(sd_bus *bus) +{ + return ::sd_bus_flush(bus); +} + +int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r) +{ + return ::sd_bus_process(bus, r); +} + +int SdBus::sd_bus_get_fd(sd_bus *bus) +{ + return ::sd_bus_get_fd(bus); +} + +int SdBus::sd_bus_get_events(sd_bus *bus) +{ + return ::sd_bus_get_events(bus); +} + +int SdBus::sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) +{ + return ::sd_bus_get_timeout(bus, timeout_usec); +} + +sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus) +{ + return ::sd_bus_flush_close_unref(bus); +} diff --git a/src/SdBus.h b/src/SdBus.h new file mode 100644 index 0000000..521f9e8 --- /dev/null +++ b/src/SdBus.h @@ -0,0 +1,53 @@ +/** + * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * + * @file SdBus.h + * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) + * + * Created on: Mar 3, 2019 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_SDBUS_H +#define SDBUS_CXX_SDBUS_H + +#include "ISdBus.h" + +class SdBus : public ISdBus +{ +public: + int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override; + int sd_bus_release_name(sd_bus *bus, const char *name) override; + int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override; + sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override; + int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) override; + sd_bus_message* sd_bus_message_unref(sd_bus_message *m) override; + int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) override; + int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override; + int sd_bus_open_user(sd_bus **ret) override; + int sd_bus_open_system(sd_bus **ret) override; + int sd_bus_flush(sd_bus *bus) override; + int sd_bus_process(sd_bus *bus, sd_bus_message **r) override; + int sd_bus_get_fd(sd_bus *bus) override; + int sd_bus_get_events(sd_bus *bus) override; + int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) override; + sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override; +}; + +#endif //SDBUS_C_SDBUS_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1905f2d..99be990 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -36,7 +36,8 @@ set(UNITTESTS_SRCS ${UNITTESTS_SOURCE_DIR}/libsdbus-c++_unittests.cpp ${UNITTESTS_SOURCE_DIR}/Message_test.cpp ${UNITTESTS_SOURCE_DIR}/Types_test.cpp - ${UNITTESTS_SOURCE_DIR}/TypeTraits_test.cpp) + ${UNITTESTS_SOURCE_DIR}/TypeTraits_test.cpp + ${UNITTESTS_SOURCE_DIR}/Connection_test.cpp) set(INTEGRATIONTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/integrationtests) set(INTEGRATIONTESTS_SRCS diff --git a/test/integrationtests/AdaptorAndProxy_test.cpp b/test/integrationtests/AdaptorAndProxy_test.cpp index b904a5b..56b0682 100644 --- a/test/integrationtests/AdaptorAndProxy_test.cpp +++ b/test/integrationtests/AdaptorAndProxy_test.cpp @@ -25,6 +25,7 @@ // Own #include "Connection.h" +#include "SdBus.h" #include "TestingAdaptor.h" #include "TestingProxy.h" @@ -87,7 +88,8 @@ public: std::unique_ptr m_proxy; }; -sdbus::internal::Connection AdaptorAndProxyFixture::m_connection{sdbus::internal::Connection::BusType::eSystem}; +sdbus::internal::Connection AdaptorAndProxyFixture::m_connection{sdbus::internal::Connection::BusType::eSystem, + std::make_unique(SdBus())}; } diff --git a/test/unittests/Connection_test.cpp b/test/unittests/Connection_test.cpp new file mode 100644 index 0000000..bc8d765 --- /dev/null +++ b/test/unittests/Connection_test.cpp @@ -0,0 +1,148 @@ +/** + * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * + * @file Connection_test.cpp + * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) + * + * Created on: Feb 4, 2019 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#include "Connection.h" +#include "unittests/mocks/SdBusMock.h" + +#include + +using ::testing::_; +using ::testing::DoAll; +using ::testing::SetArgPointee; +using ::testing::Return; +using ::testing::NiceMock; + +using BusType = sdbus::internal::Connection::BusType; + + +class ConnectionCreationTest : public ::testing::Test +{ +protected: + ConnectionCreationTest() = default; + + std::unique_ptr> mock_ { std::make_unique>() }; + sd_bus* STUB_ { reinterpret_cast(1) }; +}; + +using ASystemBusConnection = ConnectionCreationTest; +using ASessionBusConnection = ConnectionCreationTest; + +TEST_F(ASystemBusConnection, OpensAndFlushesBusWhenCreated) +{ + EXPECT_CALL(*mock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1))); + EXPECT_CALL(*mock_, sd_bus_flush(_)).Times(1); + sdbus::internal::Connection(BusType::eSystem, std::move(mock_)); +} + +TEST_F(ASessionBusConnection, OpensAndFlushesBusWhenCreated) +{ + EXPECT_CALL(*mock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1))); + EXPECT_CALL(*mock_, sd_bus_flush(_)).Times(1); + sdbus::internal::Connection(BusType::eSession, std::move(mock_)); +} + +TEST_F(ASystemBusConnection, ClosesAndUnrefsBusWhenDestructed) +{ + ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1))); + EXPECT_CALL(*mock_, sd_bus_flush_close_unref(_)).Times(1); + sdbus::internal::Connection(BusType::eSession, std::move(mock_)); +} + +TEST_F(ASessionBusConnection, ClosesAndUnrefsBusWhenDestructed) +{ + ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1))); + EXPECT_CALL(*mock_, sd_bus_flush_close_unref(_)).Times(1); + sdbus::internal::Connection(BusType::eSession, std::move(mock_)); +} + +TEST_F(ASystemBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction) +{ + ON_CALL(*mock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(-1))); + ASSERT_THROW(sdbus::internal::Connection(BusType::eSystem, std::move(mock_)), sdbus::Error); +} + +TEST_F(ASessionBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction) +{ + ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(-1))); + ASSERT_THROW(sdbus::internal::Connection(BusType::eSession, std::move(mock_)), sdbus::Error); +} + +TEST_F(ASystemBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction) +{ + ON_CALL(*mock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1))); + ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(-1)); + ASSERT_THROW(sdbus::internal::Connection(BusType::eSystem, std::move(mock_)), sdbus::Error); +} + +TEST_F(ASessionBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction) +{ + ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1))); + ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(-1)); + ASSERT_THROW(sdbus::internal::Connection(BusType::eSession, std::move(mock_)), sdbus::Error); +} + +class ConnectionRequestTest : public ::testing::TestWithParam +{ +protected: + ConnectionRequestTest() = default; + void SetUp() override + { + switch (GetParam()) + { + case BusType::eSystem: + EXPECT_CALL(*mock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1))); + break; + case BusType::eSession: + EXPECT_CALL(*mock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1))); + break; + default: + break; + } + ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(1)); + ON_CALL(*mock_, sd_bus_flush_close_unref(_)).WillByDefault(Return(STUB_)); + } + + std::unique_ptr> mock_ { std::make_unique>() }; + sd_bus* STUB_ { reinterpret_cast(1) }; +}; + +using AConnectionNameRequest = ConnectionRequestTest; + +TEST_P(AConnectionNameRequest, DoesNotThrowOnSuccess) +{ + EXPECT_CALL(*mock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1)); + sdbus::internal::Connection(GetParam(), std::move(mock_)).requestName(""); +} + +TEST_P(AConnectionNameRequest, ThrowsOnFail) +{ + EXPECT_CALL(*mock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1)); + + auto conn_ = sdbus::internal::Connection(GetParam(), std::move(mock_)) ; + ASSERT_THROW(conn_.requestName(""), sdbus::Error); +} + +INSTANTIATE_TEST_SUITE_P(Request, AConnectionNameRequest, ::testing::Values(BusType::eSystem, BusType::eSession)); diff --git a/test/unittests/mocks/SdBusMock.h b/test/unittests/mocks/SdBusMock.h new file mode 100644 index 0000000..9506c23 --- /dev/null +++ b/test/unittests/mocks/SdBusMock.h @@ -0,0 +1,55 @@ +/** + * (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland + * + * @file SdBusMock.h + * @author Ardazishvili Roman (ardazishvili.roman@yandex.ru) + * + * Created on: Mar 12, 2019 + * Project: sdbus-c++ + * Description: High-level D-Bus IPC C++ library based on sd-bus + * + * This file is part of sdbus-c++. + * + * sdbus-c++ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * sdbus-c++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with sdbus-c++. If not, see . + */ + +#ifndef SDBUS_CXX_SDBUS_MOCK_H +#define SDBUS_CXX_SDBUS_MOCK_H + +#include "ISdBus.h" + +#include + +class SdBusMock : public ISdBus +{ +public: + MOCK_METHOD3(sd_bus_request_name, int(sd_bus *bus, const char *name, uint64_t flags)); + MOCK_METHOD2(sd_bus_release_name, int(sd_bus *bus, const char *name)); + MOCK_METHOD6(sd_bus_add_object_vtable, int(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata)); + MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot)); + MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)); + MOCK_METHOD1(sd_bus_message_unref, sd_bus_message* (sd_bus_message *m)); + MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)); + MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata)); + MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret)); + MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret)); + MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus)); + MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r)); + MOCK_METHOD1(sd_bus_get_fd, int(sd_bus *bus)); + MOCK_METHOD1(sd_bus_get_events, int(sd_bus *bus)); + MOCK_METHOD2(sd_bus_get_timeout, int(sd_bus *bus, uint64_t *timeout_usec)); + MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus)); +}; + +#endif //SDBUS_CXX_SDBUS_MOCK_H