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/assert.h>
24 #include <dpl/test/test_failed.h>
25 #include <dpl/test/test_ignored.h>
26 #include <dpl/test/test_runner.h>
27 #include <dpl/test/test_runner_child.h>
28 #include <dpl/test/test_results_collector.h>
29 #include <dpl/binary_queue.h>
30 #include <dpl/exception.h>
31 #include <dpl/scoped_free.h>
32 #include <dpl/colors.h>
44 #include <sys/types.h>
49 const int CHILD_TEST_FAIL = 0;
50 const int CHILD_TEST_PASS = 1;
51 const int CHILD_TEST_IGNORED = 2;
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(static_cast<int>(message.size()));
119 std::string binary = output.str();
120 int size = binary.size();
122 if ((writeHelp(&size,
123 sizeof(int)) == ERROR) ||
124 (writeHelp(binary.c_str(), size) == ERROR))
131 PipeWrapper::Status PipeWrapper::sendPerformance(const ConstPerformanceResultPtr &performance)
134 if (m_pipefd[1] == PIPE_CLOSED) {
138 return writeHelp(&foo, sizeof(int));
140 std::string binary = performance->ToBinaryString();
141 int size = binary.size();
143 if ((writeHelp(&size,
144 sizeof(int)) == ERROR) ||
145 (writeHelp(binary.c_str(), size) == ERROR))
152 PipeWrapper::Status PipeWrapper::receive(int &code,
154 PerformanceResultPtr &performance,
157 if (m_pipefd[0] == PIPE_CLOSED) {
164 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
168 std::vector<char> buffer;
171 if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
176 DPL::BinaryQueue queue;
177 queue.AppendCopy(&buffer[0], size);
179 queue.FlattenConsume(&code, sizeof(int));
180 queue.FlattenConsume(&size, sizeof(int));
184 queue.FlattenConsume(&buffer[0], size);
185 data.assign(buffer.begin(), buffer.end());
187 if (code != CHILD_TEST_PASS)
190 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
195 performance = nullptr;
201 if ((ret = readHelp(buffer.data(), size, deadline)) != SUCCESS) {
205 queue.AppendCopy(buffer.data(), size);
207 performance.reset(new PerformanceResult(queue));
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 void PipeWrapper::closeHelp(int desc)
229 if (m_pipefd[desc] != PIPE_CLOSED) {
230 TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
231 m_pipefd[desc] = PIPE_CLOSED;
235 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
238 const char *p = static_cast<const char *>(buffer);
239 while (ready != size) {
240 int ret = write(m_pipefd[1], &p[ready], size - ready);
242 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
256 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
259 char *buffer = static_cast<char*>(buf);
260 while (ready != size) {
261 time_t wait = deadline - time(0);
262 wait = wait < 1 ? 1 : wait;
263 pollfd fds = { m_pipefd[0], POLLIN, 0 };
265 int pollReturn = poll(&fds, 1, wait * 1000);
267 if (pollReturn == 0) {
268 return TIMEOUT; // Timeout
271 if (pollReturn < -1) {
275 int ret = read(m_pipefd[0], &buffer[ready], size - ready);
277 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
281 if (ret == -1 || ret == 0) {
291 void RunChildProc(const std::function<void(void)> &testFunc)
294 if (!pipe.isReady()) {
295 throw TestFailed("Pipe creation failed");
301 throw TestFailed("Child creation failed");
306 pipe.setUsage(PipeWrapper::READONLY);
310 PerformanceResultPtr performance;
312 int pipeReturn = pipe.receive(code, message, performance, time(0) + 10);
314 if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
320 waitpid(pid, &status, 0);
322 if (pipeReturn == PipeWrapper::TIMEOUT) {
323 throw TestFailed("Timeout");
326 if (pipeReturn == PipeWrapper::ERROR) {
327 throw TestFailed("Reading pipe error");
330 TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(performance);
332 if (code == CHILD_TEST_FAIL) {
333 throw TestFailed(message);
334 } else if (code == CHILD_TEST_IGNORED) {
335 throw TestIgnored(message);
340 // End Runner after current test
341 TestRunnerSingleton::Instance().Terminate();
343 bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
347 closeOutput(); // if fails nothing we can do
350 pipe.setUsage(PipeWrapper::WRITEONLY);
354 switch (TryCatch(testFunc, msg)) {
355 case TestResult::FailStatus::FAILED:
356 code = CHILD_TEST_FAIL;
358 case TestResult::FailStatus::IGNORED:
359 code = CHILD_TEST_IGNORED;
361 case TestResult::FailStatus::NONE:
362 code = CHILD_TEST_PASS;
365 Assert(false && "Unhandled fail status");
372 pipe.send(code, msg);
373 if (code == CHILD_TEST_PASS){
374 pipe.sendPerformance(TestRunnerSingleton::Instance() \
375 .getCurrentTestCasePerformanceResult());