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