IPC: Replace PeerID witch peer's file descriptor
[platform/core/security/vasum.git] / tests / unit_tests / ipc / ut-ipc.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Jan Olszak <j.olszak@samsung.com>
5  *
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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
17  */
18
19
20 /**
21  * @file
22  * @author  Jan Olszak (j.olszak@samsung.com)
23  * @brief   Tests of the IPC
24  */
25
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
29
30
31 #include "config.hpp"
32 #include "ut.hpp"
33
34 #include "ipc/service.hpp"
35 #include "ipc/client.hpp"
36 #include "ipc/types.hpp"
37
38 #include "config/fields.hpp"
39 #include "logger/logger.hpp"
40
41 #include <atomic>
42 #include <random>
43 #include <string>
44 #include <thread>
45 #include <chrono>
46 #include <boost/filesystem.hpp>
47
48 using namespace security_containers;
49 using namespace security_containers::ipc;
50 namespace fs = boost::filesystem;
51
52 namespace {
53 struct Fixture {
54     std::string socketPath;
55
56     Fixture()
57         : socketPath(fs::unique_path("/tmp/ipc-%%%%.socket").string())
58     {
59     }
60     ~Fixture()
61     {
62         fs::remove(socketPath);
63     }
64 };
65
66 struct SendData {
67     int intVal;
68     SendData(int i = 0): intVal(i) {}
69
70     CONFIG_REGISTER
71     (
72         intVal
73     )
74 };
75
76 struct LongSendData {
77     LongSendData(int i = 0, int waitTime = 1000): mSendData(i), mWaitTime(waitTime), intVal(i) {}
78
79     template<typename Visitor>
80     void accept(Visitor visitor)
81     {
82         std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime));
83         mSendData.accept(visitor);
84     }
85     template<typename Visitor>
86     void accept(Visitor visitor) const
87     {
88         std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime));
89         mSendData.accept(visitor);
90     }
91
92     SendData mSendData;
93     int mWaitTime;
94     int intVal;
95 };
96
97 struct EmptyData {
98     CONFIG_REGISTER_EMPTY
99 };
100
101 struct ThrowOnAcceptData {
102     template<typename Visitor>
103     void accept(Visitor)
104     {
105         throw std::runtime_error("intentional failure in accept");
106     }
107     template<typename Visitor>
108     void accept(Visitor) const
109     {
110         throw std::runtime_error("intentional failure in accept const");
111     }
112 };
113
114 std::shared_ptr<EmptyData> returnEmptyCallback(const FileDescriptor, std::shared_ptr<EmptyData>&)
115 {
116     return std::shared_ptr<EmptyData>(new EmptyData());
117 }
118
119 std::shared_ptr<SendData> returnDataCallback(const FileDescriptor, std::shared_ptr<SendData>&)
120 {
121     return std::shared_ptr<SendData>(new SendData(1));
122 }
123
124 std::shared_ptr<SendData> echoCallback(const FileDescriptor, std::shared_ptr<SendData>& data)
125 {
126     return data;
127 }
128
129 std::shared_ptr<SendData> longEchoCallback(const FileDescriptor, std::shared_ptr<SendData>& data)
130 {
131     std::this_thread::sleep_for(std::chrono::seconds(1));
132     return data;
133 }
134
135 FileDescriptor connect(Service& s, Client& c)
136 {
137     // Connects the Client to the Service and returns Clients FileDescriptor
138
139     std::mutex mutex;
140     std::condition_variable cv;
141
142     FileDescriptor peerFD = 0;
143     auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFileDescriptor) {
144         std::unique_lock<std::mutex> lock(mutex);
145         peerFD = newFileDescriptor;
146         cv.notify_one();
147     };
148
149     s.setNewPeerCallback(newPeerCallback);
150
151     if (!s.isStarted()) {
152         s.start();
153     }
154
155     c.start();
156
157     std::unique_lock<std::mutex> lock(mutex);
158     BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(1000), [&peerFD]() {
159         return peerFD != 0;
160     }));
161
162     return peerFD;
163 }
164
165 void testEcho(Client& c, const MethodID methodID)
166 {
167     std::shared_ptr<SendData> sentData(new SendData(34));
168     std::shared_ptr<SendData> recvData = c.callSync<SendData, SendData>(methodID, sentData);
169     BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
170 }
171
172 void testEcho(Service& s, const MethodID methodID, const FileDescriptor peerFD)
173 {
174     std::shared_ptr<SendData> sentData(new SendData(56));
175     std::shared_ptr<SendData> recvData = s.callSync<SendData, SendData>(methodID, peerFD, sentData);
176     BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
177 }
178
179 } // namespace
180
181
182 BOOST_FIXTURE_TEST_SUITE(IPCSuite, Fixture)
183
184 BOOST_AUTO_TEST_CASE(ConstructorDestructor)
185 {
186     Service s(socketPath);
187     Client c(socketPath);
188 }
189
190 BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod)
191 {
192     Service s(socketPath);
193
194     s.addMethodHandler<EmptyData, EmptyData>(1, returnEmptyCallback);
195     s.addMethodHandler<SendData, SendData>(1, returnDataCallback);
196
197     s.start();
198
199     s.addMethodHandler<SendData, SendData>(1, echoCallback);
200     s.addMethodHandler<SendData, SendData>(2, returnDataCallback);
201
202     Client c(socketPath);
203     c.start();
204     testEcho(c, 1);
205
206     s.removeMethod(1);
207     s.removeMethod(2);
208
209     BOOST_CHECK_THROW(testEcho(c, 2), IPCException);
210 }
211
212 BOOST_AUTO_TEST_CASE(ClientAddRemoveMethod)
213 {
214     Service s(socketPath);
215     Client c(socketPath);
216     c.addMethodHandler<EmptyData, EmptyData>(1, returnEmptyCallback);
217     c.addMethodHandler<SendData, SendData>(1, returnDataCallback);
218
219     FileDescriptor peerFD = connect(s, c);
220
221     c.addMethodHandler<SendData, SendData>(1, echoCallback);
222     c.addMethodHandler<SendData, SendData>(2, returnDataCallback);
223
224     testEcho(s, 1, peerFD);
225
226     c.removeMethod(1);
227     c.removeMethod(2);
228
229     BOOST_CHECK_THROW(testEcho(s, 1, peerFD), IPCException);
230 }
231
232 BOOST_AUTO_TEST_CASE(ServiceStartStop)
233 {
234     Service s(socketPath);
235
236     s.addMethodHandler<SendData, SendData>(1, returnDataCallback);
237
238     s.start();
239     s.stop();
240     s.start();
241     s.stop();
242
243     s.start();
244     s.start();
245 }
246
247 BOOST_AUTO_TEST_CASE(ClientStartStop)
248 {
249     Service s(socketPath);
250     Client c(socketPath);
251     c.addMethodHandler<SendData, SendData>(1, returnDataCallback);
252
253     c.start();
254     c.stop();
255     c.start();
256     c.stop();
257
258     c.start();
259     c.start();
260
261     c.stop();
262     c.stop();
263 }
264
265 BOOST_AUTO_TEST_CASE(SyncClientToServiceEcho)
266 {
267     Service s(socketPath);
268     s.addMethodHandler<SendData, SendData>(1, echoCallback);
269     s.addMethodHandler<SendData, SendData>(2, echoCallback);
270
271     s.start();
272     Client c(socketPath);
273     c.start();
274     testEcho(c, 1);
275     testEcho(c, 2);
276 }
277
278 BOOST_AUTO_TEST_CASE(Restart)
279 {
280     Service s(socketPath);
281     s.addMethodHandler<SendData, SendData>(1, echoCallback);
282     s.start();
283     s.addMethodHandler<SendData, SendData>(2, echoCallback);
284
285     Client c(socketPath);
286     c.start();
287     testEcho(c, 1);
288     testEcho(c, 2);
289
290     c.stop();
291     c.start();
292
293     testEcho(c, 1);
294     testEcho(c, 2);
295
296     s.stop();
297     s.start();
298
299     testEcho(c, 1);
300     testEcho(c, 2);
301 }
302
303 BOOST_AUTO_TEST_CASE(SyncServiceToClientEcho)
304 {
305     Service s(socketPath);
306     Client c(socketPath);
307     c.addMethodHandler<SendData, SendData>(1, echoCallback);
308     FileDescriptor peerFD = connect(s, c);
309
310     std::shared_ptr<SendData> sentData(new SendData(56));
311     std::shared_ptr<SendData> recvData = s.callSync<SendData, SendData>(1, peerFD, sentData);
312     BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
313 }
314
315 BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho)
316 {
317     // Setup Service and Client
318     Service s(socketPath);
319     s.addMethodHandler<SendData, SendData>(1, echoCallback);
320     s.start();
321     Client c(socketPath);
322     c.start();
323
324     std::mutex mutex;
325     std::condition_variable cv;
326
327     //Async call
328     std::shared_ptr<SendData> sentData(new SendData(34));
329     std::shared_ptr<SendData> recvData;
330     auto dataBack = [&cv, &recvData, &mutex](ipc::Status status, std::shared_ptr<SendData>& data) {
331         BOOST_CHECK(status == ipc::Status::OK);
332         std::unique_lock<std::mutex> lock(mutex);
333         recvData = data;
334         cv.notify_one();
335     };
336     c.callAsync<SendData, SendData>(1, sentData, dataBack);
337
338     // Wait for the response
339     std::unique_lock<std::mutex> lock(mutex);
340     BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(100), [&recvData]() {
341         return static_cast<bool>(recvData);
342     }));
343
344     BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
345 }
346
347 BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho)
348 {
349     Service s(socketPath);
350     Client c(socketPath);
351     c.addMethodHandler<SendData, SendData>(1, echoCallback);
352     FileDescriptor peerFD = connect(s, c);
353
354     // Async call
355     std::shared_ptr<SendData> sentData(new SendData(56));
356     std::shared_ptr<SendData> recvData;
357
358     std::mutex mutex;
359     std::condition_variable cv;
360     auto dataBack = [&cv, &recvData, &mutex](ipc::Status status, std::shared_ptr<SendData>& data) {
361         BOOST_CHECK(status == ipc::Status::OK);
362         std::unique_lock<std::mutex> lock(mutex);
363         recvData = data;
364         cv.notify_one();
365     };
366
367     s.callAsync<SendData, SendData>(1, peerFD, sentData, dataBack);
368
369     // Wait for the response
370     std::unique_lock<std::mutex> lock(mutex);
371     BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(1000), [&recvData]() {
372         return recvData.get() != nullptr;
373     }));
374
375     BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
376 }
377
378
379 BOOST_AUTO_TEST_CASE(SyncTimeout)
380 {
381     Service s(socketPath);
382     s.addMethodHandler<SendData, SendData>(1, longEchoCallback);
383
384     s.start();
385     Client c(socketPath);
386     c.start();
387
388     std::shared_ptr<SendData> sentData(new SendData(78));
389
390     BOOST_CHECK_THROW((c.callSync<SendData, SendData>(1, sentData, 10)), IPCException); //TODO it fails from time to time
391 }
392
393 BOOST_AUTO_TEST_CASE(SerializationError)
394 {
395     Service s(socketPath);
396     s.addMethodHandler<SendData, SendData>(1, echoCallback);
397     s.start();
398
399     Client c(socketPath);
400     c.start();
401
402     std::shared_ptr<ThrowOnAcceptData> throwingData(new ThrowOnAcceptData());
403
404     BOOST_CHECK_THROW((c.callSync<ThrowOnAcceptData, SendData>(1, throwingData)), IPCSerializationException);
405
406 }
407
408 BOOST_AUTO_TEST_CASE(ParseError)
409 {
410     Service s(socketPath);
411     s.addMethodHandler<SendData, SendData>(1, echoCallback);
412     s.start();
413
414     Client c(socketPath);
415     c.start();
416
417     std::shared_ptr<SendData> sentData(new SendData(78));
418     BOOST_CHECK_THROW((c.callSync<SendData, ThrowOnAcceptData>(1, sentData, 10000)), IPCParsingException);
419 }
420
421 BOOST_AUTO_TEST_CASE(DisconnectedPeerError)
422 {
423     Service s(socketPath);
424
425     auto method = [](const FileDescriptor, std::shared_ptr<ThrowOnAcceptData>&) {
426         return std::shared_ptr<SendData>(new SendData(1));
427     };
428
429     // Method will throw during serialization and disconnect automatically
430     s.addMethodHandler<SendData, ThrowOnAcceptData>(1, method);
431     s.start();
432
433     Client c(socketPath);
434     c.start();
435
436     std::mutex mutex;
437     std::condition_variable cv;
438     ipc::Status retStatus = ipc::Status::UNDEFINED;
439
440     auto dataBack = [&cv, &retStatus, &mutex](ipc::Status status, std::shared_ptr<SendData>&) {
441         std::unique_lock<std::mutex> lock(mutex);
442         retStatus = status;
443         cv.notify_one();
444     };
445
446     std::shared_ptr<SendData> sentData(new SendData(78));
447     c.callAsync<SendData, SendData>(1, sentData, dataBack);
448
449     // Wait for the response
450     std::unique_lock<std::mutex> lock(mutex);
451     BOOST_CHECK(cv.wait_for(lock, std::chrono::seconds(10), [&retStatus]() {
452         return retStatus != ipc::Status::UNDEFINED;
453     }));
454     BOOST_CHECK(retStatus == ipc::Status::PEER_DISCONNECTED); //TODO it fails from time to time
455 }
456
457
458 BOOST_AUTO_TEST_CASE(ReadTimeout)
459 {
460     Service s(socketPath);
461     auto longEchoCallback = [](const FileDescriptor, std::shared_ptr<SendData>& data) {
462         return std::shared_ptr<LongSendData>(new LongSendData(data->intVal));
463     };
464     s.addMethodHandler<LongSendData, SendData>(1, longEchoCallback);
465     s.start();
466
467     Client c(socketPath);
468     c.start();
469
470     // Test timeout on read
471     std::shared_ptr<SendData> sentData(new SendData(334));
472     BOOST_CHECK_THROW((c.callSync<SendData, SendData>(1, sentData, 100)), IPCException);
473 }
474
475
476 BOOST_AUTO_TEST_CASE(WriteTimeout)
477 {
478     Service s(socketPath);
479     s.addMethodHandler<SendData, SendData>(1, echoCallback);
480     s.start();
481
482     Client c(socketPath);
483     c.start();
484
485     // Test echo with a minimal timeout
486     std::shared_ptr<LongSendData> sentDataA(new LongSendData(34, 10 /*ms*/));
487     std::shared_ptr<SendData> recvData = c.callSync<LongSendData, SendData>(1, sentDataA, 100);
488     BOOST_CHECK_EQUAL(recvData->intVal, sentDataA->intVal);
489
490     // Test timeout on write
491     std::shared_ptr<LongSendData> sentDataB(new LongSendData(34, 1000 /*ms*/));
492     BOOST_CHECK_THROW((c.callSync<LongSendData, SendData>(1, sentDataB, 100)), IPCTimeoutException);
493 }
494
495
496 BOOST_AUTO_TEST_CASE(AddSignalInRuntime)
497 {
498     Service s(socketPath);
499     Client c(socketPath);
500     connect(s, c);
501
502     std::atomic_bool isHandlerACalled(false);
503     auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr<SendData>&) {
504         isHandlerACalled = true;
505     };
506
507     std::atomic_bool isHandlerBCalled(false);
508     auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
509         isHandlerBCalled = true;
510     };
511
512     c.addSignalHandler<SendData>(1, handlerA);
513     c.addSignalHandler<SendData>(2, handlerB);
514
515     auto data = std::make_shared<SendData>(1);
516     s.signal<SendData>(2, data);
517     s.signal<SendData>(1, data);
518
519     // Wait for the signals to arrive
520     std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
521     BOOST_CHECK(isHandlerACalled && isHandlerBCalled);
522 }
523
524
525 BOOST_AUTO_TEST_CASE(AddSignalOffline)
526 {
527     Service s(socketPath);
528     Client c(socketPath);
529
530     std::atomic_bool isHandlerACalled(false);
531     auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr<SendData>&) {
532         isHandlerACalled = true;
533     };
534
535     std::atomic_bool isHandlerBCalled(false);
536     auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr<SendData>&) {
537         isHandlerBCalled = true;
538     };
539
540     c.addSignalHandler<SendData>(1, handlerA);
541     c.addSignalHandler<SendData>(2, handlerB);
542
543     connect(s, c);
544
545     // Wait for the information about the signals to propagate
546     std::this_thread::sleep_for(std::chrono::milliseconds(100));
547     auto data = std::make_shared<SendData>(1);
548     s.signal<SendData>(2, data);
549     s.signal<SendData>(1, data);
550
551     // Wait for the signals to arrive
552     std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for
553     BOOST_CHECK(isHandlerACalled && isHandlerBCalled);
554 }
555
556
557 // BOOST_AUTO_TEST_CASE(ConnectionLimitTest)
558 // {
559 //     unsigned oldLimit = ipc::getMaxFDNumber();
560 //     ipc::setMaxFDNumber(50);
561
562 //     // Setup Service and many Clients
563 //     Service s(socketPath);
564 //     s.addMethodHandler<SendData, SendData>(1, echoCallback);
565 //     s.start();
566
567 //     std::list<Client> clients;
568 //     for (int i = 0; i < 100; ++i) {
569 //         try {
570 //             clients.push_back(Client(socketPath));
571 //             clients.back().start();
572 //         } catch (...) {}
573 //     }
574
575 //     unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
576 //     std::mt19937 generator(seed);
577 //     for (auto it = clients.begin(); it != clients.end(); ++it) {
578 //         try {
579 //             std::shared_ptr<SendData> sentData(new SendData(generator()));
580 //             std::shared_ptr<SendData> recvData = it->callSync<SendData, SendData>(1, sentData);
581 //             BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
582 //         } catch (...) {}
583 //     }
584
585 //     ipc::setMaxFDNumber(oldLimit);
586 // }
587
588
589
590 BOOST_AUTO_TEST_SUITE_END()