Add defer macros 50/34050/9
authorLukasz Wojciechowski <l.wojciechow@partner.samsung.com>
Tue, 20 Jan 2015 14:17:12 +0000 (15:17 +0100)
committerLukasz Wojciechowski <l.wojciechow@partner.samsung.com>
Fri, 20 Mar 2015 10:37:51 +0000 (11:37 +0100)
Used to defer throwing TestException exceptions (TestFailed,
TestIgnored) by catching them and rethrowing later.

This mechanism can help in breaking test and passing test result
from places where throwing exceptions is not allowed

Change-Id: Ic9baa96596d15fccbbff4ac81b18f91b2eb89c8e

README
src/framework/include/dpl/test/test_runner.h
src/framework/src/test_runner.cpp

diff --git a/README b/README
index 92d11b3..c946fe7 100644 (file)
--- a/README
+++ b/README
@@ -156,6 +156,25 @@ dpl-test-framework
     RUNNER_ERROR_MSG
           Print error message using red color.
 
+--Defer macros-----------------------------------------------------------------
+Used to defer throwing TestException exceptions (TestFailed, TestIgnored)
+by catching them and rethrowing later. This mechanism can help in breaking
+test and passing test result from places where throwing exceptions
+is not allowed
+
+dpl-test-framework
+  test_runner.h
+    RUNNER_DEFER_TRYCATCH
+          Catches thrown TestException exceptions and stores them in TestRunner
+          structures for later use. This macro works only inside deffered scope
+          defined by RUNNER_DEFER_SCOPE, otherwise it won't catch exceptions
+    RUNNER_DEFER_SCOPE
+          Defines deferred scope. All RUNNER_DEFER_TRYCATCH macros used inside
+          the scope catch and save TestException exceptions. After scope is left
+          all saved exceptions take part in setting result of test. If there
+          is no any uncaught exception then additionally first of saved
+          exceptions is thrown.
+
 --Collectors-------------------------------------------------------------------
 Collectors are classes which collect test results. Each class does it differently.
 Collectors can be registered by --output parameter (see HOW TO RUN section) but
index 5f070c8..53764bf 100644 (file)
@@ -25,6 +25,7 @@
 #define DPL_TEST_RUNNER_H
 
 #include <chrono>
+#include <cstdlib>
 #include <cstring>
 #include <exception>
 #include <iostream>
@@ -41,6 +42,7 @@
 #include <dpl/colors.h>
 #include <dpl/gdbbacktrace.h>
 #include <dpl/singleton.h>
+#include <dpl/test/test_exception.h>
 #include <dpl/test/test_failed.h>
 #include <dpl/test/test_ignored.h>
 #include <dpl/test/test_results_collector.h>
@@ -63,6 +65,8 @@ class TestRunner
         m_currentTestCase(nullptr)
       , m_terminate(false)
       , m_allowChildLogs(false)
+      , m_deferDeepness(0U)
+      , m_firstDeferredExceptionType(DeferredExceptionType::DEFERRED_FAILED)
     {}
 
     void beginPerformanceTestTime(std::chrono::system_clock::duration maxTimeInMicroseconds);
@@ -166,6 +170,21 @@ class TestRunner
     // The runner will terminate as soon as possible (after current test).
     void Terminate();
     bool GetAllowChildLogs();
+
+    void deferFailedException(const DPL::Test::TestFailed &ex);
+    void deferIgnoredException(const DPL::Test::TestIgnored &ex);
+    void deferBegin();
+    void deferEnd();
+
+private:
+    std::vector<std::string> m_deferredExceptionsMessages;
+    std::size_t m_deferDeepness;
+    enum DeferredExceptionType {
+        DEFERRED_FAILED,
+        DEFERRED_IGNORED,
+    } m_firstDeferredExceptionType;
+    DPL::Test::TestFailed m_firstDeferredFail;
+    DPL::Test::TestIgnored m_firstDeferredIgnore;
 };
 
 typedef DPL::Singleton<TestRunner> TestRunnerSingleton;
@@ -307,4 +326,39 @@ typedef DPL::Singleton<TestRunner> TestRunnerSingleton;
                   << DPL::Colors::Text::RED_END << std::endl; \
     } while (0)
 
