1 /* Copyright (C) 2013 BMW Group
2 * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
3 * Author: Juergen Gehring (juergen.gehring@bmw.de)
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include <gtest/gtest.h>
19 #include <type_traits>
22 #include <CommonAPI/CommonAPI.h>
24 #include <CommonAPI/DBus/DBusConnection.h>
25 #include <CommonAPI/DBus/DBusProxy.h>
26 #include <CommonAPI/DBus/DBusRuntime.h>
28 #include "DBusTestUtils.h"
29 #include "DemoMainLoop.h"
31 #include "commonapi/tests/PredefinedTypeCollection.h"
32 #include "commonapi/tests/DerivedTypeCollection.h"
33 #include "commonapi/tests/TestInterfaceProxy.h"
34 #include "commonapi/tests/TestInterfaceStubDefault.h"
35 #include "commonapi/tests/TestInterfaceDBusStubAdapter.h"
37 #include "commonapi/tests/TestInterfaceDBusProxy.h"
40 const std::string testAddress1 = "local:my.first.test:commonapi.address.one";
41 const std::string testAddress2 = "local:my.second.test:commonapi.address.two";
42 const std::string testAddress3 = "local:my.third.test:commonapi.address.three";
43 const std::string testAddress4 = "local:my.fourth.test:commonapi.address.four";
44 const std::string testAddress5 = "local:my.fifth.test:commonapi.address.five";
45 const std::string testAddress6 = "local:my.sixth.test:commonapi.address.six";
46 const std::string testAddress7 = "local:my.seventh.test:commonapi.address.seven";
47 const std::string testAddress8 = "local:my.eigth.test:commonapi.address.eight";
49 //####################################################################################################################
51 class DBusBasicMainLoopTest: public ::testing::Test {
53 virtual void SetUp() {
56 virtual void TearDown() {
61 TEST_F(DBusBasicMainLoopTest, MainloopContextCanBeCreated) {
62 std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::load();
63 ASSERT_TRUE((bool) runtime);
65 std::shared_ptr<CommonAPI::MainLoopContext> context = runtime->getNewMainLoopContext();
66 ASSERT_TRUE((bool) context);
70 TEST_F(DBusBasicMainLoopTest, SeveralMainloopContextsCanBeCreated) {
71 std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::load();
72 ASSERT_TRUE((bool)runtime);
74 std::shared_ptr<CommonAPI::MainLoopContext> context1 = runtime->getNewMainLoopContext();
75 ASSERT_TRUE((bool) context1);
76 std::shared_ptr<CommonAPI::MainLoopContext> context2 = runtime->getNewMainLoopContext();
77 ASSERT_TRUE((bool) context2);
78 std::shared_ptr<CommonAPI::MainLoopContext> context3 = runtime->getNewMainLoopContext();
79 ASSERT_TRUE((bool) context3);
81 ASSERT_NE(context1, context2);
82 ASSERT_NE(context1, context3);
83 ASSERT_NE(context2, context3);
86 struct TestSource: public CommonAPI::DispatchSource {
87 TestSource(const std::string value, std::string& result): value_(value), result_(result) {}
89 bool prepare(int64_t& timeout) {
96 result_.append(value_);
102 std::string& result_;
105 TEST_F(DBusBasicMainLoopTest, PrioritiesAreHandledCorrectlyInDemoMainloop) {
106 std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::load();
107 ASSERT_TRUE((bool) runtime);
109 std::shared_ptr<CommonAPI::MainLoopContext> context = runtime->getNewMainLoopContext();
110 ASSERT_TRUE((bool) context);
112 auto mainLoop = new CommonAPI::MainLoop(context);
114 std::string result = "";
116 TestSource* testSource1Default = new TestSource("A", result);
117 TestSource* testSource2Default = new TestSource("B", result);
118 TestSource* testSource1High = new TestSource("C", result);
119 TestSource* testSource1Low = new TestSource("D", result);
120 TestSource* testSource1VeryHigh = new TestSource("E", result);
121 context->registerDispatchSource(testSource1Default);
122 context->registerDispatchSource(testSource2Default, CommonAPI::DispatchPriority::DEFAULT);
123 context->registerDispatchSource(testSource1High, CommonAPI::DispatchPriority::HIGH);
124 context->registerDispatchSource(testSource1Low, CommonAPI::DispatchPriority::LOW);
125 context->registerDispatchSource(testSource1VeryHigh, CommonAPI::DispatchPriority::VERY_HIGH);
127 mainLoop->doSingleIteration(CommonAPI::TIMEOUT_INFINITE);
129 std::string reference("ECABD");
130 ASSERT_EQ(reference, result);
134 //####################################################################################################################
136 class DBusMainLoopTest: public ::testing::Test {
138 virtual void SetUp() {
139 runtime_ = CommonAPI::Runtime::load();
140 ASSERT_TRUE((bool) runtime_);
142 context_ = runtime_->getNewMainLoopContext();
143 ASSERT_TRUE((bool) context_);
144 mainLoop_ = new CommonAPI::MainLoop(context_);
146 mainloopFactory_ = runtime_->createFactory(context_);
147 ASSERT_TRUE((bool) mainloopFactory_);
148 standardFactory_ = runtime_->createFactory();
149 ASSERT_TRUE((bool) standardFactory_);
151 servicePublisher_ = runtime_->getServicePublisher();
152 ASSERT_TRUE((bool) servicePublisher_);
155 virtual void TearDown() {
159 std::shared_ptr<CommonAPI::Runtime> runtime_;
160 std::shared_ptr<CommonAPI::MainLoopContext> context_;
161 std::shared_ptr<CommonAPI::Factory> mainloopFactory_;
162 std::shared_ptr<CommonAPI::Factory> standardFactory_;
163 std::shared_ptr<CommonAPI::ServicePublisher> servicePublisher_;
164 CommonAPI::MainLoop* mainLoop_;
168 TEST_F(DBusMainLoopTest, ServiceInDemoMainloopCanBeAddressed) {
169 std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stub = std::make_shared<
170 commonapi::tests::TestInterfaceStubDefault>();
171 ASSERT_TRUE(servicePublisher_->registerService(stub, testAddress1, mainloopFactory_));
173 auto proxy = standardFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress1);
174 ASSERT_TRUE((bool) proxy);
176 while (!proxy->isAvailable()) {
177 mainLoop_->doSingleIteration(50000);
180 uint32_t uint32Value = 42;
181 std::string stringValue = "Hai :)";
183 std::future<CommonAPI::CallStatus> futureStatus = proxy->testVoidPredefinedTypeMethodAsync(
186 [&] (const CommonAPI::CallStatus& status) {
187 EXPECT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(status));
194 ASSERT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(futureStatus.get()));
196 servicePublisher_->unregisterService(testAddress1);
200 TEST_F(DBusMainLoopTest, ProxyInDemoMainloopCanCallMethods) {
201 std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stub = std::make_shared<
202 commonapi::tests::TestInterfaceStubDefault>();
203 ASSERT_TRUE(servicePublisher_->registerService(stub, testAddress2, standardFactory_));
207 auto proxy = mainloopFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress2);
208 ASSERT_TRUE((bool) proxy);
210 while (!proxy->isAvailable()) {
211 mainLoop_->doSingleIteration();
215 uint32_t uint32Value = 42;
216 std::string stringValue = "Hai :)";
218 std::future<CommonAPI::CallStatus> futureStatus = proxy->testVoidPredefinedTypeMethodAsync(
221 [&] (const CommonAPI::CallStatus& status) {
222 EXPECT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(status));
229 ASSERT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(futureStatus.get()));
231 servicePublisher_->unregisterService(testAddress2);
234 TEST_F(DBusMainLoopTest, ProxyAndServiceInSameDemoMainloopCanCommunicate) {
235 std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stub = std::make_shared<
236 commonapi::tests::TestInterfaceStubDefault>();
237 ASSERT_TRUE(servicePublisher_->registerService(stub, testAddress4, mainloopFactory_));
239 auto proxy = mainloopFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress4);
240 ASSERT_TRUE((bool) proxy);
242 while (!proxy->isAvailable()) {
243 mainLoop_->doSingleIteration();
247 uint32_t uint32Value = 42;
248 std::string stringValue = "Hai :)";
251 std::future<CommonAPI::CallStatus> futureStatus = proxy->testVoidPredefinedTypeMethodAsync(
254 [&] (const CommonAPI::CallStatus& status) {
255 EXPECT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(status));
262 ASSERT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(futureStatus.get()));
264 servicePublisher_->unregisterService(testAddress4);
268 class BigDataTestStub: public commonapi::tests::TestInterfaceStubDefault {
269 void testDerivedTypeMethod(
270 commonapi::tests::DerivedTypeCollection::TestEnumExtended2 testEnumExtended2InValue,
271 commonapi::tests::DerivedTypeCollection::TestMap testMapInValue,
272 commonapi::tests::DerivedTypeCollection::TestEnumExtended2& testEnumExtended2OutValue,
273 commonapi::tests::DerivedTypeCollection::TestMap& testMapOutValue) {
274 testEnumExtended2OutValue = testEnumExtended2InValue;
275 testMapOutValue = testMapInValue;
280 TEST_F(DBusMainLoopTest, ProxyAndServiceInSameDemoMainloopCanHandleBigData) {
281 std::shared_ptr<BigDataTestStub> stub = std::make_shared<
283 ASSERT_TRUE(servicePublisher_->registerService(stub, testAddress8, mainloopFactory_));
285 auto proxy = mainloopFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress8);
286 ASSERT_TRUE((bool) proxy);
288 while (!proxy->isAvailable()) {
289 mainLoop_->doSingleIteration();
293 uint32_t uint32Value = 42;
294 std::string stringValue = "Hai :)";
297 commonapi::tests::DerivedTypeCollection::TestEnumExtended2 testEnumExtended2InValue =
298 commonapi::tests::DerivedTypeCollection::TestEnumExtended2::E_OK;
299 commonapi::tests::DerivedTypeCollection::TestMap testMapInValue;
301 // Estimated amount of data (differring padding at beginning/end of Map/Array etc. not taken into account):
302 // 4 + 4 + 500 * (4 + (4 + 4 + 100 * (11 + 1 + 4)) + 4 ) = 811008
303 for(uint32_t i = 0; i < 500; ++i) {
304 commonapi::tests::DerivedTypeCollection::TestArrayTestStruct testArrayTestStruct;
305 for(uint32_t j = 0; j < 100; ++j) {
306 commonapi::tests::DerivedTypeCollection::TestStruct testStruct("Hai all (:", j);
307 testArrayTestStruct.push_back(testStruct);
309 testMapInValue.insert( {i, testArrayTestStruct} );
312 std::future<CommonAPI::CallStatus> futureStatus = proxy->testDerivedTypeMethodAsync(
313 testEnumExtended2InValue,
315 [&] (const CommonAPI::CallStatus& status,
316 commonapi::tests::DerivedTypeCollection::TestEnumExtended2 testEnumExtended2OutValue,
317 commonapi::tests::DerivedTypeCollection::TestMap testMapOutValue) {
318 EXPECT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(status));
319 EXPECT_EQ(testEnumExtended2InValue, testEnumExtended2OutValue);
320 EXPECT_EQ(testMapInValue.size(), testMapOutValue.size());
327 ASSERT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(futureStatus.get()));
329 servicePublisher_->unregisterService(testAddress8);
332 TEST_F(DBusMainLoopTest, DemoMainloopClientsHandleNonavailableServices) {
333 auto proxy = mainloopFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress3);
334 ASSERT_TRUE((bool) proxy);
336 uint32_t uint32Value = 42;
337 std::string stringValue = "Hai :)";
339 std::future<CommonAPI::CallStatus> futureStatus = proxy->testVoidPredefinedTypeMethodAsync(
342 [&] (const CommonAPI::CallStatus& status) {
343 //Will never be called, @see DBusProxyAsyncCallbackHandler
344 EXPECT_EQ(toString(CommonAPI::CallStatus::NOT_AVAILABLE), toString(status));
348 ASSERT_EQ(toString(CommonAPI::CallStatus::NOT_AVAILABLE), toString(futureStatus.get()));
351 //##################################################################################################
353 class GDispatchWrapper: public GSource {
355 GDispatchWrapper(CommonAPI::DispatchSource* dispatchSource): dispatchSource_(dispatchSource) {}
356 CommonAPI::DispatchSource* dispatchSource_;
359 gboolean dispatchPrepare(GSource* source, gint* timeout) {
360 int64_t eventTimeout;
361 return static_cast<GDispatchWrapper*>(source)->dispatchSource_->prepare(eventTimeout);
364 gboolean dispatchCheck(GSource* source) {
365 return static_cast<GDispatchWrapper*>(source)->dispatchSource_->check();
368 gboolean dispatchExecute(GSource* source, GSourceFunc callback, gpointer userData) {
369 static_cast<GDispatchWrapper*>(source)->dispatchSource_->dispatch();
373 gboolean gWatchDispatcher(GIOChannel *source, GIOCondition condition, gpointer userData) {
374 CommonAPI::Watch* watch = static_cast<CommonAPI::Watch*>(userData);
375 watch->dispatch(condition);
379 gboolean gTimeoutDispatcher(void* userData) {
380 return static_cast<CommonAPI::DispatchSource*>(userData)->dispatch();
384 static GSourceFuncs standardGLibSourceCallbackFuncs = {
392 class DBusInGLibMainLoopTest: public ::testing::Test {
394 virtual void SetUp() {
397 std::shared_ptr<CommonAPI::Runtime> runtime_ = CommonAPI::Runtime::load();
398 ASSERT_TRUE((bool) runtime_);
400 context_ = runtime_->getNewMainLoopContext();
401 ASSERT_TRUE((bool) context_);
405 mainloopFactory_ = runtime_->createFactory(context_);
406 ASSERT_TRUE((bool) mainloopFactory_);
407 standardFactory_ = runtime_->createFactory();
408 ASSERT_TRUE((bool) standardFactory_);
410 servicePublisher_ = runtime_->getServicePublisher();
411 ASSERT_TRUE((bool) servicePublisher_);
414 virtual void TearDown() {
417 void doSubscriptions() {
418 context_->subscribeForTimeouts(
419 std::bind(&DBusInGLibMainLoopTest::timeoutAddedCallback, this, std::placeholders::_1, std::placeholders::_2),
420 std::bind(&DBusInGLibMainLoopTest::timeoutRemovedCallback, this, std::placeholders::_1)
423 context_->subscribeForWatches(
424 std::bind(&DBusInGLibMainLoopTest::watchAddedCallback, this, std::placeholders::_1, std::placeholders::_2),
425 std::bind(&DBusInGLibMainLoopTest::watchRemovedCallback, this, std::placeholders::_1)
428 context_->subscribeForWakeupEvents(
429 std::bind(&DBusInGLibMainLoopTest::wakeupMain, this)
435 std::shared_ptr<CommonAPI::MainLoopContext> context_;
436 std::shared_ptr<CommonAPI::Factory> mainloopFactory_;
437 std::shared_ptr<CommonAPI::Factory> standardFactory_;
438 std::shared_ptr<CommonAPI::ServicePublisher> servicePublisher_;
440 static constexpr bool mayBlock_ = true;
442 std::map<CommonAPI::DispatchSource*, GSource*> gSourceMappings;
444 GIOChannel* dbusChannel_;
446 void watchAddedCallback(CommonAPI::Watch* watch, const CommonAPI::DispatchPriority dispatchPriority) {
447 const pollfd& fileDesc = watch->getAssociatedFileDescriptor();
448 dbusChannel_ = g_io_channel_unix_new(fileDesc.fd);
450 GSource* gWatch = g_io_create_watch(dbusChannel_, static_cast<GIOCondition>(fileDesc.events));
451 g_source_set_callback(gWatch, reinterpret_cast<GSourceFunc>(&gWatchDispatcher), watch, NULL);
453 const auto& dependentSources = watch->getDependentDispatchSources();
454 for (auto dependentSourceIterator = dependentSources.begin();
455 dependentSourceIterator != dependentSources.end();
456 dependentSourceIterator++) {
457 GSource* gDispatchSource = g_source_new(&standardGLibSourceCallbackFuncs, sizeof(GDispatchWrapper));
458 static_cast<GDispatchWrapper*>(gDispatchSource)->dispatchSource_ = *dependentSourceIterator;
460 g_source_add_child_source(gWatch, gDispatchSource);
462 gSourceMappings.insert( {*dependentSourceIterator, gDispatchSource} );
464 g_source_attach(gWatch, NULL);
467 void watchRemovedCallback(CommonAPI::Watch* watch) {
468 g_source_remove_by_user_data(watch);
471 g_io_channel_unref(dbusChannel_);
475 const auto& dependentSources = watch->getDependentDispatchSources();
476 for (auto dependentSourceIterator = dependentSources.begin();
477 dependentSourceIterator != dependentSources.end();
478 dependentSourceIterator++) {
479 GSource* gDispatchSource = g_source_new(&standardGLibSourceCallbackFuncs, sizeof(GDispatchWrapper));
480 GSource* gSource = gSourceMappings.find(*dependentSourceIterator)->second;
481 g_source_destroy(gSource);
482 g_source_unref(gSource);
486 void timeoutAddedCallback(CommonAPI::Timeout* commonApiTimeoutSource, const CommonAPI::DispatchPriority dispatchPriority) {
487 GSource* gTimeoutSource = g_timeout_source_new(commonApiTimeoutSource->getTimeoutInterval());
488 g_source_set_callback(gTimeoutSource, &gTimeoutDispatcher, commonApiTimeoutSource, NULL);
489 g_source_attach(gTimeoutSource, NULL);
492 void timeoutRemovedCallback(CommonAPI::Timeout* timeout) {
493 g_source_remove_by_user_data(timeout);
497 g_main_context_wakeup(NULL);
502 TEST_F(DBusInGLibMainLoopTest, ProxyInGLibMainloopCanCallMethods) {
503 auto proxy = mainloopFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress5);
504 ASSERT_TRUE((bool) proxy);
506 std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stub = std::make_shared<
507 commonapi::tests::TestInterfaceStubDefault>();
508 ASSERT_TRUE(servicePublisher_->registerService(stub, testAddress5, standardFactory_));
510 while(!proxy->isAvailable()) {
511 g_main_context_iteration(NULL, mayBlock_);
515 uint32_t uint32Value = 24;
516 std::string stringValue = "Hai :)";
518 std::future<CommonAPI::CallStatus> futureStatus = proxy->testVoidPredefinedTypeMethodAsync(
521 [&] (const CommonAPI::CallStatus& status) {
522 EXPECT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(status));
528 g_main_context_iteration(NULL, mayBlock_);
532 ASSERT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(futureStatus.get()));
534 servicePublisher_->unregisterService(testAddress5);
538 TEST_F(DBusInGLibMainLoopTest, ServiceInGLibMainloopCanBeAddressed) {
539 auto proxy = standardFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress6);
540 ASSERT_TRUE((bool) proxy);
542 std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stub = std::make_shared<
543 commonapi::tests::TestInterfaceStubDefault>();
544 ASSERT_TRUE(servicePublisher_->registerService(stub, testAddress6, mainloopFactory_));
546 uint32_t uint32Value = 42;
547 std::string stringValue = "Ciao (:";
549 while(!proxy->isAvailable()) {
550 g_main_context_iteration(NULL, mayBlock_);
554 std::future<CommonAPI::CallStatus> futureStatus = proxy->testVoidPredefinedTypeMethodAsync(
557 [&] (const CommonAPI::CallStatus& status) {
558 EXPECT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(status));
560 //Wakeup needed as the service will be in a poll-block when the client
561 //call returns, and no other timeout is present to get him out of there.
562 g_main_context_wakeup(NULL);
567 g_main_context_iteration(NULL, mayBlock_);
571 ASSERT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(futureStatus.get()));
573 servicePublisher_->unregisterService(testAddress6);
577 TEST_F(DBusInGLibMainLoopTest, ProxyAndServiceInSameGlibMainloopCanCommunicate) {
578 auto proxy = mainloopFactory_->buildProxy<commonapi::tests::TestInterfaceProxy>(testAddress7);
579 ASSERT_TRUE((bool) proxy);
581 std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stub = std::make_shared<
582 commonapi::tests::TestInterfaceStubDefault>();
583 ASSERT_TRUE(servicePublisher_->registerService(stub, testAddress7, mainloopFactory_));
585 uint32_t uint32Value = 42;
586 std::string stringValue = "Ciao (:";
588 while(!proxy->isAvailable()) {
589 g_main_context_iteration(NULL, mayBlock_);
593 std::future<CommonAPI::CallStatus> futureStatus = proxy->testVoidPredefinedTypeMethodAsync(
596 [&] (const CommonAPI::CallStatus& status) {
597 EXPECT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(status));
599 //Wakeup needed as the service will be in a poll-block when the client
600 //call returns, and no other timeout is present to get him out of there.
601 g_main_context_wakeup(NULL);
606 g_main_context_iteration(NULL, mayBlock_);
610 ASSERT_EQ(toString(CommonAPI::CallStatus::SUCCESS), toString(futureStatus.get()));
612 servicePublisher_->unregisterService(testAddress7);
616 int main(int argc, char** argv) {
617 ::testing::InitGoogleTest(&argc, argv);
618 return RUN_ALL_TESTS();