Repository: doctest/doctest (language: cpp)

Issue:
wasm*-support?
Hi guys,

I am trying to compile a doctest'ed test program using the clang-wasm32-wasi target.
As far as I know and could found atomics and threading is not yet supported.

My problem is dat doctest seem to use a lot of std::atomic which (quote):
```error: <atomic> is not supported on this single threaded system```

I use the following arguments to clang:
```--target=wasm32-wasi --sysroot /tmp/wasi-sdk-sysroot-14.0 -D_WASI_EMULATED_SIGNAL```

Is there a possibility of supporting wasm builds? I'd hate to have to move to gtest or something for obvious reasons.

Sincerely,


Discussion / hints:

Fix patch:
diff --git a/doc/markdown/configuration.md b/doc/markdown/configuration.md
index 73b42916a..de1c4b3e1 100644
--- a/doc/markdown/configuration.md
+++ b/doc/markdown/configuration.md
@@ -30,6 +30,7 @@ Defining something ```globally``` means for every source file of the binary (exe
 - [**```DOCTEST_CONFIG_POSIX_SIGNALS```**](#doctest_config_posix_signals)
 - [**```DOCTEST_CONFIG_NO_POSIX_SIGNALS```**](#doctest_config_no_posix_signals)
 - [**```DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS```**](#doctest_config_include_type_traits)
+- [**```DOCTEST_CONFIG_NO_MULTITHREADING```**](#doctest_config_no_multithreading)
 - [**```DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS```**](#doctest_config_no_multi_lane_atomics)
 - [**```DOCTEST_CONFIG_ASSERTS_RETURN_VALUES```**](#doctest_config_asserts_return_values)
 - [**```DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED```**](#doctest_config_evaluate_asserts_even_when_disabled)
@@ -237,6 +238,16 @@ This can be used to include the ```<type_traits>``` C++11 header. That in turn w
 
 This can be defined both globally and in specific source files only.
 
+### **```DOCTEST_CONFIG_NO_MULTITHREADING```**
+
+This can be used to disable all multithreading support.
+
+Speeds up single threaded applications.
+
+Includes [**```DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS```**](#doctest_config_no_multi_lane_atomics).
+
+This should be defined only in the source file where the library is implemented (it's relevant only there).
+
 ### **```DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS```**
 
 This can be used to disable multi lane atomics. Multi lane atomics can speed up highly parallel use of assert statements, but have a small overhead for single threaded applications.
diff --git a/examples/all_features/CMakeLists.txt b/examples/all_features/CMakeLists.txt
index 4d2fe1bd7..3d6b342f1 100644
--- a/examples/all_features/CMakeLists.txt
+++ b/examples/all_features/CMakeLists.txt
@@ -97,15 +97,11 @@ doctest_add_test(NO_OUTPUT NAME quiet ${common_args} -q -sf=*test_cases_and_suit
 doctest_add_test(NAME minimal         ${common_args} -m -sf=*test_cases_and_suites*) # minimal with summary
 doctest_add_test(NAME minimal_no_fail ${common_args} -m -sf=*no_failures.cpp) # minimal
 
-################################################################################
-## DOCTEST DISABLED - EXPRESSIONS STILL EVALUATE TO RETURN PROPER VALUES
-################################################################################
-
 add_executable(disabled_but_evaluated assert_returns_disabled.cpp assert_returns_disabled_evaluate.cpp)
 target_compile_definitions(disabled_but_evaluated PRIVATE DOCTEST_CONFIG_DISABLE)
 target_link_libraries(disabled_but_evaluated doctest ${CMAKE_THREAD_LIBS_INIT})
 
-doctest_add_test_impl(NAME disabled_but_evaluated COMMAND $<TARGET_FILE:disabled_but_evaluated>)
+doctest_add_test_impl(NO_OUTPUT NAME disabled_but_evaluated COMMAND $<TARGET_FILE:disabled_but_evaluated>)
 
 if(MSVC)
     target_compile_options(disabled_but_evaluated PRIVATE /wd4702) # unreachable code
@@ -119,14 +115,20 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
 endif()
 
 ################################################################################
-## VARIATION OF THE BUILD WITH DOCTEST DISABLED - SHOULD STILL COMPILE
+## CONFIG TESTS - TESTS WITH VARIOUS CONFIGURATION OPTIONS
 ################################################################################
 
-add_executable(disabled ${files_all})
-target_compile_definitions(disabled PRIVATE DOCTEST_CONFIG_DISABLE)
-target_link_libraries(disabled doctest ${CMAKE_THREAD_LIBS_INIT})
+function(add_test_all_features test_name flags)
+    add_executable(${test_name} ${files_with_output})
+    target_compile_definitions(${test_name} PRIVATE ${flags})
+    target_link_libraries(${test_name} doctest ${CMAKE_THREAD_LIBS_INIT})
+
+    doctest_add_test_impl(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}> --no-skipped-summary --no-version -ob=name)
+endfunction()
 
-doctest_add_test_impl(NAME disabled COMMAND $<TARGET_FILE:disabled>)
+add_test_all_features(no_multithreading DOCTEST_CONFIG_NO_MULTITHREADING)
+add_test_all_features(no_multi_lane_atomics DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS)
+add_test_all_features(disabled DOCTEST_CONFIG_DISABLE)
 
 # TODO: think about fixing these in a different way! - see issue #61 or commit 6b61e8aa3818c5ea100cedc1bb48a60ea10df6e8
 if(MSVC)


Test patch:
diff --git a/doctest/doctest.h b/doctest/doctest.h
index 3b6e965b1..fbafdf129 100644
--- a/doctest/doctest.h
+++ b/doctest/doctest.h
@@ -267,7 +267,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
 
 #if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) &&             \
-        !defined(__EMSCRIPTEN__)
+        !defined(__EMSCRIPTEN__) && !defined(__wasi__)
 #define DOCTEST_CONFIG_POSIX_SIGNALS
 #endif // _WIN32
 #if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
@@ -275,7 +275,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
 
 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
-#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)                   \
+        || defined(__wasi__)
 #define DOCTEST_CONFIG_NO_EXCEPTIONS
 #endif // no exceptions
 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -290,6 +291,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
 
+#ifdef __wasi__
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
 #if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
 #define DOCTEST_CONFIG_IMPLEMENT
 #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
@@ -393,6 +398,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #define DOCTEST_PLATFORM_IPHONE
 #elif defined(_WIN32)
 #define DOCTEST_PLATFORM_WINDOWS
+#elif defined(__wasi__)
+#define DOCTEST_PLATFORM_WASI
 #else // DOCTEST_PLATFORM
 #define DOCTEST_PLATFORM_LINUX
 #endif // DOCTEST_PLATFORM
@@ -3082,8 +3089,17 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
 #include <algorithm>
 #include <iomanip>
 #include <vector>
+#ifndef DOCTEST_CONFIG_NO_MULTITHREADING
 #include <atomic>
 #include <mutex>
+#define DOCTEST_DECLARE_MUTEX(name) std::mutex name;
+#define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name) std::lock_guard<std::mutex> DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name);
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+#define DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_DECLARE_STATIC_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name)
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
 #include <set>
 #include <map>
 #include <exception>
@@ -3146,7 +3162,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
 #endif
 
 #ifndef DOCTEST_THREAD_LOCAL
-#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
 #define DOCTEST_THREAD_LOCAL
 #else // DOCTEST_MSVC
 #define DOCTEST_THREAD_LOCAL thread_local
@@ -3339,9 +3355,17 @@ typedef timer_large_integer::type ticks_t;
         ticks_t m_ticks = 0;
     };
 
-#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+#ifdef DOCTEST_CONFIG_NO_MULTITHREADING
+    template <typename T>
+    using Atomic = T;
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+    template <typename T>
+    using Atomic = std::atomic<T>;
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
+
+#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING)
     template <typename T>
-    using AtomicOrMultiLaneAtomic = std::atomic<T>;
+    using MultiLaneAtomic = Atomic<T>;
 #else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
     // Provides a multilane implementation of an atomic variable that supports add, sub, load,
     // store. Instead of using a single atomic variable, this splits up into multiple ones,
@@ -3358,8 +3382,8 @@ typedef timer_large_integer::type ticks_t;
     {
         struct CacheLineAlignedAtomic
         {
-            std::atomic<T> atomic{};
-            char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
+            Atomic<T> atomic{};
+            char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic<T>)];
         };
         CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
 
@@ -3415,24 +3439,21 @@ typedef timer_large_integer::type ticks_t;
         //    assigned in a round-robin fashion.
         // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
         //    little overhead.
-        std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
-            static std::atomic<size_t> laneCounter;
+        Atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
+            static Atomic<size_t> laneCounter;
             DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
                     laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
 
             return m_atomics[tlsLaneIdx].atomic;
         }
     };
-
-    template <typename T>
-    using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
 #endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
 
     // this holds both parameters from the command line and runtime data for tests
     struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
     {
-        AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
-        AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
+        MultiLaneAtomic<int> numAssertsCurrentTest_atomic;
+        MultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
 
         std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
 
@@ -3449,7 +3470,7 @@ typedef timer_large_integer::type ticks_t;
         std::set<decltype(subcasesStack)> subcasesPassed;
         int                               subcasesCurrentMaxLevel;
         bool                              should_reenter;
-        std::atomic<bool>                 shouldLogCurrentException;
+        Atomic<bool>                      shouldLogCurrentException;
 
         void resetRunData() {
             numTestCases                = 0;
@@ -4509,10 +4530,10 @@ namespace {
         static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
             // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
             // console just once no matter how many threads have crashed.
-            static std::mutex mutex;
+            DOCTEST_DECLARE_STATIC_MUTEX(mutex)
             static bool execute = true;
             {
-                std::lock_guard<std::mutex> lock(mutex);
+                DOCTEST_LOCK_MUTEX(mutex)
                 if(execute) {
                     bool reported = false;
                     for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
@@ -5271,7 +5292,7 @@ namespace {
     struct XmlReporter : public IReporter
     {
         XmlWriter  xml;
-        std::mutex mutex;
+        DOCTEST_DECLARE_MUTEX(mutex)
 
         // caching pointers/references to objects of these types - safe to do
         const ContextOptions& opt;
@@ -5431,7 +5452,7 @@ namespace {
         }
 
         void test_case_exception(const TestCaseException& e) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             xml.scopedElement("Exception")
                     .writeAttribute("crash", e.is_crash)
@@ -5452,7 +5473,7 @@ namespace {
             if(!rb.m_failed && !opt.success)
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             xml.startElement("Expression")
                     .writeAttribute("success", !rb.m_failed)
@@ -5478,7 +5499,7 @@ namespace {
         }
 
         void log_message(const MessageData& mb) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             xml.startElement("Message")
                     .writeAttribute("type", failureString(mb.m_severity))
@@ -5563,7 +5584,7 @@ namespace {
     struct JUnitReporter : public IReporter
     {
         XmlWriter  xml;
-        std::mutex mutex;
+        DOCTEST_DECLARE_MUTEX(mutex)
         Timer timer;
         std::vector<String> deepestSubcaseStackNames;
 
@@ -5731,7 +5752,7 @@ namespace {
         }
 
         void test_case_exception(const TestCaseException& e) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
             testCaseData.addError("exception", e.error_string.c_str());
         }
 
@@ -5745,7 +5766,7 @@ namespace {
             if(!rb.m_failed) // report only failures & ignore the `success` option
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             std::ostringstream os;
             os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
@@ -5796,7 +5817,7 @@ namespace {
         bool                          hasLoggedCurrentTestStart;
         std::vector<SubcaseSignature> subcasesStack;
         size_t                        currentSubcaseLevel;
-        std::mutex                    mutex;
+        DOCTEST_DECLARE_MUTEX(mutex)
 
         // caching pointers/references to objects of these types - safe to do
         const ContextOptions& opt;
@@ -6173,7 +6194,7 @@ namespace {
         }
 
         void test_case_exception(const TestCaseException& e) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
             if(tc->m_no_output)
                 return;
 
@@ -6212,7 +6233,7 @@ namespace {
             if((!rb.m_failed && !opt.success) || tc->m_no_output)
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             logTestStart();
 
@@ -6228,7 +6249,7 @@ namespace {
             if(tc->m_no_output)
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             logTestStart();
 
diff --git a/doctest/parts/doctest.cpp b/doctest/parts/doctest.cpp
index 7b8670a86..f13412734 100644
--- a/doctest/parts/doctest.cpp
+++ b/doctest/parts/doctest.cpp
@@ -75,8 +75,17 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
 #include <algorithm>
 #include <iomanip>
 #include <vector>
+#ifndef DOCTEST_CONFIG_NO_MULTITHREADING
 #include <atomic>
 #include <mutex>
+#define DOCTEST_DECLARE_MUTEX(name) std::mutex name;
+#define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name) std::lock_guard<std::mutex> DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name);
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+#define DOCTEST_DECLARE_MUTEX(name)
+#define DOCTEST_DECLARE_STATIC_MUTEX(name)
+#define DOCTEST_LOCK_MUTEX(name)
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
 #include <set>
 #include <map>
 #include <exception>
@@ -139,7 +148,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
 #endif
 
 #ifndef DOCTEST_THREAD_LOCAL
-#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
 #define DOCTEST_THREAD_LOCAL
 #else // DOCTEST_MSVC
 #define DOCTEST_THREAD_LOCAL thread_local
@@ -332,9 +341,17 @@ typedef timer_large_integer::type ticks_t;
         ticks_t m_ticks = 0;
     };
 
-#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+#ifdef DOCTEST_CONFIG_NO_MULTITHREADING
     template <typename T>
-    using AtomicOrMultiLaneAtomic = std::atomic<T>;
+    using Atomic = T;
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+    template <typename T>
+    using Atomic = std::atomic<T>;
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
+
+#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING)
+    template <typename T>
+    using MultiLaneAtomic = Atomic<T>;
 #else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
     // Provides a multilane implementation of an atomic variable that supports add, sub, load,
     // store. Instead of using a single atomic variable, this splits up into multiple ones,
@@ -351,8 +368,8 @@ typedef timer_large_integer::type ticks_t;
     {
         struct CacheLineAlignedAtomic
         {
-            std::atomic<T> atomic{};
-            char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
+            Atomic<T> atomic{};
+            char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic<T>)];
         };
         CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
 
@@ -408,24 +425,21 @@ typedef timer_large_integer::type ticks_t;
         //    assigned in a round-robin fashion.
         // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
         //    little overhead.
-        std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
-            static std::atomic<size_t> laneCounter;
+        Atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
+            static Atomic<size_t> laneCounter;
             DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
                     laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
 
             return m_atomics[tlsLaneIdx].atomic;
         }
     };
-
-    template <typename T>
-    using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
 #endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
 
     // this holds both parameters from the command line and runtime data for tests
     struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
     {
-        AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
-        AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
+        MultiLaneAtomic<int> numAssertsCurrentTest_atomic;
+        MultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
 
         std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
 
@@ -442,7 +456,7 @@ typedef timer_large_integer::type ticks_t;
         std::set<decltype(subcasesStack)> subcasesPassed;
         int                               subcasesCurrentMaxLevel;
         bool                              should_reenter;
-        std::atomic<bool>                 shouldLogCurrentException;
+        Atomic<bool>                      shouldLogCurrentException;
 
         void resetRunData() {
             numTestCases                = 0;
@@ -1502,10 +1516,10 @@ namespace {
         static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
             // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
             // console just once no matter how many threads have crashed.
-            static std::mutex mutex;
+            DOCTEST_DECLARE_STATIC_MUTEX(mutex)
             static bool execute = true;
             {
-                std::lock_guard<std::mutex> lock(mutex);
+                DOCTEST_LOCK_MUTEX(mutex)
                 if(execute) {
                     bool reported = false;
                     for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
@@ -2264,7 +2278,7 @@ namespace {
     struct XmlReporter : public IReporter
     {
         XmlWriter  xml;
-        std::mutex mutex;
+        DOCTEST_DECLARE_MUTEX(mutex)
 
         // caching pointers/references to objects of these types - safe to do
         const ContextOptions& opt;
@@ -2424,7 +2438,7 @@ namespace {
         }
 
         void test_case_exception(const TestCaseException& e) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             xml.scopedElement("Exception")
                     .writeAttribute("crash", e.is_crash)
@@ -2445,7 +2459,7 @@ namespace {
             if(!rb.m_failed && !opt.success)
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             xml.startElement("Expression")
                     .writeAttribute("success", !rb.m_failed)
@@ -2471,7 +2485,7 @@ namespace {
         }
 
         void log_message(const MessageData& mb) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             xml.startElement("Message")
                     .writeAttribute("type", failureString(mb.m_severity))
@@ -2556,7 +2570,7 @@ namespace {
     struct JUnitReporter : public IReporter
     {
         XmlWriter  xml;
-        std::mutex mutex;
+        DOCTEST_DECLARE_MUTEX(mutex)
         Timer timer;
         std::vector<String> deepestSubcaseStackNames;
 
@@ -2724,7 +2738,7 @@ namespace {
         }
 
         void test_case_exception(const TestCaseException& e) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
             testCaseData.addError("exception", e.error_string.c_str());
         }
 
@@ -2738,7 +2752,7 @@ namespace {
             if(!rb.m_failed) // report only failures & ignore the `success` option
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             std::ostringstream os;
             os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
@@ -2789,7 +2803,7 @@ namespace {
         bool                          hasLoggedCurrentTestStart;
         std::vector<SubcaseSignature> subcasesStack;
         size_t                        currentSubcaseLevel;
-        std::mutex                    mutex;
+        DOCTEST_DECLARE_MUTEX(mutex)
 
         // caching pointers/references to objects of these types - safe to do
         const ContextOptions& opt;
@@ -3166,7 +3180,7 @@ namespace {
         }
 
         void test_case_exception(const TestCaseException& e) override {
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
             if(tc->m_no_output)
                 return;
 
@@ -3205,7 +3219,7 @@ namespace {
             if((!rb.m_failed && !opt.success) || tc->m_no_output)
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             logTestStart();
 
@@ -3221,7 +3235,7 @@ namespace {
             if(tc->m_no_output)
                 return;
 
-            std::lock_guard<std::mutex> lock(mutex);
+            DOCTEST_LOCK_MUTEX(mutex)
 
             logTestStart();
 
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index 5f381b136..597bbad09 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -264,7 +264,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
 
 #if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) &&             \
-        !defined(__EMSCRIPTEN__)
+        !defined(__EMSCRIPTEN__) && !defined(__wasi__)
 #define DOCTEST_CONFIG_POSIX_SIGNALS
 #endif // _WIN32
 #if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
@@ -272,7 +272,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
 
 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
-#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)                   \
+        || defined(__wasi__)
 #define DOCTEST_CONFIG_NO_EXCEPTIONS
 #endif // no exceptions
 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -287,6 +288,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
 
+#ifdef __wasi__
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
 #if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
 #define DOCTEST_CONFIG_IMPLEMENT
 #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
@@ -390,6 +395,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
 #define DOCTEST_PLATFORM_IPHONE
 #elif defined(_WIN32)
 #define DOCTEST_PLATFORM_WINDOWS
+#elif defined(__wasi__)
+#define DOCTEST_PLATFORM_WASI
 #else // DOCTEST_PLATFORM
 #define DOCTEST_PLATFORM_LINUX
 #endif // DOCTEST_PLATFORM
diff --git a/examples/all_features/test_output/no_multi_lane_atomics.txt b/examples/all_features/test_output/no_multi_lane_atomics.txt
new file mode 100644
index 000000000..1d3a309f0
--- /dev/null
+++ b/examples/all_features/test_output/no_multi_lane_atomics.txt
@@ -0,0 +1,813 @@
+[doctest] run with "--help" for options
+===============================================================================
+subcases.cpp(0):
+  Scenario: vectors can be sized and resized
+     Given: A vector with some items
+      When: the size is increased
+      Then: the size and capacity change
+
+subcases.cpp(0): ERROR: CHECK( v.size() == 20 ) is NOT correct!
+  values: CHECK( 10 == 20 )
+
+===============================================================================
+subcases.cpp(0):
+  Scenario: vectors can be sized and resized
+     Given: A vector with some items
+      When: less capacity is reserved
+      Then: neither size nor capacity are changed
+
+subcases.cpp(0): ERROR: CHECK( v.size() == 10 ) is NOT correct!
+  values: CHECK( 5 == 10 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  CHECK level of asserts fail the test case but don't abort it
+
+assertion_macros.cpp(0): ERROR: CHECK( 0 ) is NOT correct!
+  values: CHECK( 0 )
+
+assertion_macros.cpp(0): ERROR: CHECK_FALSE( 1 ) is NOT correct!
+  values: CHECK_FALSE( 1 )
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(false, 0), bool ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, 0), bool ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, 0), "unrecognized" ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, 0), "unrecognized", int ) threw a DIFFERENT exception! (contents: "0")
+
+assertion_macros.cpp(0): ERROR: CHECK_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_EQ( 1, 0 ) is NOT correct!
+  values: CHECK_EQ( 1, 0 )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY( 0 ) is NOT correct!
+  values: CHECK_UNARY( 0 )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY_FALSE( 1 ) is NOT correct!
+  values: CHECK_UNARY_FALSE( 1 )
+
+assertion_macros.cpp(0): MESSAGE: reached!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 1
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE( 0 ) is NOT correct!
+  values: REQUIRE( 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 10
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 11
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_EQ( 1, 0 ) is NOT correct!
+  values: REQUIRE_EQ( 1, 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 12
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_UNARY( 0 ) is NOT correct!
+  values: REQUIRE_UNARY( 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 13
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_UNARY_FALSE( 1 ) is NOT correct!
+  values: REQUIRE_UNARY_FALSE( 1 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 2
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_FALSE( 1 ) is NOT correct!
+  values: REQUIRE_FALSE( 1 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 3
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 4
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_AS( throw_if(false, 0), bool ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 5
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_AS( throw_if(true, 0), bool ) threw a DIFFERENT exception: "0"
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 6
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH( throw_if(false, ""), "whops!" ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 7
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH( throw_if(true, ""), "whops!" ) threw a DIFFERENT exception: 
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 8
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH_AS( throw_if(false, ""), "whops!", bool ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 9
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH_AS( throw_if(true, ""), "whops!", bool ) threw a DIFFERENT exception! (contents: )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  WARN level of asserts don't fail the test case
+
+assertion_macros.cpp(0): WARNING: WARN( 0 ) is NOT correct!
+  values: WARN( 0 )
+
+assertion_macros.cpp(0): WARNING: WARN_FALSE( 1 ) is NOT correct!
+  values: WARN_FALSE( 1 )
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH( throw_if(true, ""), "whops!" ) threw a DIFFERENT exception: 
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH( throw_if(false, ""), "whops!" ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_AS( throw_if(false, 0), bool ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_AS( throw_if(true, 0), bool ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH_AS( throw_if(false, ""), "whops!", int ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH_AS( throw_if(true, ""), "whops!", int ) threw a DIFFERENT exception! (contents: )
+
+assertion_macros.cpp(0): WARNING: WARN_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+assertion_macros.cpp(0): WARNING: WARN_EQ( 1, 0 ) is NOT correct!
+  values: WARN_EQ( 1, 0 )
+
+assertion_macros.cpp(0): WARNING: WARN_UNARY( 0 ) is NOT correct!
+  values: WARN_UNARY( 0 )
+
+assertion_macros.cpp(0): WARNING: WARN_UNARY_FALSE( 1 ) is NOT correct!
+  values: WARN_UNARY_FALSE( 1 )
+
+===============================================================================
+stringification.cpp(0):
+TEST CASE:  a test case that registers an exception translator for int and then throws one
+
+stringification.cpp(0): ERROR: test case THREW exception: 5
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  a test case that will end from an exception
+
+logging.cpp(0): ERROR: forcing the many captures to be stringified
+  logged: lots of captures: 42 42 42;
+          old way of capturing - using the streaming operator: 42 42
+
+logging.cpp(0): ERROR: CHECK( some_var == 666 ) is NOT correct!
+  values: CHECK( 42 == 666 )
+  logged: someTests() returned: 42
+          this should be printed if an exception is thrown even if no assert has failed: 42
+          in a nested scope this should be printed as well: 42
+          why is this not 666 ?!
+
+logging.cpp(0): ERROR: test case THREW exception: 0
+  logged: someTests() returned: 42
+          this should be printed if an exception is thrown even if no assert has failed: 42
+          in a nested scope this should be printed as well: 42
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  a test case that will end from an exception and should print the unprinted context
+
+logging.cpp(0): ERROR: test case THREW exception: 0
+  logged: should be printed even if an exception is thrown and no assert fails before that
+
+===============================================================================
+stringification.cpp(0):
+TEST CASE:  all asserts should fail and show how the objects get stringified
+
+stringification.cpp(0): ERROR: CHECK( f1 == f2 ) is NOT correct!
+  values: CHECK( Foo{} == Foo{} )
+
+stringification.cpp(0): ERROR: CHECK( dummy == "tralala" ) is NOT correct!
+  values: CHECK( omg == tralala )
+
+stringification.cpp(0): ERROR: CHECK( "tralala" == dummy ) is NOT correct!
+  values: CHECK( tralala == omg )
+
+stringification.cpp(0): ERROR: CHECK( vec1 == vec2 ) is NOT correct!
+  values: CHECK( [1, 2, 3] == [1, 2, 4] )
+
+stringification.cpp(0): ERROR: CHECK( lst_1 == lst_2 ) is NOT correct!
+  values: CHECK( [1, 42, 3, ] == [1, 2, 666, ] )
+
+stringification.cpp(0): ERROR: CHECK( s1 == s2 ) is NOT correct!
+  values: CHECK( MyOtherType: 42 == MyOtherType: 666 )
+  logged: s1=MyOtherType: 42 s2=MyOtherType: 666
+
+stringification.cpp(0): ERROR: CHECK( s1 == s2 ) is NOT correct!
+  values: CHECK( MyOtherType: 42 == MyOtherType: 666 )
+  logged: s1=MyOtherType: 42 s2=MyOtherType: 666
+          MyOtherType: 42 is not really MyOtherType: 666
+
+stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<double>(0.5) ) is NOT correct!
+  values: CHECK( 0.5 )
+
+stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<float>(std::numeric_limits<float>::infinity()) ) is NOT correct!
+  values: CHECK( inf )
+
+stringification.cpp(0): ERROR: CHECK( "a" == doctest::Contains("aaa") ) is NOT correct!
+  values: CHECK( a == Contains( aaa ) )
+
+stringification.cpp(0): ERROR: test case THREW exception: MyTypeInherited<int>(5, 4)
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  bad stringification of type pair<int_pair>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  check return values
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  check return values no print
+
+assertion_macros.cpp(0): ERROR: CHECK( a == b ) is NOT correct!
+  values: CHECK( 4 == 2 )
+
+assertion_macros.cpp(0): ERROR: CHECK_FALSE( a != b ) is NOT correct!
+  values: CHECK_FALSE( 4 != 2 )
+
+assertion_macros.cpp(0): ERROR: CHECK_EQ( a, b ) is NOT correct!
+  values: CHECK_EQ( 4, 2 )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY( a == b ) is NOT correct!
+  values: CHECK_UNARY( false )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY_FALSE( a != b ) is NOT correct!
+  values: CHECK_UNARY_FALSE( true )
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, false) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, 2), doctest::Approx ) threw a DIFFERENT exception: "2"
+
+assertion_macros.cpp(0): ERROR: CHECK_NOTHROW( throw_if(true, 2) ) THREW exception: "2"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, 2), "1" ) threw a DIFFERENT exception: "2"
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  doesn't fail but it should have
+
+Should have failed but didn't! Marking it as failed!
+===============================================================================
+enums.cpp(0):
+TEST CASE:  enum 2
+
+enums.cpp(0): ERROR: CHECK_EQ( Zero, 1 ) is NOT correct!
+  values: CHECK_EQ( 0, 1 )
+
+enums.cpp(0): ERROR: CHECK_EQ( One, 2 ) is NOT correct!
+  values: CHECK_EQ( 1, 2 )
+
+enums.cpp(0): ERROR: CHECK_EQ( Two, 3 ) is NOT correct!
+  values: CHECK_EQ( 2, 3 )
+
+enums.cpp(0): ERROR: CHECK_EQ( TypedZero, 1 ) is NOT correct!
+  values: CHECK_EQ( 0, 1 )
+
+enums.cpp(0): ERROR: CHECK_EQ( TypedOne, 2 ) is NOT correct!
+  values: CHECK_EQ( 1, 2 )
+
+enums.cpp(0): ERROR: CHECK_EQ( TypedTwo, 3 ) is NOT correct!
+  values: CHECK_EQ( 2, 3 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassC::Zero, EnumClassC::One ) is NOT correct!
+  values: CHECK_EQ( 48, 49 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassC::One, EnumClassC::Two ) is NOT correct!
+  values: CHECK_EQ( 49, 50 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassC::Two, EnumClassC::Zero ) is NOT correct!
+  values: CHECK_EQ( 50, 48 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassSC::Zero, EnumClassSC::One ) is NOT correct!
+  values: CHECK_EQ( 48, 49 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassSC::One, EnumClassSC::Two ) is NOT correct!
+  values: CHECK_EQ( 49, 50 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassSC::Two, EnumClassSC::Zero ) is NOT correct!
+  values: CHECK_EQ( 50, 48 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassUC::Zero, EnumClassUC::One ) is NOT correct!
+  values: CHECK_EQ( 48, 49 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassUC::One, EnumClassUC::Two ) is NOT correct!
+  values: CHECK_EQ( 49, 50 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassUC::Two, EnumClassUC::Zero ) is NOT correct!
+  values: CHECK_EQ( 50, 48 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassU8::Zero, EnumClassU8::One ) is NOT correct!
+  values: CHECK_EQ( 0, 1 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassU8::One, EnumClassU8::Two ) is NOT correct!
+  values: CHECK_EQ( 1, 2 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassU8::Two, EnumClassU8::Zero ) is NOT correct!
+  values: CHECK_EQ( 2, 0 )
+
+Failed as expected so marking it as not failed
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  exceptions-related macros
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, 0), char ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(false, 0), int ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, "whops!"), "whops! no match!" ) threw a DIFFERENT exception: "whops!"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, "whops! does it match?"), "whops! no match!" ) threw a DIFFERENT exception: "whops! does it match?"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, "whops!"), "whops! no match!", bool ) threw a DIFFERENT exception! (contents: "whops!")
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, "whops!"), "whops!", int ) threw a DIFFERENT exception! (contents: "whops!")
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, "whops! does it match?"), "whops! no match!", int ) threw a DIFFERENT exception! (contents: "whops! does it match?")
+
+assertion_macros.cpp(0): ERROR: CHECK_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  exceptions-related macros for std::exception
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(false, std::runtime_error("whops!")), std::exception ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, std::runtime_error("whops!")), int ) threw a DIFFERENT exception: "whops!"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(false, ""), "whops!" ) did NOT throw at all!
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_NOTHROW( throw_if(true, std::runtime_error("whops!")) ) THREW exception: "whops!"
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  explicit failures 1
+
+logging.cpp(0): ERROR: this should not end the test case, but mark it as failing
+
+logging.cpp(0): MESSAGE: reached!
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  explicit failures 2
+
+logging.cpp(0): FATAL ERROR: fail the test case and also end it
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails - and its allowed
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+Allowed to fail so marking it as not failed
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails 1 time as it should
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+Failed exactly 1 times as expected so marking it as not failed!
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails as it should
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+Failed as expected so marking it as not failed
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+  one
+
+subcases.cpp(0): ERROR: CHECK( false ) is NOT correct!
+  values: CHECK( false )
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  one
+
+subcases.cpp(0): ERROR: test case THREW exception: exception thrown in subcase - will translate later when the whole test case has been exited (cannot translate while there is an active exception)
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  one
+
+subcases.cpp(0): ERROR: test case THREW exception: failure... but the show must go on!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+  two
+
+subcases.cpp(0): ERROR: CHECK( false ) is NOT correct!
+  values: CHECK( false )
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  two
+
+subcases.cpp(0): ERROR: test case THREW exception: exception thrown in subcase - will translate later when the whole test case has been exited (cannot translate while there is an active exception)
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  two
+
+subcases.cpp(0): ERROR: test case THREW exception: failure... but the show must go on!
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails more times than it should
+
+test_cases_and_suites.cpp(0): ERROR: 
+
+test_cases_and_suites.cpp(0): ERROR: 
+
+Didn't fail exactly 1 times so marking it as failed!
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST CASE:  fixtured test - not part of a test suite
+
+test_cases_and_suites.cpp(0): ERROR: CHECK( data == 85 ) is NOT correct!
+  values: CHECK( 21 == 85 )
+
+===============================================================================
+header.h(0):
+TEST SUITE: some TS
+TEST CASE:  in TS
+
+header.h(0): FATAL ERROR: 
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  logging the counter of a loop
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 1 != 1 )
+  logged: current iteration of loop:
+          i := 0
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 2 != 2 )
+  logged: current iteration of loop:
+          i := 1
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 4 != 4 )
+  logged: current iteration of loop:
+          i := 2
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 8 != 8 )
+  logged: current iteration of loop:
+          i := 3
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 16 != 16 )
+  logged: current iteration of loop:
+          i := 4
+
+
+root
+1
+1.1
+
+root
+2
+2.1
+
+root
+2
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  lots of nested subcases
+
+subcases.cpp(0): FATAL ERROR: 
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  multiple types<>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  multiple types<>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  multiple types<>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  normal macros
+
+assertion_macros.cpp(0): ERROR: CHECK( throw_if(true, std::runtime_error("whops!")) == 42 ) THREW exception: "whops!"
+
+assertion_macros.cpp(0): ERROR: CHECK( doctest::Approx(0.502) == 0.501 ) is NOT correct!
+  values: CHECK( Approx( 0.502 ) == 0.501 )
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: ts1
+TEST CASE:  normal test in a test suite from a decorator
+
+test_cases_and_suites.cpp(0): MESSAGE: failing because of the timeout decorator!
+
+Test case exceeded time limit of 0.000001!
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: scoped test suite
+TEST CASE:  part of scoped
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: scoped test suite
+TEST CASE:  part of scoped 2
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: some TS
+TEST CASE:  part of some TS
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST CASE:  should fail because of an exception
+
+test_cases_and_suites.cpp(0): ERROR: test case THREW exception: 0
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  some asserts used in a function called by a test case
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, false), "unknown exception", int ) threw a DIFFERENT exception! (contents: "unknown exception")
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+
+subcases.cpp(0): MESSAGE: print me twice
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+  sc1
+
+subcases.cpp(0): MESSAGE: hello! from sc1
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  from function...
+  sc1
+
+subcases.cpp(0): MESSAGE: lala
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+
+subcases.cpp(0): MESSAGE: print me twice
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+  sc2
+
+subcases.cpp(0): MESSAGE: hello! from sc2
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  from function...
+  sc2
+
+subcases.cpp(0): MESSAGE: lala
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 0
+  inner 0
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 0
+  inner 1
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 1
+  inner 0
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 1
+  inner 1
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  separate
+
+subcases.cpp(0): MESSAGE: separate msg!
+
+===============================================================================
+header.h(0):
+TEST CASE:  template 1<char>
+
+header.h(0): FATAL ERROR: 
+
+===============================================================================
+header.h(0):
+TEST CASE:  template 2<doctest::String>
+
+header.h(0): FATAL ERROR: 
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  test case should fail even though the last subcase passes
+  one
+
+subcases.cpp(0): ERROR: CHECK( false ) is NOT correct!
+  values: CHECK( false )
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  third party asserts can report failures to doctest
+
+logging.cpp(0): ERROR: MY_ASSERT(false)
+
+logging.cpp(0): FATAL ERROR: MY_ASSERT_FATAL(false)
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: this test has overridden its skip decorator
+TEST SUITE: skipped test cases
+TEST CASE:  unskipped
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  vector stuff<std::vector<int>>
+
+templated_test_cases.cpp(0): ERROR: CHECK( vec.size() == 20 ) is NOT correct!
+  values: CHECK( 10 == 20 )
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  with a funnier name\:
+  with the funniest name\,
+
+subcases.cpp(0): MESSAGE: Yes!
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  with a funnier name\:
+  with a slightly funny name :
+
+subcases.cpp(0): MESSAGE: Yep!
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  with a funnier name\:
+  without a funny name
+
+subcases.cpp(0): MESSAGE: NO!
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  without a funny name:
+
+subcases.cpp(0): MESSAGE: Nooo
+
+===============================================================================
+[doctest] test cases:  78 | 30 passed |  48 failed |
+[doctest] assertions: 213 | 99 passed | 114 failed |
+[doctest] Status: FAILURE!
+Program code.
diff --git a/examples/all_features/test_output/no_multithreading.txt b/examples/all_features/test_output/no_multithreading.txt
new file mode 100644
index 000000000..1d3a309f0
--- /dev/null
+++ b/examples/all_features/test_output/no_multithreading.txt
@@ -0,0 +1,813 @@
+[doctest] run with "--help" for options
+===============================================================================
+subcases.cpp(0):
+  Scenario: vectors can be sized and resized
+     Given: A vector with some items
+      When: the size is increased
+      Then: the size and capacity change
+
+subcases.cpp(0): ERROR: CHECK( v.size() == 20 ) is NOT correct!
+  values: CHECK( 10 == 20 )
+
+===============================================================================
+subcases.cpp(0):
+  Scenario: vectors can be sized and resized
+     Given: A vector with some items
+      When: less capacity is reserved
+      Then: neither size nor capacity are changed
+
+subcases.cpp(0): ERROR: CHECK( v.size() == 10 ) is NOT correct!
+  values: CHECK( 5 == 10 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  CHECK level of asserts fail the test case but don't abort it
+
+assertion_macros.cpp(0): ERROR: CHECK( 0 ) is NOT correct!
+  values: CHECK( 0 )
+
+assertion_macros.cpp(0): ERROR: CHECK_FALSE( 1 ) is NOT correct!
+  values: CHECK_FALSE( 1 )
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(false, 0), bool ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, 0), bool ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, 0), "unrecognized" ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, 0), "unrecognized", int ) threw a DIFFERENT exception! (contents: "0")
+
+assertion_macros.cpp(0): ERROR: CHECK_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_EQ( 1, 0 ) is NOT correct!
+  values: CHECK_EQ( 1, 0 )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY( 0 ) is NOT correct!
+  values: CHECK_UNARY( 0 )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY_FALSE( 1 ) is NOT correct!
+  values: CHECK_UNARY_FALSE( 1 )
+
+assertion_macros.cpp(0): MESSAGE: reached!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 1
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE( 0 ) is NOT correct!
+  values: REQUIRE( 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 10
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 11
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_EQ( 1, 0 ) is NOT correct!
+  values: REQUIRE_EQ( 1, 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 12
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_UNARY( 0 ) is NOT correct!
+  values: REQUIRE_UNARY( 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 13
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_UNARY_FALSE( 1 ) is NOT correct!
+  values: REQUIRE_UNARY_FALSE( 1 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 2
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_FALSE( 1 ) is NOT correct!
+  values: REQUIRE_FALSE( 1 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 3
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 4
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_AS( throw_if(false, 0), bool ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 5
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_AS( throw_if(true, 0), bool ) threw a DIFFERENT exception: "0"
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 6
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH( throw_if(false, ""), "whops!" ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 7
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH( throw_if(true, ""), "whops!" ) threw a DIFFERENT exception: 
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 8
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH_AS( throw_if(false, ""), "whops!", bool ) did NOT throw at all!
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  REQUIRE level of asserts fail and abort the test case - 9
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_THROWS_WITH_AS( throw_if(true, ""), "whops!", bool ) threw a DIFFERENT exception! (contents: )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  WARN level of asserts don't fail the test case
+
+assertion_macros.cpp(0): WARNING: WARN( 0 ) is NOT correct!
+  values: WARN( 0 )
+
+assertion_macros.cpp(0): WARNING: WARN_FALSE( 1 ) is NOT correct!
+  values: WARN_FALSE( 1 )
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH( throw_if(true, ""), "whops!" ) threw a DIFFERENT exception: 
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH( throw_if(false, ""), "whops!" ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_AS( throw_if(false, 0), bool ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_AS( throw_if(true, 0), bool ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH_AS( throw_if(false, ""), "whops!", int ) did NOT throw at all!
+
+assertion_macros.cpp(0): WARNING: WARN_THROWS_WITH_AS( throw_if(true, ""), "whops!", int ) threw a DIFFERENT exception! (contents: )
+
+assertion_macros.cpp(0): WARNING: WARN_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+assertion_macros.cpp(0): WARNING: WARN_EQ( 1, 0 ) is NOT correct!
+  values: WARN_EQ( 1, 0 )
+
+assertion_macros.cpp(0): WARNING: WARN_UNARY( 0 ) is NOT correct!
+  values: WARN_UNARY( 0 )
+
+assertion_macros.cpp(0): WARNING: WARN_UNARY_FALSE( 1 ) is NOT correct!
+  values: WARN_UNARY_FALSE( 1 )
+
+===============================================================================
+stringification.cpp(0):
+TEST CASE:  a test case that registers an exception translator for int and then throws one
+
+stringification.cpp(0): ERROR: test case THREW exception: 5
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  a test case that will end from an exception
+
+logging.cpp(0): ERROR: forcing the many captures to be stringified
+  logged: lots of captures: 42 42 42;
+          old way of capturing - using the streaming operator: 42 42
+
+logging.cpp(0): ERROR: CHECK( some_var == 666 ) is NOT correct!
+  values: CHECK( 42 == 666 )
+  logged: someTests() returned: 42
+          this should be printed if an exception is thrown even if no assert has failed: 42
+          in a nested scope this should be printed as well: 42
+          why is this not 666 ?!
+
+logging.cpp(0): ERROR: test case THREW exception: 0
+  logged: someTests() returned: 42
+          this should be printed if an exception is thrown even if no assert has failed: 42
+          in a nested scope this should be printed as well: 42
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  a test case that will end from an exception and should print the unprinted context
+
+logging.cpp(0): ERROR: test case THREW exception: 0
+  logged: should be printed even if an exception is thrown and no assert fails before that
+
+===============================================================================
+stringification.cpp(0):
+TEST CASE:  all asserts should fail and show how the objects get stringified
+
+stringification.cpp(0): ERROR: CHECK( f1 == f2 ) is NOT correct!
+  values: CHECK( Foo{} == Foo{} )
+
+stringification.cpp(0): ERROR: CHECK( dummy == "tralala" ) is NOT correct!
+  values: CHECK( omg == tralala )
+
+stringification.cpp(0): ERROR: CHECK( "tralala" == dummy ) is NOT correct!
+  values: CHECK( tralala == omg )
+
+stringification.cpp(0): ERROR: CHECK( vec1 == vec2 ) is NOT correct!
+  values: CHECK( [1, 2, 3] == [1, 2, 4] )
+
+stringification.cpp(0): ERROR: CHECK( lst_1 == lst_2 ) is NOT correct!
+  values: CHECK( [1, 42, 3, ] == [1, 2, 666, ] )
+
+stringification.cpp(0): ERROR: CHECK( s1 == s2 ) is NOT correct!
+  values: CHECK( MyOtherType: 42 == MyOtherType: 666 )
+  logged: s1=MyOtherType: 42 s2=MyOtherType: 666
+
+stringification.cpp(0): ERROR: CHECK( s1 == s2 ) is NOT correct!
+  values: CHECK( MyOtherType: 42 == MyOtherType: 666 )
+  logged: s1=MyOtherType: 42 s2=MyOtherType: 666
+          MyOtherType: 42 is not really MyOtherType: 666
+
+stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<double>(0.5) ) is NOT correct!
+  values: CHECK( 0.5 )
+
+stringification.cpp(0): ERROR: CHECK( doctest::IsNaN<float>(std::numeric_limits<float>::infinity()) ) is NOT correct!
+  values: CHECK( inf )
+
+stringification.cpp(0): ERROR: CHECK( "a" == doctest::Contains("aaa") ) is NOT correct!
+  values: CHECK( a == Contains( aaa ) )
+
+stringification.cpp(0): ERROR: test case THREW exception: MyTypeInherited<int>(5, 4)
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  bad stringification of type pair<int_pair>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  check return values
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+assertion_macros.cpp(0): MESSAGE: :D
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  check return values no print
+
+assertion_macros.cpp(0): ERROR: CHECK( a == b ) is NOT correct!
+  values: CHECK( 4 == 2 )
+
+assertion_macros.cpp(0): ERROR: CHECK_FALSE( a != b ) is NOT correct!
+  values: CHECK_FALSE( 4 != 2 )
+
+assertion_macros.cpp(0): ERROR: CHECK_EQ( a, b ) is NOT correct!
+  values: CHECK_EQ( 4, 2 )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY( a == b ) is NOT correct!
+  values: CHECK_UNARY( false )
+
+assertion_macros.cpp(0): ERROR: CHECK_UNARY_FALSE( a != b ) is NOT correct!
+  values: CHECK_UNARY_FALSE( true )
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, false) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, 2), doctest::Approx ) threw a DIFFERENT exception: "2"
+
+assertion_macros.cpp(0): ERROR: CHECK_NOTHROW( throw_if(true, 2) ) THREW exception: "2"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, 2), "1" ) threw a DIFFERENT exception: "2"
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  doesn't fail but it should have
+
+Should have failed but didn't! Marking it as failed!
+===============================================================================
+enums.cpp(0):
+TEST CASE:  enum 2
+
+enums.cpp(0): ERROR: CHECK_EQ( Zero, 1 ) is NOT correct!
+  values: CHECK_EQ( 0, 1 )
+
+enums.cpp(0): ERROR: CHECK_EQ( One, 2 ) is NOT correct!
+  values: CHECK_EQ( 1, 2 )
+
+enums.cpp(0): ERROR: CHECK_EQ( Two, 3 ) is NOT correct!
+  values: CHECK_EQ( 2, 3 )
+
+enums.cpp(0): ERROR: CHECK_EQ( TypedZero, 1 ) is NOT correct!
+  values: CHECK_EQ( 0, 1 )
+
+enums.cpp(0): ERROR: CHECK_EQ( TypedOne, 2 ) is NOT correct!
+  values: CHECK_EQ( 1, 2 )
+
+enums.cpp(0): ERROR: CHECK_EQ( TypedTwo, 3 ) is NOT correct!
+  values: CHECK_EQ( 2, 3 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassC::Zero, EnumClassC::One ) is NOT correct!
+  values: CHECK_EQ( 48, 49 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassC::One, EnumClassC::Two ) is NOT correct!
+  values: CHECK_EQ( 49, 50 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassC::Two, EnumClassC::Zero ) is NOT correct!
+  values: CHECK_EQ( 50, 48 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassSC::Zero, EnumClassSC::One ) is NOT correct!
+  values: CHECK_EQ( 48, 49 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassSC::One, EnumClassSC::Two ) is NOT correct!
+  values: CHECK_EQ( 49, 50 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassSC::Two, EnumClassSC::Zero ) is NOT correct!
+  values: CHECK_EQ( 50, 48 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassUC::Zero, EnumClassUC::One ) is NOT correct!
+  values: CHECK_EQ( 48, 49 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassUC::One, EnumClassUC::Two ) is NOT correct!
+  values: CHECK_EQ( 49, 50 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassUC::Two, EnumClassUC::Zero ) is NOT correct!
+  values: CHECK_EQ( 50, 48 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassU8::Zero, EnumClassU8::One ) is NOT correct!
+  values: CHECK_EQ( 0, 1 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassU8::One, EnumClassU8::Two ) is NOT correct!
+  values: CHECK_EQ( 1, 2 )
+
+enums.cpp(0): ERROR: CHECK_EQ( EnumClassU8::Two, EnumClassU8::Zero ) is NOT correct!
+  values: CHECK_EQ( 2, 0 )
+
+Failed as expected so marking it as not failed
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  exceptions-related macros
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, 0), char ) threw a DIFFERENT exception: "0"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(false, 0), int ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, "whops!"), "whops! no match!" ) threw a DIFFERENT exception: "whops!"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(true, "whops! does it match?"), "whops! no match!" ) threw a DIFFERENT exception: "whops! does it match?"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, "whops!"), "whops! no match!", bool ) threw a DIFFERENT exception! (contents: "whops!")
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, "whops!"), "whops!", int ) threw a DIFFERENT exception! (contents: "whops!")
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, "whops! does it match?"), "whops! no match!", int ) threw a DIFFERENT exception! (contents: "whops! does it match?")
+
+assertion_macros.cpp(0): ERROR: CHECK_NOTHROW( throw_if(true, 0) ) THREW exception: "0"
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  exceptions-related macros for std::exception
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS( throw_if(false, 0) ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(false, std::runtime_error("whops!")), std::exception ) did NOT throw at all!
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_AS( throw_if(true, std::runtime_error("whops!")), int ) threw a DIFFERENT exception: "whops!"
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH( throw_if(false, ""), "whops!" ) did NOT throw at all!
+
+assertion_macros.cpp(0): FATAL ERROR: REQUIRE_NOTHROW( throw_if(true, std::runtime_error("whops!")) ) THREW exception: "whops!"
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  explicit failures 1
+
+logging.cpp(0): ERROR: this should not end the test case, but mark it as failing
+
+logging.cpp(0): MESSAGE: reached!
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  explicit failures 2
+
+logging.cpp(0): FATAL ERROR: fail the test case and also end it
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails - and its allowed
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+Allowed to fail so marking it as not failed
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails 1 time as it should
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+Failed exactly 1 times as expected so marking it as not failed!
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails as it should
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+Failed as expected so marking it as not failed
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+  one
+
+subcases.cpp(0): ERROR: CHECK( false ) is NOT correct!
+  values: CHECK( false )
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  one
+
+subcases.cpp(0): ERROR: test case THREW exception: exception thrown in subcase - will translate later when the whole test case has been exited (cannot translate while there is an active exception)
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  one
+
+subcases.cpp(0): ERROR: test case THREW exception: failure... but the show must go on!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+  two
+
+subcases.cpp(0): ERROR: CHECK( false ) is NOT correct!
+  values: CHECK( false )
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+  level zero
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  two
+
+subcases.cpp(0): ERROR: test case THREW exception: exception thrown in subcase - will translate later when the whole test case has been exited (cannot translate while there is an active exception)
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  fails from an exception but gets re-entered to traverse all subcases
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  level zero
+  two
+
+subcases.cpp(0): ERROR: test case THREW exception: failure... but the show must go on!
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: regarding failures
+TEST SUITE: test suite with a description
+TEST CASE:  fails more times than it should
+
+test_cases_and_suites.cpp(0): ERROR: 
+
+test_cases_and_suites.cpp(0): ERROR: 
+
+Didn't fail exactly 1 times so marking it as failed!
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST CASE:  fixtured test - not part of a test suite
+
+test_cases_and_suites.cpp(0): ERROR: CHECK( data == 85 ) is NOT correct!
+  values: CHECK( 21 == 85 )
+
+===============================================================================
+header.h(0):
+TEST SUITE: some TS
+TEST CASE:  in TS
+
+header.h(0): FATAL ERROR: 
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  logging the counter of a loop
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 1 != 1 )
+  logged: current iteration of loop:
+          i := 0
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 2 != 2 )
+  logged: current iteration of loop:
+          i := 1
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 4 != 4 )
+  logged: current iteration of loop:
+          i := 2
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 8 != 8 )
+  logged: current iteration of loop:
+          i := 3
+
+logging.cpp(0): ERROR: CHECK( vec[i] != (1 << i) ) is NOT correct!
+  values: CHECK( 16 != 16 )
+  logged: current iteration of loop:
+          i := 4
+
+
+root
+1
+1.1
+
+root
+2
+2.1
+
+root
+2
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  lots of nested subcases
+
+subcases.cpp(0): FATAL ERROR: 
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  multiple types<>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  multiple types<>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  multiple types<>
+
+templated_test_cases.cpp(0): ERROR: CHECK( t2 != T2() ) is NOT correct!
+  values: CHECK( 0 != 0 )
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  normal macros
+
+assertion_macros.cpp(0): ERROR: CHECK( throw_if(true, std::runtime_error("whops!")) == 42 ) THREW exception: "whops!"
+
+assertion_macros.cpp(0): ERROR: CHECK( doctest::Approx(0.502) == 0.501 ) is NOT correct!
+  values: CHECK( Approx( 0.502 ) == 0.501 )
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: ts1
+TEST CASE:  normal test in a test suite from a decorator
+
+test_cases_and_suites.cpp(0): MESSAGE: failing because of the timeout decorator!
+
+Test case exceeded time limit of 0.000001!
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: scoped test suite
+TEST CASE:  part of scoped
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: scoped test suite
+TEST CASE:  part of scoped 2
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST SUITE: some TS
+TEST CASE:  part of some TS
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+TEST CASE:  should fail because of an exception
+
+test_cases_and_suites.cpp(0): ERROR: test case THREW exception: 0
+
+===============================================================================
+assertion_macros.cpp(0):
+TEST CASE:  some asserts used in a function called by a test case
+
+assertion_macros.cpp(0): ERROR: CHECK_THROWS_WITH_AS( throw_if(true, false), "unknown exception", int ) threw a DIFFERENT exception! (contents: "unknown exception")
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+
+subcases.cpp(0): MESSAGE: print me twice
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+  sc1
+
+subcases.cpp(0): MESSAGE: hello! from sc1
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  from function...
+  sc1
+
+subcases.cpp(0): MESSAGE: lala
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+
+subcases.cpp(0): MESSAGE: print me twice
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+  from function...
+  sc2
+
+subcases.cpp(0): MESSAGE: hello! from sc2
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases can be used in a separate function as well
+
+DEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):
+  from function...
+  sc2
+
+subcases.cpp(0): MESSAGE: lala
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 0
+  inner 0
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 0
+  inner 1
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 1
+  inner 0
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  outer 1
+  inner 1
+
+subcases.cpp(0): MESSAGE: msg!
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  subcases with changing names
+  separate
+
+subcases.cpp(0): MESSAGE: separate msg!
+
+===============================================================================
+header.h(0):
+TEST CASE:  template 1<char>
+
+header.h(0): FATAL ERROR: 
+
+===============================================================================
+header.h(0):
+TEST CASE:  template 2<doctest::String>
+
+header.h(0): FATAL ERROR: 
+
+===============================================================================
+subcases.cpp(0):
+TEST CASE:  test case should fail even though the last subcase passes
+  one
+
+subcases.cpp(0): ERROR: CHECK( false ) is NOT correct!
+  values: CHECK( false )
+
+===============================================================================
+logging.cpp(0):
+TEST CASE:  third party asserts can report failures to doctest
+
+logging.cpp(0): ERROR: MY_ASSERT(false)
+
+logging.cpp(0): FATAL ERROR: MY_ASSERT_FATAL(false)
+
+===============================================================================
+test_cases_and_suites.cpp(0):
+DESCRIPTION: this test has overridden its skip decorator
+TEST SUITE: skipped test cases
+TEST CASE:  unskipped
+
+test_cases_and_suites.cpp(0): FATAL ERROR: 
+
+===============================================================================
+templated_test_cases.cpp(0):
+TEST CASE:  vector stuff<std::vector<int>>
+
+templated_test_cases.cpp(0): ERROR: CHECK( vec.size() == 20 ) is NOT correct!
+  values: CHECK( 10 == 20 )
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  with a funnier name\:
+  with the funniest name\,
+
+subcases.cpp(0): MESSAGE: Yes!
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  with a funnier name\:
+  with a slightly funny name :
+
+subcases.cpp(0): MESSAGE: Yep!
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  with a funnier name\:
+  without a funny name
+
+subcases.cpp(0): MESSAGE: NO!
+
+===============================================================================
+subcases.cpp(0):
+TEST SUITE: with a funny name,
+TEST CASE:  without a funny name:
+
+subcases.cpp(0): MESSAGE: Nooo
+
+===============================================================================
+[doctest] test cases:  78 | 30 passed |  48 failed |
+[doctest] assertions: 213 | 99 passed | 114 failed |
+[doctest] Status: FAILURE!
+Program code.