Release 0.1.66
[platform/core/security/key-manager.git] / unit-tests / test_descriptor-set.cpp
1 /*
2  *  Copyright (c) 2014-2020 Samsung Electronics Co., Ltd. All rights reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License
15  */
16 /*
17  * @file       test_descriptor-set.cpp
18  * @author     Krzysztof Jackiewicz (k.jackiewicz@samsung.com)
19  * @version    1.0
20  */
21
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/resource.h>
27 #include <poll.h>
28 #include <fcntl.h>
29
30 #include <thread>
31 #include <memory>
32 #include <stdexcept>
33
34 #include <boost_macros_wrapper.h>
35 #include <boost/test/results_reporter.hpp>
36
37 #include <test_common.h>
38 #include <test_watched-thread.h>
39
40 #include <descriptor-set.h>
41 #include <dpl/errno_string.h>
42 #include <utils.h>
43
44 using namespace CKM;
45
46 namespace {
47
48 const int POLL_TIMEOUT = 8000;
49 const int POLL_TIMEOUT_SHORT = 1000;
50
51 const short POLLALL = std::numeric_limits<short>::max();
52
53 void closePipe(int *fd)
54 {
55         close(fd[0]);
56         close(fd[1]);
57 }
58
59 /*
60  * Declares pipe descriptor array
61  * Creates pipe and checks for error
62  * Wraps pipe in unique_ptr
63  */
64 #define PIPE(fd) \
65         int (fd)[2]; \
66         BOOST_REQUIRE_MESSAGE(0 == pipe((fd)), "Pipe creation failed: " << GetErrnoString()); \
67         auto fd##Ptr = uptr<closePipe>((fd));
68
69 void unexpectedCallback(int, short)
70 {
71         BOOST_FAIL("Unexpected callback");
72 }
73
74 void readFd(int fd, int expectedFd, short revents)
75 {
76         char buf[1];
77         BOOST_REQUIRE_MESSAGE(fd == expectedFd, "Unexpected descriptor");
78         BOOST_REQUIRE_MESSAGE(revents & POLLIN, "Unexpected event");
79         BOOST_REQUIRE_MESSAGE(1 == TEMP_FAILURE_RETRY(read(fd, buf, 1)),
80                                                   "Pipe read failed" << GetErrnoString());
81 }
82
83 void writeFd(int fd, int expectedFd, short revents)
84 {
85         BOOST_REQUIRE_MESSAGE(fd == expectedFd, "Unexpected descriptor");
86         BOOST_REQUIRE_MESSAGE(revents & POLLOUT, "Unexpected event");
87         BOOST_REQUIRE_MESSAGE(1 == TEMP_FAILURE_RETRY(write(fd, "j", 1)),
88                                                   "Pipe writing failed" << GetErrnoString());
89 }
90
91 } // anonymous namespace
92
93 BOOST_AUTO_TEST_SUITE(DESCRIPTOR_SET_TEST)
94
95 /*
96  * Wait on empty descriptor set. Function should return immediately.
97  */
98 POSITIVE_TEST_CASE(T010_Empty)
99 {
100         DescriptorSet descriptors;
101
102         BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
103 }
104
105 /*
106  * Add and remove (twice) descriptor. Wait on empty set. No callback should be called. wait() should
107  * return immediately.
108  */
109 POSITIVE_TEST_CASE(T020_AddRemove)
110 {
111         DescriptorSet descriptors;
112         descriptors.add(10, POLLALL, unexpectedCallback);
113         descriptors.remove(10);
114         descriptors.remove(10);
115
116         BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
117 }
118
119 /*
120  * Add 2 descriptors and purge all. Wait on empty set. No callback should be called. wait() should
121  * return immediately.
122  */
123 POSITIVE_TEST_CASE(T030_AddPurge)
124 {
125         DescriptorSet descriptors;
126         descriptors.add(10, POLLALL, unexpectedCallback);
127         descriptors.add(20, POLLALL, unexpectedCallback);
128         descriptors.purge();
129
130         BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
131 }
132
133 /*
134  * Add pipe[1] descriptor and wait for write possibility. Provided callback should be called
135  * immediately.
136  */
137 POSITIVE_TEST_CASE(T040_Callback)
138 {
139         DescriptorSet descriptors;
140         bool callback = false;
141
142         PIPE(fd);
143
144         descriptors.add(fd[1], POLLALL, [&callback](int, short revents) {
145                 callback = true;
146                 BOOST_REQUIRE_MESSAGE(revents & POLLOUT, "Not able to write");
147         });
148
149         BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
150         BOOST_REQUIRE_MESSAGE(callback, "Callback was not called");
151 }
152
153 /*
154  * Add pipe[1] descriptor twice with different callbacks. The first one should be overwritten and
155  * shouldn't be called. The second one should be called instead.
156  */
157 POSITIVE_TEST_CASE(T050_DoubleAdd)
158 {
159         DescriptorSet descriptors;
160         bool callback = false;
161
162         PIPE(fd);
163
164         descriptors.add(fd[1], POLLALL, unexpectedCallback);
165         descriptors.add(fd[1], POLLALL, [&callback](int, short revents) {
166                 callback = true;
167                 BOOST_REQUIRE_MESSAGE(revents & POLLOUT, "Not able to write");
168         });
169
170         BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
171         BOOST_REQUIRE_MESSAGE(callback, "Callback was not called");
172 }
173
174 /*
175  * Add pipe[0] descriptor and wait. Callback should not be called. Instead the 1s timeout should
176  * occur and a proper exception should be thrown.
177  */
178 NEGATIVE_TEST_CASE(T060_Timeout)
179 {
180         DescriptorSet descriptors;
181
182         PIPE(fd);
183
184         descriptors.add(fd[0], POLLALL, unexpectedCallback);
185
186         BOOST_REQUIRE_THROW(descriptors.wait(POLL_TIMEOUT_SHORT),
187                                                 CKM::DescriptorSet::Timeout);
188 }
189
190 /*
191  * Create pipe and try to write it. Start thread that will read it.
192  */
193 POSITIVE_TEST_CASE(T070_Write)
194 {
195         DescriptorSet descriptors;
196         bool callback = false;
197
198         PIPE(fd);
199
200         descriptors.add(fd[1], POLLOUT, [&fd, &callback](int desc, short revents) {
201                 callback = true;
202                 writeFd(desc, fd[1], revents);
203         });
204
205         {
206                 auto thread = CreateWatchedThread([fd] {
207                         char buf[1];
208                         ssize_t tmp = TEMP_FAILURE_RETRY(read(fd[0], buf, 1));
209                         THREAD_REQUIRE_MESSAGE(tmp == 1, "Pipe reading failed " << GetErrnoString());
210                 });
211
212                 BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
213         }
214
215         BOOST_REQUIRE_MESSAGE(callback, "Callback not called");
216 }
217
218 /*
219  * Create pipe and try to read it. Start thread that will write it.
220  */
221 POSITIVE_TEST_CASE(T080_Read)
222 {
223         DescriptorSet descriptors;
224         bool callback = false;
225
226         PIPE(fd);
227
228         descriptors.add(fd[0], POLLIN, [&](int desc, short revents) {
229                 callback = true;
230                 readFd(desc, fd[0], revents);
231         });
232
233         {
234                 auto thread = CreateWatchedThread([fd] {
235                         ssize_t tmp = TEMP_FAILURE_RETRY(write(fd[1], "j", 1));
236                         THREAD_REQUIRE_MESSAGE(tmp == 1, "Pipe writing failed " << GetErrnoString());
237                 });
238
239                 BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
240         }
241
242         BOOST_REQUIRE_MESSAGE(callback, "Callback not called");
243 }
244
245 /*
246  * Create two pipes. Try to read first one. Start the thread that writes it. In the callback read
247  * the pipe, remove it from the descriptor set and try to write the second pipe. The thread will
248  * read it. In second pipe callback remove the second pipe descriptor from the set.
249  */
250 POSITIVE_TEST_CASE(T090_WriteAfterRead)
251 {
252         DescriptorSet descriptors;
253         bool callback1 = false;
254         bool callback2 = false;
255
256         PIPE(fd);
257         PIPE(fd2);
258
259         descriptors.add(fd[0], POLLIN, [&](int desc, short revents) {
260                 callback1 = true;
261                 readFd(desc, fd[0], revents);
262
263                 descriptors.remove(desc);
264                 descriptors.add(fd2[1], POLLOUT, [&](int desc2, short revents2) {
265                         callback2 = true;
266                         writeFd(desc2, fd2[1], revents2);
267                         descriptors.remove(desc2);
268                 });
269         });
270
271         {
272                 auto thread = CreateWatchedThread([fd, fd2] {
273                         ssize_t tmp = TEMP_FAILURE_RETRY(write(fd[1], "j", 1));
274                         BOOST_REQUIRE_MESSAGE(tmp == 1, "Pipe writing failed " << GetErrnoString());
275
276                         char buf[1];
277                         tmp = TEMP_FAILURE_RETRY(read(fd2[0], buf, 1));
278                         THREAD_REQUIRE_MESSAGE(tmp == 1, "Pipe reading failed " << GetErrnoString());
279                 });
280
281                 BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
282                 BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
283         }
284
285         BOOST_REQUIRE_MESSAGE(callback1, "First callback not called");
286         BOOST_REQUIRE_MESSAGE(callback2, "Second callback not called");
287 }
288
289 /*
290  * Add negative descriptor and wait. Callback should not be called. Instead the 1s timeout
291  * should occur and a proper exception should be thrown.
292  */
293 NEGATIVE_TEST_CASE(T100_AddNegativeFd)
294 {
295         DescriptorSet descriptors;
296         descriptors.add(-1, POLLALL, unexpectedCallback);
297
298         BOOST_REQUIRE_THROW(descriptors.wait(POLL_TIMEOUT_SHORT), DescriptorSet::Timeout);
299 }
300
301 /*
302  * Cross the descriptor limit which should lead to EINVAL. Utilize unused descriptors only. Callback
303  * should not be called.
304  */
305 NEGATIVE_TEST_CASE(T110_DescriptorLimit)
306 {
307         DescriptorSet descriptors;
308
309         struct rlimit limit;
310         BOOST_REQUIRE(getrlimit(RLIMIT_NOFILE, &limit) == 0);
311
312         int fd = 3;
313         for(rlim_t i = 0; i <= limit.rlim_cur; ++fd) {
314                 int ret = fcntl(fd, F_GETFD);
315                 int err = errno;
316                 BOOST_REQUIRE(ret != -1 || err == EBADF);
317                 if (ret == -1) {
318                         descriptors.add(fd, POLLALL, unexpectedCallback);
319                         ++i;
320                 }
321         }
322
323         BOOST_REQUIRE_THROW(descriptors.wait(POLL_TIMEOUT_SHORT), DescriptorSet::InternalError);
324 }
325
326 /*
327  * Not opened descriptor should trigger a callback with error.
328  */
329 NEGATIVE_TEST_CASE(T120_NotOpenedDescriptor)
330 {
331         DescriptorSet descriptors;
332
333         bool callbackCalled = false;
334         auto callback = [&](int, short revents) {
335                 callbackCalled = true;
336                 BOOST_REQUIRE(revents == POLLNVAL);
337         };
338         descriptors.add(42, POLLALL, callback);
339
340         BOOST_REQUIRE_NO_THROW(descriptors.wait(POLL_TIMEOUT));
341         BOOST_REQUIRE(callbackCalled);
342 }
343
344 /*
345  * Throwing callback. The exception should be propagated outside of DescriptorSet::wait().
346  */
347 NEGATIVE_TEST_CASE(T130_ThrowingCallback)
348 {
349         DescriptorSet descriptors;
350
351         auto throwingCallback = [&](int, short) {
352                 throw std::runtime_error("");
353         };
354         descriptors.add(42, POLLALL, throwingCallback);
355
356         BOOST_REQUIRE_THROW(descriptors.wait(POLL_TIMEOUT), std::runtime_error);
357 }
358
359
360 BOOST_AUTO_TEST_SUITE_END()