2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Jan Olszak <j.olszak@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
22 * @author Jan Olszak (j.olszak@samsung.com)
23 * @brief Tests of the IPC
26 // TODO: Test connection limit
27 // TODO: Refactor tests - function for setting up env
28 // TODO: Callback wrapper that waits till the callback is called
34 #include "ipc/service.hpp"
35 #include "ipc/client.hpp"
36 #include "ipc/ipc-gsource.hpp"
37 #include "ipc/types.hpp"
38 #include "utils/glib-loop.hpp"
40 #include "config/fields.hpp"
41 #include "logger/logger.hpp"
49 #include <boost/filesystem.hpp>
51 using namespace vasum;
52 using namespace vasum::ipc;
53 using namespace vasum::utils;
54 using namespace std::placeholders;
55 namespace fs = boost::filesystem;
59 std::string socketPath;
62 : socketPath(fs::unique_path("/tmp/ipc-%%%%.socket").string())
67 fs::remove(socketPath);
73 SendData(int i = 0): intVal(i) {}
82 LongSendData(int i = 0, int waitTime = 1000): mSendData(i), mWaitTime(waitTime), intVal(i) {}
84 template<typename Visitor>
85 void accept(Visitor visitor)
87 std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime));
88 mSendData.accept(visitor);
90 template<typename Visitor>
91 void accept(Visitor visitor) const
93 std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime));
94 mSendData.accept(visitor);
103 CONFIG_REGISTER_EMPTY
106 struct ThrowOnAcceptData {
107 template<typename Visitor>
110 throw std::runtime_error("intentional failure in accept");
112 template<typename Visitor>
113 void accept(Visitor) const
115 throw std::runtime_error("intentional failure in accept const");
119 std::shared_ptr<EmptyData> returnEmptyCallback(const FileDescriptor, std::shared_ptr<EmptyData>&)
121 return std::shared_ptr<EmptyData>(new EmptyData());
124 std::shared_ptr<SendData> returnDataCallback(const FileDescriptor, std::shared_ptr<SendData>&)
126 return std::shared_ptr<SendData>(new SendData(1));
129 std::shared_ptr<SendData> echoCallback(const FileDescriptor, std::shared_ptr<SendData>& data)
134 std::shared_ptr<SendData> longEchoCallback(const FileDescriptor, std::shared_ptr<SendData>& data)
136 std::this_thread::sleep_for(std::chrono::seconds(1));
140 FileDescriptor connect(Service& s, Client& c)
142 // Connects the Client to the Service and returns Clients FileDescriptor
144 std::condition_variable cv;
146 FileDescriptor peerFD = 0;
147 auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFD) {
148 std::unique_lock<std::mutex> lock(mutex);
153 // TODO: On timeout remove the callback
154 s.setNewPeerCallback(newPeerCallback);
156 if (!s.isStarted()) {
163 std::unique_lock<std::mutex> lock(mutex);
164 BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() {
173 #if GLIB_CHECK_VERSION(2,36,0)
175 std::pair<FileDescriptor, IPCGSource::Pointer> connectServiceGSource(Service& s, Client& c)
178 std::condition_variable cv;
180 FileDescriptor peerFD = 0;
181 IPCGSource::Pointer ipcGSourcePtr = IPCGSource::create(s.getFDs(), std::bind(&Service::handle, &s, _1, _2));
183 auto newPeerCallback = [&cv, &peerFD, &mutex, ipcGSourcePtr](const FileDescriptor newFD) {
185 //TODO: Remove this if
186 ipcGSourcePtr->addFD(newFD);
188 std::unique_lock<std::mutex> lock(mutex);
194 // TODO: On timeout remove the callback
195 s.setNewPeerCallback(newPeerCallback);
196 s.setRemovedPeerCallback(std::bind(&IPCGSource::removeFD, ipcGSourcePtr, _1));
198 // Service starts to process
199 ipcGSourcePtr->attach();
203 std::unique_lock<std::mutex> lock(mutex);
204 BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() {
208 return std::make_pair(peerFD, ipcGSourcePtr);
211 std::pair<FileDescriptor, IPCGSource::Pointer> connectClientGSource(Service& s, Client& c)
213 // Connects the Client to the Service and returns Clients FileDescriptor
215 std::condition_variable cv;
217 FileDescriptor peerFD = 0;
218 auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFD) {
219 std::unique_lock<std::mutex> lock(mutex);
223 // TODO: On timeout remove the callback
224 s.setNewPeerCallback(newPeerCallback);
226 if (!s.isStarted()) {
227 // Service starts to process
233 IPCGSource::Pointer ipcGSourcePtr = IPCGSource::create(c.getFDs(),
234 std::bind(&Client::handle, &c, _1, _2));
236 ipcGSourcePtr->attach();
238 std::unique_lock<std::mutex> lock(mutex);
239 BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() {
243 return std::make_pair(peerFD, ipcGSourcePtr);
246 #endif // GLIB_CHECK_VERSION
249 void testEcho(Client& c, const MethodID methodID)
251 std::shared_ptr<SendData> sentData(new SendData(34));
252 std::shared_ptr<SendData> recvData = c.callSync<SendData, SendData>(methodID, sentData, 1000);
253 BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
256 void testEcho(Service& s, const MethodID methodID, const FileDescriptor peerFD)
258 std::shared_ptr<SendData> sentData(new SendData(56));
259 std::shared_ptr<SendData> recvData = s.callSync<SendData, SendData>(methodID, peerFD, sentData, 1000);
260 BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
266 BOOST_FIXTURE_TEST_SUITE(IPCSuite, Fixture)
268 BOOST_AUTO_TEST_CASE(ConstructorDestructor)
270 Service s(socketPath);
271 Client c(socketPath);
274 BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod)
276 Service s(socketPath);
278 s.addMethodHandler<EmptyData, EmptyData>(1, returnEmptyCallback);
279 s.addMethodHandler<SendData, SendData>(1, returnDataCallback);
283 s.addMethodHandler<SendData, SendData>(1, echoCallback);
284 s.addMethodHandler<SendData, SendData>(2, returnDataCallback);
286 Client c(socketPath);
293 BOOST_CHECK_THROW(testEcho(c, 2), IPCException);
296 BOOST_AUTO_TEST_CASE(ClientAddRemoveMethod)
298 Service s(socketPath);
299 Client c(socketPath);
300 c.addMethodHandler<EmptyData, EmptyData>(1, returnEmptyCallback);
301 c.addMethodHandler<SendData, SendData>(1, returnDataCallback);
303 FileDescriptor peerFD = connect(s, c);
305 c.addMethodHandler<SendData, SendData>(1, echoCallback);
306 c.addMethodHandler<SendData, SendData>(2, returnDataCallback);
308 testEcho(s, 1, peerFD);
313 BOOST_CHECK_THROW(testEcho(s, 1, peerFD), IPCException);
316 BOOST_AUTO_TEST_CASE(ServiceStartStop)
318 Service s(socketPath);
320 s.addMethodHandler<SendData, SendData>(1, returnDataCallback);
331 BOOST_AUTO_TEST_CASE(ClientStartStop)
333 Service s(socketPath);
334 Client c(socketPath);
335 c.addMethodHandler<SendData, SendData>(1, returnDataCallback);
349 BOOST_AUTO_TEST_CASE(SyncClientToServiceEcho)
351 Service s(socketPath);
352 s.addMethodHandler<SendData, SendData>(1, echoCallback);
353 s.addMethodHandler<SendData, SendData>(2, echoCallback);
356 Client c(socketPath);
362 BOOST_AUTO_TEST_CASE(Restart)
364 Service s(socketPath);
365 s.addMethodHandler<SendData, SendData>(1, echoCallback);
367 s.addMethodHandler<SendData, SendData>(2, echoCallback);
369 Client c(socketPath);
387 BOOST_AUTO_TEST_CASE(SyncServiceToClientEcho)
389 Service s(socketPath);
390 Client c(socketPath);
391 c.addMethodHandler<SendData, SendData>(1, echoCallback);
392 FileDescriptor peerFD = connect(s, c);
394 std::shared_ptr<SendData> sentData(new SendData(56));
395 std::shared_ptr<SendData> recvData = s.callSync<SendData, SendData>(1, peerFD, sentData);
396 BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
399 BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho)
401 // Setup Service and Client
402 Service s(socketPath);
403 s.addMethodHandler<SendData, SendData>(1, echoCallback);
405 Client c(socketPath);
409 std::condition_variable cv;
412 std::shared_ptr<SendData> sentData(new SendData(34));
413 std::shared_ptr<SendData> recvData;
414 auto dataBack = [&cv, &recvData, &mutex](ipc::Status status, std::shared_ptr<SendData>& data) {
415 BOOST_CHECK(status == ipc::Status::OK);
416 std::unique_lock<std::mutex> lock(mutex);
420 c.callAsync<SendData, SendData>(1, sentData, dataBack);
422 // Wait for the response
423 std::unique_lock<std::mutex> lock(mutex);
424 BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(100), [&recvData]() {
425 return static_cast<bool>(recvData);
428 BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
431 BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho)
433 Service s(socketPath);
434 Client c(socketPath);
435 c.addMethodHandler<SendData, SendData>(1, echoCallback);
436 FileDescriptor peerFD = connect(s, c);
439 std::shared_ptr<SendData> sentData(new SendData(56));
440 std::shared_ptr<SendData> recvData;
443 std::condition_variable cv;
444 auto dataBack = [&cv, &recvData, &mutex](ipc::Status status, std::shared_ptr<SendData>& data) {
445 BOOST_CHECK(status == ipc::Status::OK);
446 std::unique_lock<std::mutex> lock(mutex);
451 s.callAsync<SendData, SendData>(1, peerFD, sentData, dataBack);
453 // Wait for the response
454 std::unique_lock<std::mutex> lock(mutex);
455 BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(1000), [&recvData]() {
456 return recvData.get() != nullptr;
459 BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
463 BOOST_AUTO_TEST_CASE(SyncTimeout)
465 Service s(socketPath);
466 s.addMethodHandler<SendData, SendData>(1, longEchoCallback);
469 Client c(socketPath);
472 std::shared_ptr<SendData> sentData(new SendData(78));
474 BOOST_CHECK_THROW((c.callSync<SendData, SendData>(1, sentData, 10)), IPCException); //TODO it fails from time to time
477 BOOST_AUTO_TEST_CASE(SerializationError)
479 Service s(socketPath);
480 s.addMethodHandler<SendData, SendData>(1, echoCallback);
483 Client c(socketPath);
486 std::shared_ptr<ThrowOnAcceptData> throwingData(new ThrowOnAcceptData());
488 BOOST_CHECK_THROW((c.callSync<ThrowOnAcceptData, SendData>(1, throwingData)), IPCSerializationException);
492 BOOST_AUTO_TEST_CASE(ParseError)
494 Service s(socketPath);
495 s.addMethodHandler<SendData, SendData>(1, echoCallback);
498 Client c(socketPath);
501 std::shared_ptr<SendData> sentData(new SendData(78));
502 BOOST_CHECK_THROW((c.callSync<SendData, ThrowOnAcceptData>(1, sentData, 10000)), IPCParsingException);
505 BOOST_AUTO_TEST_CASE(DisconnectedPeerError)
507 Service s(socketPath);
509 auto method = [](const FileDescriptor, std::shared_ptr<ThrowOnAcceptData>&) {
510 return std::shared_ptr<SendData>(new SendData(1));
513 // Method will throw during serialization and disconnect automatically
514 s.addMethodHandler<SendData, ThrowOnAcceptData>(1, method);
517 Client c(socketPath);
521 std::condition_variable cv;
522 ipc::Status retStatus = ipc::Status::UNDEFINED;
524 auto dataBack = [&cv, &retStatus, &mutex](ipc::Status status, std::shared_ptr<SendData>&) {
525 std::unique_lock<std::mutex> lock(mutex);
530 std::shared_ptr<SendData> sentData(new SendData(78));
531 c.callAsync<SendData, SendData>(1, sentData, dataBack);
533 // Wait for the response
534 std::unique_lock<std::mutex> lock(mutex);
535 BOOST_CHECK(cv.wait_for(lock, std::chrono::seconds(10), [&retStatus]() {
536 return retStatus != ipc::Status::UNDEFINED;
538 BOOST_CHECK(retStatus == ipc::Status::PEER_DISCONNECTED); //TODO it fails from time to time
542 BOOST_AUTO_TEST_CASE(ReadTimeout)
544 Service s(socketPath);
545 auto longEchoCallback = [](const FileDescriptor, std::shared_ptr<SendData>& data) {
546 return std::shared_ptr<LongSendData>(new LongSendData(data->intVal, 4000 /*ms*/));
548 s.addMethodHandler<LongSendData, SendData>(1, longEchoCallback);
550 Client c(socketPath);
553 // Test timeout on read
554 std::shared_ptr<SendData> sentData(new SendData(334));
555 BOOST_CHECK_THROW((c.callSync<SendData, SendData>(1, sentData, 10)), IPCException);
559 BOOST_AUTO_TEST_CASE(WriteTimeout)
561 Service s(socketPath);
562 s.addMethodHandler<SendData, SendData>(1, echoCallback);
565 Client c(socketPath);
568 // Test echo with a minimal timeout
569 std::shared_ptr<LongSendData> sentDataA(new LongSendData(34, 10 /*ms*/));
570 std::shared_ptr<SendData> recvData = c.callSync<LongSendData, SendData>(1, sentDataA, 100);
571 BOOST_CHECK_EQUAL(recvData->intVal, sentDataA->intVal);
573 // Test timeout on write
574 std::shared_ptr<LongSendData> sentDataB(new LongSendData(34, 1000 /*ms*/));
575 BOOST_CHECK_THROW((c.callSync<LongSendData, SendData>(1, sentDataB, 100)), IPCTimeoutException);
579 BOOST_AUTO_TEST_CASE(AddSignalInRuntime)
581 Service s(socketPath);
582 Client c(socketPath);
585 std::atomic_bool isHandlerACalled(false);
586 auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr<SendData>&) {
587 isHandlerACalled = true;
590 std::atomic_bool isHandlerBCalled(false);
591 auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
592 isHandlerBCalled = true;
595 c.addSignalHandler<SendData>(1, handlerA);
596 c.addSignalHandler<SendData>(2, handlerB);
598 auto data = std::make_shared<SendData>(1);
599 s.signal<SendData>(2, data);
600 s.signal<SendData>(1, data);
602 // Wait for the signals to arrive
603 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
604 BOOST_CHECK(isHandlerACalled && isHandlerBCalled);
608 BOOST_AUTO_TEST_CASE(AddSignalOffline)
610 Service s(socketPath);
611 Client c(socketPath);
613 std::atomic_bool isHandlerACalled(false);
614 auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr<SendData>&) {
615 isHandlerACalled = true;
618 std::atomic_bool isHandlerBCalled(false);
619 auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
620 isHandlerBCalled = true;
623 c.addSignalHandler<SendData>(1, handlerA);
624 c.addSignalHandler<SendData>(2, handlerB);
628 // Wait for the information about the signals to propagate
629 std::this_thread::sleep_for(std::chrono::milliseconds(100));
630 auto data = std::make_shared<SendData>(1);
631 s.signal<SendData>(2, data);
632 s.signal<SendData>(1, data);
634 // Wait for the signals to arrive
635 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
636 BOOST_CHECK(isHandlerACalled && isHandlerBCalled);
640 #if GLIB_CHECK_VERSION(2,36,0)
642 BOOST_AUTO_TEST_CASE(ServiceGSource)
646 std::atomic_bool isSignalCalled(false);
647 auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
648 isSignalCalled = true;
651 IPCGSource::Pointer serviceGSource;
652 Service s(socketPath);
653 s.addMethodHandler<SendData, SendData>(1, echoCallback);
655 Client c(socketPath);
656 s.addSignalHandler<SendData>(2, signalHandler);
658 auto ret = connectServiceGSource(s, c);
659 serviceGSource = ret.second;
663 auto data = std::make_shared<SendData>(1);
664 c.signal<SendData>(2, data);
666 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
667 BOOST_CHECK(isSignalCalled);
670 BOOST_AUTO_TEST_CASE(ClientGSource)
674 std::atomic_bool isSignalCalled(false);
675 auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
676 isSignalCalled = true;
679 Service s(socketPath);
682 IPCGSource::Pointer clientGSource;
683 Client c(socketPath);
684 c.addMethodHandler<SendData, SendData>(1, echoCallback);
685 c.addSignalHandler<SendData>(2, signalHandler);
687 auto ret = connectClientGSource(s, c);
688 FileDescriptor peerFD = ret.first;
689 clientGSource = ret.second;
691 testEcho(s, 1, peerFD);
693 auto data = std::make_shared<SendData>(1);
694 s.signal<SendData>(2, data);
696 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
697 BOOST_CHECK(isSignalCalled);
700 #endif // GLIB_CHECK_VERSION
702 // BOOST_AUTO_TEST_CASE(ConnectionLimitTest)
704 // unsigned oldLimit = ipc::getMaxFDNumber();
705 // ipc::setMaxFDNumber(50);
707 // // Setup Service and many Clients
708 // Service s(socketPath);
709 // s.addMethodHandler<SendData, SendData>(1, echoCallback);
712 // std::list<Client> clients;
713 // for (int i = 0; i < 100; ++i) {
715 // clients.push_back(Client(socketPath));
716 // clients.back().start();
720 // unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
721 // std::mt19937 generator(seed);
722 // for (auto it = clients.begin(); it != clients.end(); ++it) {
724 // std::shared_ptr<SendData> sentData(new SendData(generator()));
725 // std::shared_ptr<SendData> recvData = it->callSync<SendData, SendData>(1, sentData);
726 // BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
730 // ipc::setMaxFDNumber(oldLimit);
735 BOOST_AUTO_TEST_SUITE_END()