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