doctest

FORK: The fastest feature-rich C++11/14/17/20 single-header testing framework
git clone https://git.neptards.moe/neptards/doctest.git
Log | Files | Refs | README

doctest.cpp (163731B)


      1 #if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER)
      2 
      3 #ifndef DOCTEST_SINGLE_HEADER
      4 #include "doctest_fwd.h"
      5 #endif // DOCTEST_SINGLE_HEADER
      6 
      7 DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros")
      8 
      9 #ifndef DOCTEST_LIBRARY_IMPLEMENTATION
     10 #define DOCTEST_LIBRARY_IMPLEMENTATION
     11 
     12 DOCTEST_CLANG_SUPPRESS_WARNING_POP
     13 
     14 DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
     15 
     16 DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
     17 DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors")
     18 DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
     19 DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
     20 DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32")
     21 DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations")
     22 DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch")
     23 DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum")
     24 DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default")
     25 DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn")
     26 DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion")
     27 DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces")
     28 DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers")
     29 DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
     30 DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path")
     31 
     32 DOCTEST_GCC_SUPPRESS_WARNING_PUSH
     33 DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
     34 DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
     35 DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers")
     36 DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces")
     37 DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch")
     38 DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum")
     39 DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default")
     40 DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations")
     41 DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast")
     42 DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function")
     43 DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance")
     44 DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute")
     45 
     46 DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
     47 DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data
     48 DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled
     49 DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified
     50 DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal
     51 DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch
     52 DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C
     53 DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning)
     54 DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed
     55 
     56 DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
     57 
     58 // required includes - will go only in one translation unit!
     59 #include <ctime>
     60 #include <cmath>
     61 #include <climits>
     62 // borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37
     63 #ifdef __BORLANDC__
     64 #include <math.h>
     65 #endif // __BORLANDC__
     66 #include <new>
     67 #include <cstdio>
     68 #include <cstdlib>
     69 #include <cstring>
     70 #include <limits>
     71 #include <utility>
     72 #include <fstream>
     73 #include <sstream>
     74 #include <iostream>
     75 #include <algorithm>
     76 #include <iomanip>
     77 #include <vector>
     78 #ifndef DOCTEST_CONFIG_NO_MULTITHREADING
     79 #include <atomic>
     80 #include <mutex>
     81 #define DOCTEST_DECLARE_MUTEX(name) std::mutex name;
     82 #define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name)
     83 #define DOCTEST_LOCK_MUTEX(name) std::lock_guard<std::mutex> DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name);
     84 #else // DOCTEST_CONFIG_NO_MULTITHREADING
     85 #define DOCTEST_DECLARE_MUTEX(name)
     86 #define DOCTEST_DECLARE_STATIC_MUTEX(name)
     87 #define DOCTEST_LOCK_MUTEX(name)
     88 #endif // DOCTEST_CONFIG_NO_MULTITHREADING
     89 #include <set>
     90 #include <map>
     91 #include <unordered_set>
     92 #include <exception>
     93 #include <stdexcept>
     94 #include <csignal>
     95 #include <cfloat>
     96 #include <cctype>
     97 #include <cstdint>
     98 #include <string>
     99 
    100 #ifdef DOCTEST_PLATFORM_MAC
    101 #include <sys/types.h>
    102 #include <unistd.h>
    103 #include <sys/sysctl.h>
    104 #endif // DOCTEST_PLATFORM_MAC
    105 
    106 #ifdef DOCTEST_PLATFORM_WINDOWS
    107 
    108 // defines for a leaner windows.h
    109 #ifndef WIN32_LEAN_AND_MEAN
    110 #define WIN32_LEAN_AND_MEAN
    111 #endif // WIN32_LEAN_AND_MEAN
    112 #ifndef NOMINMAX
    113 #define NOMINMAX
    114 #endif // NOMINMAX
    115 
    116 // not sure what AfxWin.h is for - here I do what Catch does
    117 #ifdef __AFXDLL
    118 #include <AfxWin.h>
    119 #else
    120 #include <windows.h>
    121 #endif
    122 #include <io.h>
    123 
    124 #else // DOCTEST_PLATFORM_WINDOWS
    125 
    126 #include <sys/time.h>
    127 #include <unistd.h>
    128 
    129 #endif // DOCTEST_PLATFORM_WINDOWS
    130 
    131 // this is a fix for https://github.com/doctest/doctest/issues/348
    132 // https://mail.gnome.org/archives/xml/2012-January/msg00000.html
    133 #if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO)
    134 #define STDOUT_FILENO fileno(stdout)
    135 #endif // HAVE_UNISTD_H
    136 
    137 DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
    138 
    139 // counts the number of elements in a C array
    140 #define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
    141 
    142 #ifdef DOCTEST_CONFIG_DISABLE
    143 #define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_disabled
    144 #else // DOCTEST_CONFIG_DISABLE
    145 #define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_not_disabled
    146 #endif // DOCTEST_CONFIG_DISABLE
    147 
    148 #ifndef DOCTEST_CONFIG_OPTIONS_PREFIX
    149 #define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-"
    150 #endif
    151 
    152 #ifndef DOCTEST_THREAD_LOCAL
    153 #if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
    154 #define DOCTEST_THREAD_LOCAL
    155 #else // DOCTEST_MSVC
    156 #define DOCTEST_THREAD_LOCAL thread_local
    157 #endif // DOCTEST_MSVC
    158 #endif // DOCTEST_THREAD_LOCAL
    159 
    160 #ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES
    161 #define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32
    162 #endif
    163 
    164 #ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE
    165 #define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64
    166 #endif
    167 
    168 #ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
    169 #define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX
    170 #else
    171 #define DOCTEST_OPTIONS_PREFIX_DISPLAY ""
    172 #endif
    173 
    174 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
    175 #define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
    176 #endif
    177 
    178 #ifndef DOCTEST_CDECL
    179 #define DOCTEST_CDECL __cdecl
    180 #endif
    181 
    182 namespace doctest {
    183 
    184 bool is_running_in_test = false;
    185 
    186 namespace {
    187     using namespace detail;
    188 
    189     template <typename Ex>
    190     DOCTEST_NORETURN void throw_exception(Ex const& e) {
    191 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
    192         throw e;
    193 #else  // DOCTEST_CONFIG_NO_EXCEPTIONS
    194         std::cerr << "doctest will terminate because it needed to throw an exception.\n"
    195                   << "The message was: " << e.what() << '\n';
    196         std::terminate();
    197 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
    198     }
    199 
    200 #ifndef DOCTEST_INTERNAL_ERROR
    201 #define DOCTEST_INTERNAL_ERROR(msg)                                                                \
    202     throw_exception(std::logic_error(                                                              \
    203             __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
    204 #endif // DOCTEST_INTERNAL_ERROR
    205 
    206     // case insensitive strcmp
    207     int stricmp(const char* a, const char* b) {
    208         for(;; a++, b++) {
    209             const int d = tolower(*a) - tolower(*b);
    210             if(d != 0 || !*a)
    211                 return d;
    212         }
    213     }
    214 
    215     struct Endianness
    216     {
    217         enum Arch
    218         {
    219             Big,
    220             Little
    221         };
    222 
    223         static Arch which() {
    224             int x = 1;
    225             // casting any data pointer to char* is allowed
    226             auto ptr = reinterpret_cast<char*>(&x);
    227             if(*ptr)
    228                 return Little;
    229             return Big;
    230         }
    231     };
    232 } // namespace
    233 
    234 namespace detail {
    235     DOCTEST_THREAD_LOCAL class
    236     {
    237         std::vector<std::streampos> stack;
    238         std::stringstream           ss;
    239 
    240     public:
    241         std::ostream* push() {
    242             stack.push_back(ss.tellp());
    243             return &ss;
    244         }
    245 
    246         String pop() {
    247             if (stack.empty())
    248                 DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!");
    249 
    250             std::streampos pos = stack.back();
    251             stack.pop_back();
    252             unsigned sz = static_cast<unsigned>(ss.tellp() - pos);
    253             ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out);
    254             return String(ss, sz);
    255         }
    256     } g_oss;
    257 
    258     std::ostream* tlssPush() {
    259         return g_oss.push();
    260     }
    261 
    262     String tlssPop() {
    263         return g_oss.pop();
    264     }
    265 
    266 #ifndef DOCTEST_CONFIG_DISABLE
    267 
    268 namespace timer_large_integer
    269 {
    270     
    271 #if defined(DOCTEST_PLATFORM_WINDOWS)
    272     using type = ULONGLONG;
    273 #else // DOCTEST_PLATFORM_WINDOWS
    274     using type = std::uint64_t;
    275 #endif // DOCTEST_PLATFORM_WINDOWS
    276 }
    277 
    278 using ticks_t = timer_large_integer::type;
    279 
    280 #ifdef DOCTEST_CONFIG_GETCURRENTTICKS
    281     ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); }
    282 #elif defined(DOCTEST_PLATFORM_WINDOWS)
    283     ticks_t getCurrentTicks() {
    284         static LARGE_INTEGER hz = { {0} }, hzo = { {0} };
    285         if(!hz.QuadPart) {
    286             QueryPerformanceFrequency(&hz);
    287             QueryPerformanceCounter(&hzo);
    288         }
    289         LARGE_INTEGER t;
    290         QueryPerformanceCounter(&t);
    291         return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart;
    292     }
    293 #else  // DOCTEST_PLATFORM_WINDOWS
    294     ticks_t getCurrentTicks() {
    295         timeval t;
    296         gettimeofday(&t, nullptr);
    297         return static_cast<ticks_t>(t.tv_sec) * 1000000 + static_cast<ticks_t>(t.tv_usec);
    298     }
    299 #endif // DOCTEST_PLATFORM_WINDOWS
    300 
    301     struct Timer
    302     {
    303         void         start() { m_ticks = getCurrentTicks(); }
    304         unsigned int getElapsedMicroseconds() const {
    305             return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
    306         }
    307         //unsigned int getElapsedMilliseconds() const {
    308         //    return static_cast<unsigned int>(getElapsedMicroseconds() / 1000);
    309         //}
    310         double getElapsedSeconds() const { return static_cast<double>(getCurrentTicks() - m_ticks) / 1000000.0; }
    311 
    312     private:
    313         ticks_t m_ticks = 0;
    314     };
    315 
    316 #ifdef DOCTEST_CONFIG_NO_MULTITHREADING
    317     template <typename T>
    318     using Atomic = T;
    319 #else // DOCTEST_CONFIG_NO_MULTITHREADING
    320     template <typename T>
    321     using Atomic = std::atomic<T>;
    322 #endif // DOCTEST_CONFIG_NO_MULTITHREADING
    323 
    324 #if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING)
    325     template <typename T>
    326     using MultiLaneAtomic = Atomic<T>;
    327 #else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
    328     // Provides a multilane implementation of an atomic variable that supports add, sub, load,
    329     // store. Instead of using a single atomic variable, this splits up into multiple ones,
    330     // each sitting on a separate cache line. The goal is to provide a speedup when most
    331     // operations are modifying. It achieves this with two properties:
    332     //
    333     // * Multiple atomics are used, so chance of congestion from the same atomic is reduced.
    334     // * Each atomic sits on a separate cache line, so false sharing is reduced.
    335     //
    336     // The disadvantage is that there is a small overhead due to the use of TLS, and load/store
    337     // is slower because all atomics have to be accessed.
    338     template <typename T>
    339     class MultiLaneAtomic
    340     {
    341         struct CacheLineAlignedAtomic
    342         {
    343             Atomic<T> atomic{};
    344             char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic<T>)];
    345         };
    346         CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
    347 
    348         static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE,
    349                       "guarantee one atomic takes exactly one cache line");
    350 
    351     public:
    352         T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; }
    353 
    354         T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); }
    355 
    356         T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
    357             return myAtomic().fetch_add(arg, order);
    358         }
    359 
    360         T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
    361             return myAtomic().fetch_sub(arg, order);
    362         }
    363 
    364         operator T() const DOCTEST_NOEXCEPT { return load(); }
    365 
    366         T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT {
    367             auto result = T();
    368             for(auto const& c : m_atomics) {
    369                 result += c.atomic.load(order);
    370             }
    371             return result;
    372         }
    373 
    374         T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this]
    375             store(desired);
    376             return desired;
    377         }
    378 
    379         void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
    380             // first value becomes desired", all others become 0.
    381             for(auto& c : m_atomics) {
    382                 c.atomic.store(desired, order);
    383                 desired = {};
    384             }
    385         }
    386 
    387     private:
    388         // Each thread has a different atomic that it operates on. If more than NumLanes threads
    389         // use this, some will use the same atomic. So performance will degrade a bit, but still
    390         // everything will work.
    391         //
    392         // The logic here is a bit tricky. The call should be as fast as possible, so that there
    393         // is minimal to no overhead in determining the correct atomic for the current thread.
    394         //
    395         // 1. A global static counter laneCounter counts continuously up.
    396         // 2. Each successive thread will use modulo operation of that counter so it gets an atomic
    397         //    assigned in a round-robin fashion.
    398         // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
    399         //    little overhead.
    400         Atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
    401             static Atomic<size_t> laneCounter;
    402             DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
    403                     laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
    404 
    405             return m_atomics[tlsLaneIdx].atomic;
    406         }
    407     };
    408 #endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
    409 
    410     // this holds both parameters from the command line and runtime data for tests
    411     struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
    412     {
    413         MultiLaneAtomic<int> numAssertsCurrentTest_atomic;
    414         MultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
    415 
    416         std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
    417 
    418         std::vector<IReporter*> reporters_currently_used;
    419 
    420         assert_handler ah = nullptr;
    421 
    422         Timer timer;
    423 
    424         std::vector<String> stringifiedContexts; // logging from INFO() due to an exception
    425 
    426         // stuff for subcases
    427         bool reachedLeaf;
    428         std::vector<SubcaseSignature> subcaseStack;
    429         std::vector<SubcaseSignature> nextSubcaseStack;
    430         std::unordered_set<unsigned long long> fullyTraversedSubcases;
    431         size_t currentSubcaseDepth;
    432         Atomic<bool> shouldLogCurrentException;
    433 
    434         void resetRunData() {
    435             numTestCases                = 0;
    436             numTestCasesPassingFilters  = 0;
    437             numTestSuitesPassingFilters = 0;
    438             numTestCasesFailed          = 0;
    439             numAsserts                  = 0;
    440             numAssertsFailed            = 0;
    441             numAssertsCurrentTest       = 0;
    442             numAssertsFailedCurrentTest = 0;
    443         }
    444 
    445         void finalizeTestCaseData() {
    446             seconds = timer.getElapsedSeconds();
    447 
    448             // update the non-atomic counters
    449             numAsserts += numAssertsCurrentTest_atomic;
    450             numAssertsFailed += numAssertsFailedCurrentTest_atomic;
    451             numAssertsCurrentTest       = numAssertsCurrentTest_atomic;
    452             numAssertsFailedCurrentTest = numAssertsFailedCurrentTest_atomic;
    453 
    454             if(numAssertsFailedCurrentTest)
    455                 failure_flags |= TestCaseFailureReason::AssertFailure;
    456 
    457             if(Approx(currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 &&
    458                Approx(seconds).epsilon(DBL_EPSILON) > currentTest->m_timeout)
    459                 failure_flags |= TestCaseFailureReason::Timeout;
    460 
    461             if(currentTest->m_should_fail) {
    462                 if(failure_flags) {
    463                     failure_flags |= TestCaseFailureReason::ShouldHaveFailedAndDid;
    464                 } else {
    465                     failure_flags |= TestCaseFailureReason::ShouldHaveFailedButDidnt;
    466                 }
    467             } else if(failure_flags && currentTest->m_may_fail) {
    468                 failure_flags |= TestCaseFailureReason::CouldHaveFailedAndDid;
    469             } else if(currentTest->m_expected_failures > 0) {
    470                 if(numAssertsFailedCurrentTest == currentTest->m_expected_failures) {
    471                     failure_flags |= TestCaseFailureReason::FailedExactlyNumTimes;
    472                 } else {
    473                     failure_flags |= TestCaseFailureReason::DidntFailExactlyNumTimes;
    474                 }
    475             }
    476 
    477             bool ok_to_fail = (TestCaseFailureReason::ShouldHaveFailedAndDid & failure_flags) ||
    478                               (TestCaseFailureReason::CouldHaveFailedAndDid & failure_flags) ||
    479                               (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags);
    480 
    481             // if any subcase has failed - the whole test case has failed
    482             testCaseSuccess = !(failure_flags && !ok_to_fail);
    483             if(!testCaseSuccess)
    484                 numTestCasesFailed++;
    485         }
    486     };
    487 
    488     ContextState* g_cs = nullptr;
    489 
    490     // used to avoid locks for the debug output
    491     // TODO: figure out if this is indeed necessary/correct - seems like either there still
    492     // could be a race or that there wouldn't be a race even if using the context directly
    493     DOCTEST_THREAD_LOCAL bool g_no_colors;
    494 
    495 #endif // DOCTEST_CONFIG_DISABLE
    496 } // namespace detail
    497 
    498 char* String::allocate(size_type sz) {
    499     if (sz <= last) {
    500         buf[sz] = '\0';
    501         setLast(last - sz);
    502         return buf;
    503     } else {
    504         setOnHeap();
    505         data.size = sz;
    506         data.capacity = data.size + 1;
    507         data.ptr = new char[data.capacity];
    508         data.ptr[sz] = '\0';
    509         return data.ptr;
    510     }
    511 }
    512 
    513 void String::setOnHeap() noexcept { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; }
    514 void String::setLast(size_type in) noexcept { buf[last] = char(in); }
    515 void String::setSize(size_type sz) noexcept {
    516     if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); }
    517     else { data.ptr[sz] = '\0'; data.size = sz; }
    518 }
    519 
    520 void String::copy(const String& other) {
    521     if(other.isOnStack()) {
    522         memcpy(buf, other.buf, len);
    523     } else {
    524         memcpy(allocate(other.data.size), other.data.ptr, other.data.size);
    525     }
    526 }
    527 
    528 String::String() noexcept {
    529     buf[0] = '\0';
    530     setLast();
    531 }
    532 
    533 String::~String() {
    534     if(!isOnStack())
    535         delete[] data.ptr;
    536 } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
    537 
    538 String::String(const char* in)
    539         : String(in, strlen(in)) {}
    540 
    541 String::String(const char* in, size_type in_size) {
    542     memcpy(allocate(in_size), in, in_size);
    543 }
    544 
    545 String::String(std::istream& in, size_type in_size) {
    546     in.read(allocate(in_size), in_size);
    547 }
    548 
    549 String::String(const String& other) { copy(other); }
    550 
    551 String& String::operator=(const String& other) {
    552     if(this != &other) {
    553         if(!isOnStack())
    554             delete[] data.ptr;
    555 
    556         copy(other);
    557     }
    558 
    559     return *this;
    560 }
    561 
    562 String& String::operator+=(const String& other) {
    563     const size_type my_old_size = size();
    564     const size_type other_size  = other.size();
    565     const size_type total_size  = my_old_size + other_size;
    566     if(isOnStack()) {
    567         if(total_size < len) {
    568             // append to the current stack space
    569             memcpy(buf + my_old_size, other.c_str(), other_size + 1);
    570             // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
    571             setLast(last - total_size);
    572         } else {
    573             // alloc new chunk
    574             char* temp = new char[total_size + 1];
    575             // copy current data to new location before writing in the union
    576             memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed
    577             // update data in union
    578             setOnHeap();
    579             data.size     = total_size;
    580             data.capacity = data.size + 1;
    581             data.ptr      = temp;
    582             // transfer the rest of the data
    583             memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
    584         }
    585     } else {
    586         if(data.capacity > total_size) {
    587             // append to the current heap block
    588             data.size = total_size;
    589             memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
    590         } else {
    591             // resize
    592             data.capacity *= 2;
    593             if(data.capacity <= total_size)
    594                 data.capacity = total_size + 1;
    595             // alloc new chunk
    596             char* temp = new char[data.capacity];
    597             // copy current data to new location before releasing it
    598             memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed
    599             // release old chunk
    600             delete[] data.ptr;
    601             // update the rest of the union members
    602             data.size = total_size;
    603             data.ptr  = temp;
    604             // transfer the rest of the data
    605             memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
    606         }
    607     }
    608 
    609     return *this;
    610 }
    611 
    612 String::String(String&& other) noexcept {
    613     memcpy(buf, other.buf, len);
    614     other.buf[0] = '\0';
    615     other.setLast();
    616 }
    617 
    618 String& String::operator=(String&& other) noexcept {
    619     if(this != &other) {
    620         if(!isOnStack())
    621             delete[] data.ptr;
    622         memcpy(buf, other.buf, len);
    623         other.buf[0] = '\0';
    624         other.setLast();
    625     }
    626     return *this;
    627 }
    628 
    629 char String::operator[](size_type i) const {
    630     return const_cast<String*>(this)->operator[](i);
    631 }
    632 
    633 char& String::operator[](size_type i) {
    634     if(isOnStack())
    635         return reinterpret_cast<char*>(buf)[i];
    636     return data.ptr[i];
    637 }
    638 
    639 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized")
    640 String::size_type String::size() const {
    641     if(isOnStack())
    642         return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32
    643     return data.size;
    644 }
    645 DOCTEST_GCC_SUPPRESS_WARNING_POP
    646 
    647 String::size_type String::capacity() const {
    648     if(isOnStack())
    649         return len;
    650     return data.capacity;
    651 }
    652 
    653 String String::substr(size_type pos, size_type cnt) && {
    654     cnt = std::min(cnt, size() - 1 - pos);
    655     char* cptr = c_str();
    656     memmove(cptr, cptr + pos, cnt);
    657     setSize(cnt);
    658     return std::move(*this);
    659 }
    660 
    661 String String::substr(size_type pos, size_type cnt) const & {
    662     cnt = std::min(cnt, size() - 1 - pos);
    663     return String{ c_str() + pos, cnt };
    664 }
    665 
    666 String::size_type String::find(char ch, size_type pos) const {
    667     const char* begin = c_str();
    668     const char* end = begin + size();
    669     const char* it = begin + pos;
    670     for (; it < end && *it != ch; it++);
    671     if (it < end) { return static_cast<size_type>(it - begin); }
    672     else { return npos; }
    673 }
    674 
    675 String::size_type String::rfind(char ch, size_type pos) const {
    676     const char* begin = c_str();
    677     const char* it = begin + std::min(pos, size() - 1);
    678     for (; it >= begin && *it != ch; it--);
    679     if (it >= begin) { return static_cast<size_type>(it - begin); }
    680     else { return npos; }
    681 }
    682 
    683 int String::compare(const char* other, bool no_case) const {
    684     if(no_case)
    685         return doctest::stricmp(c_str(), other);
    686     return std::strcmp(c_str(), other);
    687 }
    688 
    689 int String::compare(const String& other, bool no_case) const {
    690     return compare(other.c_str(), no_case);
    691 }
    692 
    693 String operator+(const String& lhs, const String& rhs) { return  String(lhs) += rhs; }
    694 
    695 bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; }
    696 bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }
    697 bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; }
    698 bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; }
    699 bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; }
    700 bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; }
    701 
    702 std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); }
    703 
    704 Contains::Contains(const String& str) : string(str) { }
    705 
    706 bool Contains::checkWith(const String& other) const {
    707     return strstr(other.c_str(), string.c_str()) != nullptr;
    708 }
    709 
    710 String toString(const Contains& in) {
    711     return "Contains( " + in.string + " )";
    712 }
    713 
    714 bool operator==(const String& lhs, const Contains& rhs) { return rhs.checkWith(lhs); }
    715 bool operator==(const Contains& lhs, const String& rhs) { return lhs.checkWith(rhs); }
    716 bool operator!=(const String& lhs, const Contains& rhs) { return !rhs.checkWith(lhs); }
    717 bool operator!=(const Contains& lhs, const String& rhs) { return !lhs.checkWith(rhs); }
    718 
    719 namespace {
    720     void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;)
    721 } // namespace
    722 
    723 namespace Color {
    724     std::ostream& operator<<(std::ostream& s, Color::Enum code) {
    725         color_to_stream(s, code);
    726         return s;
    727     }
    728 } // namespace Color
    729 
    730 // clang-format off
    731 const char* assertString(assertType::Enum at) {
    732     DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitely handled
    733     #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type
    734     #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \
    735         DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \
    736         DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \
    737         DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type)
    738     switch(at) {
    739         DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN);
    740         DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK);
    741         DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE);
    742 
    743         DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE);
    744 
    745         DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS);
    746 
    747         DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS);
    748 
    749         DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH);
    750 
    751         DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS);
    752 
    753         DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW);
    754 
    755         DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ);
    756         DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE);
    757         DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT);
    758         DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT);
    759         DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE);
    760         DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE);
    761 
    762         DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY);
    763         DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE);
    764 
    765         default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!");
    766     }
    767     DOCTEST_MSVC_SUPPRESS_WARNING_POP
    768 }
    769 // clang-format on
    770 
    771 const char* failureString(assertType::Enum at) {
    772     if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional
    773         return "WARNING";
    774     if(at & assertType::is_check) //!OCLINT bitwise operator in conditional
    775         return "ERROR";
    776     if(at & assertType::is_require) //!OCLINT bitwise operator in conditional
    777         return "FATAL ERROR";
    778     return "";
    779 }
    780 
    781 DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference")
    782 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference")
    783 // depending on the current options this will remove the path of filenames
    784 const char* skipPathFromFilename(const char* file) {
    785 #ifndef DOCTEST_CONFIG_DISABLE
    786     if(getContextOptions()->no_path_in_filenames) {
    787         auto back    = std::strrchr(file, '\\');
    788         auto forward = std::strrchr(file, '/');
    789         if(back || forward) {
    790             if(back > forward)
    791                 forward = back;
    792             return forward + 1;
    793         }
    794     }
    795 #endif // DOCTEST_CONFIG_DISABLE
    796     return file;
    797 }
    798 DOCTEST_CLANG_SUPPRESS_WARNING_POP
    799 DOCTEST_GCC_SUPPRESS_WARNING_POP
    800 
    801 bool SubcaseSignature::operator==(const SubcaseSignature& other) const {
    802     return m_line == other.m_line
    803         && std::strcmp(m_file, other.m_file) == 0
    804         && m_name == other.m_name;
    805 }
    806 
    807 bool SubcaseSignature::operator<(const SubcaseSignature& other) const {
    808     if(m_line != other.m_line)
    809         return m_line < other.m_line;
    810     if(std::strcmp(m_file, other.m_file) != 0)
    811         return std::strcmp(m_file, other.m_file) < 0;
    812     return m_name.compare(other.m_name) < 0;
    813 }
    814 
    815 DOCTEST_DEFINE_INTERFACE(IContextScope)
    816 
    817 namespace detail {
    818     void filldata<const void*>::fill(std::ostream* stream, const void* in) {
    819         if (in) { *stream << in; }
    820         else { *stream << "nullptr"; }
    821     }
    822 
    823     template <typename T>
    824     String toStreamLit(T t) {
    825         std::ostream* os = tlssPush();
    826         os->operator<<(t);
    827         return tlssPop();
    828     }
    829 }
    830 
    831 #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
    832 String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
    833 #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
    834 
    835 #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
    836 // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
    837 String toString(const std::string& in) { return in.c_str(); }
    838 #endif // VS 2019
    839 
    840 String toString(String in) { return in; }
    841 
    842 String toString(std::nullptr_t) { return "nullptr"; }
    843 
    844 String toString(bool in) { return in ? "true" : "false"; }
    845 
    846 String toString(float in) { return toStreamLit(in); }
    847 String toString(double in) { return toStreamLit(in); }
    848 String toString(double long in) { return toStreamLit(in); }
    849 
    850 String toString(char in) { return toStreamLit(static_cast<signed>(in)); }
    851 String toString(char signed in) { return toStreamLit(static_cast<signed>(in)); }
    852 String toString(char unsigned in) { return toStreamLit(static_cast<unsigned>(in)); }
    853 String toString(short in) { return toStreamLit(in); }
    854 String toString(short unsigned in) { return toStreamLit(in); }
    855 String toString(signed in) { return toStreamLit(in); }
    856 String toString(unsigned in) { return toStreamLit(in); }
    857 String toString(long in) { return toStreamLit(in); }
    858 String toString(long unsigned in) { return toStreamLit(in); }
    859 String toString(long long in) { return toStreamLit(in); }
    860 String toString(long long unsigned in) { return toStreamLit(in); }
    861 
    862 Approx::Approx(double value)
    863         : m_epsilon(static_cast<double>(std::numeric_limits<float>::epsilon()) * 100)
    864         , m_scale(1.0)
    865         , m_value(value) {}
    866 
    867 Approx Approx::operator()(double value) const {
    868     Approx approx(value);
    869     approx.epsilon(m_epsilon);
    870     approx.scale(m_scale);
    871     return approx;
    872 }
    873 
    874 Approx& Approx::epsilon(double newEpsilon) {
    875     m_epsilon = newEpsilon;
    876     return *this;
    877 }
    878 Approx& Approx::scale(double newScale) {
    879     m_scale = newScale;
    880     return *this;
    881 }
    882 
    883 bool operator==(double lhs, const Approx& rhs) {
    884     // Thanks to Richard Harris for his help refining this formula
    885     return std::fabs(lhs - rhs.m_value) <
    886            rhs.m_epsilon * (rhs.m_scale + std::max<double>(std::fabs(lhs), std::fabs(rhs.m_value)));
    887 }
    888 bool operator==(const Approx& lhs, double rhs) { return operator==(rhs, lhs); }
    889 bool operator!=(double lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
    890 bool operator!=(const Approx& lhs, double rhs) { return !operator==(rhs, lhs); }
    891 bool operator<=(double lhs, const Approx& rhs) { return lhs < rhs.m_value || lhs == rhs; }
    892 bool operator<=(const Approx& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; }
    893 bool operator>=(double lhs, const Approx& rhs) { return lhs > rhs.m_value || lhs == rhs; }
    894 bool operator>=(const Approx& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; }
    895 bool operator<(double lhs, const Approx& rhs) { return lhs < rhs.m_value && lhs != rhs; }
    896 bool operator<(const Approx& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; }
    897 bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs != rhs; }
    898 bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }
    899 
    900 String toString(const Approx& in) {
    901     return "Approx( " + doctest::toString(in.m_value) + " )";
    902 }
    903 const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); }
    904 
    905 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738)
    906 template <typename F>
    907 IsNaN<F>::operator bool() const {
    908     return std::isnan(value) ^ flipped;
    909 }
    910 DOCTEST_MSVC_SUPPRESS_WARNING_POP
    911 template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
    912 template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
    913 template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
    914 template <typename F>
    915 String toString(IsNaN<F> in) { return String(in.flipped ? "! " : "") + "IsNaN( " + doctest::toString(in.value) + " )"; }
    916 String toString(IsNaN<float> in) { return toString<float>(in); }
    917 String toString(IsNaN<double> in) { return toString<double>(in); }
    918 String toString(IsNaN<double long> in) { return toString<double long>(in); }
    919 
    920 } // namespace doctest
    921 
    922 #ifdef DOCTEST_CONFIG_DISABLE
    923 namespace doctest {
    924 Context::Context(int, const char* const*) {}
    925 Context::~Context() = default;
    926 void Context::applyCommandLine(int, const char* const*) {}
    927 void Context::addFilter(const char*, const char*) {}
    928 void Context::clearFilters() {}
    929 void Context::setOption(const char*, bool) {}
    930 void Context::setOption(const char*, int) {}
    931 void Context::setOption(const char*, const char*) {}
    932 bool Context::shouldExit() { return false; }
    933 void Context::setAsDefaultForAssertsOutOfTestCases() {}
    934 void Context::setAssertHandler(detail::assert_handler) {}
    935 void Context::setCout(std::ostream*) {}
    936 int  Context::run() { return 0; }
    937 
    938 int                         IReporter::get_num_active_contexts() { return 0; }
    939 const IContextScope* const* IReporter::get_active_contexts() { return nullptr; }
    940 int                         IReporter::get_num_stringified_contexts() { return 0; }
    941 const String*               IReporter::get_stringified_contexts() { return nullptr; }
    942 
    943 int registerReporter(const char*, int, IReporter*) { return 0; }
    944 
    945 } // namespace doctest
    946 #else // DOCTEST_CONFIG_DISABLE
    947 
    948 #if !defined(DOCTEST_CONFIG_COLORS_NONE)
    949 #if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI)
    950 #ifdef DOCTEST_PLATFORM_WINDOWS
    951 #define DOCTEST_CONFIG_COLORS_WINDOWS
    952 #else // linux
    953 #define DOCTEST_CONFIG_COLORS_ANSI
    954 #endif // platform
    955 #endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI
    956 #endif // DOCTEST_CONFIG_COLORS_NONE
    957 
    958 namespace doctest_detail_test_suite_ns {
    959 // holds the current test suite
    960 doctest::detail::TestSuite& getCurrentTestSuite() {
    961     static doctest::detail::TestSuite data{};
    962     return data;
    963 }
    964 } // namespace doctest_detail_test_suite_ns
    965 
    966 namespace doctest {
    967 namespace {
    968     // the int (priority) is part of the key for automatic sorting - sadly one can register a
    969     // reporter with a duplicate name and a different priority but hopefully that won't happen often :|
    970     using reporterMap = std::map<std::pair<int, String>, reporterCreatorFunc>;
    971 
    972     reporterMap& getReporters() {
    973         static reporterMap data;
    974         return data;
    975     }
    976     reporterMap& getListeners() {
    977         static reporterMap data;
    978         return data;
    979     }
    980 } // namespace
    981 namespace detail {
    982 #define DOCTEST_ITERATE_THROUGH_REPORTERS(function, ...)                                           \
    983     for(auto& curr_rep : g_cs->reporters_currently_used)                                           \
    984     curr_rep->function(__VA_ARGS__)
    985 
    986     bool checkIfShouldThrow(assertType::Enum at) {
    987         if(at & assertType::is_require) //!OCLINT bitwise operator in conditional
    988             return true;
    989 
    990         if((at & assertType::is_check) //!OCLINT bitwise operator in conditional
    991            && getContextOptions()->abort_after > 0 &&
    992            (g_cs->numAssertsFailed + g_cs->numAssertsFailedCurrentTest_atomic) >=
    993                    getContextOptions()->abort_after)
    994             return true;
    995 
    996         return false;
    997     }
    998 
    999 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
   1000     DOCTEST_NORETURN void throwException() {
   1001         g_cs->shouldLogCurrentException = false;
   1002         throw TestFailureException(); // NOLINT(hicpp-exception-baseclass)
   1003     }
   1004 #else // DOCTEST_CONFIG_NO_EXCEPTIONS
   1005     void throwException() {}
   1006 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
   1007 } // namespace detail
   1008 
   1009 namespace {
   1010     using namespace detail;
   1011     // matching of a string against a wildcard mask (case sensitivity configurable) taken from
   1012     // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing
   1013     int wildcmp(const char* str, const char* wild, bool caseSensitive) {
   1014         const char* cp = str;
   1015         const char* mp = wild;
   1016 
   1017         while((*str) && (*wild != '*')) {
   1018             if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) &&
   1019                (*wild != '?')) {
   1020                 return 0;
   1021             }
   1022             wild++;
   1023             str++;
   1024         }
   1025 
   1026         while(*str) {
   1027             if(*wild == '*') {
   1028                 if(!*++wild) {
   1029                     return 1;
   1030                 }
   1031                 mp = wild;
   1032                 cp = str + 1;
   1033             } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) ||
   1034                       (*wild == '?')) {
   1035                 wild++;
   1036                 str++;
   1037             } else {
   1038                 wild = mp;   //!OCLINT parameter reassignment
   1039                 str  = cp++; //!OCLINT parameter reassignment
   1040             }
   1041         }
   1042 
   1043         while(*wild == '*') {
   1044             wild++;
   1045         }
   1046         return !*wild;
   1047     }
   1048 
   1049     // checks if the name matches any of the filters (and can be configured what to do when empty)
   1050     bool matchesAny(const char* name, const std::vector<String>& filters, bool matchEmpty,
   1051         bool caseSensitive) {
   1052         if (filters.empty() && matchEmpty)
   1053             return true;
   1054         for (auto& curr : filters)
   1055             if (wildcmp(name, curr.c_str(), caseSensitive))
   1056                 return true;
   1057         return false;
   1058     }
   1059 
   1060     unsigned long long hash(unsigned long long a, unsigned long long b) {
   1061         return (a << 5) + b;
   1062     }
   1063 
   1064     // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html
   1065     unsigned long long hash(const char* str) {
   1066         unsigned long long hash = 5381;
   1067         char c;
   1068         while ((c = *str++))
   1069             hash = ((hash << 5) + hash) + c; // hash * 33 + c
   1070         return hash;
   1071     }
   1072 
   1073     unsigned long long hash(const SubcaseSignature& sig) {
   1074         return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line);
   1075     }
   1076 
   1077     unsigned long long hash(const std::vector<SubcaseSignature>& sigs, size_t count) {
   1078         unsigned long long running = 0;
   1079         auto end = sigs.begin() + count;
   1080         for (auto it = sigs.begin(); it != end; it++) {
   1081             running = hash(running, hash(*it));
   1082         }
   1083         return running;
   1084     }
   1085 
   1086     unsigned long long hash(const std::vector<SubcaseSignature>& sigs) {
   1087         unsigned long long running = 0;
   1088         for (const SubcaseSignature& sig : sigs) {
   1089             running = hash(running, hash(sig));
   1090         }
   1091         return running;
   1092     }
   1093 } // namespace
   1094 namespace detail {
   1095     bool Subcase::checkFilters() {
   1096         if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) {
   1097             if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive))
   1098                 return true;
   1099             if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive))
   1100                 return true;
   1101         }
   1102         return false;
   1103     }
   1104 
   1105     Subcase::Subcase(const String& name, const char* file, int line)
   1106             : m_signature({name, file, line}) {
   1107         if (!g_cs->reachedLeaf) {
   1108             if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size()
   1109                 || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) {
   1110                 // Going down.
   1111                 if (checkFilters()) { return; }
   1112 
   1113                 g_cs->subcaseStack.push_back(m_signature);
   1114                 g_cs->currentSubcaseDepth++;
   1115                 m_entered = true;
   1116                 DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
   1117             }
   1118         } else {
   1119             if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) {
   1120                 // This subcase is reentered via control flow.
   1121                 g_cs->currentSubcaseDepth++;
   1122                 m_entered = true;
   1123                 DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature);
   1124             } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth
   1125                     && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature)))
   1126                     == g_cs->fullyTraversedSubcases.end()) {
   1127                 if (checkFilters()) { return; }
   1128                 // This subcase is part of the one to be executed next.
   1129                 g_cs->nextSubcaseStack.clear();
   1130                 g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(),
   1131                     g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth);
   1132                 g_cs->nextSubcaseStack.push_back(m_signature);
   1133             }
   1134         }
   1135     }
   1136 
   1137     DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
   1138     DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
   1139     DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
   1140 
   1141     Subcase::~Subcase() {
   1142         if (m_entered) {
   1143             g_cs->currentSubcaseDepth--;
   1144 
   1145             if (!g_cs->reachedLeaf) {
   1146                 // Leaf.
   1147                 g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack));
   1148                 g_cs->nextSubcaseStack.clear();
   1149                 g_cs->reachedLeaf = true;
   1150             } else if (g_cs->nextSubcaseStack.empty()) {
   1151                 // All children are finished.
   1152                 g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack));
   1153             }
   1154 
   1155 #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
   1156             if(std::uncaught_exceptions() > 0
   1157 #else
   1158             if(std::uncaught_exception()
   1159 #endif
   1160                 && g_cs->shouldLogCurrentException) {
   1161                 DOCTEST_ITERATE_THROUGH_REPORTERS(
   1162                         test_case_exception, {"exception thrown in subcase - will translate later "
   1163                                                 "when the whole test case has been exited (cannot "
   1164                                                 "translate while there is an active exception)",
   1165                                                 false});
   1166                 g_cs->shouldLogCurrentException = false;
   1167             }
   1168 
   1169             DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
   1170         }
   1171     }
   1172 
   1173     DOCTEST_CLANG_SUPPRESS_WARNING_POP
   1174     DOCTEST_GCC_SUPPRESS_WARNING_POP
   1175     DOCTEST_MSVC_SUPPRESS_WARNING_POP
   1176 
   1177     Subcase::operator bool() const { return m_entered; }
   1178 
   1179     Result::Result(bool passed, const String& decomposition)
   1180             : m_passed(passed)
   1181             , m_decomp(decomposition) {}
   1182 
   1183     ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at)
   1184             : m_at(at) {}
   1185 
   1186     TestSuite& TestSuite::operator*(const char* in) {
   1187         m_test_suite = in;
   1188         return *this;
   1189     }
   1190 
   1191     TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
   1192                        const String& type, int template_id) {
   1193         m_file              = file;
   1194         m_line              = line;
   1195         m_name              = nullptr; // will be later overridden in operator*
   1196         m_test_suite        = test_suite.m_test_suite;
   1197         m_description       = test_suite.m_description;
   1198         m_skip              = test_suite.m_skip;
   1199         m_no_breaks         = test_suite.m_no_breaks;
   1200         m_no_output         = test_suite.m_no_output;
   1201         m_may_fail          = test_suite.m_may_fail;
   1202         m_should_fail       = test_suite.m_should_fail;
   1203         m_expected_failures = test_suite.m_expected_failures;
   1204         m_timeout           = test_suite.m_timeout;
   1205 
   1206         m_test        = test;
   1207         m_type        = type;
   1208         m_template_id = template_id;
   1209     }
   1210 
   1211     TestCase::TestCase(const TestCase& other)
   1212             : TestCaseData() {
   1213         *this = other;
   1214     }
   1215 
   1216     DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
   1217     TestCase& TestCase::operator=(const TestCase& other) {
   1218         TestCaseData::operator=(other);
   1219         m_test        = other.m_test;
   1220         m_type        = other.m_type;
   1221         m_template_id = other.m_template_id;
   1222         m_full_name   = other.m_full_name;
   1223 
   1224         if(m_template_id != -1)
   1225             m_name = m_full_name.c_str();
   1226         return *this;
   1227     }
   1228     DOCTEST_MSVC_SUPPRESS_WARNING_POP
   1229 
   1230     TestCase& TestCase::operator*(const char* in) {
   1231         m_name = in;
   1232         // make a new name with an appended type for templated test case
   1233         if(m_template_id != -1) {
   1234             m_full_name = String(m_name) + "<" + m_type + ">";
   1235             // redirect the name to point to the newly constructed full name
   1236             m_name = m_full_name.c_str();
   1237         }
   1238         return *this;
   1239     }
   1240 
   1241     bool TestCase::operator<(const TestCase& other) const {
   1242         // this will be used only to differentiate between test cases - not relevant for sorting
   1243         if(m_line != other.m_line)
   1244             return m_line < other.m_line;
   1245         const int name_cmp = strcmp(m_name, other.m_name);
   1246         if(name_cmp != 0)
   1247             return name_cmp < 0;
   1248         const int file_cmp = m_file.compare(other.m_file);
   1249         if(file_cmp != 0)
   1250             return file_cmp < 0;
   1251         return m_template_id < other.m_template_id;
   1252     }
   1253 
   1254     // all the registered tests
   1255     std::set<TestCase>& getRegisteredTests() {
   1256         static std::set<TestCase> data;
   1257         return data;
   1258     }
   1259 } // namespace detail
   1260 namespace {
   1261     using namespace detail;
   1262     // for sorting tests by file/line
   1263     bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) {
   1264         // this is needed because MSVC gives different case for drive letters
   1265         // for __FILE__ when evaluated in a header and a source file
   1266         const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC));
   1267         if(res != 0)
   1268             return res < 0;
   1269         if(lhs->m_line != rhs->m_line)
   1270             return lhs->m_line < rhs->m_line;
   1271         return lhs->m_template_id < rhs->m_template_id;
   1272     }
   1273 
   1274     // for sorting tests by suite/file/line
   1275     bool suiteOrderComparator(const TestCase* lhs, const TestCase* rhs) {
   1276         const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite);
   1277         if(res != 0)
   1278             return res < 0;
   1279         return fileOrderComparator(lhs, rhs);
   1280     }
   1281 
   1282     // for sorting tests by name/suite/file/line
   1283     bool nameOrderComparator(const TestCase* lhs, const TestCase* rhs) {
   1284         const int res = std::strcmp(lhs->m_name, rhs->m_name);
   1285         if(res != 0)
   1286             return res < 0;
   1287         return suiteOrderComparator(lhs, rhs);
   1288     }
   1289 
   1290     DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
   1291     void color_to_stream(std::ostream& s, Color::Enum code) {
   1292         static_cast<void>(s);    // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
   1293         static_cast<void>(code); // for DOCTEST_CONFIG_COLORS_NONE
   1294 #ifdef DOCTEST_CONFIG_COLORS_ANSI
   1295         if(g_no_colors ||
   1296            (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false))
   1297             return;
   1298 
   1299         auto col = "";
   1300         // clang-format off
   1301             switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement
   1302                 case Color::Red:         col = "[0;31m"; break;
   1303                 case Color::Green:       col = "[0;32m"; break;
   1304                 case Color::Blue:        col = "[0;34m"; break;
   1305                 case Color::Cyan:        col = "[0;36m"; break;
   1306                 case Color::Yellow:      col = "[0;33m"; break;
   1307                 case Color::Grey:        col = "[1;30m"; break;
   1308                 case Color::LightGrey:   col = "[0;37m"; break;
   1309                 case Color::BrightRed:   col = "[1;31m"; break;
   1310                 case Color::BrightGreen: col = "[1;32m"; break;
   1311                 case Color::BrightWhite: col = "[1;37m"; break;
   1312                 case Color::Bright: // invalid
   1313                 case Color::None:
   1314                 case Color::White:
   1315                 default:                 col = "[0m";
   1316             }
   1317         // clang-format on
   1318         s << "\033" << col;
   1319 #endif // DOCTEST_CONFIG_COLORS_ANSI
   1320 
   1321 #ifdef DOCTEST_CONFIG_COLORS_WINDOWS
   1322         if(g_no_colors ||
   1323            (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false))
   1324             return;
   1325 
   1326         static struct ConsoleHelper {
   1327             HANDLE stdoutHandle;
   1328             WORD   origFgAttrs;
   1329             WORD   origBgAttrs;
   1330 
   1331             ConsoleHelper() {
   1332                 stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
   1333                 CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
   1334                 GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo);
   1335                 origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED |
   1336                     BACKGROUND_BLUE | BACKGROUND_INTENSITY);
   1337                 origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED |
   1338                     FOREGROUND_BLUE | FOREGROUND_INTENSITY);
   1339             }
   1340         } ch;
   1341 
   1342 #define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs)
   1343 
   1344         // clang-format off
   1345         switch (code) {
   1346             case Color::White:       DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
   1347             case Color::Red:         DOCTEST_SET_ATTR(FOREGROUND_RED);                                      break;
   1348             case Color::Green:       DOCTEST_SET_ATTR(FOREGROUND_GREEN);                                    break;
   1349             case Color::Blue:        DOCTEST_SET_ATTR(FOREGROUND_BLUE);                                     break;
   1350             case Color::Cyan:        DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN);                  break;
   1351             case Color::Yellow:      DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN);                   break;
   1352             case Color::Grey:        DOCTEST_SET_ATTR(0);                                                   break;
   1353             case Color::LightGrey:   DOCTEST_SET_ATTR(FOREGROUND_INTENSITY);                                break;
   1354             case Color::BrightRed:   DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED);               break;
   1355             case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN);             break;
   1356             case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
   1357             case Color::None:
   1358             case Color::Bright: // invalid
   1359             default:                 DOCTEST_SET_ATTR(ch.origFgAttrs);
   1360         }
   1361             // clang-format on
   1362 #endif // DOCTEST_CONFIG_COLORS_WINDOWS
   1363     }
   1364     DOCTEST_CLANG_SUPPRESS_WARNING_POP
   1365 
   1366     std::vector<const IExceptionTranslator*>& getExceptionTranslators() {
   1367         static std::vector<const IExceptionTranslator*> data;
   1368         return data;
   1369     }
   1370 
   1371     String translateActiveException() {
   1372 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
   1373         String res;
   1374         auto&  translators = getExceptionTranslators();
   1375         for(auto& curr : translators)
   1376             if(curr->translate(res))
   1377                 return res;
   1378         // clang-format off
   1379         DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value")
   1380         try {
   1381             throw;
   1382         } catch(std::exception& ex) {
   1383             return ex.what();
   1384         } catch(std::string& msg) {
   1385             return msg.c_str();
   1386         } catch(const char* msg) {
   1387             return msg;
   1388         } catch(...) {
   1389             return "unknown exception";
   1390         }
   1391         DOCTEST_GCC_SUPPRESS_WARNING_POP
   1392 // clang-format on
   1393 #else  // DOCTEST_CONFIG_NO_EXCEPTIONS
   1394         return "";
   1395 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
   1396     }
   1397 } // namespace
   1398 
   1399 namespace detail {
   1400     // used by the macros for registering tests
   1401     int regTest(const TestCase& tc) {
   1402         getRegisteredTests().insert(tc);
   1403         return 0;
   1404     }
   1405 
   1406     // sets the current test suite
   1407     int setTestSuite(const TestSuite& ts) {
   1408         doctest_detail_test_suite_ns::getCurrentTestSuite() = ts;
   1409         return 0;
   1410     }
   1411 
   1412 #ifdef DOCTEST_IS_DEBUGGER_ACTIVE
   1413     bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); }
   1414 #else // DOCTEST_IS_DEBUGGER_ACTIVE
   1415 #ifdef DOCTEST_PLATFORM_LINUX
   1416     class ErrnoGuard {
   1417     public:
   1418         ErrnoGuard() : m_oldErrno(errno) {}
   1419         ~ErrnoGuard() { errno = m_oldErrno; }
   1420     private:
   1421         int m_oldErrno;
   1422     };
   1423     // See the comments in Catch2 for the reasoning behind this implementation:
   1424     // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102
   1425     bool isDebuggerActive() {
   1426         ErrnoGuard guard;
   1427         std::ifstream in("/proc/self/status");
   1428         for(std::string line; std::getline(in, line);) {
   1429             static const int PREFIX_LEN = 11;
   1430             if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) {
   1431                 return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
   1432             }
   1433         }
   1434         return false;
   1435     }
   1436 #elif defined(DOCTEST_PLATFORM_MAC)
   1437     // The following function is taken directly from the following technical note:
   1438     // https://developer.apple.com/library/archive/qa/qa1361/_index.html
   1439     // Returns true if the current process is being debugged (either
   1440     // running under the debugger or has a debugger attached post facto).
   1441     bool isDebuggerActive() {
   1442         int        mib[4];
   1443         kinfo_proc info;
   1444         size_t     size;
   1445         // Initialize the flags so that, if sysctl fails for some bizarre
   1446         // reason, we get a predictable result.
   1447         info.kp_proc.p_flag = 0;
   1448         // Initialize mib, which tells sysctl the info we want, in this case
   1449         // we're looking for information about a specific process ID.
   1450         mib[0] = CTL_KERN;
   1451         mib[1] = KERN_PROC;
   1452         mib[2] = KERN_PROC_PID;
   1453         mib[3] = getpid();
   1454         // Call sysctl.
   1455         size = sizeof(info);
   1456         if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) {
   1457             std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n";
   1458             return false;
   1459         }
   1460         // We're being debugged if the P_TRACED flag is set.
   1461         return ((info.kp_proc.p_flag & P_TRACED) != 0);
   1462     }
   1463 #elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__)
   1464     bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; }
   1465 #else
   1466     bool isDebuggerActive() { return false; }
   1467 #endif // Platform
   1468 #endif // DOCTEST_IS_DEBUGGER_ACTIVE
   1469 
   1470     void registerExceptionTranslatorImpl(const IExceptionTranslator* et) {
   1471         if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) ==
   1472            getExceptionTranslators().end())
   1473             getExceptionTranslators().push_back(et);
   1474     }
   1475 
   1476     DOCTEST_THREAD_LOCAL std::vector<IContextScope*> g_infoContexts; // for logging with INFO()
   1477 
   1478     ContextScopeBase::ContextScopeBase() {
   1479         g_infoContexts.push_back(this);
   1480     }
   1481 
   1482     ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) noexcept {
   1483         if (other.need_to_destroy) {
   1484             other.destroy();
   1485         }
   1486         other.need_to_destroy = false;
   1487         g_infoContexts.push_back(this);
   1488     }
   1489 
   1490     DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17
   1491     DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
   1492     DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
   1493 
   1494     // destroy cannot be inlined into the destructor because that would mean calling stringify after
   1495     // ContextScope has been destroyed (base class destructors run after derived class destructors).
   1496     // Instead, ContextScope calls this method directly from its destructor.
   1497     void ContextScopeBase::destroy() {
   1498 #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
   1499         if(std::uncaught_exceptions() > 0) {
   1500 #else
   1501         if(std::uncaught_exception()) {
   1502 #endif
   1503             std::ostringstream s;
   1504             this->stringify(&s);
   1505             g_cs->stringifiedContexts.push_back(s.str().c_str());
   1506         }
   1507         g_infoContexts.pop_back();
   1508     }
   1509 
   1510     DOCTEST_CLANG_SUPPRESS_WARNING_POP
   1511     DOCTEST_GCC_SUPPRESS_WARNING_POP
   1512     DOCTEST_MSVC_SUPPRESS_WARNING_POP
   1513 } // namespace detail
   1514 namespace {
   1515     using namespace detail;
   1516 
   1517 #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
   1518     struct FatalConditionHandler
   1519     {
   1520         static void reset() {}
   1521         static void allocateAltStackMem() {}
   1522         static void freeAltStackMem() {}
   1523     };
   1524 #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
   1525 
   1526     void reportFatal(const std::string&);
   1527 
   1528 #ifdef DOCTEST_PLATFORM_WINDOWS
   1529 
   1530     struct SignalDefs
   1531     {
   1532         DWORD id;
   1533         const char* name;
   1534     };
   1535     // There is no 1-1 mapping between signals and windows exceptions.
   1536     // Windows can easily distinguish between SO and SigSegV,
   1537     // but SigInt, SigTerm, etc are handled differently.
   1538     SignalDefs signalDefs[] = {
   1539             {static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),
   1540              "SIGILL - Illegal instruction signal"},
   1541             {static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"},
   1542             {static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION),
   1543              "SIGSEGV - Segmentation violation signal"},
   1544             {static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"},
   1545     };
   1546 
   1547     struct FatalConditionHandler
   1548     {
   1549         static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
   1550             // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
   1551             // console just once no matter how many threads have crashed.
   1552             DOCTEST_DECLARE_STATIC_MUTEX(mutex)
   1553             static bool execute = true;
   1554             {
   1555                 DOCTEST_LOCK_MUTEX(mutex)
   1556                 if(execute) {
   1557                     bool reported = false;
   1558                     for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
   1559                         if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
   1560                             reportFatal(signalDefs[i].name);
   1561                             reported = true;
   1562                             break;
   1563                         }
   1564                     }
   1565                     if(reported == false)
   1566                         reportFatal("Unhandled SEH exception caught");
   1567                     if(isDebuggerActive() && !g_cs->no_breaks)
   1568                         DOCTEST_BREAK_INTO_DEBUGGER();
   1569                 }
   1570                 execute = false;
   1571             }
   1572             std::exit(EXIT_FAILURE);
   1573         }
   1574 
   1575         static void allocateAltStackMem() {}
   1576         static void freeAltStackMem() {}
   1577 
   1578         FatalConditionHandler() {
   1579             isSet = true;
   1580             // 32k seems enough for doctest to handle stack overflow,
   1581             // but the value was found experimentally, so there is no strong guarantee
   1582             guaranteeSize = 32 * 1024;
   1583             // Register an unhandled exception filter
   1584             previousTop = SetUnhandledExceptionFilter(handleException);
   1585             // Pass in guarantee size to be filled
   1586             SetThreadStackGuarantee(&guaranteeSize);
   1587 
   1588             // On Windows uncaught exceptions from another thread, exceptions from
   1589             // destructors, or calls to std::terminate are not a SEH exception
   1590 
   1591             // The terminal handler gets called when:
   1592             // - std::terminate is called FROM THE TEST RUNNER THREAD
   1593             // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD
   1594             original_terminate_handler = std::get_terminate();
   1595             std::set_terminate([]() DOCTEST_NOEXCEPT {
   1596                 reportFatal("Terminate handler called");
   1597                 if(isDebuggerActive() && !g_cs->no_breaks)
   1598                     DOCTEST_BREAK_INTO_DEBUGGER();
   1599                 std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well
   1600             });
   1601 
   1602             // SIGABRT is raised when:
   1603             // - std::terminate is called FROM A DIFFERENT THREAD
   1604             // - an exception is thrown from a destructor FROM A DIFFERENT THREAD
   1605             // - an uncaught exception is thrown FROM A DIFFERENT THREAD
   1606             prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT {
   1607                 if(signal == SIGABRT) {
   1608                     reportFatal("SIGABRT - Abort (abnormal termination) signal");
   1609                     if(isDebuggerActive() && !g_cs->no_breaks)
   1610                         DOCTEST_BREAK_INTO_DEBUGGER();
   1611                     std::exit(EXIT_FAILURE);
   1612                 }
   1613             });
   1614 
   1615             // The following settings are taken from google test, and more
   1616             // specifically from UnitTest::Run() inside of gtest.cc
   1617 
   1618             // the user does not want to see pop-up dialogs about crashes
   1619             prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
   1620                                              SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
   1621             // This forces the abort message to go to stderr in all circumstances.
   1622             prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR);
   1623             // In the debug version, Visual Studio pops up a separate dialog
   1624             // offering a choice to debug the aborted program - we want to disable that.
   1625             prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
   1626             // In debug mode, the Windows CRT can crash with an assertion over invalid
   1627             // input (e.g. passing an invalid file descriptor). The default handling
   1628             // for these assertions is to pop up a dialog and wait for user input.
   1629             // Instead ask the CRT to dump such assertions to stderr non-interactively.
   1630             prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
   1631             prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
   1632         }
   1633 
   1634         static void reset() {
   1635             if(isSet) {
   1636                 // Unregister handler and restore the old guarantee
   1637                 SetUnhandledExceptionFilter(previousTop);
   1638                 SetThreadStackGuarantee(&guaranteeSize);
   1639                 std::set_terminate(original_terminate_handler);
   1640                 std::signal(SIGABRT, prev_sigabrt_handler);
   1641                 SetErrorMode(prev_error_mode_1);
   1642                 _set_error_mode(prev_error_mode_2);
   1643                 _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
   1644                 static_cast<void>(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode));
   1645                 static_cast<void>(_CrtSetReportFile(_CRT_ASSERT, prev_report_file));
   1646                 isSet = false;
   1647             }
   1648         }
   1649 
   1650         ~FatalConditionHandler() { reset(); }
   1651 
   1652     private:
   1653         static UINT         prev_error_mode_1;
   1654         static int          prev_error_mode_2;
   1655         static unsigned int prev_abort_behavior;
   1656         static int          prev_report_mode;
   1657         static _HFILE       prev_report_file;
   1658         static void (DOCTEST_CDECL *prev_sigabrt_handler)(int);
   1659         static std::terminate_handler original_terminate_handler;
   1660         static bool isSet;
   1661         static ULONG guaranteeSize;
   1662         static LPTOP_LEVEL_EXCEPTION_FILTER previousTop;
   1663     };
   1664 
   1665     UINT         FatalConditionHandler::prev_error_mode_1;
   1666     int          FatalConditionHandler::prev_error_mode_2;
   1667     unsigned int FatalConditionHandler::prev_abort_behavior;
   1668     int          FatalConditionHandler::prev_report_mode;
   1669     _HFILE       FatalConditionHandler::prev_report_file;
   1670     void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int);
   1671     std::terminate_handler FatalConditionHandler::original_terminate_handler;
   1672     bool FatalConditionHandler::isSet = false;
   1673     ULONG FatalConditionHandler::guaranteeSize = 0;
   1674     LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr;
   1675 
   1676 #else // DOCTEST_PLATFORM_WINDOWS
   1677 
   1678     struct SignalDefs
   1679     {
   1680         int         id;
   1681         const char* name;
   1682     };
   1683     SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"},
   1684                                {SIGILL, "SIGILL - Illegal instruction signal"},
   1685                                {SIGFPE, "SIGFPE - Floating point error signal"},
   1686                                {SIGSEGV, "SIGSEGV - Segmentation violation signal"},
   1687                                {SIGTERM, "SIGTERM - Termination request signal"},
   1688                                {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}};
   1689 
   1690     struct FatalConditionHandler
   1691     {
   1692         static bool             isSet;
   1693         static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)];
   1694         static stack_t          oldSigStack;
   1695         static size_t           altStackSize;
   1696         static char*            altStackMem;
   1697 
   1698         static void handleSignal(int sig) {
   1699             const char* name = "<unknown signal>";
   1700             for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
   1701                 SignalDefs& def = signalDefs[i];
   1702                 if(sig == def.id) {
   1703                     name = def.name;
   1704                     break;
   1705                 }
   1706             }
   1707             reset();
   1708             reportFatal(name);
   1709             raise(sig);
   1710         }
   1711 
   1712         static void allocateAltStackMem() {
   1713             altStackMem = new char[altStackSize];
   1714         }
   1715 
   1716         static void freeAltStackMem() {
   1717             delete[] altStackMem;
   1718         }
   1719 
   1720         FatalConditionHandler() {
   1721             isSet = true;
   1722             stack_t sigStack;
   1723             sigStack.ss_sp    = altStackMem;
   1724             sigStack.ss_size  = altStackSize;
   1725             sigStack.ss_flags = 0;
   1726             sigaltstack(&sigStack, &oldSigStack);
   1727             struct sigaction sa = {};
   1728             sa.sa_handler       = handleSignal;
   1729             sa.sa_flags         = SA_ONSTACK;
   1730             for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
   1731                 sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
   1732             }
   1733         }
   1734 
   1735         ~FatalConditionHandler() { reset(); }
   1736         static void reset() {
   1737             if(isSet) {
   1738                 // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
   1739                 for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
   1740                     sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
   1741                 }
   1742                 // Return the old stack
   1743                 sigaltstack(&oldSigStack, nullptr);
   1744                 isSet = false;
   1745             }
   1746         }
   1747     };
   1748 
   1749     bool             FatalConditionHandler::isSet = false;
   1750     struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {};
   1751     stack_t          FatalConditionHandler::oldSigStack = {};
   1752     size_t           FatalConditionHandler::altStackSize = 4 * SIGSTKSZ;
   1753     char*            FatalConditionHandler::altStackMem = nullptr;
   1754 
   1755 #endif // DOCTEST_PLATFORM_WINDOWS
   1756 #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
   1757 
   1758 } // namespace
   1759 
   1760 namespace {
   1761     using namespace detail;
   1762 
   1763 #ifdef DOCTEST_PLATFORM_WINDOWS
   1764 #define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text)
   1765 #else
   1766     // TODO: integration with XCode and other IDEs
   1767 #define DOCTEST_OUTPUT_DEBUG_STRING(text)
   1768 #endif // Platform
   1769 
   1770     void addAssert(assertType::Enum at) {
   1771         if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional
   1772             g_cs->numAssertsCurrentTest_atomic++;
   1773     }
   1774 
   1775     void addFailedAssert(assertType::Enum at) {
   1776         if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional
   1777             g_cs->numAssertsFailedCurrentTest_atomic++;
   1778     }
   1779 
   1780 #if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH)
   1781     void reportFatal(const std::string& message) {
   1782         g_cs->failure_flags |= TestCaseFailureReason::Crash;
   1783 
   1784         DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true});
   1785 
   1786         while (g_cs->subcaseStack.size()) {
   1787             g_cs->subcaseStack.pop_back();
   1788             DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
   1789         }
   1790 
   1791         g_cs->finalizeTestCaseData();
   1792 
   1793         DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs);
   1794 
   1795         DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs);
   1796     }
   1797 #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
   1798 } // namespace
   1799 
   1800 AssertData::AssertData(assertType::Enum at, const char* file, int line, const char* expr,
   1801     const char* exception_type, const StringContains& exception_string)
   1802     : m_test_case(g_cs->currentTest), m_at(at), m_file(file), m_line(line), m_expr(expr),
   1803     m_failed(true), m_threw(false), m_threw_as(false), m_exception_type(exception_type),
   1804     m_exception_string(exception_string) {
   1805 #if DOCTEST_MSVC
   1806     if (m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC
   1807         ++m_expr;
   1808 #endif // MSVC
   1809 }
   1810 
   1811 namespace detail {
   1812     ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
   1813                                  const char* exception_type, const String& exception_string)
   1814         : AssertData(at, file, line, expr, exception_type, exception_string) { }
   1815 
   1816     ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
   1817         const char* exception_type, const Contains& exception_string)
   1818         : AssertData(at, file, line, expr, exception_type, exception_string) { }
   1819 
   1820     void ResultBuilder::setResult(const Result& res) {
   1821         m_decomp = res.m_decomp;
   1822         m_failed = !res.m_passed;
   1823     }
   1824 
   1825     void ResultBuilder::translateException() {
   1826         m_threw     = true;
   1827         m_exception = translateActiveException();
   1828     }
   1829 
   1830     bool ResultBuilder::log() {
   1831         if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
   1832             m_failed = !m_threw;
   1833         } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT
   1834             m_failed = !m_threw_as || !m_exception_string.check(m_exception);
   1835         } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
   1836             m_failed = !m_threw_as;
   1837         } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
   1838             m_failed = !m_exception_string.check(m_exception);
   1839         } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
   1840             m_failed = m_threw;
   1841         }
   1842 
   1843         if(m_exception.size())
   1844             m_exception = "\"" + m_exception + "\"";
   1845 
   1846         if(is_running_in_test) {
   1847             addAssert(m_at);
   1848             DOCTEST_ITERATE_THROUGH_REPORTERS(log_assert, *this);
   1849 
   1850             if(m_failed)
   1851                 addFailedAssert(m_at);
   1852         } else if(m_failed) {
   1853             failed_out_of_a_testing_context(*this);
   1854         }
   1855 
   1856         return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks &&
   1857             (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
   1858     }
   1859 
   1860     void ResultBuilder::react() const {
   1861         if(m_failed && checkIfShouldThrow(m_at))
   1862             throwException();
   1863     }
   1864 
   1865     void failed_out_of_a_testing_context(const AssertData& ad) {
   1866         if(g_cs->ah)
   1867             g_cs->ah(ad);
   1868         else
   1869             std::abort();
   1870     }
   1871 
   1872     bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
   1873                        const Result& result) {
   1874         bool failed = !result.m_passed;
   1875 
   1876         // ###################################################################################
   1877         // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
   1878         // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
   1879         // ###################################################################################
   1880         DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp);
   1881         DOCTEST_ASSERT_IN_TESTS(result.m_decomp);
   1882         return !failed;
   1883     }
   1884 
   1885     MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) {
   1886         m_stream   = tlssPush();
   1887         m_file     = file;
   1888         m_line     = line;
   1889         m_severity = severity;
   1890     }
   1891 
   1892     MessageBuilder::~MessageBuilder() {
   1893         if (!logged)
   1894             tlssPop();
   1895     }
   1896 
   1897     DOCTEST_DEFINE_INTERFACE(IExceptionTranslator)
   1898 
   1899     bool MessageBuilder::log() {
   1900         if (!logged) {
   1901             m_string = tlssPop();
   1902             logged = true;
   1903         }
   1904         
   1905         DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this);
   1906 
   1907         const bool isWarn = m_severity & assertType::is_warn;
   1908 
   1909         // warn is just a message in this context so we don't treat it as an assert
   1910         if(!isWarn) {
   1911             addAssert(m_severity);
   1912             addFailedAssert(m_severity);
   1913         }
   1914 
   1915         return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn &&
   1916             (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
   1917     }
   1918 
   1919     void MessageBuilder::react() {
   1920         if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional
   1921             throwException();
   1922     }
   1923 } // namespace detail
   1924 namespace {
   1925     using namespace detail;
   1926 
   1927     // clang-format off
   1928 
   1929 // =================================================================================================
   1930 // The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp
   1931 // This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched.
   1932 // =================================================================================================
   1933 
   1934     class XmlEncode {
   1935     public:
   1936         enum ForWhat { ForTextNodes, ForAttributes };
   1937 
   1938         XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
   1939 
   1940         void encodeTo( std::ostream& os ) const;
   1941 
   1942         friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
   1943 
   1944     private:
   1945         std::string m_str;
   1946         ForWhat m_forWhat;
   1947     };
   1948 
   1949     class XmlWriter {
   1950     public:
   1951 
   1952         class ScopedElement {
   1953         public:
   1954             ScopedElement( XmlWriter* writer );
   1955 
   1956             ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT;
   1957             ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT;
   1958 
   1959             ~ScopedElement();
   1960 
   1961             ScopedElement& writeText( std::string const& text, bool indent = true );
   1962 
   1963             template<typename T>
   1964             ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
   1965                 m_writer->writeAttribute( name, attribute );
   1966                 return *this;
   1967             }
   1968 
   1969         private:
   1970             mutable XmlWriter* m_writer = nullptr;
   1971         };
   1972 
   1973         XmlWriter( std::ostream& os = std::cout );
   1974         ~XmlWriter();
   1975 
   1976         XmlWriter( XmlWriter const& ) = delete;
   1977         XmlWriter& operator=( XmlWriter const& ) = delete;
   1978 
   1979         XmlWriter& startElement( std::string const& name );
   1980 
   1981         ScopedElement scopedElement( std::string const& name );
   1982 
   1983         XmlWriter& endElement();
   1984 
   1985         XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
   1986 
   1987         XmlWriter& writeAttribute( std::string const& name, const char* attribute );
   1988 
   1989         XmlWriter& writeAttribute( std::string const& name, bool attribute );
   1990 
   1991         template<typename T>
   1992         XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
   1993         std::stringstream rss;
   1994             rss << attribute;
   1995             return writeAttribute( name, rss.str() );
   1996         }
   1997 
   1998         XmlWriter& writeText( std::string const& text, bool indent = true );
   1999 
   2000         //XmlWriter& writeComment( std::string const& text );
   2001 
   2002         //void writeStylesheetRef( std::string const& url );
   2003 
   2004         //XmlWriter& writeBlankLine();
   2005 
   2006         void ensureTagClosed();
   2007 
   2008         void writeDeclaration();
   2009 
   2010     private:
   2011 
   2012         void newlineIfNecessary();
   2013 
   2014         bool m_tagIsOpen = false;
   2015         bool m_needsNewline = false;
   2016         std::vector<std::string> m_tags;
   2017         std::string m_indent;
   2018         std::ostream& m_os;
   2019     };
   2020 
   2021 // =================================================================================================
   2022 // The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp
   2023 // This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched.
   2024 // =================================================================================================
   2025 
   2026 using uchar = unsigned char;
   2027 
   2028 namespace {
   2029 
   2030     size_t trailingBytes(unsigned char c) {
   2031         if ((c & 0xE0) == 0xC0) {
   2032             return 2;
   2033         }
   2034         if ((c & 0xF0) == 0xE0) {
   2035             return 3;
   2036         }
   2037         if ((c & 0xF8) == 0xF0) {
   2038             return 4;
   2039         }
   2040         DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
   2041     }
   2042 
   2043     uint32_t headerValue(unsigned char c) {
   2044         if ((c & 0xE0) == 0xC0) {
   2045             return c & 0x1F;
   2046         }
   2047         if ((c & 0xF0) == 0xE0) {
   2048             return c & 0x0F;
   2049         }
   2050         if ((c & 0xF8) == 0xF0) {
   2051             return c & 0x07;
   2052         }
   2053         DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
   2054     }
   2055 
   2056     void hexEscapeChar(std::ostream& os, unsigned char c) {
   2057         std::ios_base::fmtflags f(os.flags());
   2058         os << "\\x"
   2059             << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
   2060             << static_cast<int>(c);
   2061         os.flags(f);
   2062     }
   2063 
   2064 } // anonymous namespace
   2065 
   2066     XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
   2067     :   m_str( str ),
   2068         m_forWhat( forWhat )
   2069     {}
   2070 
   2071     void XmlEncode::encodeTo( std::ostream& os ) const {
   2072         // Apostrophe escaping not necessary if we always use " to write attributes
   2073         // (see: https://www.w3.org/TR/xml/#syntax)
   2074 
   2075         for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
   2076             uchar c = m_str[idx];
   2077             switch (c) {
   2078             case '<':   os << "&lt;"; break;
   2079             case '&':   os << "&amp;"; break;
   2080 
   2081             case '>':
   2082                 // See: https://www.w3.org/TR/xml/#syntax
   2083                 if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
   2084                     os << "&gt;";
   2085                 else
   2086                     os << c;
   2087                 break;
   2088 
   2089             case '\"':
   2090                 if (m_forWhat == ForAttributes)
   2091                     os << "&quot;";
   2092                 else
   2093                     os << c;
   2094                 break;
   2095 
   2096             default:
   2097                 // Check for control characters and invalid utf-8
   2098 
   2099                 // Escape control characters in standard ascii
   2100                 // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
   2101                 if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
   2102                     hexEscapeChar(os, c);
   2103                     break;
   2104                 }
   2105 
   2106                 // Plain ASCII: Write it to stream
   2107                 if (c < 0x7F) {
   2108                     os << c;
   2109                     break;
   2110                 }
   2111 
   2112                 // UTF-8 territory
   2113                 // Check if the encoding is valid and if it is not, hex escape bytes.
   2114                 // Important: We do not check the exact decoded values for validity, only the encoding format
   2115                 // First check that this bytes is a valid lead byte:
   2116                 // This means that it is not encoded as 1111 1XXX
   2117                 // Or as 10XX XXXX
   2118                 if (c <  0xC0 ||
   2119                     c >= 0xF8) {
   2120                     hexEscapeChar(os, c);
   2121                     break;
   2122                 }
   2123 
   2124                 auto encBytes = trailingBytes(c);
   2125                 // Are there enough bytes left to avoid accessing out-of-bounds memory?
   2126                 if (idx + encBytes - 1 >= m_str.size()) {
   2127                     hexEscapeChar(os, c);
   2128                     break;
   2129                 }
   2130                 // The header is valid, check data
   2131                 // The next encBytes bytes must together be a valid utf-8
   2132                 // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
   2133                 bool valid = true;
   2134                 uint32_t value = headerValue(c);
   2135                 for (std::size_t n = 1; n < encBytes; ++n) {
   2136                     uchar nc = m_str[idx + n];
   2137                     valid &= ((nc & 0xC0) == 0x80);
   2138                     value = (value << 6) | (nc & 0x3F);
   2139                 }
   2140 
   2141                 if (
   2142                     // Wrong bit pattern of following bytes
   2143                     (!valid) ||
   2144                     // Overlong encodings
   2145                     (value < 0x80) ||
   2146                     (                 value < 0x800   && encBytes > 2) || // removed "0x80 <= value &&" because redundant
   2147                     (0x800 < value && value < 0x10000 && encBytes > 3) ||
   2148                     // Encoded value out of range
   2149                     (value >= 0x110000)
   2150                     ) {
   2151                     hexEscapeChar(os, c);
   2152                     break;
   2153                 }
   2154 
   2155                 // If we got here, this is in fact a valid(ish) utf-8 sequence
   2156                 for (std::size_t n = 0; n < encBytes; ++n) {
   2157                     os << m_str[idx + n];
   2158                 }
   2159                 idx += encBytes - 1;
   2160                 break;
   2161             }
   2162         }
   2163     }
   2164 
   2165     std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
   2166         xmlEncode.encodeTo( os );
   2167         return os;
   2168     }
   2169 
   2170     XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
   2171     :   m_writer( writer )
   2172     {}
   2173 
   2174     XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT
   2175     :   m_writer( other.m_writer ){
   2176         other.m_writer = nullptr;
   2177     }
   2178     XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT {
   2179         if ( m_writer ) {
   2180             m_writer->endElement();
   2181         }
   2182         m_writer = other.m_writer;
   2183         other.m_writer = nullptr;
   2184         return *this;
   2185     }
   2186 
   2187 
   2188     XmlWriter::ScopedElement::~ScopedElement() {
   2189         if( m_writer )
   2190             m_writer->endElement();
   2191     }
   2192 
   2193     XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
   2194         m_writer->writeText( text, indent );
   2195         return *this;
   2196     }
   2197 
   2198     XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
   2199     {
   2200         // writeDeclaration(); // called explicitly by the reporters that use the writer class - see issue #627
   2201     }
   2202 
   2203     XmlWriter::~XmlWriter() {
   2204         while( !m_tags.empty() )
   2205             endElement();
   2206     }
   2207 
   2208     XmlWriter& XmlWriter::startElement( std::string const& name ) {
   2209         ensureTagClosed();
   2210         newlineIfNecessary();
   2211         m_os << m_indent << '<' << name;
   2212         m_tags.push_back( name );
   2213         m_indent += "  ";
   2214         m_tagIsOpen = true;
   2215         return *this;
   2216     }
   2217 
   2218     XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
   2219         ScopedElement scoped( this );
   2220         startElement( name );
   2221         return scoped;
   2222     }
   2223 
   2224     XmlWriter& XmlWriter::endElement() {
   2225         newlineIfNecessary();
   2226         m_indent = m_indent.substr( 0, m_indent.size()-2 );
   2227         if( m_tagIsOpen ) {
   2228             m_os << "/>";
   2229             m_tagIsOpen = false;
   2230         }
   2231         else {
   2232             m_os << m_indent << "</" << m_tags.back() << ">";
   2233         }
   2234         m_os << std::endl;
   2235         m_tags.pop_back();
   2236         return *this;
   2237     }
   2238 
   2239     XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
   2240         if( !name.empty() && !attribute.empty() )
   2241             m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
   2242         return *this;
   2243     }
   2244 
   2245     XmlWriter& XmlWriter::writeAttribute( std::string const& name, const char* attribute ) {
   2246         if( !name.empty() && attribute && attribute[0] != '\0' )
   2247             m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
   2248         return *this;
   2249     }
   2250 
   2251     XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
   2252         m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
   2253         return *this;
   2254     }
   2255 
   2256     XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
   2257         if( !text.empty() ){
   2258             bool tagWasOpen = m_tagIsOpen;
   2259             ensureTagClosed();
   2260             if( tagWasOpen && indent )
   2261                 m_os << m_indent;
   2262             m_os << XmlEncode( text );
   2263             m_needsNewline = true;
   2264         }
   2265         return *this;
   2266     }
   2267 
   2268     //XmlWriter& XmlWriter::writeComment( std::string const& text ) {
   2269     //    ensureTagClosed();
   2270     //    m_os << m_indent << "<!--" << text << "-->";
   2271     //    m_needsNewline = true;
   2272     //    return *this;
   2273     //}
   2274 
   2275     //void XmlWriter::writeStylesheetRef( std::string const& url ) {
   2276     //    m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
   2277     //}
   2278 
   2279     //XmlWriter& XmlWriter::writeBlankLine() {
   2280     //    ensureTagClosed();
   2281     //    m_os << '\n';
   2282     //    return *this;
   2283     //}
   2284 
   2285     void XmlWriter::ensureTagClosed() {
   2286         if( m_tagIsOpen ) {
   2287             m_os << ">" << std::endl;
   2288             m_tagIsOpen = false;
   2289         }
   2290     }
   2291 
   2292     void XmlWriter::writeDeclaration() {
   2293         m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
   2294     }
   2295 
   2296     void XmlWriter::newlineIfNecessary() {
   2297         if( m_needsNewline ) {
   2298             m_os << std::endl;
   2299             m_needsNewline = false;
   2300         }
   2301     }
   2302 
   2303 // =================================================================================================
   2304 // End of copy-pasted code from Catch
   2305 // =================================================================================================
   2306 
   2307     // clang-format on
   2308 
   2309     struct XmlReporter : public IReporter
   2310     {
   2311         XmlWriter xml;
   2312         DOCTEST_DECLARE_MUTEX(mutex)
   2313 
   2314         // caching pointers/references to objects of these types - safe to do
   2315         const ContextOptions& opt;
   2316         const TestCaseData*   tc = nullptr;
   2317 
   2318         XmlReporter(const ContextOptions& co)
   2319                 : xml(*co.cout)
   2320                 , opt(co) {}
   2321 
   2322         void log_contexts() {
   2323             int num_contexts = get_num_active_contexts();
   2324             if(num_contexts) {
   2325                 auto              contexts = get_active_contexts();
   2326                 std::stringstream ss;
   2327                 for(int i = 0; i < num_contexts; ++i) {
   2328                     contexts[i]->stringify(&ss);
   2329                     xml.scopedElement("Info").writeText(ss.str());
   2330                     ss.str("");
   2331                 }
   2332             }
   2333         }
   2334 
   2335         unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; }
   2336 
   2337         void test_case_start_impl(const TestCaseData& in) {
   2338             bool open_ts_tag = false;
   2339             if(tc != nullptr) { // we have already opened a test suite
   2340                 if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) {
   2341                     xml.endElement();
   2342                     open_ts_tag = true;
   2343                 }
   2344             }
   2345             else {
   2346                 open_ts_tag = true; // first test case ==> first test suite
   2347             }
   2348 
   2349             if(open_ts_tag) {
   2350                 xml.startElement("TestSuite");
   2351                 xml.writeAttribute("name", in.m_test_suite);
   2352             }
   2353 
   2354             tc = &in;
   2355             xml.startElement("TestCase")
   2356                     .writeAttribute("name", in.m_name)
   2357                     .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str()))
   2358                     .writeAttribute("line", line(in.m_line))
   2359                     .writeAttribute("description", in.m_description);
   2360 
   2361             if(Approx(in.m_timeout) != 0)
   2362                 xml.writeAttribute("timeout", in.m_timeout);
   2363             if(in.m_may_fail)
   2364                 xml.writeAttribute("may_fail", true);
   2365             if(in.m_should_fail)
   2366                 xml.writeAttribute("should_fail", true);
   2367         }
   2368 
   2369         // =========================================================================================
   2370         // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
   2371         // =========================================================================================
   2372 
   2373         void report_query(const QueryData& in) override {
   2374             test_run_start();
   2375             if(opt.list_reporters) {
   2376                 for(auto& curr : getListeners())
   2377                     xml.scopedElement("Listener")
   2378                             .writeAttribute("priority", curr.first.first)
   2379                             .writeAttribute("name", curr.first.second);
   2380                 for(auto& curr : getReporters())
   2381                     xml.scopedElement("Reporter")
   2382                             .writeAttribute("priority", curr.first.first)
   2383                             .writeAttribute("name", curr.first.second);
   2384             } else if(opt.count || opt.list_test_cases) {
   2385                 for(unsigned i = 0; i < in.num_data; ++i) {
   2386                     xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name)
   2387                         .writeAttribute("testsuite", in.data[i]->m_test_suite)
   2388                         .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str()))
   2389                         .writeAttribute("line", line(in.data[i]->m_line))
   2390                         .writeAttribute("skipped", in.data[i]->m_skip);
   2391                 }
   2392                 xml.scopedElement("OverallResultsTestCases")
   2393                         .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
   2394             } else if(opt.list_test_suites) {
   2395                 for(unsigned i = 0; i < in.num_data; ++i)
   2396                     xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite);
   2397                 xml.scopedElement("OverallResultsTestCases")
   2398                         .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
   2399                 xml.scopedElement("OverallResultsTestSuites")
   2400                         .writeAttribute("unskipped", in.run_stats->numTestSuitesPassingFilters);
   2401             }
   2402             xml.endElement();
   2403         }
   2404 
   2405         void test_run_start() override {
   2406             xml.writeDeclaration();
   2407 
   2408             // remove .exe extension - mainly to have the same output on UNIX and Windows
   2409             std::string binary_name = skipPathFromFilename(opt.binary_name.c_str());
   2410 #ifdef DOCTEST_PLATFORM_WINDOWS
   2411             if(binary_name.rfind(".exe") != std::string::npos)
   2412                 binary_name = binary_name.substr(0, binary_name.length() - 4);
   2413 #endif // DOCTEST_PLATFORM_WINDOWS
   2414 
   2415             xml.startElement("doctest").writeAttribute("binary", binary_name);
   2416             if(opt.no_version == false)
   2417                 xml.writeAttribute("version", DOCTEST_VERSION_STR);
   2418 
   2419             // only the consequential ones (TODO: filters)
   2420             xml.scopedElement("Options")
   2421                     .writeAttribute("order_by", opt.order_by.c_str())
   2422                     .writeAttribute("rand_seed", opt.rand_seed)
   2423                     .writeAttribute("first", opt.first)
   2424                     .writeAttribute("last", opt.last)
   2425                     .writeAttribute("abort_after", opt.abort_after)
   2426                     .writeAttribute("subcase_filter_levels", opt.subcase_filter_levels)
   2427                     .writeAttribute("case_sensitive", opt.case_sensitive)
   2428                     .writeAttribute("no_throw", opt.no_throw)
   2429                     .writeAttribute("no_skip", opt.no_skip);
   2430         }
   2431 
   2432         void test_run_end(const TestRunStats& p) override {
   2433             if(tc) // the TestSuite tag - only if there has been at least 1 test case
   2434                 xml.endElement();
   2435 
   2436             xml.scopedElement("OverallResultsAsserts")
   2437                     .writeAttribute("successes", p.numAsserts - p.numAssertsFailed)
   2438                     .writeAttribute("failures", p.numAssertsFailed);
   2439 
   2440             xml.startElement("OverallResultsTestCases")
   2441                     .writeAttribute("successes",
   2442                                     p.numTestCasesPassingFilters - p.numTestCasesFailed)
   2443                     .writeAttribute("failures", p.numTestCasesFailed);
   2444             if(opt.no_skipped_summary == false)
   2445                 xml.writeAttribute("skipped", p.numTestCases - p.numTestCasesPassingFilters);
   2446             xml.endElement();
   2447 
   2448             xml.endElement();
   2449         }
   2450 
   2451         void test_case_start(const TestCaseData& in) override {
   2452             test_case_start_impl(in);
   2453             xml.ensureTagClosed();
   2454         }
   2455         
   2456         void test_case_reenter(const TestCaseData&) override {}
   2457 
   2458         void test_case_end(const CurrentTestCaseStats& st) override {
   2459             xml.startElement("OverallResultsAsserts")
   2460                     .writeAttribute("successes",
   2461                                     st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest)
   2462                     .writeAttribute("failures", st.numAssertsFailedCurrentTest)
   2463                     .writeAttribute("test_case_success", st.testCaseSuccess);
   2464             if(opt.duration)
   2465                 xml.writeAttribute("duration", st.seconds);
   2466             if(tc->m_expected_failures)
   2467                 xml.writeAttribute("expected_failures", tc->m_expected_failures);
   2468             xml.endElement();
   2469 
   2470             xml.endElement();
   2471         }
   2472 
   2473         void test_case_exception(const TestCaseException& e) override {
   2474             DOCTEST_LOCK_MUTEX(mutex)
   2475 
   2476             xml.scopedElement("Exception")
   2477                     .writeAttribute("crash", e.is_crash)
   2478                     .writeText(e.error_string.c_str());
   2479         }
   2480 
   2481         void subcase_start(const SubcaseSignature& in) override {
   2482             xml.startElement("SubCase")
   2483                     .writeAttribute("name", in.m_name)
   2484                     .writeAttribute("filename", skipPathFromFilename(in.m_file))
   2485                     .writeAttribute("line", line(in.m_line));
   2486             xml.ensureTagClosed();
   2487         }
   2488 
   2489         void subcase_end() override { xml.endElement(); }
   2490 
   2491         void log_assert(const AssertData& rb) override {
   2492             if(!rb.m_failed && !opt.success)
   2493                 return;
   2494 
   2495             DOCTEST_LOCK_MUTEX(mutex)
   2496 
   2497             xml.startElement("Expression")
   2498                     .writeAttribute("success", !rb.m_failed)
   2499                     .writeAttribute("type", assertString(rb.m_at))
   2500                     .writeAttribute("filename", skipPathFromFilename(rb.m_file))
   2501                     .writeAttribute("line", line(rb.m_line));
   2502 
   2503             xml.scopedElement("Original").writeText(rb.m_expr);
   2504 
   2505             if(rb.m_threw)
   2506                 xml.scopedElement("Exception").writeText(rb.m_exception.c_str());
   2507 
   2508             if(rb.m_at & assertType::is_throws_as)
   2509                 xml.scopedElement("ExpectedException").writeText(rb.m_exception_type);
   2510             if(rb.m_at & assertType::is_throws_with)
   2511                 xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string.c_str());
   2512             if((rb.m_at & assertType::is_normal) && !rb.m_threw)
   2513                 xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str());
   2514 
   2515             log_contexts();
   2516 
   2517             xml.endElement();
   2518         }
   2519 
   2520         void log_message(const MessageData& mb) override {
   2521             DOCTEST_LOCK_MUTEX(mutex)
   2522 
   2523             xml.startElement("Message")
   2524                     .writeAttribute("type", failureString(mb.m_severity))
   2525                     .writeAttribute("filename", skipPathFromFilename(mb.m_file))
   2526                     .writeAttribute("line", line(mb.m_line));
   2527 
   2528             xml.scopedElement("Text").writeText(mb.m_string.c_str());
   2529 
   2530             log_contexts();
   2531 
   2532             xml.endElement();
   2533         }
   2534 
   2535         void test_case_skipped(const TestCaseData& in) override {
   2536             if(opt.no_skipped_summary == false) {
   2537                 test_case_start_impl(in);
   2538                 xml.writeAttribute("skipped", "true");
   2539                 xml.endElement();
   2540             }
   2541         }
   2542     };
   2543 
   2544     DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter);
   2545 
   2546     void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) {
   2547         if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==
   2548             0) //!OCLINT bitwise operator in conditional
   2549             s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) "
   2550                 << Color::None;
   2551 
   2552         if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
   2553             s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n";
   2554         } else if((rb.m_at & assertType::is_throws_as) &&
   2555                     (rb.m_at & assertType::is_throws_with)) { //!OCLINT
   2556             s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
   2557                 << rb.m_exception_string.c_str()
   2558                 << "\", " << rb.m_exception_type << " ) " << Color::None;
   2559             if(rb.m_threw) {
   2560                 if(!rb.m_failed) {
   2561                     s << "threw as expected!\n";
   2562                 } else {
   2563                     s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n";
   2564                 }
   2565             } else {
   2566                 s << "did NOT throw at all!\n";
   2567             }
   2568         } else if(rb.m_at &
   2569                     assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
   2570             s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", "
   2571                 << rb.m_exception_type << " ) " << Color::None
   2572                 << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" :
   2573                                                 "threw a DIFFERENT exception: ") :
   2574                                 "did NOT throw at all!")
   2575                 << Color::Cyan << rb.m_exception << "\n";
   2576         } else if(rb.m_at &
   2577                     assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
   2578             s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
   2579                 << rb.m_exception_string.c_str()
   2580                 << "\" ) " << Color::None
   2581                 << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
   2582                                                 "threw a DIFFERENT exception: ") :
   2583                                 "did NOT throw at all!")
   2584                 << Color::Cyan << rb.m_exception << "\n";
   2585         } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
   2586             s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan
   2587                 << rb.m_exception << "\n";
   2588         } else {
   2589             s << (rb.m_threw ? "THREW exception: " :
   2590                                 (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n"));
   2591             if(rb.m_threw)
   2592                 s << rb.m_exception << "\n";
   2593             else
   2594                 s << "  values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n";
   2595         }
   2596     }
   2597 
   2598     // TODO:
   2599     // - log_message()
   2600     // - respond to queries
   2601     // - honor remaining options
   2602     // - more attributes in tags
   2603     struct JUnitReporter : public IReporter
   2604     {
   2605         XmlWriter xml;
   2606         DOCTEST_DECLARE_MUTEX(mutex)
   2607         Timer timer;
   2608         std::vector<String> deepestSubcaseStackNames;
   2609 
   2610         struct JUnitTestCaseData
   2611         {
   2612             static std::string getCurrentTimestamp() {
   2613                 // Beware, this is not reentrant because of backward compatibility issues
   2614                 // Also, UTC only, again because of backward compatibility (%z is C++11)
   2615                 time_t rawtime;
   2616                 std::time(&rawtime);
   2617                 auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
   2618 
   2619                 std::tm timeInfo;
   2620 #ifdef DOCTEST_PLATFORM_WINDOWS
   2621                 gmtime_s(&timeInfo, &rawtime);
   2622 #else // DOCTEST_PLATFORM_WINDOWS
   2623                 gmtime_r(&rawtime, &timeInfo);
   2624 #endif // DOCTEST_PLATFORM_WINDOWS
   2625 
   2626                 char timeStamp[timeStampSize];
   2627                 const char* const fmt = "%Y-%m-%dT%H:%M:%SZ";
   2628 
   2629                 std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
   2630                 return std::string(timeStamp);
   2631             }
   2632 
   2633             struct JUnitTestMessage
   2634             {
   2635                 JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details)
   2636                     : message(_message), type(_type), details(_details) {}
   2637 
   2638                 JUnitTestMessage(const std::string& _message, const std::string& _details)
   2639                     : message(_message), type(), details(_details) {}
   2640 
   2641                 std::string message, type, details;
   2642             };
   2643 
   2644             struct JUnitTestCase
   2645             {
   2646                 JUnitTestCase(const std::string& _classname, const std::string& _name)
   2647                     : classname(_classname), name(_name), time(0), failures() {}
   2648 
   2649                 std::string classname, name;
   2650                 double time;
   2651                 std::vector<JUnitTestMessage> failures, errors;
   2652             };
   2653 
   2654             void add(const std::string& classname, const std::string& name) {
   2655                 testcases.emplace_back(classname, name);
   2656             }
   2657 
   2658             void appendSubcaseNamesToLastTestcase(std::vector<String> nameStack) {
   2659                 for(auto& curr: nameStack)
   2660                     if(curr.size())
   2661                         testcases.back().name += std::string("/") + curr.c_str();
   2662             }
   2663 
   2664             void addTime(double time) {
   2665                 if(time < 1e-4)
   2666                     time = 0;
   2667                 testcases.back().time = time;
   2668                 totalSeconds += time;
   2669             }
   2670 
   2671             void addFailure(const std::string& message, const std::string& type, const std::string& details) {
   2672                 testcases.back().failures.emplace_back(message, type, details);
   2673                 ++totalFailures;
   2674             }
   2675 
   2676             void addError(const std::string& message, const std::string& details) {
   2677                 testcases.back().errors.emplace_back(message, details);
   2678                 ++totalErrors;
   2679             }
   2680 
   2681             std::vector<JUnitTestCase> testcases;
   2682             double totalSeconds = 0;
   2683             int totalErrors = 0, totalFailures = 0;
   2684         };
   2685 
   2686         JUnitTestCaseData testCaseData;
   2687 
   2688         // caching pointers/references to objects of these types - safe to do
   2689         const ContextOptions& opt;
   2690         const TestCaseData*   tc = nullptr;
   2691 
   2692         JUnitReporter(const ContextOptions& co)
   2693                 : xml(*co.cout)
   2694                 , opt(co) {}
   2695 
   2696         unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; }
   2697 
   2698         // =========================================================================================
   2699         // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
   2700         // =========================================================================================
   2701 
   2702         void report_query(const QueryData&) override {
   2703             xml.writeDeclaration();
   2704         }
   2705 
   2706         void test_run_start() override {
   2707             xml.writeDeclaration();
   2708         }
   2709 
   2710         void test_run_end(const TestRunStats& p) override {
   2711             // remove .exe extension - mainly to have the same output on UNIX and Windows
   2712             std::string binary_name = skipPathFromFilename(opt.binary_name.c_str());
   2713 #ifdef DOCTEST_PLATFORM_WINDOWS
   2714             if(binary_name.rfind(".exe") != std::string::npos)
   2715                 binary_name = binary_name.substr(0, binary_name.length() - 4);
   2716 #endif // DOCTEST_PLATFORM_WINDOWS
   2717             xml.startElement("testsuites");
   2718             xml.startElement("testsuite").writeAttribute("name", binary_name)
   2719                     .writeAttribute("errors", testCaseData.totalErrors)
   2720                     .writeAttribute("failures", testCaseData.totalFailures)
   2721                     .writeAttribute("tests", p.numAsserts);
   2722             if(opt.no_time_in_output == false) {
   2723                 xml.writeAttribute("time", testCaseData.totalSeconds);
   2724                 xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp());
   2725             }
   2726             if(opt.no_version == false)
   2727                 xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR);
   2728 
   2729             for(const auto& testCase : testCaseData.testcases) {
   2730                 xml.startElement("testcase")
   2731                     .writeAttribute("classname", testCase.classname)
   2732                     .writeAttribute("name", testCase.name);
   2733                 if(opt.no_time_in_output == false)
   2734                     xml.writeAttribute("time", testCase.time);
   2735                 // This is not ideal, but it should be enough to mimic gtest's junit output.
   2736                 xml.writeAttribute("status", "run");
   2737 
   2738                 for(const auto& failure : testCase.failures) {
   2739                     xml.scopedElement("failure")
   2740                         .writeAttribute("message", failure.message)
   2741                         .writeAttribute("type", failure.type)
   2742                         .writeText(failure.details, false);
   2743                 }
   2744 
   2745                 for(const auto& error : testCase.errors) {
   2746                     xml.scopedElement("error")
   2747                         .writeAttribute("message", error.message)
   2748                         .writeText(error.details);
   2749                 }
   2750 
   2751                 xml.endElement();
   2752             }
   2753             xml.endElement();
   2754             xml.endElement();
   2755         }
   2756 
   2757         void test_case_start(const TestCaseData& in) override {
   2758             testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
   2759             timer.start();
   2760         }
   2761 
   2762         void test_case_reenter(const TestCaseData& in) override {
   2763             testCaseData.addTime(timer.getElapsedSeconds());
   2764             testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
   2765             deepestSubcaseStackNames.clear();
   2766 
   2767             timer.start();
   2768             testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
   2769         }
   2770 
   2771         void test_case_end(const CurrentTestCaseStats&) override {
   2772             testCaseData.addTime(timer.getElapsedSeconds());
   2773             testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
   2774             deepestSubcaseStackNames.clear();
   2775         }
   2776 
   2777         void test_case_exception(const TestCaseException& e) override {
   2778             DOCTEST_LOCK_MUTEX(mutex)
   2779             testCaseData.addError("exception", e.error_string.c_str());
   2780         }
   2781 
   2782         void subcase_start(const SubcaseSignature& in) override {
   2783             deepestSubcaseStackNames.push_back(in.m_name);
   2784         }
   2785 
   2786         void subcase_end() override {}
   2787 
   2788         void log_assert(const AssertData& rb) override {
   2789             if(!rb.m_failed) // report only failures & ignore the `success` option
   2790                 return;
   2791 
   2792             DOCTEST_LOCK_MUTEX(mutex)
   2793 
   2794             std::ostringstream os;
   2795             os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
   2796               << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl;
   2797 
   2798             fulltext_log_assert_to_stream(os, rb);
   2799             log_contexts(os);
   2800             testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str());
   2801         }
   2802 
   2803         void log_message(const MessageData&) override {}
   2804 
   2805         void test_case_skipped(const TestCaseData&) override {}
   2806 
   2807         void log_contexts(std::ostringstream& s) {
   2808             int num_contexts = get_num_active_contexts();
   2809             if(num_contexts) {
   2810                 auto contexts = get_active_contexts();
   2811 
   2812                 s << "  logged: ";
   2813                 for(int i = 0; i < num_contexts; ++i) {
   2814                     s << (i == 0 ? "" : "          ");
   2815                     contexts[i]->stringify(&s);
   2816                     s << std::endl;
   2817                 }
   2818             }
   2819         }
   2820     };
   2821 
   2822     DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter);
   2823 
   2824     struct Whitespace
   2825     {
   2826         int nrSpaces;
   2827         explicit Whitespace(int nr)
   2828                 : nrSpaces(nr) {}
   2829     };
   2830 
   2831     std::ostream& operator<<(std::ostream& out, const Whitespace& ws) {
   2832         if(ws.nrSpaces != 0)
   2833             out << std::setw(ws.nrSpaces) << ' ';
   2834         return out;
   2835     }
   2836 
   2837     struct ConsoleReporter : public IReporter
   2838     {
   2839         std::ostream&                 s;
   2840         bool                          hasLoggedCurrentTestStart;
   2841         std::vector<SubcaseSignature> subcasesStack;
   2842         size_t                        currentSubcaseLevel;
   2843         DOCTEST_DECLARE_MUTEX(mutex)
   2844 
   2845         // caching pointers/references to objects of these types - safe to do
   2846         const ContextOptions& opt;
   2847         const TestCaseData*   tc;
   2848 
   2849         ConsoleReporter(const ContextOptions& co)
   2850                 : s(*co.cout)
   2851                 , opt(co) {}
   2852 
   2853         ConsoleReporter(const ContextOptions& co, std::ostream& ostr)
   2854                 : s(ostr)
   2855                 , opt(co) {}
   2856 
   2857         // =========================================================================================
   2858         // WHAT FOLLOWS ARE HELPERS USED BY THE OVERRIDES OF THE VIRTUAL METHODS OF THE INTERFACE
   2859         // =========================================================================================
   2860 
   2861         void separator_to_stream() {
   2862             s << Color::Yellow
   2863               << "==============================================================================="
   2864                  "\n";
   2865         }
   2866 
   2867         const char* getSuccessOrFailString(bool success, assertType::Enum at,
   2868                                            const char* success_str) {
   2869             if(success)
   2870                 return success_str;
   2871             return failureString(at);
   2872         }
   2873 
   2874         Color::Enum getSuccessOrFailColor(bool success, assertType::Enum at) {
   2875             return success ? Color::BrightGreen :
   2876                              (at & assertType::is_warn) ? Color::Yellow : Color::Red;
   2877         }
   2878 
   2879         void successOrFailColoredStringToStream(bool success, assertType::Enum at,
   2880                                                 const char* success_str = "SUCCESS") {
   2881             s << getSuccessOrFailColor(success, at)
   2882               << getSuccessOrFailString(success, at, success_str) << ": ";
   2883         }
   2884 
   2885         void log_contexts() {
   2886             int num_contexts = get_num_active_contexts();
   2887             if(num_contexts) {
   2888                 auto contexts = get_active_contexts();
   2889 
   2890                 s << Color::None << "  logged: ";
   2891                 for(int i = 0; i < num_contexts; ++i) {
   2892                     s << (i == 0 ? "" : "          ");
   2893                     contexts[i]->stringify(&s);
   2894                     s << "\n";
   2895                 }
   2896             }
   2897 
   2898             s << "\n";
   2899         }
   2900 
   2901         // this was requested to be made virtual so users could override it
   2902         virtual void file_line_to_stream(const char* file, int line,
   2903                                         const char* tail = "") {
   2904             s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(")
   2905             << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option
   2906             << (opt.gnu_file_line ? ":" : "):") << tail;
   2907         }
   2908 
   2909         void logTestStart() {
   2910             if(hasLoggedCurrentTestStart)
   2911                 return;
   2912 
   2913             separator_to_stream();
   2914             file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n");
   2915             if(tc->m_description)
   2916                 s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n";
   2917             if(tc->m_test_suite && tc->m_test_suite[0] != '\0')
   2918                 s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n";
   2919             if(strncmp(tc->m_name, "  Scenario:", 11) != 0)
   2920                 s << Color::Yellow << "TEST CASE:  ";
   2921             s << Color::None << tc->m_name << "\n";
   2922 
   2923             for(size_t i = 0; i < currentSubcaseLevel; ++i) {
   2924                 if(subcasesStack[i].m_name[0] != '\0')
   2925                     s << "  " << subcasesStack[i].m_name << "\n";
   2926             }
   2927 
   2928             if(currentSubcaseLevel != subcasesStack.size()) {
   2929                 s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None;
   2930                 for(size_t i = 0; i < subcasesStack.size(); ++i) {
   2931                     if(subcasesStack[i].m_name[0] != '\0')
   2932                         s << "  " << subcasesStack[i].m_name << "\n";
   2933                 }
   2934             }
   2935 
   2936             s << "\n";
   2937 
   2938             hasLoggedCurrentTestStart = true;
   2939         }
   2940 
   2941         void printVersion() {
   2942             if(opt.no_version == false)
   2943                 s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \""
   2944                   << DOCTEST_VERSION_STR << "\"\n";
   2945         }
   2946 
   2947         void printIntro() {
   2948             if(opt.no_intro == false) {
   2949                 printVersion();
   2950                 s << Color::Cyan << "[doctest] " << Color::None
   2951                   << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
   2952             }
   2953         }
   2954 
   2955         void printHelp() {
   2956             int sizePrefixDisplay = static_cast<int>(strlen(DOCTEST_OPTIONS_PREFIX_DISPLAY));
   2957             printVersion();
   2958             // clang-format off
   2959             s << Color::Cyan << "[doctest]\n" << Color::None;
   2960             s << Color::Cyan << "[doctest] " << Color::None;
   2961             s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n";
   2962             s << Color::Cyan << "[doctest] " << Color::None;
   2963             s << "filter  values: \"str1,str2,str3\" (comma separated strings)\n";
   2964             s << Color::Cyan << "[doctest]\n" << Color::None;
   2965             s << Color::Cyan << "[doctest] " << Color::None;
   2966             s << "filters use wildcards for matching strings\n";
   2967             s << Color::Cyan << "[doctest] " << Color::None;
   2968             s << "something passes a filter if any of the strings in a filter matches\n";
   2969 #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
   2970             s << Color::Cyan << "[doctest]\n" << Color::None;
   2971             s << Color::Cyan << "[doctest] " << Color::None;
   2972             s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"" DOCTEST_CONFIG_OPTIONS_PREFIX "\" PREFIX!!!\n";
   2973 #endif
   2974             s << Color::Cyan << "[doctest]\n" << Color::None;
   2975             s << Color::Cyan << "[doctest] " << Color::None;
   2976             s << "Query flags - the program quits after them. Available:\n\n";
   2977             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "?,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "help, -" DOCTEST_OPTIONS_PREFIX_DISPLAY "h                      "
   2978               << Whitespace(sizePrefixDisplay*0) <<  "prints this message\n";
   2979             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "v,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "version                       "
   2980               << Whitespace(sizePrefixDisplay*1) << "prints the version\n";
   2981             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "c,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "count                         "
   2982               << Whitespace(sizePrefixDisplay*1) << "prints the number of matching tests\n";
   2983             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ltc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-cases               "
   2984               << Whitespace(sizePrefixDisplay*1) << "lists all matching tests by name\n";
   2985             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-suites              "
   2986               << Whitespace(sizePrefixDisplay*1) << "lists all matching test suites\n";
   2987             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lr,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-reporters                "
   2988               << Whitespace(sizePrefixDisplay*1) << "lists all registered reporters\n\n";
   2989             // ================================================================================== << 79
   2990             s << Color::Cyan << "[doctest] " << Color::None;
   2991             s << "The available <int>/<string> options/filters are:\n\n";
   2992             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case=<filters>           "
   2993               << Whitespace(sizePrefixDisplay*1) << "filters     tests by their name\n";
   2994             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude=<filters>   "
   2995               << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n";
   2996             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file=<filters>         "
   2997               << Whitespace(sizePrefixDisplay*1) << "filters     tests by their file\n";
   2998             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude=<filters> "
   2999               << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n";
   3000             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite=<filters>          "
   3001               << Whitespace(sizePrefixDisplay*1) << "filters     tests by their test suite\n";
   3002             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude=<filters>  "
   3003               << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n";
   3004             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase=<filters>             "
   3005               << Whitespace(sizePrefixDisplay*1) << "filters     subcases by their name\n";
   3006             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude=<filters>     "
   3007               << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n";
   3008             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters=<filters>           "
   3009               << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n";
   3010             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out=<string>                  "
   3011               << Whitespace(sizePrefixDisplay*1) << "output filename\n";
   3012             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by=<string>             "
   3013               << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n";
   3014             s << Whitespace(sizePrefixDisplay*3) << "                                       <string> - [file/suite/name/rand/none]\n";
   3015             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed=<int>               "
   3016               << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n";
   3017             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first=<int>                   "
   3018               << Whitespace(sizePrefixDisplay*1) << "the first test passing the filters to\n";
   3019             s << Whitespace(sizePrefixDisplay*3) << "                                       execute - for range-based execution\n";
   3020             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "l,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "last=<int>                    "
   3021               << Whitespace(sizePrefixDisplay*1) << "the last test passing the filters to\n";
   3022             s << Whitespace(sizePrefixDisplay*3) << "                                       execute - for range-based execution\n";
   3023             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "aa,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "abort-after=<int>             "
   3024               << Whitespace(sizePrefixDisplay*1) << "stop after <int> failed assertions\n";
   3025             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels=<int>   "
   3026               << Whitespace(sizePrefixDisplay*1) << "apply filters for the first <int> levels\n";
   3027             s << Color::Cyan << "\n[doctest] " << Color::None;
   3028             s << "Bool options - can be used like flags and true is assumed. Available:\n\n";
   3029             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "s,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "success=<bool>                "
   3030               << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n";
   3031             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive=<bool>         "
   3032               << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n";
   3033             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit=<bool>                   "
   3034               << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n";
   3035             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration=<bool>               "
   3036               << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n";
   3037             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal=<bool>                "
   3038               << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n";
   3039             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q,   --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet=<bool>                  "
   3040               << Whitespace(sizePrefixDisplay*1) << "no console output\n";
   3041             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw=<bool>               "
   3042               << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n";
   3043             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode=<bool>            "
   3044               << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n";
   3045             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run=<bool>                 "
   3046               << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n";
   3047             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro=<bool>               "
   3048               << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n";
   3049             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version=<bool>             "
   3050               << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n";
   3051             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors=<bool>              "
   3052               << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n";
   3053             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors=<bool>           "
   3054               << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n";
   3055             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks=<bool>              "
   3056               << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n";
   3057             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns,  --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip=<bool>                "
   3058               << Whitespace(sizePrefixDisplay*1) << "don't skip test cases marked as skip\n";
   3059             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "gfl, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "gnu-file-line=<bool>          "
   3060               << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n";
   3061             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames=<bool>      "
   3062               << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n";
   3063             s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers=<bool>        "
   3064               << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n";
   3065             // ================================================================================== << 79
   3066             // clang-format on
   3067 
   3068             s << Color::Cyan << "\n[doctest] " << Color::None;
   3069             s << "for more information visit the project documentation\n\n";
   3070         }
   3071 
   3072         void printRegisteredReporters() {
   3073             printVersion();
   3074             auto printReporters = [this] (const reporterMap& reporters, const char* type) {
   3075                 if(reporters.size()) {
   3076                     s << Color::Cyan << "[doctest] " << Color::None << "listing all registered " << type << "\n";
   3077                     for(auto& curr : reporters)
   3078                         s << "priority: " << std::setw(5) << curr.first.first
   3079                           << " name: " << curr.first.second << "\n";
   3080                 }
   3081             };
   3082             printReporters(getListeners(), "listeners");
   3083             printReporters(getReporters(), "reporters");
   3084         }
   3085 
   3086         // =========================================================================================
   3087         // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
   3088         // =========================================================================================
   3089 
   3090         void report_query(const QueryData& in) override {
   3091             if(opt.version) {
   3092                 printVersion();
   3093             } else if(opt.help) {
   3094                 printHelp();
   3095             } else if(opt.list_reporters) {
   3096                 printRegisteredReporters();
   3097             } else if(opt.count || opt.list_test_cases) {
   3098                 if(opt.list_test_cases) {
   3099                     s << Color::Cyan << "[doctest] " << Color::None
   3100                       << "listing all test case names\n";
   3101                     separator_to_stream();
   3102                 }
   3103 
   3104                 for(unsigned i = 0; i < in.num_data; ++i)
   3105                     s << Color::None << in.data[i]->m_name << "\n";
   3106 
   3107                 separator_to_stream();
   3108 
   3109                 s << Color::Cyan << "[doctest] " << Color::None
   3110                   << "unskipped test cases passing the current filters: "
   3111                   << g_cs->numTestCasesPassingFilters << "\n";
   3112 
   3113             } else if(opt.list_test_suites) {
   3114                 s << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n";
   3115                 separator_to_stream();
   3116 
   3117                 for(unsigned i = 0; i < in.num_data; ++i)
   3118                     s << Color::None << in.data[i]->m_test_suite << "\n";
   3119 
   3120                 separator_to_stream();
   3121 
   3122                 s << Color::Cyan << "[doctest] " << Color::None
   3123                   << "unskipped test cases passing the current filters: "
   3124                   << g_cs->numTestCasesPassingFilters << "\n";
   3125                 s << Color::Cyan << "[doctest] " << Color::None
   3126                   << "test suites with unskipped test cases passing the current filters: "
   3127                   << g_cs->numTestSuitesPassingFilters << "\n";
   3128             }
   3129         }
   3130 
   3131         void test_run_start() override {
   3132             if(!opt.minimal)
   3133                 printIntro();
   3134         }
   3135 
   3136         void test_run_end(const TestRunStats& p) override {
   3137             if(opt.minimal && p.numTestCasesFailed == 0)
   3138                 return;
   3139 
   3140             separator_to_stream();
   3141             s << std::dec;
   3142 
   3143             auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
   3144             auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
   3145             auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
   3146             const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
   3147             s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth)
   3148               << p.numTestCasesPassingFilters << " | "
   3149               << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None :
   3150                                                                           Color::Green)
   3151               << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed"
   3152               << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None)
   3153               << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |";
   3154             if(opt.no_skipped_summary == false) {
   3155                 const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters;
   3156                 s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped
   3157                   << " skipped" << Color::None;
   3158             }
   3159             s << "\n";
   3160             s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth)
   3161               << p.numAsserts << " | "
   3162               << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green)
   3163               << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None
   3164               << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth)
   3165               << p.numAssertsFailed << " failed" << Color::None << " |\n";
   3166             s << Color::Cyan << "[doctest] " << Color::None
   3167               << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green)
   3168               << ((p.numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl;
   3169         }
   3170 
   3171         void test_case_start(const TestCaseData& in) override {
   3172             hasLoggedCurrentTestStart = false;
   3173             tc                        = &in;
   3174             subcasesStack.clear();
   3175             currentSubcaseLevel = 0;
   3176         }
   3177         
   3178         void test_case_reenter(const TestCaseData&) override {
   3179             subcasesStack.clear();
   3180         }
   3181 
   3182         void test_case_end(const CurrentTestCaseStats& st) override {
   3183             if(tc->m_no_output)
   3184                 return;
   3185 
   3186             // log the preamble of the test case only if there is something
   3187             // else to print - something other than that an assert has failed
   3188             if(opt.duration ||
   3189                (st.failure_flags && st.failure_flags != static_cast<int>(TestCaseFailureReason::AssertFailure)))
   3190                 logTestStart();
   3191 
   3192             if(opt.duration)
   3193                 s << Color::None << std::setprecision(6) << std::fixed << st.seconds
   3194                   << " s: " << tc->m_name << "\n";
   3195 
   3196             if(st.failure_flags & TestCaseFailureReason::Timeout)
   3197                 s << Color::Red << "Test case exceeded time limit of " << std::setprecision(6)
   3198                   << std::fixed << tc->m_timeout << "!\n";
   3199 
   3200             if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedButDidnt) {
   3201                 s << Color::Red << "Should have failed but didn't! Marking it as failed!\n";
   3202             } else if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedAndDid) {
   3203                 s << Color::Yellow << "Failed as expected so marking it as not failed\n";
   3204             } else if(st.failure_flags & TestCaseFailureReason::CouldHaveFailedAndDid) {
   3205                 s << Color::Yellow << "Allowed to fail so marking it as not failed\n";
   3206             } else if(st.failure_flags & TestCaseFailureReason::DidntFailExactlyNumTimes) {
   3207                 s << Color::Red << "Didn't fail exactly " << tc->m_expected_failures
   3208                   << " times so marking it as failed!\n";
   3209             } else if(st.failure_flags & TestCaseFailureReason::FailedExactlyNumTimes) {
   3210                 s << Color::Yellow << "Failed exactly " << tc->m_expected_failures
   3211                   << " times as expected so marking it as not failed!\n";
   3212             }
   3213             if(st.failure_flags & TestCaseFailureReason::TooManyFailedAsserts) {
   3214                 s << Color::Red << "Aborting - too many failed asserts!\n";
   3215             }
   3216             s << Color::None; // lgtm [cpp/useless-expression]
   3217         }
   3218 
   3219         void test_case_exception(const TestCaseException& e) override {
   3220             DOCTEST_LOCK_MUTEX(mutex)
   3221             if(tc->m_no_output)
   3222                 return;
   3223 
   3224             logTestStart();
   3225 
   3226             file_line_to_stream(tc->m_file.c_str(), tc->m_line, " ");
   3227             successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require :
   3228                                                                    assertType::is_check);
   3229             s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ")
   3230               << Color::Cyan << e.error_string << "\n";
   3231 
   3232             int num_stringified_contexts = get_num_stringified_contexts();
   3233             if(num_stringified_contexts) {
   3234                 auto stringified_contexts = get_stringified_contexts();
   3235                 s << Color::None << "  logged: ";
   3236                 for(int i = num_stringified_contexts; i > 0; --i) {
   3237                     s << (i == num_stringified_contexts ? "" : "          ")
   3238                       << stringified_contexts[i - 1] << "\n";
   3239                 }
   3240             }
   3241             s << "\n" << Color::None;
   3242         }
   3243 
   3244         void subcase_start(const SubcaseSignature& subc) override {
   3245             subcasesStack.push_back(subc);
   3246             ++currentSubcaseLevel;
   3247             hasLoggedCurrentTestStart = false;
   3248         }
   3249 
   3250         void subcase_end() override {
   3251             --currentSubcaseLevel;
   3252             hasLoggedCurrentTestStart = false;
   3253         }
   3254 
   3255         void log_assert(const AssertData& rb) override {
   3256             if((!rb.m_failed && !opt.success) || tc->m_no_output)
   3257                 return;
   3258 
   3259             DOCTEST_LOCK_MUTEX(mutex)
   3260 
   3261             logTestStart();
   3262 
   3263             file_line_to_stream(rb.m_file, rb.m_line, " ");
   3264             successOrFailColoredStringToStream(!rb.m_failed, rb.m_at);
   3265 
   3266             fulltext_log_assert_to_stream(s, rb);
   3267 
   3268             log_contexts();
   3269         }
   3270 
   3271         void log_message(const MessageData& mb) override {
   3272             if(tc->m_no_output)
   3273                 return;
   3274 
   3275             DOCTEST_LOCK_MUTEX(mutex)
   3276 
   3277             logTestStart();
   3278 
   3279             file_line_to_stream(mb.m_file, mb.m_line, " ");
   3280             s << getSuccessOrFailColor(false, mb.m_severity)
   3281               << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity,
   3282                                         "MESSAGE") << ": ";
   3283             s << Color::None << mb.m_string << "\n";
   3284             log_contexts();
   3285         }
   3286 
   3287         void test_case_skipped(const TestCaseData&) override {}
   3288     };
   3289 
   3290     DOCTEST_REGISTER_REPORTER("console", 0, ConsoleReporter);
   3291 
   3292 #ifdef DOCTEST_PLATFORM_WINDOWS
   3293     struct DebugOutputWindowReporter : public ConsoleReporter
   3294     {
   3295         DOCTEST_THREAD_LOCAL static std::ostringstream oss;
   3296 
   3297         DebugOutputWindowReporter(const ContextOptions& co)
   3298                 : ConsoleReporter(co, oss) {}
   3299 
   3300 #define DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(func, type, arg)                                    \
   3301     void func(type arg) override {                                                                 \
   3302         bool with_col = g_no_colors;                                                               \
   3303         g_no_colors   = false;                                                                     \
   3304         ConsoleReporter::func(arg);                                                                \
   3305         if(oss.tellp() != std::streampos{}) {                                                      \
   3306             DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str());                                        \
   3307             oss.str("");                                                                           \
   3308         }                                                                                          \
   3309         g_no_colors = with_col;                                                                    \
   3310     }
   3311 
   3312         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY)
   3313         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in)
   3314         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in)
   3315         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in)
   3316         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in)
   3317         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in)
   3318         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in)
   3319         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY)
   3320         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_assert, const AssertData&, in)
   3321         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_message, const MessageData&, in)
   3322         DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_skipped, const TestCaseData&, in)
   3323     };
   3324 
   3325     DOCTEST_THREAD_LOCAL std::ostringstream DebugOutputWindowReporter::oss;
   3326 #endif // DOCTEST_PLATFORM_WINDOWS
   3327 
   3328     // the implementation of parseOption()
   3329     bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) {
   3330         // going from the end to the beginning and stopping on the first occurrence from the end
   3331         for(int i = argc; i > 0; --i) {
   3332             auto index = i - 1;
   3333             auto temp = std::strstr(argv[index], pattern);
   3334             if(temp && (value || strlen(temp) == strlen(pattern))) { //!OCLINT prefer early exits and continue
   3335                 // eliminate matches in which the chars before the option are not '-'
   3336                 bool noBadCharsFound = true;
   3337                 auto curr            = argv[index];
   3338                 while(curr != temp) {
   3339                     if(*curr++ != '-') {
   3340                         noBadCharsFound = false;
   3341                         break;
   3342                     }
   3343                 }
   3344                 if(noBadCharsFound && argv[index][0] == '-') {
   3345                     if(value) {
   3346                         // parsing the value of an option
   3347                         temp += strlen(pattern);
   3348                         const unsigned len = strlen(temp);
   3349                         if(len) {
   3350                             *value = temp;
   3351                             return true;
   3352                         }
   3353                     } else {
   3354                         // just a flag - no value
   3355                         return true;
   3356                     }
   3357                 }
   3358             }
   3359         }
   3360         return false;
   3361     }
   3362 
   3363     // parses an option and returns the string after the '=' character
   3364     bool parseOption(int argc, const char* const* argv, const char* pattern, String* value = nullptr,
   3365                      const String& defaultVal = String()) {
   3366         if(value)
   3367             *value = defaultVal;
   3368 #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
   3369         // offset (normally 3 for "dt-") to skip prefix
   3370         if(parseOptionImpl(argc, argv, pattern + strlen(DOCTEST_CONFIG_OPTIONS_PREFIX), value))
   3371             return true;
   3372 #endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
   3373         return parseOptionImpl(argc, argv, pattern, value);
   3374     }
   3375 
   3376     // locates a flag on the command line
   3377     bool parseFlag(int argc, const char* const* argv, const char* pattern) {
   3378         return parseOption(argc, argv, pattern);
   3379     }
   3380 
   3381     // parses a comma separated list of words after a pattern in one of the arguments in argv
   3382     bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern,
   3383                            std::vector<String>& res) {
   3384         String filtersString;
   3385         if(parseOption(argc, argv, pattern, &filtersString)) {
   3386             // tokenize with "," as a separator, unless escaped with backslash
   3387             std::ostringstream s;
   3388             auto flush = [&s, &res]() {
   3389                 auto string = s.str();
   3390                 if(string.size() > 0) {
   3391                     res.push_back(string.c_str());
   3392                 }
   3393                 s.str("");
   3394             };
   3395 
   3396             bool seenBackslash = false;
   3397             const char* current = filtersString.c_str();
   3398             const char* end = current + strlen(current);
   3399             while(current != end) {
   3400                 char character = *current++;
   3401                 if(seenBackslash) {
   3402                     seenBackslash = false;
   3403                     if(character == ',' || character == '\\') {
   3404                         s.put(character);
   3405                         continue;
   3406                     }
   3407                     s.put('\\');
   3408                 }
   3409                 if(character == '\\') {
   3410                     seenBackslash = true;
   3411                 } else if(character == ',') {
   3412                     flush();
   3413                 } else {
   3414                     s.put(character);
   3415                 }
   3416             }
   3417 
   3418             if(seenBackslash) {
   3419                 s.put('\\');
   3420             }
   3421             flush();
   3422             return true;
   3423         }
   3424         return false;
   3425     }
   3426 
   3427     enum optionType
   3428     {
   3429         option_bool,
   3430         option_int
   3431     };
   3432 
   3433     // parses an int/bool option from the command line
   3434     bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type,
   3435                         int& res) {
   3436         String parsedValue;
   3437         if(!parseOption(argc, argv, pattern, &parsedValue))
   3438             return false;
   3439 
   3440         if(type) {
   3441             // integer
   3442             // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse...
   3443             int theInt = std::atoi(parsedValue.c_str());
   3444             if (theInt != 0) {
   3445                 res = theInt; //!OCLINT parameter reassignment
   3446                 return true;
   3447             }
   3448         } else {
   3449             // boolean
   3450             const char positive[][5] = { "1", "true", "on", "yes" };  // 5 - strlen("true") + 1
   3451             const char negative[][6] = { "0", "false", "off", "no" }; // 6 - strlen("false") + 1
   3452 
   3453             // if the value matches any of the positive/negative possibilities
   3454             for (unsigned i = 0; i < 4; i++) {
   3455                 if (parsedValue.compare(positive[i], true) == 0) {
   3456                     res = 1; //!OCLINT parameter reassignment
   3457                     return true;
   3458                 }
   3459                 if (parsedValue.compare(negative[i], true) == 0) {
   3460                     res = 0; //!OCLINT parameter reassignment
   3461                     return true;
   3462                 }
   3463             }
   3464         }
   3465         return false;
   3466     }
   3467 } // namespace
   3468 
   3469 Context::Context(int argc, const char* const* argv)
   3470         : p(new detail::ContextState) {
   3471     parseArgs(argc, argv, true);
   3472     if(argc)
   3473         p->binary_name = argv[0];
   3474 }
   3475 
   3476 Context::~Context() {
   3477     if(g_cs == p)
   3478         g_cs = nullptr;
   3479     delete p;
   3480 }
   3481 
   3482 void Context::applyCommandLine(int argc, const char* const* argv) {
   3483     parseArgs(argc, argv);
   3484     if(argc)
   3485         p->binary_name = argv[0];
   3486 }
   3487 
   3488 // parses args
   3489 void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
   3490     using namespace detail;
   3491 
   3492     // clang-format off
   3493     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file=",        p->filters[0]);
   3494     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sf=",                 p->filters[0]);
   3495     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file-exclude=",p->filters[1]);
   3496     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sfe=",                p->filters[1]);
   3497     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite=",         p->filters[2]);
   3498     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ts=",                 p->filters[2]);
   3499     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite-exclude=", p->filters[3]);
   3500     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tse=",                p->filters[3]);
   3501     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case=",          p->filters[4]);
   3502     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tc=",                 p->filters[4]);
   3503     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case-exclude=",  p->filters[5]);
   3504     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tce=",                p->filters[5]);
   3505     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase=",            p->filters[6]);
   3506     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sc=",                 p->filters[6]);
   3507     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase-exclude=",    p->filters[7]);
   3508     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sce=",                p->filters[7]);
   3509     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "reporters=",          p->filters[8]);
   3510     parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "r=",                  p->filters[8]);
   3511     // clang-format on
   3512 
   3513     int    intRes = 0;
   3514     String strRes;
   3515 
   3516 #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default)                                   \
   3517     if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) ||  \
   3518        parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes))   \
   3519         p->var = static_cast<bool>(intRes);                                                        \
   3520     else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) ||                           \
   3521             parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname))                            \
   3522         p->var = true;                                                                             \
   3523     else if(withDefaults)                                                                          \
   3524     p->var = default
   3525 
   3526 #define DOCTEST_PARSE_INT_OPTION(name, sname, var, default)                                        \
   3527     if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_int, intRes) ||   \
   3528        parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_int, intRes))    \
   3529         p->var = intRes;                                                                           \
   3530     else if(withDefaults)                                                                          \
   3531     p->var = default
   3532 
   3533 #define DOCTEST_PARSE_STR_OPTION(name, sname, var, default)                                        \
   3534     if(parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", &strRes, default) ||        \
   3535        parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", &strRes, default) ||       \
   3536        withDefaults)                                                                               \
   3537     p->var = strRes
   3538 
   3539     // clang-format off
   3540     DOCTEST_PARSE_STR_OPTION("out", "o", out, "");
   3541     DOCTEST_PARSE_STR_OPTION("order-by", "ob", order_by, "file");
   3542     DOCTEST_PARSE_INT_OPTION("rand-seed", "rs", rand_seed, 0);
   3543 
   3544     DOCTEST_PARSE_INT_OPTION("first", "f", first, 0);
   3545     DOCTEST_PARSE_INT_OPTION("last", "l", last, UINT_MAX);
   3546 
   3547     DOCTEST_PARSE_INT_OPTION("abort-after", "aa", abort_after, 0);
   3548     DOCTEST_PARSE_INT_OPTION("subcase-filter-levels", "scfl", subcase_filter_levels, INT_MAX);
   3549 
   3550     DOCTEST_PARSE_AS_BOOL_OR_FLAG("success", "s", success, false);
   3551     DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false);
   3552     DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false);
   3553     DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false);
   3554     DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false);
   3555     DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false);
   3556     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false);
   3557     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false);
   3558     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false);
   3559     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false);
   3560     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false);
   3561     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false);
   3562     DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false);
   3563     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-breaks", "nb", no_breaks, false);
   3564     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false);
   3565     DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC));
   3566     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false);
   3567     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false);
   3568     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false);
   3569     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false);
   3570     DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false);
   3571     // clang-format on
   3572 
   3573     if(withDefaults) {
   3574         p->help             = false;
   3575         p->version          = false;
   3576         p->count            = false;
   3577         p->list_test_cases  = false;
   3578         p->list_test_suites = false;
   3579         p->list_reporters   = false;
   3580     }
   3581     if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "help") ||
   3582        parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "h") ||
   3583        parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "?")) {
   3584         p->help = true;
   3585         p->exit = true;
   3586     }
   3587     if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "version") ||
   3588        parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "v")) {
   3589         p->version = true;
   3590         p->exit    = true;
   3591     }
   3592     if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "count") ||
   3593        parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "c")) {
   3594         p->count = true;
   3595         p->exit  = true;
   3596     }
   3597     if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-cases") ||
   3598        parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ltc")) {
   3599         p->list_test_cases = true;
   3600         p->exit            = true;
   3601     }
   3602     if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-suites") ||
   3603        parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lts")) {
   3604         p->list_test_suites = true;
   3605         p->exit             = true;
   3606     }
   3607     if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-reporters") ||
   3608        parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lr")) {
   3609         p->list_reporters = true;
   3610         p->exit           = true;
   3611     }
   3612 }
   3613 
   3614 // allows the user to add procedurally to the filters from the command line
   3615 void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); }
   3616 
   3617 // allows the user to clear all filters from the command line
   3618 void Context::clearFilters() {
   3619     for(auto& curr : p->filters)
   3620         curr.clear();
   3621 }
   3622 
   3623 // allows the user to override procedurally the bool options from the command line
   3624 void Context::setOption(const char* option, bool value) {
   3625     setOption(option, value ? "true" : "false");
   3626 }
   3627 
   3628 // allows the user to override procedurally the int options from the command line
   3629 void Context::setOption(const char* option, int value) {
   3630     setOption(option, toString(value).c_str());
   3631 }
   3632 
   3633 // allows the user to override procedurally the string options from the command line
   3634 void Context::setOption(const char* option, const char* value) {
   3635     auto argv   = String("-") + option + "=" + value;
   3636     auto lvalue = argv.c_str();
   3637     parseArgs(1, &lvalue);
   3638 }
   3639 
   3640 // users should query this in their main() and exit the program if true
   3641 bool Context::shouldExit() { return p->exit; }
   3642 
   3643 void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; }
   3644 
   3645 void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; }
   3646 
   3647 void Context::setCout(std::ostream* out) { p->cout = out; }
   3648 
   3649 static class DiscardOStream : public std::ostream
   3650 {
   3651 private:
   3652     class : public std::streambuf
   3653     {
   3654     private:
   3655         // allowing some buffering decreases the amount of calls to overflow
   3656         char buf[1024];
   3657 
   3658     protected:
   3659         std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; }
   3660 
   3661         int_type overflow(int_type ch) override {
   3662             setp(std::begin(buf), std::end(buf));
   3663             return traits_type::not_eof(ch);
   3664         }
   3665     } discardBuf;
   3666 
   3667 public:
   3668     DiscardOStream()
   3669             : std::ostream(&discardBuf) {}
   3670 } discardOut;
   3671 
   3672 // the main function that does all the filtering and test running
   3673 int Context::run() {
   3674     using namespace detail;
   3675 
   3676     // save the old context state in case such was setup - for using asserts out of a testing context
   3677     auto old_cs = g_cs;
   3678     // this is the current contest
   3679     g_cs               = p;
   3680     is_running_in_test = true;
   3681 
   3682     g_no_colors = p->no_colors;
   3683     p->resetRunData();
   3684 
   3685     std::fstream fstr;
   3686     if(p->cout == nullptr) {
   3687         if(p->quiet) {
   3688             p->cout = &discardOut;
   3689         } else if(p->out.size()) {
   3690             // to a file if specified
   3691             fstr.open(p->out.c_str(), std::fstream::out);
   3692             p->cout = &fstr;
   3693         } else {
   3694             // stdout by default
   3695             p->cout = &std::cout;
   3696         }
   3697     }
   3698 
   3699     FatalConditionHandler::allocateAltStackMem();
   3700 
   3701     auto cleanup_and_return = [&]() {
   3702         FatalConditionHandler::freeAltStackMem();
   3703 
   3704         if(fstr.is_open())
   3705             fstr.close();
   3706 
   3707         // restore context
   3708         g_cs               = old_cs;
   3709         is_running_in_test = false;
   3710 
   3711         // we have to free the reporters which were allocated when the run started
   3712         for(auto& curr : p->reporters_currently_used)
   3713             delete curr;
   3714         p->reporters_currently_used.clear();
   3715 
   3716         if(p->numTestCasesFailed && !p->no_exitcode)
   3717             return EXIT_FAILURE;
   3718         return EXIT_SUCCESS;
   3719     };
   3720 
   3721     // setup default reporter if none is given through the command line
   3722     if(p->filters[8].empty())
   3723         p->filters[8].push_back("console");
   3724 
   3725     // check to see if any of the registered reporters has been selected
   3726     for(auto& curr : getReporters()) {
   3727         if(matchesAny(curr.first.second.c_str(), p->filters[8], false, p->case_sensitive))
   3728             p->reporters_currently_used.push_back(curr.second(*g_cs));
   3729     }
   3730 
   3731     // TODO: check if there is nothing in reporters_currently_used
   3732 
   3733     // prepend all listeners
   3734     for(auto& curr : getListeners())
   3735         p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs));
   3736 
   3737 #ifdef DOCTEST_PLATFORM_WINDOWS
   3738     if(isDebuggerActive() && p->no_debug_output == false)
   3739         p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs));
   3740 #endif // DOCTEST_PLATFORM_WINDOWS
   3741 
   3742     // handle version, help and no_run
   3743     if(p->no_run || p->version || p->help || p->list_reporters) {
   3744         DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, QueryData());
   3745 
   3746         return cleanup_and_return();
   3747     }
   3748 
   3749     std::vector<const TestCase*> testArray;
   3750     for(auto& curr : getRegisteredTests())
   3751         testArray.push_back(&curr);
   3752     p->numTestCases = testArray.size();
   3753 
   3754     // sort the collected records
   3755     if(!testArray.empty()) {
   3756         if(p->order_by.compare("file", true) == 0) {
   3757             std::sort(testArray.begin(), testArray.end(), fileOrderComparator);
   3758         } else if(p->order_by.compare("suite", true) == 0) {
   3759             std::sort(testArray.begin(), testArray.end(), suiteOrderComparator);
   3760         } else if(p->order_by.compare("name", true) == 0) {
   3761             std::sort(testArray.begin(), testArray.end(), nameOrderComparator);
   3762         } else if(p->order_by.compare("rand", true) == 0) {
   3763             std::srand(p->rand_seed);
   3764 
   3765             // random_shuffle implementation
   3766             const auto first = &testArray[0];
   3767             for(size_t i = testArray.size() - 1; i > 0; --i) {
   3768                 int idxToSwap = std::rand() % (i + 1);
   3769 
   3770                 const auto temp = first[i];
   3771 
   3772                 first[i]         = first[idxToSwap];
   3773                 first[idxToSwap] = temp;
   3774             }
   3775         } else if(p->order_by.compare("none", true) == 0) {
   3776             // means no sorting - beneficial for death tests which call into the executable
   3777             // with a specific test case in mind - we don't want to slow down the startup times
   3778         }
   3779     }
   3780 
   3781     std::set<String> testSuitesPassingFilt;
   3782 
   3783     bool                             query_mode = p->count || p->list_test_cases || p->list_test_suites;
   3784     std::vector<const TestCaseData*> queryResults;
   3785 
   3786     if(!query_mode)
   3787         DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY);
   3788 
   3789     // invoke the registered functions if they match the filter criteria (or just count them)
   3790     for(auto& curr : testArray) {
   3791         const auto& tc = *curr;
   3792 
   3793         bool skip_me = false;
   3794         if(tc.m_skip && !p->no_skip)
   3795             skip_me = true;
   3796 
   3797         if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive))
   3798             skip_me = true;
   3799         if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive))
   3800             skip_me = true;
   3801         if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive))
   3802             skip_me = true;
   3803         if(matchesAny(tc.m_test_suite, p->filters[3], false, p->case_sensitive))
   3804             skip_me = true;
   3805         if(!matchesAny(tc.m_name, p->filters[4], true, p->case_sensitive))
   3806             skip_me = true;
   3807         if(matchesAny(tc.m_name, p->filters[5], false, p->case_sensitive))
   3808             skip_me = true;
   3809 
   3810         if(!skip_me)
   3811             p->numTestCasesPassingFilters++;
   3812 
   3813         // skip the test if it is not in the execution range
   3814         if((p->last < p->numTestCasesPassingFilters && p->first <= p->last) ||
   3815            (p->first > p->numTestCasesPassingFilters))
   3816             skip_me = true;
   3817 
   3818         if(skip_me) {
   3819             if(!query_mode)
   3820                 DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_skipped, tc);
   3821             continue;
   3822         }
   3823 
   3824         // do not execute the test if we are to only count the number of filter passing tests
   3825         if(p->count)
   3826             continue;
   3827 
   3828         // print the name of the test and don't execute it
   3829         if(p->list_test_cases) {
   3830             queryResults.push_back(&tc);
   3831             continue;
   3832         }
   3833 
   3834         // print the name of the test suite if not done already and don't execute it
   3835         if(p->list_test_suites) {
   3836             if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') {
   3837                 queryResults.push_back(&tc);
   3838                 testSuitesPassingFilt.insert(tc.m_test_suite);
   3839                 p->numTestSuitesPassingFilters++;
   3840             }
   3841             continue;
   3842         }
   3843 
   3844         // execute the test if it passes all the filtering
   3845         {
   3846             p->currentTest = &tc;
   3847 
   3848             p->failure_flags = TestCaseFailureReason::None;
   3849             p->seconds       = 0;
   3850 
   3851             // reset atomic counters
   3852             p->numAssertsFailedCurrentTest_atomic = 0;
   3853             p->numAssertsCurrentTest_atomic       = 0;
   3854 
   3855             p->fullyTraversedSubcases.clear();
   3856 
   3857             DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc);
   3858 
   3859             p->timer.start();
   3860             
   3861             bool run_test = true;
   3862 
   3863             do {
   3864                 // reset some of the fields for subcases (except for the set of fully passed ones)
   3865                 p->reachedLeaf = false;
   3866                 // May not be empty if previous subcase exited via exception.
   3867                 p->subcaseStack.clear();
   3868                 p->currentSubcaseDepth = 0;
   3869 
   3870                 p->shouldLogCurrentException = true;
   3871 
   3872                 // reset stuff for logging with INFO()
   3873                 p->stringifiedContexts.clear();
   3874 
   3875 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
   3876                 try {
   3877 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
   3878 // MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method)
   3879 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable
   3880                     FatalConditionHandler fatalConditionHandler; // Handle signals
   3881                     // execute the test
   3882                     tc.m_test();
   3883                     fatalConditionHandler.reset();
   3884 DOCTEST_MSVC_SUPPRESS_WARNING_POP
   3885 #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
   3886                 } catch(const TestFailureException&) {
   3887                     p->failure_flags |= TestCaseFailureReason::AssertFailure;
   3888                 } catch(...) {
   3889                     DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception,
   3890                                                       {translateActiveException(), false});
   3891                     p->failure_flags |= TestCaseFailureReason::Exception;
   3892                 }
   3893 #endif // DOCTEST_CONFIG_NO_EXCEPTIONS
   3894 
   3895                 // exit this loop if enough assertions have failed - even if there are more subcases
   3896                 if(p->abort_after > 0 &&
   3897                    p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) {
   3898                     run_test = false;
   3899                     p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts;
   3900                 }
   3901                 
   3902                 if(!p->nextSubcaseStack.empty() && run_test)
   3903                     DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc);
   3904                 if(p->nextSubcaseStack.empty())
   3905                     run_test = false;
   3906             } while(run_test);
   3907 
   3908             p->finalizeTestCaseData();
   3909 
   3910             DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs);
   3911 
   3912             p->currentTest = nullptr;
   3913 
   3914             // stop executing tests if enough assertions have failed
   3915             if(p->abort_after > 0 && p->numAssertsFailed >= p->abort_after)
   3916                 break;
   3917         }
   3918     }
   3919 
   3920     if(!query_mode) {
   3921         DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs);
   3922     } else {
   3923         QueryData qdata;
   3924         qdata.run_stats = g_cs;
   3925         qdata.data      = queryResults.data();
   3926         qdata.num_data  = unsigned(queryResults.size());
   3927         DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata);
   3928     }
   3929 
   3930     return cleanup_and_return();
   3931 }
   3932 
   3933 DOCTEST_DEFINE_INTERFACE(IReporter)
   3934 
   3935 int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); }
   3936 const IContextScope* const* IReporter::get_active_contexts() {
   3937     return get_num_active_contexts() ? &detail::g_infoContexts[0] : nullptr;
   3938 }
   3939 
   3940 int IReporter::get_num_stringified_contexts() { return detail::g_cs->stringifiedContexts.size(); }
   3941 const String* IReporter::get_stringified_contexts() {
   3942     return get_num_stringified_contexts() ? &detail::g_cs->stringifiedContexts[0] : nullptr;
   3943 }
   3944 
   3945 namespace detail {
   3946     void registerReporterImpl(const char* name, int priority, reporterCreatorFunc c, bool isReporter) {
   3947         if(isReporter)
   3948             getReporters().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c));
   3949         else
   3950             getListeners().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c));
   3951     }
   3952 } // namespace detail
   3953 
   3954 } // namespace doctest
   3955 
   3956 #endif // DOCTEST_CONFIG_DISABLE
   3957 
   3958 #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
   3959 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182
   3960 int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); }
   3961 DOCTEST_MSVC_SUPPRESS_WARNING_POP
   3962 #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
   3963 
   3964 DOCTEST_CLANG_SUPPRESS_WARNING_POP
   3965 DOCTEST_MSVC_SUPPRESS_WARNING_POP
   3966 DOCTEST_GCC_SUPPRESS_WARNING_POP
   3967 
   3968 DOCTEST_SUPPRESS_COMMON_WARNINGS_POP
   3969 
   3970 #endif // DOCTEST_LIBRARY_IMPLEMENTATION
   3971 #endif // DOCTEST_CONFIG_IMPLEMENT