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