2 * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * @file test_runner_child.cpp
18 * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
20 * @brief This file is the implementation file of test runner
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/colors.h>
41 #include <sys/types.h>
46 const int CHILD_TEST_FAIL = 0;
47 const int CHILD_TEST_PASS = 1;
48 const int CHILD_TEST_IGNORED = 2;
50 const int MSG_TYPE_MESSAGE = 0; // sizeof(Message) + Message
51 const int MSG_TYPE_PERF_TIME = 1; // perfTime + maxTime
56 if (-1 == (devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY))))
59 // replace stdout with /dev/null
60 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)))
63 // replace stderr with /dev/null
64 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)))
74 } // namespace anonymous
79 PipeWrapper::PipeWrapper()
81 if (-1 == pipe(m_pipefd)) {
82 m_pipefd[0] = PIPE_CLOSED;
83 m_pipefd[1] = PIPE_CLOSED;
87 PipeWrapper::~PipeWrapper()
93 bool PipeWrapper::isReady()
95 return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
98 void PipeWrapper::setUsage(Usage usage)
100 if (usage == READONLY) {
103 if (usage == WRITEONLY) {
108 PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
110 if (m_pipefd[1] == PIPE_CLOSED) {
114 std::ostringstream output;
115 output << toBinaryString(code);
116 output << toBinaryString(MSG_TYPE_MESSAGE);
117 output << toBinaryString(static_cast<int>(message.size()));
120 std::string binary = output.str();
121 int size = binary.size();
123 if ((writeHelp(&size,
124 sizeof(int)) == ERROR) ||
125 (writeHelp(binary.c_str(), size) == ERROR))
132 PipeWrapper::Status PipeWrapper::sendTime(int code,
133 std::chrono::system_clock::duration time,
134 std::chrono::system_clock::duration timeMax)
136 if (m_pipefd[1] == PIPE_CLOSED) {
140 std::ostringstream output;
141 output << toBinaryString(code);
142 output << toBinaryString(MSG_TYPE_PERF_TIME);
143 output << toBinaryString(time);
144 output << toBinaryString(timeMax);
146 std::string binary = output.str();
147 int size = binary.size();
149 if ((writeHelp(&size,
150 sizeof(int)) == ERROR) ||
151 (writeHelp(binary.c_str(), size) == ERROR))
158 PipeWrapper::Status PipeWrapper::receive(int &code,
161 std::chrono::system_clock::duration &time,
162 std::chrono::system_clock::duration &timeMax,
165 if (m_pipefd[0] == PIPE_CLOSED) {
172 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
176 std::vector<char> buffer;
179 if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
184 DPL::BinaryQueue queue;
185 queue.AppendCopy(&buffer[0], size);
187 queue.FlattenConsume(&code, sizeof(int));
188 queue.FlattenConsume(&msgType, sizeof(int));
191 case MSG_TYPE_MESSAGE:
192 queue.FlattenConsume(&size, sizeof(int));
196 queue.FlattenConsume(&buffer[0], size);
197 data.assign(buffer.begin(), buffer.end());
199 case MSG_TYPE_PERF_TIME:
200 queue.FlattenConsume(&time, sizeof(std::chrono::system_clock::duration));
201 queue.FlattenConsume(&timeMax, sizeof(std::chrono::system_clock::duration));
206 } catch (DPL::BinaryQueue::Exception::Base &e) {
212 void PipeWrapper::closeAll()
218 std::string PipeWrapper::toBinaryString(int data)
220 char buffer[sizeof(int)];
221 memcpy(buffer, &data, sizeof(int));
222 return std::string(buffer, buffer + sizeof(int));
225 std::string PipeWrapper::toBinaryString(std::chrono::system_clock::duration data)
227 char buffer[sizeof(std::chrono::system_clock::duration)];
228 memcpy(buffer, &data, sizeof(std::chrono::system_clock::duration));
229 return std::string(buffer, buffer + sizeof(std::chrono::system_clock::duration));
232 void PipeWrapper::closeHelp(int desc)
234 if (m_pipefd[desc] != PIPE_CLOSED) {
235 TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
236 m_pipefd[desc] = PIPE_CLOSED;
240 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
243 const char *p = static_cast<const char *>(buffer);
244 while (ready != size) {
245 int ret = write(m_pipefd[1], &p[ready], size - ready);
247 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
261 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
264 char *buffer = static_cast<char*>(buf);
265 while (ready != size) {
266 time_t wait = deadline - time(0);
267 wait = wait < 1 ? 1 : wait;
268 pollfd fds = { m_pipefd[0], POLLIN, 0 };
270 int pollReturn = poll(&fds, 1, wait * 1000);
272 if (pollReturn == 0) {
273 return TIMEOUT; // Timeout
276 if (pollReturn < -1) {
280 int ret = read(m_pipefd[0], &buffer[ready], size - ready);
282 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
286 if (ret == -1 || ret == 0) {
296 void RunChildProc(TestRunner::TestCase procChild)
299 if (!pipe.isReady()) {
300 throw TestRunner::TestFailed("Pipe creation failed");
306 throw TestRunner::TestFailed("Child creation failed");
311 pipe.setUsage(PipeWrapper::READONLY);
315 std::chrono::system_clock::duration time_m;
316 std::chrono::system_clock::duration timeMax_m;
319 int pipeReturn = pipe.receive(code, msgType, message, time_m, timeMax_m, time(0) + 10);
321 if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
327 waitpid(pid, &status, 0);
329 if (pipeReturn == PipeWrapper::TIMEOUT) {
330 throw TestRunner::TestFailed("Timeout");
333 if (pipeReturn == PipeWrapper::ERROR) {
334 throw TestRunner::TestFailed("Reading pipe error");
337 if (code == CHILD_TEST_PASS && msgType == MSG_TYPE_PERF_TIME) {
338 DPL::Test::TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(true,
343 if (code == CHILD_TEST_FAIL) {
344 throw TestRunner::TestFailed(message);
345 } else if (code == CHILD_TEST_IGNORED) {
346 throw TestRunner::Ignored(message);
351 // End Runner after current test
352 TestRunnerSingleton::Instance().Terminate();
354 int code = CHILD_TEST_PASS;
356 bool isPerformanceTest;
357 std::chrono::system_clock::duration time_m;
358 std::chrono::system_clock::duration timeMax_m;
360 bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
364 closeOutput(); // if fails nothing we can do
367 pipe.setUsage(PipeWrapper::WRITEONLY);
371 } catch (const DPL::Test::TestRunner::TestFailed &e) {
372 msg = e.GetMessage();
373 code = CHILD_TEST_FAIL;
374 } catch (const DPL::Test::TestRunner::Ignored &e) {
375 msg = e.GetMessage();
376 code = CHILD_TEST_IGNORED;
377 } catch (...) { // catch all exception generated by "user" code
378 msg = "unhandled exeception";
379 code = CHILD_TEST_FAIL;
386 DPL::Test::TestRunnerSingleton::Instance().getCurrentTestCasePerformanceResult(isPerformanceTest,
390 if (code == CHILD_TEST_PASS && isPerformanceTest){
395 pipe.send(code, msg);