Fix RUNNER_CHILD_TEST framework.
[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 PIPE_CLOSED = -1;
49
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 class PipeWrapper : DPL::Noncopyable
80 {
81   public:
82     enum Usage {
83         READONLY,
84         WRITEONLY
85     };
86
87     enum Status {
88         SUCCESS,
89         TIMEOUT,
90         ERROR
91     };
92
93     PipeWrapper()
94     {
95         if (-1 == pipe(m_pipefd)) {
96             m_pipefd[0] = PIPE_CLOSED;
97             m_pipefd[1] = PIPE_CLOSED;
98         }
99     }
100
101     bool isReady()
102     {
103         return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
104     }
105
106     void setUsage(Usage usage)
107     {
108         if (usage == READONLY) {
109             closeHelp(1);
110         }
111         if (usage == WRITEONLY) {
112             closeHelp(0);
113         }
114     }
115     ~PipeWrapper()
116     {
117         closeHelp(0);
118         closeHelp(1);
119     }
120
121     Status send(int code, std::string &message)
122     {
123         if (m_pipefd[1] == PIPE_CLOSED) {
124             return ERROR;
125         }
126
127         std::ostringstream output;
128         output << toBinaryString(code);
129         output << toBinaryString(static_cast<int>(message.size()));
130         output << message;
131
132         std::string binary = output.str();
133         int size = binary.size();
134
135         if ((writeHelp(&size,
136                        sizeof(int)) == ERROR) ||
137             (writeHelp(binary.c_str(), size) == ERROR))
138         {
139             return ERROR;
140         }
141         return SUCCESS;
142     }
143
144     Status receive(int &code, std::string &data, time_t deadline)
145     {
146         if (m_pipefd[0] == PIPE_CLOSED) {
147             return ERROR;
148         }
149
150         int size;
151         Status ret;
152
153         if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
154             return ret;
155         }
156
157         std::vector<char> buffer;
158         buffer.resize(size);
159
160         if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
161             return ret;
162         }
163
164         try {
165             DPL::BinaryQueue queue;
166             queue.AppendCopy(&buffer[0], size);
167
168             queue.FlattenConsume(&code, sizeof(int));
169             queue.FlattenConsume(&size, sizeof(int));
170
171             buffer.resize(size);
172
173             queue.FlattenConsume(&buffer[0], size);
174             data.assign(buffer.begin(), buffer.end());
175         } catch (DPL::BinaryQueue::Exception::Base &e) {
176             return ERROR;
177         }
178         return SUCCESS;
179     }
180
181     void closeAll()
182     {
183         closeHelp(0);
184         closeHelp(1);
185     }
186
187   private:
188     std::string toBinaryString(int data)
189     {
190         char buffer[sizeof(int)];
191         memcpy(buffer, &data, sizeof(int));
192         return std::string(buffer, buffer + sizeof(int));
193     }
194
195     void closeHelp(int desc)
196     {
197         if (m_pipefd[desc] != PIPE_CLOSED) {
198             TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
199             m_pipefd[desc] = PIPE_CLOSED;
200         }
201     }
202
203     Status writeHelp(const void *buffer, int size)
204     {
205         int ready = 0;
206         const char *p = static_cast<const char *>(buffer);
207         while (ready != size) {
208             int ret = write(m_pipefd[1], &p[ready], size - ready);
209
210             if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
211                 continue;
212             }
213
214             if (ret == -1) {
215                 closeHelp(1);
216                 return ERROR;
217             }
218
219             ready += ret;
220         }
221         return SUCCESS;
222     }
223
224     Status readHelp(void *buf, int size, time_t deadline)
225     {
226         int ready = 0;
227         char *buffer = static_cast<char*>(buf);
228         while (ready != size) {
229             time_t wait = deadline - time(0);
230             wait = wait < 1 ? 1 : wait;
231             pollfd fds = { m_pipefd[0], POLLIN, 0 };
232
233             int pollReturn = poll(&fds, 1, wait * 1000);
234
235             if (pollReturn == 0) {
236                 return TIMEOUT; // Timeout
237             }
238
239             if (pollReturn < -1) {
240                 return ERROR;
241             }
242
243             int ret = read(m_pipefd[0], &buffer[ready], size - ready);
244
245             if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
246                 continue;
247             }
248
249             if (ret == -1 || ret == 0) {
250                 closeHelp(0);
251                 return ERROR;
252             }
253
254             ready += ret;
255         }
256         return SUCCESS;
257     }
258
259     int m_pipefd[2];
260 };
261
262 void RunChildProc(TestRunner::TestCase procChild)
263 {
264     PipeWrapper pipe;
265     if (!pipe.isReady()) {
266         throw TestRunner::TestFailed("Pipe creation failed");
267     }
268
269     pid_t pid = fork();
270
271     if (pid == -1) {
272         throw TestRunner::TestFailed("Child creation failed");
273     }
274
275     if (pid != 0) {
276         // parent code
277         pipe.setUsage(PipeWrapper::READONLY);
278
279         int code;
280         std::string message;
281
282         int pipeReturn = pipe.receive(code, message, time(0) + 10);
283
284         if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
285             pipe.closeAll();
286             kill(pid, SIGKILL);
287         }
288
289         int status;
290         waitpid(pid, &status, 0);
291
292         if (pipeReturn == PipeWrapper::TIMEOUT) {
293             throw TestRunner::TestFailed("Timeout");
294         }
295
296         if (pipeReturn == PipeWrapper::ERROR) {
297             throw TestRunner::TestFailed("Reading pipe error");
298         }
299
300         if (code == CHILD_TEST_FAIL) {
301             throw TestRunner::TestFailed(message);
302         } else if (code == CHILD_TEST_IGNORED) {
303             throw TestRunner::Ignored(message);
304         }
305     } else {
306         // child code
307
308         // End Runner after current test
309         TestRunnerSingleton::Instance().Terminate();
310
311         int code = CHILD_TEST_PASS;
312         std::string msg;
313
314         bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
315
316         close(STDIN_FILENO);
317         if (!allowLogs) {
318             closeOutput(); // if fails nothing we can do
319         }
320
321         pipe.setUsage(PipeWrapper::WRITEONLY);
322
323         try {
324             procChild();
325         } catch (const DPL::Test::TestRunner::TestFailed &e) {
326             msg = e.GetMessage();
327             code = CHILD_TEST_FAIL;
328         } catch (const DPL::Test::TestRunner::Ignored &e) {
329             msg = e.GetMessage();
330             code = CHILD_TEST_IGNORED;
331         } catch (...) { // catch all exception generated by "user" code
332             msg = "unhandled exeception";
333             code = CHILD_TEST_FAIL;
334         }
335
336         if (allowLogs) {
337             closeOutput();
338         }
339
340         pipe.send(code, msg);
341     }
342 }
343 } // namespace Test
344 } // namespace DPL