2 * Copyright (c) 2013-2017 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/test/safe_cleanup.h>
30 #include <dpl/binary_queue.h>
31 #include <dpl/exception.h>
32 #include <dpl/scoped_free.h>
33 #include <dpl/colors.h>
45 #include <sys/types.h>
50 const int CHILD_TEST_FAIL = 0;
51 const int CHILD_TEST_PASS = 1;
52 const int CHILD_TEST_IGNORED = 2;
57 if (-1 == (devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY))))
60 // replace stdout with /dev/null
61 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)))
64 // replace stderr with /dev/null
65 if (-1 == TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)))
75 } // namespace anonymous
80 PipeWrapper::PipeWrapper()
82 if (-1 == pipe(m_pipefd)) {
83 m_pipefd[0] = PIPE_CLOSED;
84 m_pipefd[1] = PIPE_CLOSED;
88 PipeWrapper::~PipeWrapper()
94 bool PipeWrapper::isReady()
96 return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
99 void PipeWrapper::setUsage(Usage usage)
101 if (usage == READONLY) {
104 if (usage == WRITEONLY) {
109 PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
111 if (m_pipefd[1] == PIPE_CLOSED) {
115 std::ostringstream output;
116 output << toBinaryString(code);
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::sendPerformance(const ConstPerformanceResultPtr &performance)
135 if (m_pipefd[1] == PIPE_CLOSED) {
139 return writeHelp(&foo, sizeof(int));
141 std::string binary = performance->ToBinaryString();
142 int size = binary.size();
144 if ((writeHelp(&size,
145 sizeof(int)) == ERROR) ||
146 (writeHelp(binary.c_str(), size) == ERROR))
153 PipeWrapper::Status PipeWrapper::receive(int &code,
155 PerformanceResultPtr &performance,
158 if (m_pipefd[0] == PIPE_CLOSED) {
165 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
169 std::vector<char> buffer;
172 if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
177 DPL::BinaryQueue queue;
178 queue.AppendCopy(&buffer[0], size);
180 queue.FlattenConsume(&code, sizeof(int));
181 queue.FlattenConsume(&size, sizeof(int));
185 queue.FlattenConsume(&buffer[0], size);
186 data.assign(buffer.begin(), buffer.end());
188 if (code != CHILD_TEST_PASS)
191 if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
196 performance = nullptr;
202 if ((ret = readHelp(buffer.data(), size, deadline)) != SUCCESS) {
206 queue.AppendCopy(buffer.data(), size);
208 performance.reset(new PerformanceResult(queue));
209 } catch (DPL::BinaryQueue::Exception::Base &e) {
215 void PipeWrapper::closeAll()
221 std::string PipeWrapper::toBinaryString(int data)
223 char buffer[sizeof(int)];
224 memcpy(buffer, &data, sizeof(int));
225 return std::string(buffer, buffer + sizeof(int));
228 void PipeWrapper::closeHelp(int desc)
230 if (m_pipefd[desc] != PIPE_CLOSED) {
231 TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
232 m_pipefd[desc] = PIPE_CLOSED;
236 PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
239 const char *p = static_cast<const char *>(buffer);
240 while (ready != size) {
241 int ret = write(m_pipefd[1], &p[ready], size - ready);
243 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
257 PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
260 char *buffer = static_cast<char*>(buf);
261 while (ready != size) {
262 time_t wait = deadline - time(0);
263 wait = wait < 1 ? 1 : wait;
264 pollfd fds = { m_pipefd[0], POLLIN, 0 };
266 int pollReturn = poll(&fds, 1, wait * 1000);
268 if (pollReturn == 0) {
269 return TIMEOUT; // Timeout
272 if (pollReturn < -1) {
276 int ret = read(m_pipefd[0], &buffer[ready], size - ready);
278 if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
282 if (ret == -1 || ret == 0) {
292 void RunChildProc(const std::function<void(void)> &testFunc)
295 if (!pipe.isReady()) {
296 throw TestFailed("Pipe creation failed");
302 throw TestFailed("Child creation failed");
307 pipe.setUsage(PipeWrapper::READONLY);
311 PerformanceResultPtr performance;
313 int pipeReturn = pipe.receive(code, message, performance, time(0) + 90);
315 if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
321 waitpid(pid, &status, 0);
323 if (pipeReturn == PipeWrapper::TIMEOUT) {
324 throw TestFailed("Timeout");
327 if (pipeReturn == PipeWrapper::ERROR) {
328 throw TestFailed("Reading pipe error");
331 TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(performance);
333 if (code == CHILD_TEST_FAIL) {
334 throw TestFailed(message);
335 } else if (code == CHILD_TEST_IGNORED) {
336 throw TestIgnored(message);
341 // End Runner after current test
342 TestRunnerSingleton::Instance().Terminate();
344 bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
348 closeOutput(); // if fails nothing we can do
351 pipe.setUsage(PipeWrapper::WRITEONLY);
353 SafeCleanup::reset();
357 switch (TryCatch(testFunc, msg)) {
358 case TestResult::FailStatus::FAILED:
359 code = CHILD_TEST_FAIL;
361 case TestResult::FailStatus::IGNORED:
362 code = CHILD_TEST_IGNORED;
364 case TestResult::FailStatus::NONE:
365 code = CHILD_TEST_PASS;
368 Assert(false && "Unhandled fail status");
372 if (code != CHILD_TEST_IGNORED)
374 cleanup = SafeCleanup::dump();
375 if (!cleanup.empty()) {
376 code = CHILD_TEST_FAIL;
387 pipe.send(code, msg);
388 if (code == CHILD_TEST_PASS){
389 pipe.sendPerformance(TestRunnerSingleton::Instance() \
390 .getCurrentTestCasePerformanceResult());