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;
52 const int MSG_TYPE_MESSAGE = 0; // sizeof(Message) + Message
53 const int MSG_TYPE_PERF_TIME = 1; // perfTime + maxTime
58 if (-1 == (devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY))))
61 // replace stdout with /dev/null
62 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)))
65 // replace stderr with /dev/null
66 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)))
76 } // namespace anonymous
81 PipeWrapper::PipeWrapper()
83 if (-1 == pipe(m_pipefd)) {
84 m_pipefd[0] = PIPE_CLOSED;
85 m_pipefd[1] = PIPE_CLOSED;
89 PipeWrapper::~PipeWrapper()
95 bool PipeWrapper::isReady()
97 return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
100 void PipeWrapper::setUsage(Usage usage)
102 if (usage == READONLY) {
105 if (usage == WRITEONLY) {
110 PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
112 if (m_pipefd[1] == PIPE_CLOSED) {
116 std::ostringstream output;
117 output << toBinaryString(code);
118 output << toBinaryString(MSG_TYPE_MESSAGE);
119 output << toBinaryString(static_cast<int>(message.size()));
122 std::string binary = output.str();
123 int size = binary.size();
125 if ((writeHelp(&size,
126 sizeof(int)) == ERROR) ||
127 (writeHelp(binary.c_str(), size) == ERROR))
134 PipeWrapper::Status PipeWrapper::sendTime(int code,
135 std::chrono::system_clock::duration time,
136 std::chrono::system_clock::duration timeMax)
138 if (m_pipefd[1] == PIPE_CLOSED) {
142 std::ostringstream output;
143 output << toBinaryString(code);
144 output << toBinaryString(MSG_TYPE_PERF_TIME);
145 output << toBinaryString(time);
146 output << toBinaryString(timeMax);
148 std::string binary = output.str();
149 int size = binary.size();
151 if ((writeHelp(&size,
152 sizeof(int)) == ERROR) ||
153 (writeHelp(binary.c_str(), size) == ERROR))
160 PipeWrapper::Status PipeWrapper::receive(int &code,
163 std::chrono::system_clock::duration &time,
164 std::chrono::system_clock::duration &timeMax,
167 if (m_pipefd[0] == PIPE_CLOSED) {
174 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
178 std::vector<char> buffer;
181 if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
186 DPL::BinaryQueue queue;
187 queue.AppendCopy(&buffer[0], size);
189 queue.FlattenConsume(&code, sizeof(int));
190 queue.FlattenConsume(&msgType, sizeof(int));
193 case MSG_TYPE_MESSAGE:
194 queue.FlattenConsume(&size, sizeof(int));
198 queue.FlattenConsume(&buffer[0], size);
199 data.assign(buffer.begin(), buffer.end());
201 case MSG_TYPE_PERF_TIME:
202 queue.FlattenConsume(&time, sizeof(std::chrono::system_clock::duration));
203 queue.FlattenConsume(&timeMax, sizeof(std::chrono::system_clock::duration));
208 } catch (DPL::BinaryQueue::Exception::Base &e) {
214 void PipeWrapper::closeAll()
220 std::string PipeWrapper::toBinaryString(int data)
222 char buffer[sizeof(int)];
223 memcpy(buffer, &data, sizeof(int));
224 return std::string(buffer, buffer + sizeof(int));
227 std::string PipeWrapper::toBinaryString(std::chrono::system_clock::duration data)
229 char buffer[sizeof(std::chrono::system_clock::duration)];
230 memcpy(buffer, &data, sizeof(std::chrono::system_clock::duration));
231 return std::string(buffer, buffer + sizeof(std::chrono::system_clock::duration));
234 void PipeWrapper::closeHelp(int desc)
236 if (m_pipefd[desc] != PIPE_CLOSED) {
237 TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
238 m_pipefd[desc] = PIPE_CLOSED;
242 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
245 const char *p = static_cast<const char *>(buffer);
246 while (ready != size) {
247 int ret = write(m_pipefd[1], &p[ready], size - ready);
249 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
263 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
266 char *buffer = static_cast<char*>(buf);
267 while (ready != size) {
268 time_t wait = deadline - time(0);
269 wait = wait < 1 ? 1 : wait;
270 pollfd fds = { m_pipefd[0], POLLIN, 0 };
272 int pollReturn = poll(&fds, 1, wait * 1000);
274 if (pollReturn == 0) {
275 return TIMEOUT; // Timeout
278 if (pollReturn < -1) {
282 int ret = read(m_pipefd[0], &buffer[ready], size - ready);
284 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
288 if (ret == -1 || ret == 0) {
298 void RunChildProc(TestRunner::TestCase procChild)
301 if (!pipe.isReady()) {
302 throw TestFailed("Pipe creation failed");
308 throw TestFailed("Child creation failed");
313 pipe.setUsage(PipeWrapper::READONLY);
317 std::chrono::system_clock::duration time_m;
318 std::chrono::system_clock::duration timeMax_m;
321 int pipeReturn = pipe.receive(code, msgType, message, time_m, timeMax_m, time(0) + 10);
323 if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
329 waitpid(pid, &status, 0);
331 if (pipeReturn == PipeWrapper::TIMEOUT) {
332 throw TestFailed("Timeout");
335 if (pipeReturn == PipeWrapper::ERROR) {
336 throw TestFailed("Reading pipe error");
339 if (code == CHILD_TEST_PASS && msgType == MSG_TYPE_PERF_TIME) {
340 DPL::Test::TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(true,
345 if (code == CHILD_TEST_FAIL) {
346 throw TestFailed(message);
347 } else if (code == CHILD_TEST_IGNORED) {
348 throw TestIgnored(message);
353 // End Runner after current test
354 TestRunnerSingleton::Instance().Terminate();
356 int code = CHILD_TEST_PASS;
358 bool isPerformanceTest;
359 std::chrono::system_clock::duration time_m;
360 std::chrono::system_clock::duration timeMax_m;
362 bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
366 closeOutput(); // if fails nothing we can do
369 pipe.setUsage(PipeWrapper::WRITEONLY);
373 } catch (const DPL::Test::TestFailed &e) {
374 msg = e.GetMessage();
375 code = CHILD_TEST_FAIL;
376 } catch (const DPL::Test::TestIgnored &e) {
377 msg = e.GetMessage();
378 code = CHILD_TEST_IGNORED;
379 } catch (...) { // catch all exception generated by "user" code
380 msg = "unhandled exeception";
381 code = CHILD_TEST_FAIL;
388 DPL::Test::TestRunnerSingleton::Instance().getCurrentTestCasePerformanceResult(isPerformanceTest,
392 if (code == CHILD_TEST_PASS && isPerformanceTest){
397 pipe.send(code, msg);