Running many processes in one test case would work fine on those rules.
* Using wait(), waitpid(), waitid() functions within test case body is forbidden
(waiting for child processes is handeled by framework).
* Process exiting properly should exit only 0.
* Communication by signals is forbidden - signals could be cought by parent and
ignored.
Tests also provided. To run them build this flags below and run them as
wrt-commons-tests-test --output=text --runignored .
flags: --define "WITH_TESTS ON" --define "WITH_CHILD ON"
All tests from DPL_TESTS_TEST_MULTIPROCESS group should pass.
[Issue#] SSDWSSP-438
[Bug/Feature] Calling fork() inside RUNNER_TEST causes errors for whole process
group while failed or ignored. Additional path for each redundant
failed process is created.
[Cause] N/A
[Solution] Creating test case type controlling processes.
[Verification] Applying it to failing multiprocess tests. All errors should
be collected as one and no additional process should run after
test case ends.
Change-Id: I371528de102def369107cab3c5cffe0b2f53e9e5
${PROJECT_SOURCE_DIR}/modules/test/src/test_results_collector.cpp
${PROJECT_SOURCE_DIR}/modules/test/src/test_runner.cpp
${PROJECT_SOURCE_DIR}/modules/test/src/test_runner_child.cpp
+ ${PROJECT_SOURCE_DIR}/modules/test/src/test_runner_multiprocess.cpp
${PROJECT_SOURCE_DIR}/modules/test/src/process_pipe.cpp
PARENT_SCOPE
)
${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_results_collector.h
${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner.h
${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner_child.h
+ ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner_multiprocess.h
${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/process_pipe.h
PARENT_SCOPE
)
namespace DPL {
namespace Test {
+
+class PipeWrapper : DPL::Noncopyable
+{
+ public:
+ enum Usage {
+ READONLY,
+ WRITEONLY
+ };
+
+ enum Status {
+ SUCCESS,
+ TIMEOUT,
+ ERROR
+ };
+
+ PipeWrapper();
+
+ bool isReady();
+
+ void setUsage(Usage usage);
+
+ virtual ~PipeWrapper();
+
+ Status send(int code, std::string &message);
+
+ Status receive(int &code, std::string &data, time_t deadline);
+
+ void closeAll();
+
+ protected:
+
+ std::string toBinaryString(int data);
+
+ void closeHelp(int desc);
+
+ Status writeHelp(const void *buffer, int size);
+
+ Status readHelp(void *buf, int size, time_t deadline);
+
+ static const int PIPE_CLOSED = -1;
+
+ int m_pipefd[2];
+};
+
void RunChildProc(TestRunner::TestCase procChild);
} // namespace Test
} // namespace DPL
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @file test_runner_multiprocess.h
+ * @author Marcin Niesluchowski (m.niesluchow@samsung.com)
+ * @version 1.0
+ * @brief This file is the header file of multiprocess test runner
+ */
+#ifndef DPL_TEST_RUNNER_MULTIPROCESS_H
+#define DPL_TEST_RUNNER_MULTIPROCESS_H
+
+#include <dpl/test/test_runner_child.h>
+
+namespace DPL {
+namespace Test {
+
+class SimplePipeWrapper :
+ public PipeWrapper
+{
+ public:
+ SimplePipeWrapper();
+
+ virtual ~SimplePipeWrapper();
+
+ Status send(std::string &message);
+ Status receive(std::string &data, bool &empty, time_t deadline);
+};
+
+void RunMultiProc(TestRunner::TestCase procMulti);
+} // namespace Test
+} // namespace DPL
+
+#define RUNNER_MULTIPROCESS_TEST(Proc) \
+ void Proc(); \
+ void Proc##Multi(); \
+ static int Static##Proc##Init() \
+ { \
+ DPL::Test::TestRunnerSingleton::Instance().RegisterTest(#Proc, &Proc); \
+ return 0; \
+ } \
+ const int DPL_UNUSED Static##Proc##InitVar = Static##Proc##Init(); \
+ void Proc(){ \
+ DPL::Test::RunMultiProc(&Proc##Multi); \
+ } \
+ void Proc##Multi()
+
+#endif // DPL_TEST_RUNNER_MULTIPROCESS_H
#include <dpl/utils/wrt_global_settings.h>
namespace {
-const int PIPE_CLOSED = -1;
-
const int CHILD_TEST_FAIL = 0;
const int CHILD_TEST_PASS = 1;
const int CHILD_TEST_IGNORED = 2;
namespace DPL {
namespace Test {
-class PipeWrapper : DPL::Noncopyable
+
+PipeWrapper::PipeWrapper()
{
- public:
- enum Usage {
- READONLY,
- WRITEONLY
- };
-
- enum Status {
- SUCCESS,
- TIMEOUT,
- ERROR
- };
-
- PipeWrapper()
- {
- if (-1 == pipe(m_pipefd)) {
- m_pipefd[0] = PIPE_CLOSED;
- m_pipefd[1] = PIPE_CLOSED;
- }
+ if (-1 == pipe(m_pipefd)) {
+ m_pipefd[0] = PIPE_CLOSED;
+ m_pipefd[1] = PIPE_CLOSED;
}
+}
- bool isReady()
- {
- return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
- }
+PipeWrapper::~PipeWrapper()
+{
+ closeHelp(0);
+ closeHelp(1);
+}
- void setUsage(Usage usage)
- {
- if (usage == READONLY) {
- closeHelp(1);
- }
- if (usage == WRITEONLY) {
- closeHelp(0);
- }
+bool PipeWrapper::isReady()
+{
+ return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
+}
+
+void PipeWrapper::setUsage(Usage usage)
+{
+ if (usage == READONLY) {
+ closeHelp(1);
}
- ~PipeWrapper()
- {
+ if (usage == WRITEONLY) {
closeHelp(0);
- closeHelp(1);
}
+}
- Status send(int code, std::string &message)
- {
- if (m_pipefd[1] == PIPE_CLOSED) {
- return ERROR;
- }
+PipeWrapper::Status PipeWrapper::send(int code, std::string &message)
+{
+ if (m_pipefd[1] == PIPE_CLOSED) {
+ return ERROR;
+ }
- std::ostringstream output;
- output << toBinaryString(code);
- output << toBinaryString(static_cast<int>(message.size()));
- output << message;
+ std::ostringstream output;
+ output << toBinaryString(code);
+ output << toBinaryString(static_cast<int>(message.size()));
+ output << message;
- std::string binary = output.str();
- int size = binary.size();
+ std::string binary = output.str();
+ int size = binary.size();
- if ((writeHelp(&size,
- sizeof(int)) == ERROR) ||
- (writeHelp(binary.c_str(), size) == ERROR))
- {
- return ERROR;
- }
- return SUCCESS;
+ if ((writeHelp(&size,
+ sizeof(int)) == ERROR) ||
+ (writeHelp(binary.c_str(), size) == ERROR))
+ {
+ return ERROR;
}
+ return SUCCESS;
+}
- Status receive(int &code, std::string &data, time_t deadline)
- {
- if (m_pipefd[0] == PIPE_CLOSED) {
- return ERROR;
- }
+PipeWrapper::Status PipeWrapper::receive(int &code, std::string &data, time_t deadline)
+{
+ if (m_pipefd[0] == PIPE_CLOSED) {
+ return ERROR;
+ }
- int size;
- Status ret;
+ int size;
+ Status ret;
- if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
- return ret;
- }
+ if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
+ return ret;
+ }
- std::vector<char> buffer;
- buffer.resize(size);
+ std::vector<char> buffer;
+ buffer.resize(size);
- if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
- return ret;
- }
+ if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS) {
+ return ret;
+ }
- try {
- DPL::BinaryQueue queue;
- queue.AppendCopy(&buffer[0], size);
+ try {
+ DPL::BinaryQueue queue;
+ queue.AppendCopy(&buffer[0], size);
- queue.FlattenConsume(&code, sizeof(int));
- queue.FlattenConsume(&size, sizeof(int));
+ queue.FlattenConsume(&code, sizeof(int));
+ queue.FlattenConsume(&size, sizeof(int));
- buffer.resize(size);
+ buffer.resize(size);
- queue.FlattenConsume(&buffer[0], size);
- data.assign(buffer.begin(), buffer.end());
- } catch (DPL::BinaryQueue::Exception::Base &e) {
- return ERROR;
- }
- return SUCCESS;
+ queue.FlattenConsume(&buffer[0], size);
+ data.assign(buffer.begin(), buffer.end());
+ } catch (DPL::BinaryQueue::Exception::Base &e) {
+ return ERROR;
}
+ return SUCCESS;
+}
- void closeAll()
- {
- closeHelp(0);
- closeHelp(1);
- }
+void PipeWrapper::closeAll()
+{
+ closeHelp(0);
+ closeHelp(1);
+}
- private:
- std::string toBinaryString(int data)
- {
- char buffer[sizeof(int)];
- memcpy(buffer, &data, sizeof(int));
- return std::string(buffer, buffer + sizeof(int));
- }
+std::string PipeWrapper::toBinaryString(int data)
+{
+ char buffer[sizeof(int)];
+ memcpy(buffer, &data, sizeof(int));
+ return std::string(buffer, buffer + sizeof(int));
+}
- void closeHelp(int desc)
- {
- if (m_pipefd[desc] != PIPE_CLOSED) {
- TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
- m_pipefd[desc] = PIPE_CLOSED;
- }
+void PipeWrapper::closeHelp(int desc)
+{
+ if (m_pipefd[desc] != PIPE_CLOSED) {
+ TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
+ m_pipefd[desc] = PIPE_CLOSED;
}
+}
- Status writeHelp(const void *buffer, int size)
- {
- int ready = 0;
- const char *p = static_cast<const char *>(buffer);
- while (ready != size) {
- int ret = write(m_pipefd[1], &p[ready], size - ready);
-
- if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
- continue;
- }
+PipeWrapper::Status PipeWrapper::writeHelp(const void *buffer, int size)
+{
+ int ready = 0;
+ const char *p = static_cast<const char *>(buffer);
+ while (ready != size) {
+ int ret = write(m_pipefd[1], &p[ready], size - ready);
- if (ret == -1) {
- closeHelp(1);
- return ERROR;
- }
+ if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
+ continue;
+ }
- ready += ret;
+ if (ret == -1) {
+ closeHelp(1);
+ return ERROR;
}
- return SUCCESS;
- }
- Status readHelp(void *buf, int size, time_t deadline)
- {
- int ready = 0;
- char *buffer = static_cast<char*>(buf);
- while (ready != size) {
- time_t wait = deadline - time(0);
- wait = wait < 1 ? 1 : wait;
- pollfd fds = { m_pipefd[0], POLLIN, 0 };
+ ready += ret;
+ }
+ return SUCCESS;
+}
- int pollReturn = poll(&fds, 1, wait * 1000);
+PipeWrapper::Status PipeWrapper::readHelp(void *buf, int size, time_t deadline)
+{
+ int ready = 0;
+ char *buffer = static_cast<char*>(buf);
+ while (ready != size) {
+ time_t wait = deadline - time(0);
+ wait = wait < 1 ? 1 : wait;
+ pollfd fds = { m_pipefd[0], POLLIN, 0 };
- if (pollReturn == 0) {
- return TIMEOUT; // Timeout
- }
+ int pollReturn = poll(&fds, 1, wait * 1000);
- if (pollReturn < -1) {
- return ERROR;
- }
+ if (pollReturn == 0) {
+ return TIMEOUT; // Timeout
+ }
- int ret = read(m_pipefd[0], &buffer[ready], size - ready);
+ if (pollReturn < -1) {
+ return ERROR;
+ }
- if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
- continue;
- }
+ int ret = read(m_pipefd[0], &buffer[ready], size - ready);
- if (ret == -1 || ret == 0) {
- closeHelp(0);
- return ERROR;
- }
+ if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
+ continue;
+ }
- ready += ret;
+ if (ret == -1 || ret == 0) {
+ closeHelp(0);
+ return ERROR;
}
- return SUCCESS;
- }
- int m_pipefd[2];
-};
+ ready += ret;
+ }
+ return SUCCESS;
+}
void RunChildProc(TestRunner::TestCase procChild)
{
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @file test_runner_multiprocess.cpp
+ * @author Marcin Niesluchowski (m.niesluchow@samsung.com)
+ * @version 1.0
+ * @brief This file is the implementation file of multiprocess test runner
+ */
+
+#include <sys/file.h>
+#include <dpl/test/test_runner.h>
+#include <dpl/test/test_runner_child.h>
+#include <dpl/test/test_runner_multiprocess.h>
+#include <poll.h>
+#include <limits.h>
+#include <sys/wait.h>
+
+namespace {
+
+const int MULTI_TEST_ERROR = -1;
+const int MULTI_TEST_PASS = 0;
+const int MULTI_TEST_FAILED = 1;
+const int MULTI_TEST_IGNORED = 2;
+const int MULTI_TEST_INTERNAL = 3;
+
+}
+
+namespace DPL {
+namespace Test {
+
+SimplePipeWrapper::SimplePipeWrapper()
+: PipeWrapper()
+{
+
+}
+
+SimplePipeWrapper::~SimplePipeWrapper()
+{
+
+}
+
+PipeWrapper::Status SimplePipeWrapper::send(std::string &message)
+{
+ if (m_pipefd[1] == PIPE_CLOSED) {
+ return ERROR;
+ }
+
+ if (message.size() > PIPE_BUF-1) {
+ return ERROR;
+ }
+
+ char buffer[PIPE_BUF] = { 0 };
+
+
+ for(unsigned int i = 0; i < message.size(); ++i) {
+ buffer[i] = message[i];
+ }
+
+ return writeHelp(buffer, PIPE_BUF);
+}
+
+PipeWrapper::Status SimplePipeWrapper::receive(std::string &data, bool &empty, time_t deadline)
+{
+ if (m_pipefd[0] == PIPE_CLOSED) {
+ return ERROR;
+ }
+
+ empty = false;
+
+ data.resize(PIPE_BUF);
+
+ char buffer[PIPE_BUF] = { 0 };
+
+ int ready = 0;
+ while (ready != PIPE_BUF) {
+ time_t wait = deadline - time(0);
+ wait = wait < 1 ? 1 : wait;
+ pollfd fds = { m_pipefd[0], POLLIN, 0 };
+
+ int pollReturn = poll(&fds, 1, wait * 1000);
+
+ if (pollReturn == 0) {
+ return TIMEOUT; // Timeout
+ }
+
+ if (pollReturn < -1) {
+ return ERROR;
+ }
+ int ret = read(m_pipefd[0], &buffer[ready], PIPE_BUF - ready);
+ if (ret == -1 && (errno == EAGAIN || errno == EINTR)) {
+ continue;
+ }
+
+ if (ret == -1) {
+ closeHelp(0);
+ return ERROR;
+ }
+ if (ret == 0) {
+ empty = true;
+ break;
+ }
+
+ ready += ret;
+ }
+
+
+ for(unsigned int i = 0; i < PIPE_BUF; ++i){
+ if(buffer[i] == 0) {
+ data.resize(i);
+ return SUCCESS;
+ }
+ data[i] = buffer[i];
+ }
+
+ return ERROR;
+}
+
+void RunMultiProc(TestRunner::TestCase procMulti)
+{
+ SimplePipeWrapper pipe;
+ int code = MULTI_TEST_PASS;
+ std::string msg = "";
+ int pipeReturn;
+
+ int waitStatus;
+
+ pid_t top_pid = getpid();
+
+ if (!pipe.isReady()) {
+ throw TestRunner::TestFailed("Pipe creation failed");
+ }
+ // pipe
+
+ try {
+ procMulti();
+ } catch (const TestRunner::TestFailed &e) {
+ code = MULTI_TEST_FAILED;
+ msg = e.GetMessage();
+ } catch (const TestRunner::Ignored &e) {
+ code = MULTI_TEST_IGNORED;
+ msg = e.GetMessage();
+ } catch (const DPL::Exception &e) {
+ code = MULTI_TEST_INTERNAL;
+ msg = "DPL exception:" + e.GetMessage();
+ } catch (const std::exception &) {
+ code = MULTI_TEST_INTERNAL;
+ msg = "std exception";
+ } catch (...) {
+ // Unknown exception failure
+ code = MULTI_TEST_INTERNAL;
+ msg = "unknown exception";
+ }
+
+ while (true) {
+ pid_t child_pid = wait(&waitStatus);
+ if (child_pid == -1) {
+ if (errno == ECHILD) {
+ if (top_pid == getpid()) {
+ std::string recMsg="";
+
+ pipe.setUsage(PipeWrapper::READONLY);
+
+ bool empty=false;
+ while(true) {
+ pipeReturn = pipe.receive(recMsg, empty, time(0) + 10);
+
+ if (empty) {
+ break;
+ }
+ if (pipeReturn == PipeWrapper::ERROR) {
+ pipe.closeAll();
+ throw TestRunner::TestFailed("Reading pipe error");
+ } else if (pipeReturn == PipeWrapper::TIMEOUT) {
+ pipe.closeAll();
+ throw TestRunner::TestFailed("Timeout error");
+ }
+ msg = msg + "\n" + recMsg;
+ }
+ pipe.closeAll();
+
+ switch(code) {
+ case MULTI_TEST_PASS:
+ return;
+ case MULTI_TEST_FAILED:
+ throw TestRunner::TestFailed(msg);
+ case MULTI_TEST_IGNORED:
+ throw TestRunner::Ignored(msg);
+ case MULTI_TEST_INTERNAL:
+ throw TestRunner::TestFailed(msg);
+ default:
+ throw TestRunner::TestFailed(msg);
+ }
+ } else {
+ pipe.setUsage(PipeWrapper::WRITEONLY);
+
+ pipeReturn = pipe.send(msg);
+
+ if (pipeReturn == PipeWrapper::ERROR) {
+ pipe.closeAll();
+ code = MULTI_TEST_ERROR;
+ }
+
+ exit(code);
+ }
+ }
+ } else if (WIFEXITED(waitStatus)) {
+ if ((signed char)WEXITSTATUS(waitStatus) == MULTI_TEST_FAILED) {
+ switch (code) {
+ case MULTI_TEST_PASS:
+ code = MULTI_TEST_FAILED;
+ break;
+ case MULTI_TEST_FAILED:
+ break;
+ case MULTI_TEST_IGNORED:
+ code = MULTI_TEST_FAILED;
+ break;
+ case MULTI_TEST_INTERNAL:
+ break;
+ default:
+ break;
+ }
+ } else if ((signed char)WEXITSTATUS(waitStatus) == MULTI_TEST_IGNORED) {
+ switch (code) {
+ case MULTI_TEST_PASS:
+ code = MULTI_TEST_IGNORED;
+ break;
+ case MULTI_TEST_FAILED:
+ break;
+ case MULTI_TEST_IGNORED:
+ break;
+ case MULTI_TEST_INTERNAL:
+ break;
+ default:
+ break;
+ }
+ } else if ((signed char)WEXITSTATUS(waitStatus) == MULTI_TEST_INTERNAL) {
+ switch (code) {
+ case MULTI_TEST_PASS:
+ code = MULTI_TEST_INTERNAL;
+ break;
+ case MULTI_TEST_FAILED:
+ code = MULTI_TEST_INTERNAL;
+ break;
+ case MULTI_TEST_IGNORED:
+ code = MULTI_TEST_INTERNAL;
+ break;
+ case MULTI_TEST_INTERNAL:
+ break;
+ default:
+ break;
+ }
+ } else if ((signed char)WEXITSTATUS(waitStatus) != MULTI_TEST_PASS) {
+ code = MULTI_TEST_ERROR;
+ msg = "PROCESS BAD CODE RETURN";
+ }
+ }
+ }
+}
+} // namespace Test
+} // namespace DPL
# Set DPL tests sources
SET(DPL_TESTS_UTIL_SOURCES
${TESTS_DIR}/test/main.cpp
+ ${TESTS_DIR}/test/runner_multiprocess.cpp
${TESTS_DIR}/test/runner_child.cpp
${TESTS_DIR}/test/test_process_pipe.cpp
)
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @file runner_multiprocess.cpp
+ * @author Marcin Niesluchowski (m.niesluchow@samsung.com)
+ * @version 1.0
+ * @brief Implementation file for test cases for engine internal tests
+ */
+
+#include <dpl/test/test_runner_multiprocess.h>
+#include <dpl/test/test_runner.h>
+#include <dpl/log/log.h>
+#include <list>
+
+namespace {
+std::list<std::string> split_string(std::string str, std::string delimiter)
+{
+ size_t pos = 0;
+ std::string token;
+ std::list<std::string> stringList;
+ while ((pos = str.find(delimiter)) != std::string::npos) {
+ token = str.substr(0, pos);
+ stringList.push_back(token);
+ str.erase(0, pos + delimiter.length());
+ }
+ if(str.length() != 0){
+ stringList.push_back(token);
+ }
+ return stringList;
+}
+}
+
+#define RUNNER_MULTIPROCESS_TEST_EXPECT(name, messages) \
+ static void testExpectFunction##name(); \
+ RUNNER_TEST(name) \
+ { \
+ Try \
+ { \
+ DPL::Test::RunMultiProc(&testExpectFunction##name); \
+ } \
+ Catch(DPL::Test::TestRunner::TestFailed) \
+ { \
+ std::string eMsg = messages; \
+ std::list<std::string> eMessages = split_string(eMsg, "|"); \
+ std::string rMessage = _rethrown_exception.GetMessage(); \
+ if(eMsg.length() == 0 && rMessage.length() != 0) { \
+ RUNNER_ASSERT_MSG(false, rMessage); \
+ } \
+ bool failedFound = false; \
+ for(std::list<std::string>::iterator it = eMessages.begin(); \
+ it != eMessages.end(); \
+ ++it) \
+ { \
+ if (!(*it).compare("TEST_FAILED")) { \
+ failedFound = true; \
+ continue; \
+ } \
+ RUNNER_ASSERT_MSG(rMessage.find(*it)!=std::string::npos, \
+ "Key word " << *it << " not found in " << rMessage); \
+ } \
+ RUNNER_ASSERT_MSG( \
+ rMessage.find("Reading pipe error")==std::string::npos, \
+ "Reading pipe error"); \
+ RUNNER_ASSERT_MSG( \
+ rMessage.find("Timeout error")==std::string::npos, \
+ "Timeout error"); \
+ RUNNER_ASSERT_MSG(failedFound, "No TEST_FAILED found"); \
+ } \
+ Catch(DPL::Test::TestRunner::Ignored) \
+ { \
+ std::string eMsg = messages; \
+ std::list<std::string> eMessages = split_string(eMsg, "|"); \
+ std::string rMessage = _rethrown_exception.GetMessage(); \
+ if(eMsg.length() == 0 && rMessage.length() != 0) { \
+ RUNNER_ASSERT_MSG(false, rMessage); \
+ } \
+ bool ignoredFound = false; \
+ for(std::list<std::string>::iterator it = eMessages.begin(); \
+ it != eMessages.end(); \
+ ++it) \
+ { \
+ if (!(*it).compare("TEST_IGNORED")) { \
+ ignoredFound = true; \
+ continue; \
+ } \
+ RUNNER_ASSERT_MSG(rMessage.find(*it)!=std::string::npos, \
+ "Key word " << *it << " not found in " << rMessage); \
+ } \
+ RUNNER_ASSERT_MSG( \
+ rMessage.find("Reading pipe error")==std::string::npos, \
+ "Reading pipe error"); \
+ RUNNER_ASSERT_MSG( \
+ rMessage.find("Timeout error")==std::string::npos, \
+ "Timeout error"); \
+ RUNNER_ASSERT_MSG(ignoredFound, "No TEST_IGNORED found"); \
+ } \
+ } \
+ void testExpectFunction##name() \
+
+RUNNER_TEST_GROUP_INIT(DPL_TESTS_TEST_MULTIPROCESS)
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm00_pass, "")
+{
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm01_pass, "")
+{
+ pid_t pid = fork();
+ if(pid){
+ sleep(2);
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+ } else {
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+ }
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm02_pass, "")
+{
+ pid_t pid = fork();
+ if(pid){
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+ } else {
+ sleep(2);
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+ }
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm03_pass, "")
+{
+ pid_t pid = fork();
+ if(pid){
+ pid = fork();
+ if(pid){
+ sleep(1);
+ } else {
+ sleep(2);
+ }
+ } else {
+ if(pid){
+ sleep(2);
+ } else {
+ sleep(1);
+ }
+ }
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm04_fail, "TEST_FAILED|"
+ "This test should fail")
+{
+ RUNNER_ASSERT_MSG(0, "This test should fail");
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm05_fail,"TEST_FAILED|"
+ "Test failed 1|"
+ "Test failed 2|"
+ "Test failed 3|"
+ "Test failed 4")
+{
+ pid_t pid = fork();
+ if(pid){
+ pid = fork();
+ if(pid){
+ RUNNER_ASSERT_MSG(0, "Test failed 1");
+ } else {
+ RUNNER_ASSERT_MSG(0, "Test failed 2");
+ }
+ } else {
+ pid = fork();
+ if(pid){
+ RUNNER_ASSERT_MSG(0, "Test failed 3");
+ } else {
+ RUNNER_ASSERT_MSG(0, "Test failed 4");
+ }
+ }
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm06_fail, "TEST_FAILED|"
+ "Test failed 1|"
+ "Test failed 2|"
+ "Test failed 3|"
+ "Test failed 4")
+{
+ pid_t pid = fork();
+ if(pid){
+ pid = fork();
+ if(pid){
+ sleep(2);
+ RUNNER_ASSERT_MSG(0, "Test failed 1");
+ } else {
+ RUNNER_ASSERT_MSG(0, "Test failed 2");
+ }
+ } else {
+ pid = fork();
+ if(pid){
+ RUNNER_ASSERT_MSG(0, "Test failed 3");
+ } else {
+ RUNNER_ASSERT_MSG(0, "Test failed 4");
+ }
+ }
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm07_fail, "TEST_FAILED|"
+ "Test failed 1|"
+ "Test failed 2|"
+ "Test failed 3|"
+ "Test failed 4")
+{
+ pid_t pid = fork();
+ if(pid){
+ pid = fork();
+ if(pid){
+ RUNNER_ASSERT_MSG(0, "Test failed 1");
+ } else {
+ RUNNER_ASSERT_MSG(0, "Test failed 2");
+ }
+ } else {
+ pid = fork();
+ if(pid){
+ sleep(2);
+ RUNNER_ASSERT_MSG(0, "Test failed 3");
+ } else {
+ RUNNER_ASSERT_MSG(0, "Test failed 4");
+ }
+ }
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm08_fail_unknown_exception, "TEST_FAILED|"
+ "unknown exception")
+{
+ throw("hello");
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm09_fail_unknown_exception, "TEST_FAILED|"
+ "unknown exception")
+{
+ throw(1);
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm10_ignore, "TEST_IGNORED|"
+ "Test ignored")
+{
+ RUNNER_IGNORED_MSG("Test ignored");
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm11_ignore, "TEST_IGNORED|"
+ "Test ignored 1|"
+ "Test ignored 2|"
+ "Test ignored 3|"
+ "Test ignored 4")
+{
+ pid_t pid = fork();
+ if(pid){
+ pid = fork();
+ if(pid){
+ RUNNER_IGNORED_MSG("Test ignored 1");
+ } else {
+ RUNNER_IGNORED_MSG("Test ignored 2");
+ }
+ } else {
+ pid = fork();
+ if(pid){
+ sleep(2);
+ RUNNER_IGNORED_MSG("Test ignored 3");
+ } else {
+ RUNNER_IGNORED_MSG("Test ignored 4");
+ }
+ }
+}
+
+RUNNER_MULTIPROCESS_TEST_EXPECT(tm12_fail, "TEST_FAILED|"
+ "Test failed 1|"
+ "Test ignored 2|"
+ "Test ignored 3|"
+ "Test ignored 4")
+{
+ pid_t pid = fork();
+ if(pid){
+ pid = fork();
+ if(pid){
+ RUNNER_ASSERT_MSG(0, "Test failed 1");
+ } else {
+ RUNNER_IGNORED_MSG("Test ignored 2");
+ }
+ } else {
+ pid = fork();
+ if(pid){
+ sleep(2);
+ RUNNER_IGNORED_MSG("Test ignored 3");
+ } else {
+ RUNNER_IGNORED_MSG("Test ignored 4");
+ }
+ }
+}