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(100), [&retStatus]() {
536 return retStatus != ipc::Status::UNDEFINED;
539 // The disconnection might have happened:
540 // - after sending the message (PEER_DISCONNECTED)
541 // - during external serialization (SERIALIZATION_ERROR)
542 BOOST_CHECK(retStatus == ipc::Status::PEER_DISCONNECTED || retStatus == ipc::Status::SERIALIZATION_ERROR);
546 BOOST_AUTO_TEST_CASE(ReadTimeout)
548 Service s(socketPath);
549 auto longEchoCallback = [](const FileDescriptor, std::shared_ptr<SendData>& data) {
550 return std::shared_ptr<LongSendData>(new LongSendData(data->intVal, 4000 /*ms*/));
552 s.addMethodHandler<LongSendData, SendData>(1, longEchoCallback);
554 Client c(socketPath);
557 // Test timeout on read
558 std::shared_ptr<SendData> sentData(new SendData(334));
559 BOOST_CHECK_THROW((c.callSync<SendData, SendData>(1, sentData, 10)), IPCException);
563 BOOST_AUTO_TEST_CASE(WriteTimeout)
565 Service s(socketPath);
566 s.addMethodHandler<SendData, SendData>(1, echoCallback);
569 Client c(socketPath);
572 // Test echo with a minimal timeout
573 std::shared_ptr<LongSendData> sentDataA(new LongSendData(34, 10 /*ms*/));
574 std::shared_ptr<SendData> recvData = c.callSync<LongSendData, SendData>(1, sentDataA, 100);
575 BOOST_CHECK_EQUAL(recvData->intVal, sentDataA->intVal);
577 // Test timeout on write
578 std::shared_ptr<LongSendData> sentDataB(new LongSendData(34, 1000 /*ms*/));
579 BOOST_CHECK_THROW((c.callSync<LongSendData, SendData>(1, sentDataB, 100)), IPCTimeoutException);
583 BOOST_AUTO_TEST_CASE(AddSignalInRuntime)
585 Service s(socketPath);
586 Client c(socketPath);
589 std::atomic_bool isHandlerACalled(false);
590 auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr<SendData>&) {
591 isHandlerACalled = true;
594 std::atomic_bool isHandlerBCalled(false);
595 auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
596 isHandlerBCalled = true;
599 c.addSignalHandler<SendData>(1, handlerA);
600 c.addSignalHandler<SendData>(2, handlerB);
602 auto data = std::make_shared<SendData>(1);
603 s.signal<SendData>(2, data);
604 s.signal<SendData>(1, data);
606 // Wait for the signals to arrive
607 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
608 BOOST_CHECK(isHandlerACalled && isHandlerBCalled);
612 BOOST_AUTO_TEST_CASE(AddSignalOffline)
614 Service s(socketPath);
615 Client c(socketPath);
617 std::atomic_bool isHandlerACalled(false);
618 auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr<SendData>&) {
619 isHandlerACalled = true;
622 std::atomic_bool isHandlerBCalled(false);
623 auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
624 isHandlerBCalled = true;
627 c.addSignalHandler<SendData>(1, handlerA);
628 c.addSignalHandler<SendData>(2, handlerB);
632 // Wait for the information about the signals to propagate
633 std::this_thread::sleep_for(std::chrono::milliseconds(100));
634 auto data = std::make_shared<SendData>(1);
635 s.signal<SendData>(2, data);
636 s.signal<SendData>(1, data);
638 // Wait for the signals to arrive
639 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
640 BOOST_CHECK(isHandlerACalled && isHandlerBCalled);
644 #if GLIB_CHECK_VERSION(2,36,0)
646 BOOST_AUTO_TEST_CASE(ServiceGSource)
650 std::atomic_bool isSignalCalled(false);
651 auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
652 isSignalCalled = true;
655 IPCGSource::Pointer serviceGSource;
656 Service s(socketPath);
657 s.addMethodHandler<SendData, SendData>(1, echoCallback);
659 Client c(socketPath);
660 s.addSignalHandler<SendData>(2, signalHandler);
662 auto ret = connectServiceGSource(s, c);
663 serviceGSource = ret.second;
667 auto data = std::make_shared<SendData>(1);
668 c.signal<SendData>(2, data);
670 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
671 BOOST_CHECK(isSignalCalled);
674 BOOST_AUTO_TEST_CASE(ClientGSource)
678 std::atomic_bool isSignalCalled(false);
679 auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
680 isSignalCalled = true;
683 Service s(socketPath);
686 IPCGSource::Pointer clientGSource;
687 Client c(socketPath);
688 c.addMethodHandler<SendData, SendData>(1, echoCallback);
689 c.addSignalHandler<SendData>(2, signalHandler);
691 auto ret = connectClientGSource(s, c);
692 FileDescriptor peerFD = ret.first;
693 clientGSource = ret.second;
695 testEcho(s, 1, peerFD);
697 auto data = std::make_shared<SendData>(1);
698 s.signal<SendData>(2, data);
700 std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
701 BOOST_CHECK(isSignalCalled);
704 #endif // GLIB_CHECK_VERSION
706 // BOOST_AUTO_TEST_CASE(ConnectionLimitTest)
708 // unsigned oldLimit = ipc::getMaxFDNumber();
709 // ipc::setMaxFDNumber(50);
711 // // Setup Service and many Clients
712 // Service s(socketPath);
713 // s.addMethodHandler<SendData, SendData>(1, echoCallback);
716 // std::list<Client> clients;
717 // for (int i = 0; i < 100; ++i) {
719 // clients.push_back(Client(socketPath));
720 // clients.back().start();
724 // unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
725 // std::mt19937 generator(seed);
726 // for (auto it = clients.begin(); it != clients.end(); ++it) {
728 // std::shared_ptr<SendData> sentData(new SendData(generator()));
729 // std::shared_ptr<SendData> recvData = it->callSync<SendData, SendData>(1, sentData);
730 // BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
734 // ipc::setMaxFDNumber(oldLimit);
739 BOOST_AUTO_TEST_SUITE_END()