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
#define DPL_TEST_RUNNER_H
#include <chrono>
+#include <cstdlib>
#include <cstring>
#include <exception>
#include <iostream>
#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>
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);
// 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;
<< 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
TestRunner::Status TestRunner::RunTestCase(const TestCaseStruct& testCase)
{
setCurrentTestCase(&(const_cast<TestCaseStruct &>(testCase)));
+ m_deferDeepness = 0U;
try {
testCase.proc();
} catch (const TestFailed &e) {
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