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 << "<"; break; 2079 case '&': os << "&"; 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 << ">"; 2085 else 2086 os << c; 2087 break; 2088 2089 case '\"': 2090 if (m_forWhat == ForAttributes) 2091 os << """; 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 = ∈ 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 = ∈ 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