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