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