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