Initialize Tizen 2.3
[framework/web/wrt-commons.git] / modules / test / 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/foreach.h>
30 #include <dpl/colors.h>
31 #include <pcrecpp.h>
32 #include <algorithm>
33 #include <cstdio>
34 #include <memory.h>
35 #include <libgen.h>
36 #include <cstring>
37 #include <cstdlib>
38 #include <ctime>
39 #include <unistd.h>
40 #include <poll.h>
41 #include <fcntl.h>
42 #include <sys/types.h>
43 #include <sys/wait.h>
44 #include <sys/stat.h>
45 #include <dpl/utils/wrt_global_settings.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::receive(int &code, std::string &data, time_t deadline)
131 {
132     if (m_pipefd[0] == PIPE_CLOSED) {
133         return ERROR;
134     }
135
136     int size;
137     Status ret;
138
139     if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
140         return ret;
141     }
142
143     std::vector<char> buffer;
144     buffer.resize(size);
145
146     if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
147         return ret;
148     }
149
150     try {
151         DPL::BinaryQueue queue;
152         queue.AppendCopy(&buffer[0], size);
153
154         queue.FlattenConsume(&code, sizeof(int));
155         queue.FlattenConsume(&size, sizeof(int));
156
157         buffer.resize(size);
158
159         queue.FlattenConsume(&buffer[0], size);
160         data.assign(buffer.begin(), buffer.end());
161     } catch (DPL::BinaryQueue::Exception::Base &e) {
162         return ERROR;
163     }
164     return SUCCESS;
165 }
166
167 void PipeWrapper::closeAll()
168 {
169     closeHelp(0);
170     closeHelp(1);
171 }
172
173 std::string PipeWrapper::toBinaryString(int data)
174 {
175     char buffer[sizeof(int)];
176     memcpy(buffer, &data, sizeof(int));
177     return std::string(buffer, buffer + sizeof(int));
178 }
179
180 void PipeWrapper::closeHelp(int desc)
181 {
182     if (m_pipefd[desc] != PIPE_CLOSED) {
183         TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
184         m_pipefd[desc] = PIPE_CLOSED;
185     }
186 }
187
188 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
189 {
190     int ready = 0;
191     const char *p = static_cast<const char *>(buffer);
192     while (ready != size) {
193         int ret = write(m_pipefd[1], &p[ready], size - ready);
194
195         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
196             continue;
197         }
198
199         if (ret == -1) {
200             closeHelp(1);
201             return ERROR;
202         }
203
204         ready += ret;
205     }
206     return SUCCESS;
207 }
208
209 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
210 {
211     int ready = 0;
212     char *buffer = static_cast<char*>(buf);
213     while (ready != size) {
214         time_t wait = deadline - time(0);
215         wait = wait < 1 ? 1 : wait;
216         pollfd fds = { m_pipefd[0], POLLIN, 0 };
217
218         int pollReturn = poll(&fds, 1, wait * 1000);
219
220         if (pollReturn == 0) {
221             return TIMEOUT; // Timeout
222         }
223
224         if (pollReturn < -1) {
225             return ERROR;
226         }
227
228         int ret = read(m_pipefd[0], &buffer[ready], size - ready);
229
230         if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
231             continue;
232         }
233
234         if (ret == -1 || ret == 0) {
235             closeHelp(0);
236             return ERROR;
237         }
238
239         ready += ret;
240     }
241     return SUCCESS;
242 }
243
244 void RunChildProc(TestRunner::TestCase procChild)
245 {
246     PipeWrapper pipe;
247     if (!pipe.isReady()) {
248         throw TestRunner::TestFailed("Pipe creation failed");
249     }
250
251     pid_t pid = fork();
252
253     if (pid == -1) {
254         throw TestRunner::TestFailed("Child creation failed");
255     }
256
257     if (pid != 0) {
258         // parent code
259         pipe.setUsage(PipeWrapper::READONLY);
260
261         int code;
262         std::string message;
263
264         int pipeReturn = pipe.receive(code, message, time(0) + 10);
265
266         if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
267             pipe.closeAll();
268             kill(pid, SIGKILL);
269         }
270
271         int status;
272         waitpid(pid, &status, 0);
273
274         if (pipeReturn == PipeWrapper::TIMEOUT) {
275             throw TestRunner::TestFailed("Timeout");
276         }
277
278         if (pipeReturn == PipeWrapper::ERROR) {
279             throw TestRunner::TestFailed("Reading pipe error");
280         }
281
282         if (code == CHILD_TEST_FAIL) {
283             throw TestRunner::TestFailed(message);
284         } else if (code == CHILD_TEST_IGNORED) {
285             throw TestRunner::Ignored(message);
286         }
287     } else {
288         // child code
289
290         // End Runner after current test
291         TestRunnerSingleton::Instance().Terminate();
292
293         int code = CHILD_TEST_PASS;
294         std::string msg;
295
296         bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
297
298         close(STDIN_FILENO);
299         if (!allowLogs) {
300             closeOutput(); // if fails nothing we can do
301         }
302
303         pipe.setUsage(PipeWrapper::WRITEONLY);
304
305         try {
306             procChild();
307         } catch (const DPL::Test::TestRunner::TestFailed &e) {
308             msg = e.GetMessage();
309             code = CHILD_TEST_FAIL;
310         } catch (const DPL::Test::TestRunner::Ignored &e) {
311             msg = e.GetMessage();
312             code = CHILD_TEST_IGNORED;
313         } catch (...) { // catch all exception generated by "user" code
314             msg = "unhandled exeception";
315             code = CHILD_TEST_FAIL;
316         }
317
318         if (allowLogs) {
319             closeOutput();
320         }
321
322         pipe.send(code, msg);
323     }
324 }
325 } // namespace Test
326 } // namespace DPL