Adding test case type RUNNER_MULTIPROCESS_TEST.
authorMarcin Niesluchowski <m.niesluchow@samsung.com>
Tue, 13 Aug 2013 10:36:19 +0000 (12:36 +0200)
committerGerrit Code Review <gerrit@gerrit.vlan144.tizendev.org>
Sat, 31 Aug 2013 08:21:49 +0000 (08:21 +0000)
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

modules/test/config.cmake
modules/test/include/dpl/test/test_runner_child.h
modules/test/include/dpl/test/test_runner_multiprocess.h [new file with mode: 0644]
modules/test/src/test_runner_child.cpp
modules/test/src/test_runner_multiprocess.cpp [new file with mode: 0644]
tests/test/CMakeLists.txt
tests/test/runner_multiprocess.cpp [new file with mode: 0644]

index d08366c..7938eaf 100644 (file)
@@ -23,6 +23,7 @@ SET(DPL_TEST_ENGINE_SOURCES
     ${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
 )
@@ -32,6 +33,7 @@ SET(DPL_TEST_ENGINE_HEADERS
     ${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
 )
index b20c371..1da0f1b 100644 (file)
 
 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
diff --git a/modules/test/include/dpl/test/test_runner_multiprocess.h b/modules/test/include/dpl/test/test_runner_multiprocess.h
new file mode 100644 (file)
index 0000000..279b5ef
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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
index db1ba29..8e793e8 100644 (file)
@@ -45,8 +45,6 @@
 #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;
@@ -76,188 +74,172 @@ end:
 
 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)
 {
diff --git a/modules/test/src/test_runner_multiprocess.cpp b/modules/test/src/test_runner_multiprocess.cpp
new file mode 100644 (file)
index 0000000..c242480
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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
index 48fb045..500e52b 100644 (file)
@@ -23,6 +23,7 @@ SET(TARGET_NAME "wrt-commons-tests-test")
 # 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
 )
diff --git a/tests/test/runner_multiprocess.cpp b/tests/test/runner_multiprocess.cpp
new file mode 100644 (file)
index 0000000..fcac88e
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * 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");
+        }
+    }
+}