+/**
+ * DEFER MACROS
+ *
+ * Use them to defer fails and ignores in test cases.
+ * Some code constructions disallow to throw. Such places can be surrounded
+ * with RUNNER_DEFER_SCOPE macro. RUNNER_DEFER_TRYCATCH macro can be used to catch possibly thrown
+ * exceptions within such scope. Possibly catched exceptions will be rethrown
+ * when leaving RUNNER_DEFER_SCOPE scope.
+ * Macros can be safely nested.
+ */
+
+
+#define RUNNER_DEFER_TRYCATCH(scope)                                              \
+    do {                                                                          \
+        try                                                                       \
+        {                                                                         \
+            scope                                                                 \
+        }                                                                         \
+        catch (const DPL::Test::TestFailed &ex)                                   \
+        {                                                                         \
+            DPL::Test::TestRunnerSingleton::Instance().deferFailedException(ex);  \
+        }                                                                         \
+        catch (const DPL::Test::TestIgnored &ex)                                  \
+        {                                                                         \
+            DPL::Test::TestRunnerSingleton::Instance().deferIgnoredException(ex); \
+        }                                                                         \
+    } while (0)                                                                   \
+
+#define RUNNER_DEFER_SCOPE(scope)                                \
+    do {                                                         \
+        DPL::Test::TestRunnerSingleton::Instance().deferBegin(); \
+        scope                                                    \
+        DPL::Test::TestRunnerSingleton::Instance().deferEnd();   \
+    } while (0)                                                  \
+
 #endif // DPL_TEST_RUNNER_H
index dea2dda..0b4099c 100644 (file)
@@ -209,6 +209,7 @@ bool TestRunner::filterByXML(std::map<std::string, bool> & casesMap)
 TestRunner::Status TestRunner::RunTestCase(const TestCaseStruct& testCase)
 {
     setCurrentTestCase(&(const_cast<TestCaseStruct &>(testCase)));
+    m_deferDeepness = 0U;
     try {
         testCase.proc();
     } catch (const TestFailed &e) {
@@ -720,5 +721,60 @@ bool TestRunner::GetAllowChildLogs()
     return m_allowChildLogs;
 }
 
+void TestRunner::deferFailedException(const DPL::Test::TestFailed &ex)
+{
+    if (m_deferDeepness <= 0)
+        throw ex;
+
+    if (m_deferredExceptionsMessages.empty()) {
+        m_firstDeferredFail = ex;
+        m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_FAILED;
+    }
+    m_deferredExceptionsMessages.push_back(ex.GetMessage());
+}
+
+void TestRunner::deferIgnoredException(const DPL::Test::TestIgnored &ex)
+{
+    if (m_deferDeepness <= 0)
+        throw ex;
+
+    if (m_deferredExceptionsMessages.empty()) {
+        m_firstDeferredIgnore = ex;
+        m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_IGNORED;
+    }
+    m_deferredExceptionsMessages.push_back(ex.GetMessage());
+}
+
+void TestRunner::deferBegin()
+{
+    m_deferDeepness++;
 }
+
+void TestRunner::deferEnd()
+{
+    if (m_deferDeepness > 0)
+        m_deferDeepness--;
+
+    if (m_deferDeepness > 0)
+        return;
+
+    bool oops = std::uncaught_exception();
+    size_t additionalExceptions = oops ? 0 : 1;
+    for (size_t i = additionalExceptions; i < m_deferredExceptionsMessages.size(); ++i)
+        addFailReason(m_deferredExceptionsMessages[i]);
+
+    if (!oops && !m_deferredExceptionsMessages.empty())
+    {
+        m_deferredExceptionsMessages.clear();
+        switch (m_firstDeferredExceptionType) {
+            case DeferredExceptionType::DEFERRED_FAILED:
+                throw m_firstDeferredFail;
+            case DeferredExceptionType::DEFERRED_IGNORED:
+                throw m_firstDeferredIgnore;
+        }
+    }
+    m_deferredExceptionsMessages.clear();
+}
+
+} // namespace Test
 } // namespace DPL