19ed08c5d066b47f918172a52c176947f3d74cc8
[platform/core/test/security-tests.git] / src / framework / src / test_runner_child.cpp
1 /*
2  * Copyright (c) 2013 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_runner_child.cpp
18  * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19  * @version     1.0
20  * @brief       This file is the implementation file of test runner
21  */
22 #include <stddef.h>
23 #include <dpl/test/test_runner.h>
24 #include <dpl/test/test_runner_child.h>
25 #include <dpl/test/test_results_collector.h>
26 #include <dpl/binary_queue.h>
27 #include <dpl/exception.h>
28 #include <dpl/scoped_free.h>
29 #include <dpl/colors.h>
30 #include <pcrecpp.h>
31 #include <algorithm>
32 #include <cstdio>
33 #include <memory.h>
34 #include <libgen.h>
35 #include <cstring>
36 #include <cstdlib>
37 #include <ctime>
38 #include <unistd.h>
39 #include <poll.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/wait.h>
43 #include <sys/stat.h>
44
45 namespace {
46 const int CHILD_TEST_FAIL    = 0;
47 const int CHILD_TEST_PASS    = 1;
48 const int CHILD_TEST_IGNORED = 2;
49
50 const int MSG_TYPE_MESSAGE   = 0; // sizeof(Message) + Message
51 const int MSG_TYPE_PERF_TIME = 1; // perfTime + maxTime
52
53 int closeOutput() {
54     int devnull;
55     int retcode = -1;
56     if (-1 == (devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY))))
57         return -1;
58
59     // replace stdout with /dev/null
60     if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)))
61         goto end;
62
63     // replace stderr with /dev/null
64     if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)))
65         goto end;
66
67     retcode = 0;
68
69 end:
70     close(devnull);
71     return retcode;
72 }
73
74 } // namespace anonymous
75
76 namespace DPL {
77 namespace Test {
78
79 PipeWrapper::PipeWrapper()
80 {
81     if (-1 == pipe(m_pipefd)) {
82         m_pipefd[0] = PIPE_CLOSED;
83         m_pipefd[1] = PIPE_CLOSED;
84     }
85 }
86
87 PipeWrapper::~PipeWrapper()
88 {
89     closeHelp(0);
90     closeHelp(1);
91 }
92
93 bool PipeWrapper::isReady()
94 {
95     return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
96 }
97
98 void PipeWrapper::setUsage(Usage usage)
99 {
100     if (usage == READONLY) {
101         closeHelp(1);
102     }
103     if (usage == WRITEONLY) {
104         closeHelp(0);
105     }
106 }
107
108 PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
109 {
110     if (m_pipefd[1] == PIPE_CLOSED) {
111         return ERROR;
112     }
113
114     std::ostringstream output;
115     output << toBinaryString(code);
116     output << toBinaryString(MSG_TYPE_MESSAGE);
117     output << toBinaryString(static_cast<int>(message.size()));
118     output << message;
119
120     std::string binary = output.str();
121     int size = binary.size();
122
123     if ((writeHelp(&size,
124                    sizeof(int)) == ERROR) ||
125         (writeHelp(binary.c_str(), size) == ERROR))
126     {
127         return ERROR;
128     }
129     return SUCCESS;
130 }
131
132 PipeWrapper::Status PipeWrapper::sendTime(int code,
133                                           std::chrono::system_clock::duration time,
134                                           std::chrono::system_clock::duration timeMax)
135 {
136     if (m_pipefd[1] == PIPE_CLOSED) {
137         return ERROR;
138     }
139
140     std::ostringstream output;
141     output << toBinaryString(code);
142     output << toBinaryString(MSG_TYPE_PERF_TIME);
143     output << toBinaryString(time);
144     output << toBinaryString(timeMax);
145
146     std::string binary = output.str();
147     int size = binary.size();
148
149     if ((writeHelp(&size,
150                    sizeof(int)) == ERROR) ||
151         (writeHelp(binary.c_str(), size) == ERROR))
152     {
153         return ERROR;
154     }
155     return SUCCESS;
156 }
157
158 PipeWrapper::Status PipeWrapper::receive(int &code,
159                                          int &msgType,
160                                          std::string &data,
161                                          std::chrono::system_clock::duration &time,
162                                          std::chrono::system_clock::duration &timeMax,
163                                          time_t deadline)
164 {
165     if (m_pipefd[0] == PIPE_CLOSED) {
166         return ERROR;
167     }
168
169     int size;
170     Status ret;
171
172     if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
173         return ret;
174     }
175
176     std::vector<char> buffer;
177     buffer.resize(size);
178
179     if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
180         return ret;
181     }
182
183     try {
184         DPL::BinaryQueue queue;
185         queue.AppendCopy(&buffer[0], size);
186
187         queue.FlattenConsume(&code, sizeof(int));
188         queue.FlattenConsume(&msgType, sizeof(int));
189
190         switch (msgType) {
191         case MSG_TYPE_MESSAGE:
192             queue.FlattenConsume(&size, sizeof(int));
193
194             buffer.resize(size);
195
196             queue.FlattenConsume(&buffer[0], size);
197             data.assign(buffer.begin(), buffer.end());
198             break;
199         case MSG_TYPE_PERF_TIME:
200             queue.FlattenConsume(&time, sizeof(std::chrono::system_clock::duration));
201             queue.FlattenConsume(&timeMax, sizeof(std::chrono::system_clock::duration));
202             break;
203         default:
204             return ERROR;
205         }
206     } catch (DPL::BinaryQueue::Exception::Base &e) {
207         return ERROR;
208     }
209     return SUCCESS;
210 }
211
212 void PipeWrapper::closeAll()
213 {
214     closeHelp(0);
215     closeHelp(1);
216 }
217
218 std::string PipeWrapper::toBinaryString(int data)
219 {
220     char buffer[sizeof(int)];
221     memcpy(buffer, &data, sizeof(int));
222     return std::string(buffer, buffer + sizeof(int));
223 }
224
225 std::string PipeWrapper::toBinaryString(std::chrono::system_clock::duration data)
226 {
227     char buffer[sizeof(std::chrono::system_clock::duration)];
228     memcpy(buffer, &data, sizeof(std::chrono::system_clock::duration));
229     return std::string(buffer, buffer + sizeof(std::chrono::system_clock::duration));
230 }
231
232 void PipeWrapper::closeHelp(int desc)
233 {
234     if (m_pipefd[desc] != PIPE_CLOSED) {
235         TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
236         m_pipefd[desc] = PIPE_CLOSED;
237     }
238 }
239
240 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
241 {
242     int ready = 0;
243     const char *p = static_cast<const char *>(buffer);
244     while (ready != size) {
245         int ret = write(m_pipefd[1], &p[ready], size - ready);
246
247         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
248             continue;
249         }
250
251         if (ret == -1) {
252             closeHelp(1);
253             return ERROR;
254         }
255
256         ready += ret;
257     }
258     return SUCCESS;
259 }
260
261 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
262 {
263     int ready = 0;
264     char *buffer = static_cast<char*>(buf);
265     while (ready != size) {
266         time_t wait = deadline - time(0);
267         wait = wait < 1 ? 1 : wait;
268         pollfd fds = { m_pipefd[0], POLLIN, 0 };
269
270         int pollReturn = poll(&fds, 1, wait * 1000);
271
272         if (pollReturn == 0) {
273             return TIMEOUT; // Timeout
274         }
275
276         if (pollReturn < -1) {
277             return ERROR;
278         }
279
280         int ret = read(m_pipefd[0], &buffer[ready], size - ready);
281
282         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
283             continue;
284         }
285
286         if (ret == -1 || ret == 0) {
287             closeHelp(0);
288             return ERROR;
289         }
290
291         ready += ret;
292     }
293     return SUCCESS;
294 }
295
296 void RunChildProc(TestRunner::TestCase procChild)
297 {
298     PipeWrapper pipe;
299     if (!pipe.isReady()) {
300         throw TestRunner::TestFailed("Pipe creation failed");
301     }
302
303     pid_t pid = fork();
304
305     if (pid == -1) {
306         throw TestRunner::TestFailed("Child creation failed");
307     }
308
309     if (pid != 0) {
310         // parent code
311         pipe.setUsage(PipeWrapper::READONLY);
312
313         int code;
314         int msgType;
315         std::chrono::system_clock::duration time_m;
316         std::chrono::system_clock::duration timeMax_m;
317         std::string message;
318
319         int pipeReturn = pipe.receive(code, msgType, message, time_m, timeMax_m, time(0) + 10);
320
321         if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
322             pipe.closeAll();
323             kill(pid, SIGKILL);
324         }
325
326         int status;
327         waitpid(pid, &status, 0);
328
329         if (pipeReturn == PipeWrapper::TIMEOUT) {
330             throw TestRunner::TestFailed("Timeout");
331         }
332
333         if (pipeReturn == PipeWrapper::ERROR) {
334             throw TestRunner::TestFailed("Reading pipe error");
335         }
336
337         if (code == CHILD_TEST_PASS && msgType == MSG_TYPE_PERF_TIME) {
338             DPL::Test::TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(true,
339                                                                                            time_m,
340                                                                                            timeMax_m);
341         }
342
343         if (code == CHILD_TEST_FAIL) {
344             throw TestRunner::TestFailed(message);
345         } else if (code == CHILD_TEST_IGNORED) {
346             throw TestRunner::Ignored(message);
347         }
348     } else {
349         // child code
350
351         // End Runner after current test
352         TestRunnerSingleton::Instance().Terminate();
353
354         int code = CHILD_TEST_PASS;
355         std::string msg;
356         bool isPerformanceTest;
357         std::chrono::system_clock::duration time_m;
358         std::chrono::system_clock::duration timeMax_m;
359
360         bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
361
362         close(STDIN_FILENO);
363         if (!allowLogs) {
364             closeOutput(); // if fails nothing we can do
365         }
366
367         pipe.setUsage(PipeWrapper::WRITEONLY);
368
369         try {
370             procChild();
371         } catch (const DPL::Test::TestRunner::TestFailed &e) {
372             msg = e.GetMessage();
373             code = CHILD_TEST_FAIL;
374         } catch (const DPL::Test::TestRunner::Ignored &e) {
375             msg = e.GetMessage();
376             code = CHILD_TEST_IGNORED;
377         } catch (...) { // catch all exception generated by "user" code
378             msg = "unhandled exeception";
379             code = CHILD_TEST_FAIL;
380         }
381
382         if (allowLogs) {
383             closeOutput();
384         }
385
386         DPL::Test::TestRunnerSingleton::Instance().getCurrentTestCasePerformanceResult(isPerformanceTest,
387                                                                                        time_m,
388                                                                                        timeMax_m);
389
390         if (code == CHILD_TEST_PASS && isPerformanceTest){
391             pipe.sendTime(code,
392                     time_m,
393                     timeMax_m);
394         } else {
395             pipe.send(code, msg);
396         }
397     }
398 }
399 } // namespace Test
400 } // namespace DPL