2 * Copyright (c) 2013-2015 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_failed.h>
24 #include <dpl/test/test_ignored.h>
25 #include <dpl/test/test_runner.h>
26 #include <dpl/test/test_runner_child.h>
27 #include <dpl/test/test_results_collector.h>
28 #include <dpl/binary_queue.h>
29 #include <dpl/exception.h>
30 #include <dpl/scoped_free.h>
31 #include <dpl/colors.h>
43 #include <sys/types.h>
48 const int CHILD_TEST_FAIL = 0;
49 const int CHILD_TEST_PASS = 1;
50 const int CHILD_TEST_IGNORED = 2;
55 if (-1 == (devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY))))
58 // replace stdout with /dev/null
59 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)))
62 // replace stderr with /dev/null
63 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)))
73 } // namespace anonymous
78 PipeWrapper::PipeWrapper()
80 if (-1 == pipe(m_pipefd)) {
81 m_pipefd[0] = PIPE_CLOSED;
82 m_pipefd[1] = PIPE_CLOSED;
86 PipeWrapper::~PipeWrapper()
92 bool PipeWrapper::isReady()
94 return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
97 void PipeWrapper::setUsage(Usage usage)
99 if (usage == READONLY) {
102 if (usage == WRITEONLY) {
107 PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
109 if (m_pipefd[1] == PIPE_CLOSED) {
113 std::ostringstream output;
114 output << toBinaryString(code);
115 output << toBinaryString(static_cast<int>(message.size()));
118 std::string binary = output.str();
119 int size = binary.size();
121 if ((writeHelp(&size,
122 sizeof(int)) == ERROR) ||
123 (writeHelp(binary.c_str(), size) == ERROR))
130 PipeWrapper::Status PipeWrapper::sendPerformance(const ConstPerformanceResultPtr &performance)
133 if (m_pipefd[1] == PIPE_CLOSED) {
137 return writeHelp(&foo, sizeof(int));
139 std::string binary = performance->ToBinaryString();
140 int size = binary.size();
142 if ((writeHelp(&size,
143 sizeof(int)) == ERROR) ||
144 (writeHelp(binary.c_str(), size) == ERROR))
151 PipeWrapper::Status PipeWrapper::receive(int &code,
153 PerformanceResultPtr &performance,
156 if (m_pipefd[0] == PIPE_CLOSED) {
163 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
167 std::vector<char> buffer;
170 if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
175 DPL::BinaryQueue queue;
176 queue.AppendCopy(&buffer[0], size);
178 queue.FlattenConsume(&code, sizeof(int));
179 queue.FlattenConsume(&size, sizeof(int));
183 queue.FlattenConsume(&buffer[0], size);
184 data.assign(buffer.begin(), buffer.end());
186 if (code != CHILD_TEST_PASS)
189 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
194 performance = nullptr;
200 if ((ret = readHelp(buffer.data(), size, deadline)) != SUCCESS) {
204 queue.AppendCopy(buffer.data(), size);
206 performance.reset(new PerformanceResult(queue));
207 } catch (DPL::BinaryQueue::Exception::Base &e) {
213 void PipeWrapper::closeAll()
219 std::string PipeWrapper::toBinaryString(int data)
221 char buffer[sizeof(int)];
222 memcpy(buffer, &data, sizeof(int));
223 return std::string(buffer, buffer + sizeof(int));
226 void PipeWrapper::closeHelp(int desc)
228 if (m_pipefd[desc] != PIPE_CLOSED) {
229 TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
230 m_pipefd[desc] = PIPE_CLOSED;
234 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
237 const char *p = static_cast<const char *>(buffer);
238 while (ready != size) {
239 int ret = write(m_pipefd[1], &p[ready], size - ready);
241 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
255 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
258 char *buffer = static_cast<char*>(buf);
259 while (ready != size) {
260 time_t wait = deadline - time(0);
261 wait = wait < 1 ? 1 : wait;
262 pollfd fds = { m_pipefd[0], POLLIN, 0 };
264 int pollReturn = poll(&fds, 1, wait * 1000);
266 if (pollReturn == 0) {
267 return TIMEOUT; // Timeout
270 if (pollReturn < -1) {
274 int ret = read(m_pipefd[0], &buffer[ready], size - ready);
276 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
280 if (ret == -1 || ret == 0) {
290 void RunChildProc(TestRunner::TestCase procChild)
293 if (!pipe.isReady()) {
294 throw TestFailed("Pipe creation failed");
300 throw TestFailed("Child creation failed");
305 pipe.setUsage(PipeWrapper::READONLY);
309 PerformanceResultPtr performance;
311 int pipeReturn = pipe.receive(code, message, performance, time(0) + 10);
313 if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
319 waitpid(pid, &status, 0);
321 if (pipeReturn == PipeWrapper::TIMEOUT) {
322 throw TestFailed("Timeout");
325 if (pipeReturn == PipeWrapper::ERROR) {
326 throw TestFailed("Reading pipe error");
329 TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(performance);
331 if (code == CHILD_TEST_FAIL) {
332 throw TestFailed(message);
333 } else if (code == CHILD_TEST_IGNORED) {
334 throw TestIgnored(message);
339 // End Runner after current test
340 TestRunnerSingleton::Instance().Terminate();
342 int code = CHILD_TEST_PASS;
345 bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
349 closeOutput(); // if fails nothing we can do
352 pipe.setUsage(PipeWrapper::WRITEONLY);
356 } catch (const DPL::Test::TestFailed &e) {
357 msg = e.GetMessage();
358 code = CHILD_TEST_FAIL;
359 } catch (const DPL::Test::TestIgnored &e) {
360 msg = e.GetMessage();
361 code = CHILD_TEST_IGNORED;
362 } catch (...) { // catch all exception generated by "user" code
363 msg = "unhandled exeception";
364 code = CHILD_TEST_FAIL;
371 pipe.send(code, msg);
372 if (code == CHILD_TEST_PASS){
373 pipe.sendPerformance(TestRunnerSingleton::Instance() \
374 .getCurrentTestCasePerformanceResult());