Add init and finish functionality
[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/assert.h>
24 #include <dpl/test/test_failed.h>
25 #include <dpl/test/test_ignored.h>
26 #include <dpl/test/test_runner.h>
27 #include <dpl/test/test_runner_child.h>
28 #include <dpl/test/test_results_collector.h>
29 #include <dpl/binary_queue.h>
30 #include <dpl/exception.h>
31 #include <dpl/scoped_free.h>
32 #include <dpl/colors.h>
33 #include <pcrecpp.h>
34 #include <algorithm>
35 #include <cstdio>
36 #include <memory.h>
37 #include <libgen.h>
38 #include <cstring>
39 #include <cstdlib>
40 #include <ctime>
41 #include <unistd.h>
42 #include <poll.h>
43 #include <fcntl.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <sys/stat.h>
47
48 namespace {
49 const int CHILD_TEST_FAIL    = 0;
50 const int CHILD_TEST_PASS    = 1;
51 const int CHILD_TEST_IGNORED = 2;
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(static_cast<int>(message.size()));
117     output << message;
118
119     std::string binary = output.str();
120     int size = binary.size();
121
122     if ((writeHelp(&size,
123                    sizeof(int)) == ERROR) ||
124         (writeHelp(binary.c_str(), size) == ERROR))
125     {
126         return ERROR;
127     }
128     return SUCCESS;
129 }
130
131 PipeWrapper::Status PipeWrapper::sendPerformance(const ConstPerformanceResultPtr &performance)
132 {
133     int foo = 0;
134     if (m_pipefd[1] == PIPE_CLOSED) {
135         return ERROR;
136     }
137     if (!performance)
138         return writeHelp(&foo, sizeof(int));
139
140     std::string binary = performance->ToBinaryString();
141     int size = binary.size();
142
143     if ((writeHelp(&size,
144                    sizeof(int)) == ERROR) ||
145         (writeHelp(binary.c_str(), size) == ERROR))
146     {
147         return ERROR;
148     }
149     return SUCCESS;
150 }
151
152 PipeWrapper::Status PipeWrapper::receive(int &code,
153                                          std::string &data,
154                                          PerformanceResultPtr &performance,
155                                          time_t deadline)
156 {
157     if (m_pipefd[0] == PIPE_CLOSED) {
158         return ERROR;
159     }
160
161     int size;
162     Status ret;
163
164     if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
165         return ret;
166     }
167
168     std::vector<char> buffer;
169     buffer.resize(size);
170
171     if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
172         return ret;
173     }
174
175     try {
176         DPL::BinaryQueue queue;
177         queue.AppendCopy(&buffer[0], size);
178
179         queue.FlattenConsume(&code, sizeof(int));
180         queue.FlattenConsume(&size, sizeof(int));
181
182         buffer.resize(size);
183
184         queue.FlattenConsume(&buffer[0], size);
185         data.assign(buffer.begin(), buffer.end());
186
187         if (code != CHILD_TEST_PASS)
188             return SUCCESS;
189
190         if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
191             return ret;
192         }
193
194         if (size == 0) {
195             performance = nullptr;
196             return SUCCESS;
197         }
198
199         buffer.resize(size);
200
201         if ((ret = readHelp(buffer.data(), size, deadline)) != SUCCESS) {
202             return ret;
203         }
204
205         queue.AppendCopy(buffer.data(), size);
206
207         performance.reset(new PerformanceResult(queue));
208     } catch (DPL::BinaryQueue::Exception::Base &e) {
209         return ERROR;
210     }
211     return SUCCESS;
212 }
213
214 void PipeWrapper::closeAll()
215 {
216     closeHelp(0);
217     closeHelp(1);
218 }
219
220 std::string PipeWrapper::toBinaryString(int data)
221 {
222     char buffer[sizeof(int)];
223     memcpy(buffer, &data, sizeof(int));
224     return std::string(buffer, buffer + sizeof(int));
225 }
226
227 void PipeWrapper::closeHelp(int desc)
228 {
229     if (m_pipefd[desc] != PIPE_CLOSED) {
230         TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
231         m_pipefd[desc] = PIPE_CLOSED;
232     }
233 }
234
235 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
236 {
237     int ready = 0;
238     const char *p = static_cast<const char *>(buffer);
239     while (ready != size) {
240         int ret = write(m_pipefd[1], &p[ready], size - ready);
241
242         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
243             continue;
244         }
245
246         if (ret == -1) {
247             closeHelp(1);
248             return ERROR;
249         }
250
251         ready += ret;
252     }
253     return SUCCESS;
254 }
255
256 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
257 {
258     int ready = 0;
259     char *buffer = static_cast<char*>(buf);
260     while (ready != size) {
261         time_t wait = deadline - time(0);
262         wait = wait < 1 ? 1 : wait;
263         pollfd fds = { m_pipefd[0], POLLIN, 0 };
264
265         int pollReturn = poll(&fds, 1, wait * 1000);
266
267         if (pollReturn == 0) {
268             return TIMEOUT; // Timeout
269         }
270
271         if (pollReturn < -1) {
272             return ERROR;
273         }
274
275         int ret = read(m_pipefd[0], &buffer[ready], size - ready);
276
277         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
278             continue;
279         }
280
281         if (ret == -1 || ret == 0) {
282             closeHelp(0);
283             return ERROR;
284         }
285
286         ready += ret;
287     }
288     return SUCCESS;
289 }
290
291 void RunChildProc(const std::function<void(void)> &testFunc)
292 {
293     PipeWrapper pipe;
294     if (!pipe.isReady()) {
295         throw TestFailed("Pipe creation failed");
296     }
297
298     pid_t pid = fork();
299
300     if (pid == -1) {
301         throw TestFailed("Child creation failed");
302     }
303
304     if (pid != 0) {
305         // parent code
306         pipe.setUsage(PipeWrapper::READONLY);
307
308         int code;
309         std::string message;
310         PerformanceResultPtr performance;
311
312         int pipeReturn = pipe.receive(code, message, performance, time(0) + 10);
313
314         if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
315             pipe.closeAll();
316             kill(pid, SIGKILL);
317         }
318
319         int status;
320         waitpid(pid, &status, 0);
321
322         if (pipeReturn == PipeWrapper::TIMEOUT) {
323             throw TestFailed("Timeout");
324         }
325
326         if (pipeReturn == PipeWrapper::ERROR) {
327             throw TestFailed("Reading pipe error");
328         }
329
330         TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(performance);
331
332         if (code == CHILD_TEST_FAIL) {
333             throw TestFailed(message);
334         } else if (code == CHILD_TEST_IGNORED) {
335             throw TestIgnored(message);
336         }
337     } else {
338         // child code
339
340         // End Runner after current test
341         TestRunnerSingleton::Instance().Terminate();
342
343         bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
344
345         close(STDIN_FILENO);
346         if (!allowLogs) {
347             closeOutput(); // if fails nothing we can do
348         }
349
350         pipe.setUsage(PipeWrapper::WRITEONLY);
351
352         int code;
353         std::string msg;
354         switch (TryCatch(testFunc, msg)) {
355             case TestResult::FailStatus::FAILED:
356                 code = CHILD_TEST_FAIL;
357                 break;
358             case TestResult::FailStatus::IGNORED:
359                 code = CHILD_TEST_IGNORED;
360                 break;
361             case TestResult::FailStatus::NONE:
362                 code = CHILD_TEST_PASS;
363                 break;
364             default:
365                 Assert(false && "Unhandled fail status");
366         }
367
368         if (allowLogs) {
369             closeOutput();
370         }
371
372         pipe.send(code, msg);
373         if (code == CHILD_TEST_PASS){
374             pipe.sendPerformance(TestRunnerSingleton::Instance() \
375                  .getCurrentTestCasePerformanceResult());
376         }
377     }
378 }
379 } // namespace Test
380 } // namespace DPL