diff options
author | ciappi | 2022-03-17 20:40:49 +0100 |
---|---|---|
committer | ciappi | 2022-03-17 20:40:49 +0100 |
commit | 73b0490d2f5dbf2d34a8678bf6d9c09b5f2d064a (patch) | |
tree | 02bbf1f83388f3f687d5f5cba024ff4304a5539c | |
parent | f35f88c3c5c98344e09c3870b6a43653a8459fe6 (diff) | |
download | aur-73b0490d2f5dbf2d34a8678bf6d9c09b5f2d064a.tar.gz |
Apply patch to update doctest
-rw-r--r-- | .SRCINFO | 4 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | PKGBUILD | 12 | ||||
-rw-r--r-- | doctest.patch | 4833 |
4 files changed, 4846 insertions, 4 deletions
@@ -1,7 +1,7 @@ pkgbase = lfortran pkgdesc = Modern interactive LLVM-based Fortran compiler pkgver = 0.14.0 - pkgrel = 1 + pkgrel = 2 url = https://lfortran.org arch = x86_64 license = BSD @@ -10,6 +10,8 @@ pkgbase = lfortran depends = zlib depends = ncurses source = https://lfortran.github.io/tarballs/release/lfortran-0.14.0.tar.gz + source = doctest.patch sha256sums = fc3c1d592c56ae2636065ec0228db747f154f65a0867f6311bc8091efd5c13a7 + sha256sums = c0a8eabdefe530e65a22ac5b19948c7cbf77d8c46a7afeef8792e9e38006c5d5 pkgname = lfortran diff --git a/.gitignore b/.gitignore index 018a3de08144..498667d7dff1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ !PKGBUILD !.SRCINFO !.gitignore +!doctest.patch @@ -1,7 +1,7 @@ # Maintainer: Ciappi <marco.scopesi@gmail.com> pkgname=lfortran pkgver=0.14.0 -pkgrel=1 +pkgrel=2 pkgdesc="Modern interactive LLVM-based Fortran compiler" arch=('x86_64') url="https://lfortran.org" @@ -18,10 +18,16 @@ backup=() options=() install= changelog= -source=("https://lfortran.github.io/tarballs/release/"$pkgname-$pkgver.tar.gz) -sha256sums=('fc3c1d592c56ae2636065ec0228db747f154f65a0867f6311bc8091efd5c13a7') +source=("https://lfortran.github.io/tarballs/release/"$pkgname-$pkgver.tar.gz "doctest.patch") +sha256sums=('fc3c1d592c56ae2636065ec0228db747f154f65a0867f6311bc8091efd5c13a7' + 'c0a8eabdefe530e65a22ac5b19948c7cbf77d8c46a7afeef8792e9e38006c5d5') noextract=() +prepare() { + cd "$pkgname-$pkgver" + patch --forward --strip=1 --input="${srcdir}/doctest.patch" +} + build() { cd "$srcdir/$pkgname-$pkgver" cmake -DWITH_LLVM=yes -DCMAKE_INSTALL_PREFIX=/usr . diff --git a/doctest.patch b/doctest.patch new file mode 100644 index 000000000000..4b812d76e8eb --- /dev/null +++ b/doctest.patch @@ -0,0 +1,4833 @@ +diff --unified --recursive --text package.orig/src/tests/doctest.h package.new/src/tests/doctest.h +--- package.orig/src/tests/doctest.h 2021-09-23 08:22:52.000000000 +0200 ++++ package.new/src/tests/doctest.h 2022-01-10 18:39:35.000000000 +0100 +@@ -4,14 +4,14 @@ + // + // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD + // +-// Copyright (c) 2016-2019 Viktor Kirilov ++// Copyright (c) 2016-2021 Viktor Kirilov + // + // Distributed under the MIT Software License + // See accompanying file LICENSE.txt or copy at + // https://opensource.org/licenses/MIT + // + // The documentation can be found at the library's page: +-// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md ++// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md + // + // ================================================================================================= + // ================================================================================================= +@@ -47,9 +47,17 @@ + // ================================================================================================= + + #define DOCTEST_VERSION_MAJOR 2 +-#define DOCTEST_VERSION_MINOR 3 +-#define DOCTEST_VERSION_PATCH 4 +-#define DOCTEST_VERSION_STR "2.3.4" ++#define DOCTEST_VERSION_MINOR 4 ++#define DOCTEST_VERSION_PATCH 8 ++ ++// util we need here ++#define DOCTEST_TOSTR_IMPL(x) #x ++#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x) ++ ++#define DOCTEST_VERSION_STR \ ++ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \ ++ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \ ++ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH) + + #define DOCTEST_VERSION \ + (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) +@@ -137,84 +145,93 @@ + // == COMPILER WARNINGS ============================================================================ + // ================================================================================================= + ++// both the header and the implementation suppress all of these, ++// so it only makes sense to aggregrate them like so ++#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \ ++ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \ ++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \ ++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \ ++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \ ++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \ ++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") \ ++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \ ++ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \ ++ \ ++ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \ ++ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \ ++ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ ++ /* these 4 also disabled globally via cmake: */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \ ++ /* */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ ++ /* static analysis */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */ ++ ++#define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \ ++ DOCTEST_CLANG_SUPPRESS_WARNING_POP \ ++ DOCTEST_GCC_SUPPRESS_WARNING_POP \ ++ DOCTEST_MSVC_SUPPRESS_WARNING_POP ++ ++DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH ++ + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + + DOCTEST_GCC_SUPPRESS_WARNING_PUSH +-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") +-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") + DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") + DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") +-DOCTEST_GCC_SUPPRESS_WARNING("-Winline") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") + DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo") + + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning +-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning +-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration +-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression +-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated +-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant +-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding +-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted +-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted +-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted +-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted + DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted +-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe +-// static analysis +-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept' +-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable +-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ... +-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtr... +- +-// 4548 - expression before comma has no effect; expected expression with side - effect +-// 4265 - class has virtual functions, but destructor is not virtual +-// 4986 - exception specification does not match previous declaration +-// 4350 - behavior change: 'member1' called instead of 'member2' +-// 4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' +-// 4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch +-// 4774 - format string expected in argument 'x' is not a string literal +-// 4820 - padding in structs +- +-// only 4 should be disabled globally: +-// - 4514 # unreferenced inline function has been removed +-// - 4571 # SEH related +-// - 4710 # function not inlined +-// - 4711 # function 'x' selected for automatic inline expansion + + #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4548) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4265) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4986) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4350) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4668) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4365) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4774) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4820) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4625) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4626) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(5027) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(5026) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(4623) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(5039) \ +- DOCTEST_MSVC_SUPPRESS_WARNING(5045) ++ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ ++ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ + + #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP + +@@ -227,6 +244,7 @@ + // GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html + // MSVC version table: + // https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering ++// MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022) + // MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019) + // MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017) + // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +@@ -236,6 +254,10 @@ + // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) + // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + ++// Universal Windows Platform support ++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) ++#define DOCTEST_CONFIG_NO_WINDOWS_SEH ++#endif // WINAPI_FAMILY + #if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) + #define DOCTEST_CONFIG_WINDOWS_SEH + #endif // MSVC +@@ -300,15 +322,39 @@ + #define DOCTEST_NOINLINE __declspec(noinline) + #define DOCTEST_UNUSED + #define DOCTEST_ALIGNMENT(x) +-#else // MSVC ++#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0) ++#define DOCTEST_NOINLINE ++#define DOCTEST_UNUSED ++#define DOCTEST_ALIGNMENT(x) ++#else + #define DOCTEST_NOINLINE __attribute__((noinline)) + #define DOCTEST_UNUSED __attribute__((unused)) + #define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) +-#endif // MSVC ++#endif ++ ++#ifndef DOCTEST_NORETURN ++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) ++#define DOCTEST_NORETURN ++#else // DOCTEST_MSVC ++#define DOCTEST_NORETURN [[noreturn]] ++#endif // DOCTEST_MSVC ++#endif // DOCTEST_NORETURN + +-#ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK +-#define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5 +-#endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK ++#ifndef DOCTEST_NOEXCEPT ++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) ++#define DOCTEST_NOEXCEPT ++#else // DOCTEST_MSVC ++#define DOCTEST_NOEXCEPT noexcept ++#endif // DOCTEST_MSVC ++#endif // DOCTEST_NOEXCEPT ++ ++#ifndef DOCTEST_CONSTEXPR ++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) ++#define DOCTEST_CONSTEXPR const ++#else // DOCTEST_MSVC ++#define DOCTEST_CONSTEXPR constexpr ++#endif // DOCTEST_MSVC ++#endif // DOCTEST_CONSTEXPR + + // ================================================================================================= + // == FEATURE DETECTION END ======================================================================== +@@ -323,8 +369,6 @@ + #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) + #endif // __COUNTER__ + +-#define DOCTEST_TOSTR(x) #x +- + #ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE + #define DOCTEST_REF_WRAP(x) x& + #else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE +@@ -342,23 +386,31 @@ + #define DOCTEST_PLATFORM_LINUX + #endif // DOCTEST_PLATFORM + +-// clang-format off +-#define DOCTEST_DELETE_COPIES(type) type(const type&) = delete; type& operator=(const type&) = delete +-#define DOCTEST_DECLARE_COPIES(type) type(const type&); type& operator=(const type&) +-#define DOCTEST_DEFINE_COPIES(type) type::type(const type&) = default; type& type::operator=(const type&) = default +-#define DOCTEST_DECLARE_DEFAULTS(type) type(); ~type() +-#define DOCTEST_DEFINE_DEFAULTS(type) type::type() = default; type::~type() = default +-// clang-format on ++namespace doctest { namespace detail { ++ static DOCTEST_CONSTEXPR int consume(const int*, int) { return 0; } ++}} + +-#define DOCTEST_GLOBAL_NO_WARNINGS(var) \ ++#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \ +- DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \ +- static int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp) +-#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP ++ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \ ++ DOCTEST_CLANG_SUPPRESS_WARNING_POP + ++#ifndef DOCTEST_BREAK_INTO_DEBUGGER + // should probably take a look at https://github.com/scottt/debugbreak +-#ifdef DOCTEST_PLATFORM_MAC +-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) ++#ifdef DOCTEST_PLATFORM_LINUX ++#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) ++// Break at the location of the failing check if possible ++#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler) ++#else ++#include <signal.h> ++#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP) ++#endif ++#elif defined(DOCTEST_PLATFORM_MAC) ++#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) ++#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler) ++#else ++#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler) ++#endif + #elif DOCTEST_MSVC + #define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() + #elif defined(__MINGW32__) +@@ -367,40 +419,40 @@ + DOCTEST_GCC_SUPPRESS_WARNING_POP + #define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() + #else // linux +-#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0) ++#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast<void>(0)) + #endif // linux ++#endif // DOCTEST_BREAK_INTO_DEBUGGER + + // this is kept here for backwards compatibility since the config option was changed + #ifdef DOCTEST_CONFIG_USE_IOSFWD + #define DOCTEST_CONFIG_USE_STD_HEADERS + #endif // DOCTEST_CONFIG_USE_IOSFWD + +-#ifdef DOCTEST_CONFIG_USE_STD_HEADERS +-#include <iosfwd> +-#include <cstddef> +-#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 +-#include <ostream> +-#endif // VS 2019 +-#else // DOCTEST_CONFIG_USE_STD_HEADERS +- ++// for clang - always include ciso646 (which drags some std stuff) because ++// we want to check if we are using libc++ with the _LIBCPP_VERSION macro in ++// which case we don't want to forward declare stuff from std - for reference: ++// https://github.com/doctest/doctest/issues/126 ++// https://github.com/doctest/doctest/issues/356 + #if DOCTEST_CLANG +-// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier) + #include <ciso646> +-#endif // clang +- + #ifdef _LIBCPP_VERSION +-#define DOCTEST_STD_NAMESPACE_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD +-#define DOCTEST_STD_NAMESPACE_END _LIBCPP_END_NAMESPACE_STD +-#else // _LIBCPP_VERSION +-#define DOCTEST_STD_NAMESPACE_BEGIN namespace std { +-#define DOCTEST_STD_NAMESPACE_END } ++#define DOCTEST_CONFIG_USE_STD_HEADERS + #endif // _LIBCPP_VERSION ++#endif // clang ++ ++#ifdef DOCTEST_CONFIG_USE_STD_HEADERS ++#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS ++#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS ++#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS ++#include <cstddef> ++#include <ostream> ++#include <istream> ++#else // DOCTEST_CONFIG_USE_STD_HEADERS + + // Forward declaring 'X' in namespace std is not permitted by the C++ Standard. + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643) + +-DOCTEST_STD_NAMESPACE_BEGIN // NOLINT (cert-dcl58-cpp) ++namespace std { // NOLINT (cert-dcl58-cpp) + typedef decltype(nullptr) nullptr_t; + template <class charT> + struct char_traits; +@@ -409,17 +461,20 @@ + template <class charT, class traits> + class basic_ostream; + typedef basic_ostream<char, char_traits<char>> ostream; ++template <class charT, class traits> ++class basic_istream; ++typedef basic_istream<char, char_traits<char>> istream; + template <class... Types> + class tuple; + #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 +-template <class _Ty> ++// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 ++template <class Ty> + class allocator; +-template <class _Elem, class _Traits, class _Alloc> ++template <class Elem, class Traits, class Alloc> + class basic_string; + using string = basic_string<char, char_traits<char>, allocator<char>>; + #endif // VS 2019 +-DOCTEST_STD_NAMESPACE_END ++} // namespace std + + DOCTEST_MSVC_SUPPRESS_WARNING_POP + +@@ -471,6 +526,8 @@ + view data; + }; + ++ char* allocate(unsigned sz); ++ + bool isOnStack() const { return (buf[last] & 128) == 0; } + void setOnHeap(); + void setLast(unsigned in = last); +@@ -485,11 +542,12 @@ + String(const char* in); + String(const char* in, unsigned in_size); + ++ String(std::istream& in, unsigned in_size); ++ + String(const String& other); + String& operator=(const String& other); + + String& operator+=(const String& other); +- String operator+(const String& other) const; + + String(String&& other); + String& operator=(String&& other); +@@ -512,6 +570,8 @@ + int compare(const String& other, bool no_case = false) const; + }; + ++DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs); ++ + DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs); + DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs); + DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs); +@@ -592,6 +652,10 @@ + DT_WARN_THROWS_WITH = is_throws_with | is_warn, + DT_CHECK_THROWS_WITH = is_throws_with | is_check, + DT_REQUIRE_THROWS_WITH = is_throws_with | is_require, ++ ++ DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn, ++ DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check, ++ DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require, + + DT_WARN_NOTHROW = is_nothrow | is_warn, + DT_CHECK_NOTHROW = is_nothrow | is_check, +@@ -637,19 +701,18 @@ + + struct DOCTEST_INTERFACE TestCaseData + { +- const char* m_file; // the file in which the test was registered ++ String m_file; // the file in which the test was registered (using String - see #350) + unsigned m_line; // the line where the test was registered + const char* m_name; // name of the test case + const char* m_test_suite; // the test suite in which the test was added + const char* m_description; + bool m_skip; ++ bool m_no_breaks; ++ bool m_no_output; + bool m_may_fail; + bool m_should_fail; + int m_expected_failures; + double m_timeout; +- +- DOCTEST_DECLARE_DEFAULTS(TestCaseData); +- DOCTEST_DECLARE_COPIES(TestCaseData); + }; + + struct DOCTEST_INTERFACE AssertData +@@ -672,9 +735,7 @@ + // for specific exception-related asserts + bool m_threw_as; + const char* m_exception_type; +- +- DOCTEST_DECLARE_DEFAULTS(AssertData); +- DOCTEST_DELETE_COPIES(AssertData); ++ const char* m_exception_string; + }; + + struct DOCTEST_INTERFACE MessageData +@@ -683,39 +744,34 @@ + const char* m_file; + int m_line; + assertType::Enum m_severity; +- +- DOCTEST_DECLARE_DEFAULTS(MessageData); +- DOCTEST_DELETE_COPIES(MessageData); + }; + + struct DOCTEST_INTERFACE SubcaseSignature + { +- const char* m_name; ++ String m_name; + const char* m_file; + int m_line; + +- SubcaseSignature(const char* name, const char* file, int line); +- + bool operator<(const SubcaseSignature& other) const; +- +- DOCTEST_DECLARE_DEFAULTS(SubcaseSignature); +- DOCTEST_DECLARE_COPIES(SubcaseSignature); + }; + + struct DOCTEST_INTERFACE IContextScope + { +- DOCTEST_DELETE_COPIES(IContextScope); +- + IContextScope(); + virtual ~IContextScope(); + virtual void stringify(std::ostream*) const = 0; + }; + ++namespace detail { ++ struct DOCTEST_INTERFACE TestCase; ++} // namespace detail ++ + struct ContextOptions //!OCLINT too many fields + { +- std::ostream* cout; // stdout stream - std::cout by default +- std::ostream* cerr; // stderr stream - std::cerr by default +- String binary_name; // the test binary name ++ std::ostream* cout = nullptr; // stdout stream ++ String binary_name; // the test binary name ++ ++ const detail::TestCase* currentTest = nullptr; + + // == parameters from the command line + String out; // output filename +@@ -732,9 +788,12 @@ + bool case_sensitive; // if filtering should be case sensitive + bool exit; // if the program should be exited after the tests are ran/whatever + bool duration; // print the time duration of each test case ++ bool minimal; // minimal console output (only test failures) ++ bool quiet; // no console output + bool no_throw; // to skip exceptions-related assertion macros + bool no_exitcode; // if the framework should return 0 as the exitcode + bool no_run; // to not run the tests at all (can be done with an "*" exclude) ++ bool no_intro; // to not print the intro of the framework + bool no_version; // to not print the version of the framework + bool no_colors; // if output to the console should be colorized + bool force_colors; // forces the use of colors even when a tty cannot be detected +@@ -743,21 +802,19 @@ + bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): + bool no_path_in_filenames; // if the path to files should be removed from the output + bool no_line_numbers; // if source code line numbers should be omitted from the output ++ bool no_debug_output; // no output in the debug console when a debugger is attached + bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! ++ bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!! + + bool help; // to print the help + bool version; // to print the version +- bool count; // if only the count of matching tests is to be retreived ++ bool count; // if only the count of matching tests is to be retrieved + bool list_test_cases; // to list all tests matching the filters + bool list_test_suites; // to list all suites matching the filters + bool list_reporters; // lists all registered reporters +- +- DOCTEST_DECLARE_DEFAULTS(ContextOptions); +- DOCTEST_DELETE_COPIES(ContextOptions); + }; + + namespace detail { +-#if defined(DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || defined(DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS) + template <bool CONDITION, typename TYPE = void> + struct enable_if + {}; +@@ -765,15 +822,48 @@ + template <typename TYPE> + struct enable_if<true, TYPE> + { typedef TYPE type; }; +-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + // clang-format off + template<class T> struct remove_reference { typedef T type; }; + template<class T> struct remove_reference<T&> { typedef T type; }; + template<class T> struct remove_reference<T&&> { typedef T type; }; + ++ template<typename T, typename U = T&&> U declval(int); ++ ++ template<typename T> T declval(long); ++ ++ template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ; ++ ++ template<class T> struct is_lvalue_reference { const static bool value=false; }; ++ template<class T> struct is_lvalue_reference<T&> { const static bool value=true; }; ++ ++ template<class T> struct is_rvalue_reference { const static bool value=false; }; ++ template<class T> struct is_rvalue_reference<T&&> { const static bool value=true; }; ++ ++ template <class T> ++ inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT ++ { ++ return static_cast<T&&>(t); ++ } ++ ++ template <class T> ++ inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT ++ { ++ static_assert(!is_lvalue_reference<T>::value, ++ "Can not forward an rvalue as an lvalue."); ++ return static_cast<T&&>(t); ++ } ++ + template<class T> struct remove_const { typedef T type; }; + template<class T> struct remove_const<const T> { typedef T type; }; ++#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS ++ template<class T> struct is_enum : public std::is_enum<T> {}; ++ template<class T> struct underlying_type : public std::underlying_type<T> {}; ++#else ++ // Use compiler intrinsics ++ template<class T> struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); }; ++ template<class T> struct underlying_type { typedef __underlying_type(T) type; }; ++#endif + // clang-format on + + template <typename T> +@@ -782,38 +872,27 @@ + { static const bool value = false; }; + + namespace has_insertion_operator_impl { +- typedef char no; +- typedef char yes[2]; +- +- struct any_t +- { +- template <typename T> +- // cppcheck-suppress noExplicitConstructor +- any_t(const DOCTEST_REF_WRAP(T)); ++ std::ostream &os(); ++ template<class T> ++ DOCTEST_REF_WRAP(T) val(); ++ ++ template<class, class = void> ++ struct check { ++ static DOCTEST_CONSTEXPR bool value = false; + }; + +- yes& testStreamable(std::ostream&); +- no testStreamable(no); +- +- no operator<<(const std::ostream&, const any_t&); +- +- template <typename T> +- struct has_insertion_operator +- { +- static std::ostream& s; +- static const DOCTEST_REF_WRAP(T) t; +- static const bool value = sizeof(decltype(testStreamable(s << t))) == sizeof(yes); ++ template<class T> ++ struct check<T, decltype(os() << val<T>(), void())> { ++ static DOCTEST_CONSTEXPR bool value = true; + }; + } // namespace has_insertion_operator_impl + +- template <typename T> +- struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T> +- {}; ++ template<class T> ++ using has_insertion_operator = has_insertion_operator_impl::check<const T>; + +- DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num); ++ DOCTEST_INTERFACE std::ostream* tlssPush(); ++ DOCTEST_INTERFACE String tlssPop(); + +- DOCTEST_INTERFACE std::ostream* getTlsOss(); // returns a thread-local ostringstream +- DOCTEST_INTERFACE String getTlsOssResult(); + + template <bool C> + struct StringMakerBase +@@ -824,13 +903,61 @@ + } + }; + ++ // Vector<int> and various type other than pointer or array. ++ template<typename T> ++ struct filldata ++ { ++ static void fill(std::ostream* stream, const T &in) { ++ *stream << in; ++ } ++ }; ++ ++ template<typename T,unsigned long N> ++ struct filldata<T[N]> ++ { ++ static void fill(std::ostream* stream, const T (&in)[N]) { ++ for (unsigned long i = 0; i < N; i++) { ++ *stream << in[i]; ++ } ++ } ++ }; ++ ++ // Specialized since we don't want the terminating null byte! ++ template<unsigned long N> ++ struct filldata<const char[N]> ++ { ++ static void fill(std::ostream* stream, const char(&in)[N]) { ++ *stream << in; ++ } ++ }; ++ ++ template<typename T> ++ void filloss(std::ostream* stream, const T& in) { ++ filldata<T>::fill(stream, in); ++ } ++ ++ template<typename T,unsigned long N> ++ void filloss(std::ostream* stream, const T (&in)[N]) { ++ // T[N], T(&)[N], T(&&)[N] have same behaviour. ++ // Hence remove reference. ++ filldata<typename remove_reference<decltype(in)>::type>::fill(stream, in); ++ } ++ + template <> + struct StringMakerBase<true> + { + template <typename T> + static String convert(const DOCTEST_REF_WRAP(T) in) { +- *getTlsOss() << in; +- return getTlsOssResult(); ++ /* When parameter "in" is a null terminated const char* it works. ++ * When parameter "in" is a T arr[N] without '\0' we can fill the ++ * stringstream with N objects (T=char).If in is char pointer * ++ * without '\0' , it would cause segfault ++ * stepping over unaccessible memory. ++ */ ++ ++ std::ostream* stream = tlssPush(); ++ filloss(stream, in); ++ return tlssPop(); + } + }; + +@@ -872,7 +999,7 @@ + } + }; + +-template <typename T> ++template <typename T, typename detail::enable_if<!detail::is_enum<T>::value, bool>::type = true> + String toString(const DOCTEST_REF_WRAP(T) value) { + return StringMaker<T>::convert(value); + } +@@ -899,8 +1026,14 @@ + DOCTEST_INTERFACE String toString(int long long unsigned in); + DOCTEST_INTERFACE String toString(std::nullptr_t in); + ++template <typename T, typename detail::enable_if<detail::is_enum<T>::value, bool>::type = true> ++String toString(const DOCTEST_REF_WRAP(T) value) { ++ typedef typename detail::underlying_type<T>::type UT; ++ return toString(static_cast<UT>(value)); ++} ++ + #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 ++// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 + DOCTEST_INTERFACE String toString(const std::string& in); + #endif // VS 2019 + +@@ -909,8 +1042,6 @@ + public: + explicit Approx(double value); + +- DOCTEST_DECLARE_COPIES(Approx); +- + Approx operator()(double value) const; + + #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +@@ -1010,14 +1141,12 @@ + + struct DOCTEST_INTERFACE TestFailureException + { +- DOCTEST_DECLARE_DEFAULTS(TestFailureException); +- DOCTEST_DECLARE_COPIES(TestFailureException); + }; + + DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); + + #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS +- [[noreturn]] ++ DOCTEST_NORETURN + #endif // DOCTEST_CONFIG_NO_EXCEPTIONS + DOCTEST_INTERFACE void throwException(); + +@@ -1026,24 +1155,42 @@ + SubcaseSignature m_signature; + bool m_entered = false; + +- Subcase(const char* name, const char* file, int line); ++ Subcase(const String& name, const char* file, int line); + ~Subcase(); + +- DOCTEST_DELETE_COPIES(Subcase); +- + operator bool() const; + }; + + template <typename L, typename R> + String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, + const DOCTEST_REF_WRAP(R) rhs) { ++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + return toString(lhs) + op + toString(rhs); + } + ++#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) ++DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") ++#endif ++ ++// This will check if there is any way it could find a operator like member or friend and uses it. ++// If not it doesn't find the operator or if the operator at global scope is defined after ++// this template, the template won't be instantiated due to SFINAE. Once the template is not ++// instantiated it can look for global operator using normal conversions. ++#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval<L>() op doctest::detail::declval<R>()),ret{}) ++ + #define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ + template <typename R> \ +- DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \ +- bool res = op_macro(lhs, rhs); \ ++ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R&& rhs) { \ ++ bool res = op_macro(doctest::detail::forward<const L>(lhs), doctest::detail::forward<const R>(rhs)); \ ++ if(m_at & assertType::is_false) \ ++ res = !res; \ ++ if(!res || doctest::getContextOptions()->success) \ ++ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ ++ return Result(res); \ ++ } \ ++ template <typename R ,typename enable_if<!doctest::detail::is_rvalue_reference<R>::value, void >::type* = nullptr> \ ++ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R& rhs) { \ ++ bool res = op_macro(doctest::detail::forward<const L>(lhs), rhs); \ + if(m_at & assertType::is_false) \ + res = !res; \ + if(!res || doctest::getContextOptions()->success) \ +@@ -1067,12 +1214,10 @@ + bool m_passed; + String m_decomp; + ++ Result() = default; + Result(bool passed, const String& decomposition = String()); + +- DOCTEST_DECLARE_DEFAULTS(Result); +- DOCTEST_DECLARE_COPIES(Result); +- +- // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence ++ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence + DOCTEST_FORBIT_EXPRESSION(Result, &) + DOCTEST_FORBIT_EXPRESSION(Result, ^) + DOCTEST_FORBIT_EXPRESSION(Result, |) +@@ -1114,7 +1259,7 @@ + //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") + + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +- // http://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389 ++ // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389 + DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch + DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch + DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch +@@ -1127,6 +1272,7 @@ + #define DOCTEST_COMPARISON_RETURN_TYPE bool + #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + #define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type ++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } + inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } + inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } +@@ -1173,12 +1319,15 @@ + L lhs; + assertType::Enum m_at; + +- explicit Expression_lhs(L in, assertType::Enum at) +- : lhs(in) ++ explicit Expression_lhs(L&& in, assertType::Enum at) ++ : lhs(doctest::detail::forward<L>(in)) + , m_at(at) {} + + DOCTEST_NOINLINE operator Result() { +- bool res = !!lhs; ++// this is needed only for MSVC 2015 ++DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool ++ bool res = static_cast<bool>(lhs); ++DOCTEST_MSVC_SUPPRESS_WARNING_POP + if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional + res = !res; + +@@ -1187,6 +1336,9 @@ + return Result(res); + } + ++ /* This is required for user-defined conversions from Expression_lhs to L */ ++ operator L() const { return lhs; } ++ + // clang-format off + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional +@@ -1196,7 +1348,7 @@ + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional + // clang-format on + +- // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence ++ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |) +@@ -1227,37 +1379,42 @@ + + #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + ++#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) ++DOCTEST_CLANG_SUPPRESS_WARNING_POP ++#endif ++ + struct DOCTEST_INTERFACE ExpressionDecomposer + { + assertType::Enum m_at; + + ExpressionDecomposer(assertType::Enum at); + +- DOCTEST_DECLARE_DEFAULTS(ExpressionDecomposer); +- DOCTEST_DELETE_COPIES(ExpressionDecomposer); +- + // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) + // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... + // https://github.com/catchorg/Catch2/issues/870 + // https://github.com/catchorg/Catch2/issues/565 + template <typename L> +- Expression_lhs<const DOCTEST_REF_WRAP(L)> operator<<(const DOCTEST_REF_WRAP(L) operand) { +- return Expression_lhs<const DOCTEST_REF_WRAP(L)>(operand, m_at); ++ Expression_lhs<const L> operator<<(const L &&operand) { ++ return Expression_lhs<const L>(doctest::detail::forward<const L>(operand), m_at); ++ } ++ ++ template <typename L,typename enable_if<!doctest::detail::is_rvalue_reference<L>::value,void >::type* = nullptr> ++ Expression_lhs<const L&> operator<<(const L &operand) { ++ return Expression_lhs<const L&>(operand, m_at); + } + }; + + struct DOCTEST_INTERFACE TestSuite + { +- const char* m_test_suite; +- const char* m_description; +- bool m_skip; +- bool m_may_fail; +- bool m_should_fail; +- int m_expected_failures; +- double m_timeout; +- +- DOCTEST_DECLARE_DEFAULTS(TestSuite); +- DOCTEST_DECLARE_COPIES(TestSuite); ++ const char* m_test_suite = nullptr; ++ const char* m_description = nullptr; ++ bool m_skip = false; ++ bool m_no_breaks = false; ++ bool m_no_output = false; ++ bool m_may_fail = false; ++ bool m_should_fail = false; ++ int m_expected_failures = 0; ++ double m_timeout = 0; + + TestSuite& operator*(const char* in); + +@@ -1281,8 +1438,6 @@ + TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, + const char* type = "", int template_id = -1); + +- DOCTEST_DECLARE_DEFAULTS(TestCase); +- + TestCase(const TestCase& other); + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function +@@ -1327,33 +1482,31 @@ + template <class L, class R> struct RelationalComparator<n, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } }; + // clang-format on + +- DOCTEST_BINARY_RELATIONAL_OP(0, eq) +- DOCTEST_BINARY_RELATIONAL_OP(1, ne) +- DOCTEST_BINARY_RELATIONAL_OP(2, gt) +- DOCTEST_BINARY_RELATIONAL_OP(3, lt) +- DOCTEST_BINARY_RELATIONAL_OP(4, ge) +- DOCTEST_BINARY_RELATIONAL_OP(5, le) ++ DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq) ++ DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne) ++ DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt) ++ DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt) ++ DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge) ++ DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le) + + struct DOCTEST_INTERFACE ResultBuilder : public AssertData + { + ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, +- const char* exception_type = ""); +- +- DOCTEST_DECLARE_DEFAULTS(ResultBuilder); +- DOCTEST_DELETE_COPIES(ResultBuilder); ++ const char* exception_type = "", const char* exception_string = ""); + + void setResult(const Result& res); + + template <int comparison, typename L, typename R> +- DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs, ++ DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs, + const DOCTEST_REF_WRAP(R) rhs) { + m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs); + if(m_failed || getContextOptions()->success) + m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); ++ return !m_failed; + } + + template <typename L> +- DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) { ++ DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) { + m_failed = !val; + + if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional +@@ -1361,6 +1514,8 @@ + + if(m_failed || getContextOptions()->success) + m_decomp = toString(val); ++ ++ return !m_failed; + } + + void translateException(); +@@ -1380,7 +1535,7 @@ + + DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad); + +- DOCTEST_INTERFACE void decomp_assert(assertType::Enum at, const char* file, int line, ++ DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line, + const char* expr, Result result); + + #define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \ +@@ -1396,7 +1551,7 @@ + if(checkIfShouldThrow(at)) \ + throwException(); \ + } \ +- return; \ ++ return !failed; \ + } \ + } while(false) + +@@ -1411,7 +1566,7 @@ + throwException() + + template <int comparison, typename L, typename R> +- DOCTEST_NOINLINE void binary_assert(assertType::Enum at, const char* file, int line, ++ DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line, + const char* expr, const DOCTEST_REF_WRAP(L) lhs, + const DOCTEST_REF_WRAP(R) rhs) { + bool failed = !RelationalComparator<comparison, L, R>()(lhs, rhs); +@@ -1422,10 +1577,11 @@ + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); + DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); ++ return !failed; + } + + template <typename L> +- DOCTEST_NOINLINE void unary_assert(assertType::Enum at, const char* file, int line, ++ DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line, + const char* expr, const DOCTEST_REF_WRAP(L) val) { + bool failed = !val; + +@@ -1438,12 +1594,11 @@ + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(toString(val)); + DOCTEST_ASSERT_IN_TESTS(toString(val)); ++ return !failed; + } + + struct DOCTEST_INTERFACE IExceptionTranslator + { +- DOCTEST_DELETE_COPIES(IExceptionTranslator); +- + IExceptionTranslator(); + virtual ~IExceptionTranslator(); + virtual bool translate(String&) const = 0; +@@ -1464,9 +1619,9 @@ + } catch(T ex) { // NOLINT + res = m_translateFunction(ex); //!OCLINT parameter reassignment + return true; +- } catch(...) {} //!OCLINT - empty catch statement +-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS +- ((void)res); // to silence -Wunused-parameter ++ } catch(...) {} //!OCLINT - empty catch statement ++#endif // DOCTEST_CONFIG_NO_EXCEPTIONS ++ static_cast<void>(res); // to silence -Wunused-parameter + return false; + } + +@@ -1533,40 +1688,57 @@ + class DOCTEST_INTERFACE ContextScopeBase : public IContextScope { + protected: + ContextScopeBase(); ++ ContextScopeBase(ContextScopeBase&& other); + + void destroy(); ++ bool need_to_destroy{true}; + }; + +- template <typename L> class DOCTEST_INTERFACE ContextScope : public ContextScopeBase ++ template <typename L> class ContextScope : public ContextScopeBase + { +- const L &lambda_; ++ const L lambda_; + + public: + explicit ContextScope(const L &lambda) : lambda_(lambda) {} + +- ContextScope(ContextScope &&other) : lambda_(other.lambda_) {} ++ ContextScope(ContextScope &&other) : ContextScopeBase(static_cast<ContextScopeBase&&>(other)), lambda_(other.lambda_) {} + + void stringify(std::ostream* s) const override { lambda_(s); } + +- ~ContextScope() override { destroy(); } ++ ~ContextScope() override { ++ if (need_to_destroy) { ++ destroy(); ++ } ++ } + }; + + struct DOCTEST_INTERFACE MessageBuilder : public MessageData + { + std::ostream* m_stream; ++ bool logged = false; + + MessageBuilder(const char* file, int line, assertType::Enum severity); + MessageBuilder() = delete; + ~MessageBuilder(); + +- DOCTEST_DELETE_COPIES(MessageBuilder); +- ++ // the preferred way of chaining parameters for stringification + template <typename T> +- MessageBuilder& operator<<(const T& in) { ++ MessageBuilder& operator,(const T& in) { + toStream(m_stream, in); + return *this; + } + ++ // kept here just for backwards-compatibility - the comma operator should be preferred now ++ template <typename T> ++ MessageBuilder& operator<<(const T& in) { return this->operator,(in); } ++ ++ // the `,` operator has the lowest operator precedence - if `<<` is used by the user then ++ // the `,` operator will be called last which is not what we want and thus the `*` operator ++ // is used first (has higher operator precedence compared to `<<`) so that we guarantee that ++ // an operator of the MessageBuilder class is called first before the rest of the parameters ++ template <typename T> ++ MessageBuilder& operator*(const T& in) { return this->operator,(in); } ++ + bool log(); + void react(); + }; +@@ -1590,6 +1762,8 @@ + DOCTEST_DEFINE_DECORATOR(test_suite, const char*, ""); + DOCTEST_DEFINE_DECORATOR(description, const char*, ""); + DOCTEST_DEFINE_DECORATOR(skip, bool, true); ++DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true); ++DOCTEST_DEFINE_DECORATOR(no_output, bool, true); + DOCTEST_DEFINE_DECORATOR(timeout, double, 0); + DOCTEST_DEFINE_DECORATOR(may_fail, bool, true); + DOCTEST_DEFINE_DECORATOR(should_fail, bool, true); +@@ -1634,14 +1808,13 @@ + public: + explicit Context(int argc = 0, const char* const* argv = nullptr); + +- DOCTEST_DELETE_COPIES(Context); +- + ~Context(); + + void applyCommandLine(int argc, const char* const* argv); + + void addFilter(const char* filter, const char* value); + void clearFilters(); ++ void setOption(const char* option, bool value); + void setOption(const char* option, int value); + void setOption(const char* option, const char* value); + +@@ -1651,6 +1824,8 @@ + + void setAssertHandler(detail::assert_handler ah); + ++ void setCout(std::ostream* out); ++ + int run(); + }; + +@@ -1677,9 +1852,7 @@ + int numAssertsFailedCurrentTest; + double seconds; + int failure_flags; // use TestCaseFailureReason::Enum +- +- DOCTEST_DECLARE_DEFAULTS(CurrentTestCaseStats); +- DOCTEST_DELETE_COPIES(CurrentTestCaseStats); ++ bool testCaseSuccess; + }; + + struct DOCTEST_INTERFACE TestCaseException +@@ -1696,16 +1869,13 @@ + unsigned numTestCasesFailed; + int numAsserts; + int numAssertsFailed; +- +- DOCTEST_DECLARE_DEFAULTS(TestRunStats); +- DOCTEST_DELETE_COPIES(TestRunStats); + }; + + struct QueryData + { +- const TestRunStats* run_stats = nullptr; +- String* data = nullptr; +- unsigned num_data = 0; ++ const TestRunStats* run_stats = nullptr; ++ const TestCaseData** data = nullptr; ++ unsigned num_data = 0; + }; + + struct DOCTEST_INTERFACE IReporter +@@ -1724,6 +1894,8 @@ + + // called when a test case is started (safe to cache a pointer to the input) + virtual void test_case_start(const TestCaseData&) = 0; ++ // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input) ++ virtual void test_case_reenter(const TestCaseData&) = 0; + // called when a test case has ended + virtual void test_case_end(const CurrentTestCaseStats&) = 0; + +@@ -1778,10 +1950,11 @@ + #if !defined(DOCTEST_CONFIG_DISABLE) + + // common code in asserts - for convenience +-#define DOCTEST_ASSERT_LOG_AND_REACT(b) \ ++#define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \ + if(b.log()) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ +- b.react() ++ b.react(); \ ++ return !b.m_failed + + #ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS + #define DOCTEST_WRAP_IN_TRY(x) x; +@@ -1789,27 +1962,26 @@ + #define DOCTEST_WRAP_IN_TRY(x) \ + try { \ + x; \ +- } catch(...) { _DOCTEST_RB.translateException(); } ++ } catch(...) { DOCTEST_RB.translateException(); } + #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS + + #ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS +-#define DOCTEST_CAST_TO_VOID(x) \ ++#define DOCTEST_CAST_TO_VOID(...) \ + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \ +- static_cast<void>(x); \ ++ static_cast<void>(__VA_ARGS__); \ + DOCTEST_GCC_SUPPRESS_WARNING_POP + #else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS +-#define DOCTEST_CAST_TO_VOID(x) x; ++#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__; + #endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS + + // registers the test by initializing a dummy var with a function + #define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \ +- global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ ++ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \ + doctest::detail::regTest( \ + doctest::detail::TestCase( \ + f, __FILE__, __LINE__, \ + doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ +- decorators); \ +- DOCTEST_GLOBAL_NO_WARNINGS_END() ++ decorators)) + + #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ + namespace { \ +@@ -1832,18 +2004,18 @@ + + #define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \ + static doctest::detail::funcType proxy() { return f; } \ +- DOCTEST_REGISTER_FUNCTION(inline const, proxy(), decorators) \ ++ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \ + static void f() + + // for registering tests + #define DOCTEST_TEST_CASE(decorators) \ +- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) ++ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) + + // for registering tests in classes - requires C++17 for inline variables! + #if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L) + #define DOCTEST_TEST_CASE_CLASS(decorators) \ +- DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), \ +- DOCTEST_ANONYMOUS(_DOCTEST_ANON_PROXY_), \ ++ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \ ++ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \ + decorators) + #else // DOCTEST_TEST_CASE_CLASS + #define DOCTEST_TEST_CASE_CLASS(...) \ +@@ -1852,8 +2024,8 @@ + + // for registering tests with a fixture + #define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ +- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \ +- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) ++ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \ ++ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) + + // for converting types to strings without the <typeinfo> header and demangling + #define DOCTEST_TYPE_TO_STRING_IMPL(...) \ +@@ -1866,7 +2038,7 @@ + DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ + } \ + } \ +- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++ static_assert(true, "") + + #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ + template <typename T> \ +@@ -1897,20 +2069,20 @@ + + #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \ + DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \ +- DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) ++ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)) + + #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \ +- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = \ +- doctest::detail::instantiationHelper(DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0));\ +- DOCTEST_GLOBAL_NO_WARNINGS_END() ++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), \ ++ doctest::detail::instantiationHelper( \ ++ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0))) + + #define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ +- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \ +- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \ ++ static_assert(true, "") + + #define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ +- DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) \ +- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \ ++ static_assert(true, "") + + #define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \ +@@ -1919,11 +2091,11 @@ + static void anon() + + #define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \ +- DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) ++ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) + + // for subcases + #define DOCTEST_SUBCASE(name) \ +- if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ ++ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ + doctest::detail::Subcase(name, __FILE__, __LINE__)) + + // for grouping tests in test suites by using code blocks +@@ -1932,10 +2104,12 @@ + static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \ + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \ +- static doctest::detail::TestSuite data; \ ++ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \ ++ static doctest::detail::TestSuite data{}; \ + static bool inited = false; \ + DOCTEST_MSVC_SUPPRESS_WARNING_POP \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP \ ++ DOCTEST_GCC_SUPPRESS_WARNING_POP \ + if(!inited) { \ + data* decorators; \ + inited = true; \ +@@ -1947,79 +2121,79 @@ + namespace ns_name + + #define DOCTEST_TEST_SUITE(decorators) \ +- DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_)) ++ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_)) + + // for starting a testsuite block + #define DOCTEST_TEST_SUITE_BEGIN(decorators) \ +- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ +- doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \ +- DOCTEST_GLOBAL_NO_WARNINGS_END() \ +- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \ ++ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \ ++ static_assert(true, "") + + // for ending a testsuite block + #define DOCTEST_TEST_SUITE_END \ +- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ +- doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \ +- DOCTEST_GLOBAL_NO_WARNINGS_END() \ +- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \ ++ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \ ++ typedef int DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) + + // for registering exception translators + #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ + inline doctest::String translatorName(signature); \ +- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \ +- doctest::registerExceptionTranslator(translatorName); \ +- DOCTEST_GLOBAL_NO_WARNINGS_END() \ ++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \ ++ doctest::registerExceptionTranslator(translatorName)) \ + doctest::String translatorName(signature) + + #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ +- DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \ ++ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \ + signature) + + // for registering reporters + #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \ +- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \ +- doctest::registerReporter<reporter>(name, priority, true); \ +- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), \ ++ doctest::registerReporter<reporter>(name, priority, true)) \ ++ static_assert(true, "") + + // for registering listeners + #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \ +- DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \ +- doctest::registerReporter<reporter>(name, priority, false); \ +- DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +- +-// for logging +-#define DOCTEST_INFO(expression) \ +- DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \ +- DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), expression) +- +-#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, expression) \ +- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4626) \ +- auto lambda_name = [&](std::ostream* s_name) { \ ++ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), \ ++ doctest::registerReporter<reporter>(name, priority, false)) \ ++ static_assert(true, "") ++ ++// clang-format off ++// for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557 ++#define DOCTEST_INFO(...) \ ++ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \ ++ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \ ++ __VA_ARGS__) ++// clang-format on ++ ++#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \ ++ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \ ++ [&](std::ostream* s_name) { \ + doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \ + mb_name.m_stream = s_name; \ +- mb_name << expression; \ +- }; \ +- DOCTEST_MSVC_SUPPRESS_WARNING_POP \ +- auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope(lambda_name) ++ mb_name * __VA_ARGS__; \ ++ }) + +-#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x) ++#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x) + +-#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \ +- do { \ ++#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \ ++ [&] { \ + doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ +- mb << x; \ +- DOCTEST_ASSERT_LOG_AND_REACT(mb); \ +- } while((void)0, 0) ++ mb * __VA_ARGS__; \ ++ if(mb.log()) \ ++ DOCTEST_BREAK_INTO_DEBUGGER(); \ ++ mb.react(); \ ++ }() + + // clang-format off +-#define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) +-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) +-#define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) ++#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) ++#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) ++#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) + // clang-format on + +-#define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x) +-#define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x) +-#define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x) ++#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__) ++#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__) ++#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__) + + #define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility. + +@@ -2027,21 +2201,24 @@ + + #define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ +- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ ++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ +- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \ ++ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \ + doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ + << __VA_ARGS__)) \ +- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \ ++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP + + #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ +- do { \ ++ [&] { \ + DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ +- } while((void)0, 0) ++ }() + + #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + ++// necessary for <ASSERT>_MESSAGE ++#define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1 ++ + #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ + doctest::detail::decomp_assert( \ +@@ -2059,102 +2236,113 @@ + #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) + + // clang-format off +-#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while((void)0, 0) +-#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while((void)0, 0) +-#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while((void)0, 0) +-#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while((void)0, 0) +-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while((void)0, 0) +-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while((void)0, 0) ++#define DOCTEST_WARN_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); }() ++#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); }() ++#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); }() ++#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); }() ++#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); }() ++#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); }() + // clang-format on + +-#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, ...) \ +- do { \ ++#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \ ++ [&] { \ + if(!doctest::getContextOptions()->no_throw) { \ +- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ +- __LINE__, #expr, #__VA_ARGS__); \ ++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ ++ __LINE__, #expr, #__VA_ARGS__, message); \ + try { \ + DOCTEST_CAST_TO_VOID(expr) \ +- } catch(const doctest::detail::remove_const< \ +- doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ +- _DOCTEST_RB.translateException(); \ +- _DOCTEST_RB.m_threw_as = true; \ +- } catch(...) { _DOCTEST_RB.translateException(); } \ +- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ ++ } catch(const typename doctest::detail::remove_const< \ ++ typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ ++ DOCTEST_RB.translateException(); \ ++ DOCTEST_RB.m_threw_as = true; \ ++ } catch(...) { DOCTEST_RB.translateException(); } \ ++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ ++ } else { \ ++ return false; \ + } \ +- } while((void)0, 0) ++ }() + +-#define DOCTEST_ASSERT_THROWS_WITH(expr, assert_type, ...) \ +- do { \ ++#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ ++ [&] { \ + if(!doctest::getContextOptions()->no_throw) { \ +- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ +- __LINE__, #expr, __VA_ARGS__); \ ++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ ++ __LINE__, expr_str, "", __VA_ARGS__); \ + try { \ + DOCTEST_CAST_TO_VOID(expr) \ +- } catch(...) { _DOCTEST_RB.translateException(); } \ +- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ ++ } catch(...) { DOCTEST_RB.translateException(); } \ ++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ ++ } else { \ ++ return false; \ + } \ +- } while((void)0, 0) ++ }() + +-#define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \ +- do { \ +- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ +- __LINE__, #expr); \ ++#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ ++ [&] { \ ++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ ++ __LINE__, #__VA_ARGS__); \ + try { \ +- DOCTEST_CAST_TO_VOID(expr) \ +- } catch(...) { _DOCTEST_RB.translateException(); } \ +- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ +- } while((void)0, 0) ++ DOCTEST_CAST_TO_VOID(__VA_ARGS__) \ ++ } catch(...) { DOCTEST_RB.translateException(); } \ ++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ ++ }() + + // clang-format off +-#define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS, "") +-#define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS, "") +-#define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS, "") +- +-#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, __VA_ARGS__) +-#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, __VA_ARGS__) +-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, __VA_ARGS__) +- +-#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS_WITH, __VA_ARGS__) +-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) +-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) +- +-#define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW) +-#define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW) +-#define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW) +- +-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while((void)0, 0) +-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while((void)0, 0) +-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while((void)0, 0) +-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while((void)0, 0) +-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while((void)0, 0) +-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while((void)0, 0) +-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH(expr, ex); } while((void)0, 0) +-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH(expr, ex); } while((void)0, 0) +-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH(expr, ex); } while((void)0, 0) +-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while((void)0, 0) +-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while((void)0, 0) +-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while((void)0, 0) ++#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "") ++#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "") ++#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "") ++ ++#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__) ++#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__) ++#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__) ++ ++#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__) ++#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) ++#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) ++ ++#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__) ++#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__) ++#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__) ++ ++#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__) ++#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__) ++#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__) ++ ++#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); }() ++#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); }() ++#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); }() ++#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); }() ++#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); }() ++#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); }() ++#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); }() ++#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); }() ++#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); }() ++#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); }() ++#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); }() ++#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); }() ++#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); }() ++#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); }() ++#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); }() + // clang-format on + + #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS + + #define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \ +- do { \ +- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ ++ [&] { \ ++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + DOCTEST_WRAP_IN_TRY( \ +- _DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \ ++ DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \ + __VA_ARGS__)) \ +- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ +- } while((void)0, 0) ++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ ++ }() + + #define DOCTEST_UNARY_ASSERT(assert_type, ...) \ +- do { \ +- doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ ++ [&] { \ ++ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ +- DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \ +- DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ +- } while((void)0, 0) ++ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \ ++ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ ++ }() + + #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +@@ -2205,6 +2393,9 @@ + #undef DOCTEST_WARN_THROWS_WITH + #undef DOCTEST_CHECK_THROWS_WITH + #undef DOCTEST_REQUIRE_THROWS_WITH ++#undef DOCTEST_WARN_THROWS_WITH_AS ++#undef DOCTEST_CHECK_THROWS_WITH_AS ++#undef DOCTEST_REQUIRE_THROWS_WITH_AS + #undef DOCTEST_WARN_NOTHROW + #undef DOCTEST_CHECK_NOTHROW + #undef DOCTEST_REQUIRE_NOTHROW +@@ -2218,37 +2409,46 @@ + #undef DOCTEST_WARN_THROWS_WITH_MESSAGE + #undef DOCTEST_CHECK_THROWS_WITH_MESSAGE + #undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE ++#undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE ++#undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE ++#undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE + #undef DOCTEST_WARN_NOTHROW_MESSAGE + #undef DOCTEST_CHECK_NOTHROW_MESSAGE + #undef DOCTEST_REQUIRE_NOTHROW_MESSAGE + + #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +-#define DOCTEST_WARN_THROWS(expr) ((void)0) +-#define DOCTEST_CHECK_THROWS(expr) ((void)0) +-#define DOCTEST_REQUIRE_THROWS(expr) ((void)0) +-#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) +-#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) +-#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) +-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) +-#define DOCTEST_WARN_NOTHROW(expr) ((void)0) +-#define DOCTEST_CHECK_NOTHROW(expr) ((void)0) +-#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) +- +-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) ++#define DOCTEST_WARN_THROWS(...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS(...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_AS(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_AS(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) ++#define DOCTEST_WARN_NOTHROW(...) ([] { return false; }) ++#define DOCTEST_CHECK_NOTHROW(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_NOTHROW(...) ([] { return false; }) ++ ++#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) ++#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) + + #else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +@@ -2290,35 +2490,32 @@ + + // for registering tests + #define DOCTEST_TEST_CASE(name) \ +- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) ++ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) + + // for registering tests in classes + #define DOCTEST_TEST_CASE_CLASS(name) \ +- DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) ++ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) + + // for registering tests with a fixture + #define DOCTEST_TEST_CASE_FIXTURE(x, name) \ +- DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \ +- DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) ++ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \ ++ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) + + // for converting types to strings without the <typeinfo> header and demangling +-#define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++#define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "") + #define DOCTEST_TYPE_TO_STRING_IMPL(...) + + // for typed tests + #define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ + template <typename type> \ +- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() ++ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() + + #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ + template <typename type> \ +- inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() ++ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() + +-#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ +- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +- +-#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ +- typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "") ++#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "") + + // for subcases + #define DOCTEST_SUBCASE(name) +@@ -2327,92 +2524,159 @@ + #define DOCTEST_TEST_SUITE(name) namespace + + // for starting a testsuite block +-#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++#define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "") + + // for ending a testsuite block +-#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) ++#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) + + #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ + template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \ +- static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature) ++ static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature) + + #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) + #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) + +-#define DOCTEST_INFO(x) ((void)0) +-#define DOCTEST_CAPTURE(x) ((void)0) +-#define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0) +-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0) +-#define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0) +-#define DOCTEST_MESSAGE(x) ((void)0) +-#define DOCTEST_FAIL_CHECK(x) ((void)0) +-#define DOCTEST_FAIL(x) ((void)0) +- +-#define DOCTEST_WARN(...) ((void)0) +-#define DOCTEST_CHECK(...) ((void)0) +-#define DOCTEST_REQUIRE(...) ((void)0) +-#define DOCTEST_WARN_FALSE(...) ((void)0) +-#define DOCTEST_CHECK_FALSE(...) ((void)0) +-#define DOCTEST_REQUIRE_FALSE(...) ((void)0) +- +-#define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0) +-#define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0) +-#define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0) +-#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0) +-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0) +-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0) +- +-#define DOCTEST_WARN_THROWS(expr) ((void)0) +-#define DOCTEST_CHECK_THROWS(expr) ((void)0) +-#define DOCTEST_REQUIRE_THROWS(expr) ((void)0) +-#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) +-#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) +-#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) +-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) +-#define DOCTEST_WARN_NOTHROW(expr) ((void)0) +-#define DOCTEST_CHECK_NOTHROW(expr) ((void)0) +-#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) +- +-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, ex, msg) ((void)0) +-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) +-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) +- +-#define DOCTEST_WARN_EQ(...) ((void)0) +-#define DOCTEST_CHECK_EQ(...) ((void)0) +-#define DOCTEST_REQUIRE_EQ(...) ((void)0) +-#define DOCTEST_WARN_NE(...) ((void)0) +-#define DOCTEST_CHECK_NE(...) ((void)0) +-#define DOCTEST_REQUIRE_NE(...) ((void)0) +-#define DOCTEST_WARN_GT(...) ((void)0) +-#define DOCTEST_CHECK_GT(...) ((void)0) +-#define DOCTEST_REQUIRE_GT(...) ((void)0) +-#define DOCTEST_WARN_LT(...) ((void)0) +-#define DOCTEST_CHECK_LT(...) ((void)0) +-#define DOCTEST_REQUIRE_LT(...) ((void)0) +-#define DOCTEST_WARN_GE(...) ((void)0) +-#define DOCTEST_CHECK_GE(...) ((void)0) +-#define DOCTEST_REQUIRE_GE(...) ((void)0) +-#define DOCTEST_WARN_LE(...) ((void)0) +-#define DOCTEST_CHECK_LE(...) ((void)0) +-#define DOCTEST_REQUIRE_LE(...) ((void)0) +- +-#define DOCTEST_WARN_UNARY(...) ((void)0) +-#define DOCTEST_CHECK_UNARY(...) ((void)0) +-#define DOCTEST_REQUIRE_UNARY(...) ((void)0) +-#define DOCTEST_WARN_UNARY_FALSE(...) ((void)0) +-#define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0) +-#define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0) ++#define DOCTEST_INFO(...) (static_cast<void>(0)) ++#define DOCTEST_CAPTURE(x) (static_cast<void>(0)) ++#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast<void>(0)) ++#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast<void>(0)) ++#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast<void>(0)) ++#define DOCTEST_MESSAGE(...) (static_cast<void>(0)) ++#define DOCTEST_FAIL_CHECK(...) (static_cast<void>(0)) ++#define DOCTEST_FAIL(...) (static_cast<void>(0)) ++ ++#ifdef DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED ++ ++#define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }() ++#define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }() ++#define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }() ++#define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }() ++#define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }() ++ ++#define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }() ++#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }() ++#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }() ++#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() ++#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() ++#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() ++ ++namespace doctest { ++namespace detail { ++#define DOCTEST_RELATIONAL_OP(name, op) \ ++ template <typename L, typename R> \ ++ bool name(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs op rhs; } ++ ++ DOCTEST_RELATIONAL_OP(eq, ==) ++ DOCTEST_RELATIONAL_OP(ne, !=) ++ DOCTEST_RELATIONAL_OP(lt, <) ++ DOCTEST_RELATIONAL_OP(gt, >) ++ DOCTEST_RELATIONAL_OP(le, <=) ++ DOCTEST_RELATIONAL_OP(ge, >=) ++} // namespace detail ++} // namespace doctest ++ ++#define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() ++#define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() ++#define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() ++#define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() ++#define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() ++#define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() ++#define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() ++#define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() ++#define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() ++#define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() ++#define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() ++#define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() ++#define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }() ++#define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }() ++#define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }() ++#define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() ++#define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() ++#define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() ++ ++#else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED ++ ++#define DOCTEST_WARN(...) ([] { return false; }) ++#define DOCTEST_CHECK(...) ([] { return false; }) ++#define DOCTEST_REQUIRE(...) ([] { return false; }) ++#define DOCTEST_WARN_FALSE(...) ([] { return false; }) ++#define DOCTEST_CHECK_FALSE(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_FALSE(...) ([] { return false; }) ++ ++#define DOCTEST_WARN_MESSAGE(cond, ...) ([] { return false; }) ++#define DOCTEST_CHECK_MESSAGE(cond, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_MESSAGE(cond, ...) ([] { return false; }) ++#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) ([] { return false; }) ++#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) ([] { return false; }) ++ ++#define DOCTEST_WARN_EQ(...) ([] { return false; }) ++#define DOCTEST_CHECK_EQ(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_EQ(...) ([] { return false; }) ++#define DOCTEST_WARN_NE(...) ([] { return false; }) ++#define DOCTEST_CHECK_NE(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_NE(...) ([] { return false; }) ++#define DOCTEST_WARN_GT(...) ([] { return false; }) ++#define DOCTEST_CHECK_GT(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_GT(...) ([] { return false; }) ++#define DOCTEST_WARN_LT(...) ([] { return false; }) ++#define DOCTEST_CHECK_LT(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_LT(...) ([] { return false; }) ++#define DOCTEST_WARN_GE(...) ([] { return false; }) ++#define DOCTEST_CHECK_GE(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_GE(...) ([] { return false; }) ++#define DOCTEST_WARN_LE(...) ([] { return false; }) ++#define DOCTEST_CHECK_LE(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_LE(...) ([] { return false; }) ++ ++#define DOCTEST_WARN_UNARY(...) ([] { return false; }) ++#define DOCTEST_CHECK_UNARY(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_UNARY(...) ([] { return false; }) ++#define DOCTEST_WARN_UNARY_FALSE(...) ([] { return false; }) ++#define DOCTEST_CHECK_UNARY_FALSE(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_UNARY_FALSE(...) ([] { return false; }) ++ ++#endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED ++ ++// TODO: think about if these also need to work properly even when doctest is disabled ++#define DOCTEST_WARN_THROWS(...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS(...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_AS(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_AS(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) ++#define DOCTEST_WARN_NOTHROW(...) ([] { return false; }) ++#define DOCTEST_CHECK_NOTHROW(...) ([] { return false; }) ++#define DOCTEST_REQUIRE_NOTHROW(...) ([] { return false; }) ++ ++#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) ++#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) ++#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) ++#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) ++#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) + + #endif // DOCTEST_CONFIG_DISABLE + +@@ -2444,7 +2708,7 @@ + #define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE + #define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE + +-#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INVOKE ++#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__) + // clang-format on + + // BDD style macros +@@ -2464,132 +2728,138 @@ + // == SHORT VERSIONS OF THE MACROS + #if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) + +-#define TEST_CASE DOCTEST_TEST_CASE +-#define TEST_CASE_CLASS DOCTEST_TEST_CASE_CLASS +-#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE +-#define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING +-#define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE +-#define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE +-#define TEST_CASE_TEMPLATE_INVOKE DOCTEST_TEST_CASE_TEMPLATE_INVOKE +-#define TEST_CASE_TEMPLATE_APPLY DOCTEST_TEST_CASE_TEMPLATE_APPLY +-#define SUBCASE DOCTEST_SUBCASE +-#define TEST_SUITE DOCTEST_TEST_SUITE +-#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN ++#define TEST_CASE(name) DOCTEST_TEST_CASE(name) ++#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) ++#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) ++#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) ++#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) ++#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) ++#define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__) ++#define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__) ++#define SUBCASE(name) DOCTEST_SUBCASE(name) ++#define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators) ++#define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name) + #define TEST_SUITE_END DOCTEST_TEST_SUITE_END +-#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR +-#define REGISTER_REPORTER DOCTEST_REGISTER_REPORTER +-#define REGISTER_LISTENER DOCTEST_REGISTER_LISTENER +-#define INFO DOCTEST_INFO +-#define CAPTURE DOCTEST_CAPTURE +-#define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT +-#define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT +-#define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT +-#define MESSAGE DOCTEST_MESSAGE +-#define FAIL_CHECK DOCTEST_FAIL_CHECK +-#define FAIL DOCTEST_FAIL +-#define TO_LVALUE DOCTEST_TO_LVALUE +- +-#define WARN DOCTEST_WARN +-#define WARN_FALSE DOCTEST_WARN_FALSE +-#define WARN_THROWS DOCTEST_WARN_THROWS +-#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS +-#define WARN_THROWS_WITH DOCTEST_WARN_THROWS_WITH +-#define WARN_NOTHROW DOCTEST_WARN_NOTHROW +-#define CHECK DOCTEST_CHECK +-#define CHECK_FALSE DOCTEST_CHECK_FALSE +-#define CHECK_THROWS DOCTEST_CHECK_THROWS +-#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS +-#define CHECK_THROWS_WITH DOCTEST_CHECK_THROWS_WITH +-#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW +-#define REQUIRE DOCTEST_REQUIRE +-#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE +-#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS +-#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS +-#define REQUIRE_THROWS_WITH DOCTEST_REQUIRE_THROWS_WITH +-#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW +- +-#define WARN_MESSAGE DOCTEST_WARN_MESSAGE +-#define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE +-#define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE +-#define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE +-#define WARN_THROWS_WITH_MESSAGE DOCTEST_WARN_THROWS_WITH_MESSAGE +-#define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE +-#define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE +-#define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE +-#define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE +-#define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE +-#define CHECK_THROWS_WITH_MESSAGE DOCTEST_CHECK_THROWS_WITH_MESSAGE +-#define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE +-#define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE +-#define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE +-#define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE +-#define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE +-#define REQUIRE_THROWS_WITH_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_MESSAGE +-#define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE +- +-#define SCENARIO DOCTEST_SCENARIO +-#define SCENARIO_CLASS DOCTEST_SCENARIO_CLASS +-#define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE +-#define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE +-#define GIVEN DOCTEST_GIVEN +-#define WHEN DOCTEST_WHEN +-#define AND_WHEN DOCTEST_AND_WHEN +-#define THEN DOCTEST_THEN +-#define AND_THEN DOCTEST_AND_THEN +- +-#define WARN_EQ DOCTEST_WARN_EQ +-#define CHECK_EQ DOCTEST_CHECK_EQ +-#define REQUIRE_EQ DOCTEST_REQUIRE_EQ +-#define WARN_NE DOCTEST_WARN_NE +-#define CHECK_NE DOCTEST_CHECK_NE +-#define REQUIRE_NE DOCTEST_REQUIRE_NE +-#define WARN_GT DOCTEST_WARN_GT +-#define CHECK_GT DOCTEST_CHECK_GT +-#define REQUIRE_GT DOCTEST_REQUIRE_GT +-#define WARN_LT DOCTEST_WARN_LT +-#define CHECK_LT DOCTEST_CHECK_LT +-#define REQUIRE_LT DOCTEST_REQUIRE_LT +-#define WARN_GE DOCTEST_WARN_GE +-#define CHECK_GE DOCTEST_CHECK_GE +-#define REQUIRE_GE DOCTEST_REQUIRE_GE +-#define WARN_LE DOCTEST_WARN_LE +-#define CHECK_LE DOCTEST_CHECK_LE +-#define REQUIRE_LE DOCTEST_REQUIRE_LE +-#define WARN_UNARY DOCTEST_WARN_UNARY +-#define CHECK_UNARY DOCTEST_CHECK_UNARY +-#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY +-#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE +-#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE +-#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE ++#define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) ++#define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter) ++#define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter) ++#define INFO(...) DOCTEST_INFO(__VA_ARGS__) ++#define CAPTURE(x) DOCTEST_CAPTURE(x) ++#define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__) ++#define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__) ++#define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__) ++#define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__) ++#define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__) ++#define FAIL(...) DOCTEST_FAIL(__VA_ARGS__) ++#define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__) ++ ++#define WARN(...) DOCTEST_WARN(__VA_ARGS__) ++#define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__) ++#define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__) ++#define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__) ++#define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__) ++#define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__) ++#define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__) ++#define CHECK(...) DOCTEST_CHECK(__VA_ARGS__) ++#define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__) ++#define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__) ++#define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__) ++#define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__) ++#define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__) ++#define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__) ++#define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__) ++#define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__) ++#define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__) ++#define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__) ++#define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__) ++#define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__) ++#define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__) ++ ++#define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__) ++#define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__) ++#define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__) ++#define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) ++#define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) ++#define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) ++#define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__) ++#define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__) ++#define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__) ++#define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__) ++#define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) ++#define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) ++#define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) ++#define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__) ++#define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__) ++#define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__) ++#define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__) ++#define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) ++#define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) ++#define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) ++#define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__) ++ ++#define SCENARIO(name) DOCTEST_SCENARIO(name) ++#define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name) ++#define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__) ++#define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) ++#define GIVEN(name) DOCTEST_GIVEN(name) ++#define WHEN(name) DOCTEST_WHEN(name) ++#define AND_WHEN(name) DOCTEST_AND_WHEN(name) ++#define THEN(name) DOCTEST_THEN(name) ++#define AND_THEN(name) DOCTEST_AND_THEN(name) ++ ++#define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__) ++#define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__) ++#define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__) ++#define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__) ++#define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__) ++#define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__) ++#define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__) ++#define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__) ++#define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__) ++#define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__) ++#define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__) ++#define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__) ++#define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__) ++#define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__) ++#define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__) ++#define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__) ++#define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__) ++#define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__) ++#define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__) ++#define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__) ++#define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__) ++#define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__) ++#define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__) ++#define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__) + + // KEPT FOR BACKWARDS COMPATIBILITY +-#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ +-#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ +-#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ +-#define FAST_WARN_NE DOCTEST_FAST_WARN_NE +-#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE +-#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE +-#define FAST_WARN_GT DOCTEST_FAST_WARN_GT +-#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT +-#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT +-#define FAST_WARN_LT DOCTEST_FAST_WARN_LT +-#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT +-#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT +-#define FAST_WARN_GE DOCTEST_FAST_WARN_GE +-#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE +-#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE +-#define FAST_WARN_LE DOCTEST_FAST_WARN_LE +-#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE +-#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE +- +-#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY +-#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY +-#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY +-#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE +-#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE +-#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE ++#define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__) ++#define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__) ++#define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__) ++#define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__) ++#define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__) ++#define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__) ++#define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__) ++#define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__) ++#define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__) ++#define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__) ++#define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__) ++#define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__) ++#define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__) ++#define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__) ++#define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__) ++#define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__) ++#define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__) ++#define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__) ++ ++#define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__) ++#define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__) ++#define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__) ++#define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__) ++#define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__) ++#define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__) + +-#define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE ++#define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__) + + #endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES + +@@ -2626,6 +2896,8 @@ + DOCTEST_MSVC_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + ++DOCTEST_SUPPRESS_COMMON_WARNINGS_POP ++ + #endif // DOCTEST_LIBRARY_INCLUDED + + #ifndef DOCTEST_SINGLE_HEADER +@@ -2645,13 +2917,11 @@ + + DOCTEST_CLANG_SUPPRESS_WARNING_POP + ++DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH ++ + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") +@@ -2659,64 +2929,35 @@ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") +-DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") ++DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") + + DOCTEST_GCC_SUPPRESS_WARNING_PUSH +-DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") + DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") +-DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") + DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") + DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") + DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") +-DOCTEST_GCC_SUPPRESS_WARNING("-Winline") + DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") + DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") + DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") + DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") + DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") + DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function") + DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance") +-DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") + DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute") + + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +-DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning +-DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning +-DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration + DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data +-DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression +-DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated +-DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant + DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled + DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified + DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal + DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch +-DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs +-DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe + DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C +-DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff +-DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted +-DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted +-DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted +-DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted + DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning) +-// static analysis +-DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept' +-DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable +-DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ... +-DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtor... ++DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed + + DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN + +@@ -2724,7 +2965,7 @@ + #include <ctime> + #include <cmath> + #include <climits> +-// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37 ++// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37 + #ifdef __BORLANDC__ + #include <math.h> + #endif // __BORLANDC__ +@@ -2746,9 +2987,7 @@ + #include <map> + #include <exception> + #include <stdexcept> +-#ifdef DOCTEST_CONFIG_POSIX_SIGNALS + #include <csignal> +-#endif // DOCTEST_CONFIG_POSIX_SIGNALS + #include <cfloat> + #include <cctype> + #include <cstdint> +@@ -2773,7 +3012,7 @@ + #ifdef __AFXDLL + #include <AfxWin.h> + #else +-#include <Windows.h> ++#include <windows.h> + #endif + #include <io.h> + +@@ -2784,6 +3023,12 @@ + + #endif // DOCTEST_PLATFORM_WINDOWS + ++// this is a fix for https://github.com/doctest/doctest/issues/348 ++// https://mail.gnome.org/archives/xml/2012-January/msg00000.html ++#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) ++#define STDOUT_FILENO fileno(stdout) ++#endif // HAVE_UNISTD_H ++ + DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END + + // counts the number of elements in a C array +@@ -2800,7 +3045,19 @@ + #endif + + #ifndef DOCTEST_THREAD_LOCAL ++#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) ++#define DOCTEST_THREAD_LOCAL ++#else // DOCTEST_MSVC + #define DOCTEST_THREAD_LOCAL thread_local ++#endif // DOCTEST_MSVC ++#endif // DOCTEST_THREAD_LOCAL ++ ++#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES ++#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32 ++#endif ++ ++#ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE ++#define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64 + #endif + + #ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS +@@ -2809,12 +3066,38 @@ + #define DOCTEST_OPTIONS_PREFIX_DISPLAY "" + #endif + ++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) ++#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS ++#endif ++ ++#ifndef DOCTEST_CDECL ++#define DOCTEST_CDECL __cdecl ++#endif ++ + namespace doctest { + + bool is_running_in_test = false; + + namespace { + using namespace detail; ++ ++ template <typename Ex> ++ DOCTEST_NORETURN void throw_exception(Ex const& e) { ++#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS ++ throw e; ++#else // DOCTEST_CONFIG_NO_EXCEPTIONS ++ std::cerr << "doctest will terminate because it needed to throw an exception.\n" ++ << "The message was: " << e.what() << '\n'; ++ std::terminate(); ++#endif // DOCTEST_CONFIG_NO_EXCEPTIONS ++ } ++ ++#ifndef DOCTEST_INTERNAL_ERROR ++#define DOCTEST_INTERNAL_ERROR(msg) \ ++ throw_exception(std::logic_error( \ ++ __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) ++#endif // DOCTEST_INTERNAL_ERROR ++ + // case insensitive strcmp + int stricmp(const char* a, const char* b) { + for(;; a++, b++) { +@@ -2858,8 +3141,6 @@ + } // namespace + + namespace detail { +- void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); } +- + String rawMemoryToString(const void* object, unsigned size) { + // Reverse order for little endian architectures + int i = 0, end = static_cast<int>(size), inc = 1; +@@ -2869,49 +3150,76 @@ + } + + unsigned const char* bytes = static_cast<unsigned const char*>(object); +- std::ostringstream oss; +- oss << "0x" << std::setfill('0') << std::hex; ++ std::ostream* oss = tlssPush(); ++ *oss << "0x" << std::setfill('0') << std::hex; + for(; i != end; i += inc) +- oss << std::setw(2) << static_cast<unsigned>(bytes[i]); +- return oss.str().c_str(); ++ *oss << std::setw(2) << static_cast<unsigned>(bytes[i]); ++ return tlssPop(); + } + +- DOCTEST_THREAD_LOCAL std::ostringstream g_oss; // NOLINT(cert-err58-cpp) ++ DOCTEST_THREAD_LOCAL class ++ { ++ std::vector<std::streampos> stack; ++ std::stringstream ss; ++ ++ public: ++ std::ostream* push() { ++ stack.push_back(ss.tellp()); ++ return &ss; ++ } ++ ++ String pop() { ++ if (stack.empty()) ++ DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!"); ++ ++ std::streampos pos = stack.back(); ++ stack.pop_back(); ++ unsigned sz = static_cast<unsigned>(ss.tellp() - pos); ++ ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out); ++ return String(ss, sz); ++ } ++ } g_oss; + +- std::ostream* getTlsOss() { +- g_oss.clear(); // there shouldn't be anything worth clearing in the flags +- g_oss.str(""); // the slow way of resetting a string stream +- //g_oss.seekp(0); // optimal reset - as seen here: https://stackoverflow.com/a/624291/3162383 +- return &g_oss; ++ std::ostream* tlssPush() { ++ return g_oss.push(); + } + +- String getTlsOssResult() { +- //g_oss << std::ends; // needed - as shown here: https://stackoverflow.com/a/624291/3162383 +- return g_oss.str().c_str(); ++ String tlssPop() { ++ return g_oss.pop(); + } + + #ifndef DOCTEST_CONFIG_DISABLE + +- typedef uint64_t UInt64; ++namespace timer_large_integer ++{ ++ ++#if defined(DOCTEST_PLATFORM_WINDOWS) ++ typedef ULONGLONG type; ++#else // DOCTEST_PLATFORM_WINDOWS ++ typedef std::uint64_t type; ++#endif // DOCTEST_PLATFORM_WINDOWS ++} ++ ++typedef timer_large_integer::type ticks_t; + + #ifdef DOCTEST_CONFIG_GETCURRENTTICKS +- UInt64 getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } ++ ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } + #elif defined(DOCTEST_PLATFORM_WINDOWS) +- UInt64 getCurrentTicks() { +- static UInt64 hz = 0, hzo = 0; +- if(!hz) { +- QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&hz)); +- QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&hzo)); +- } +- UInt64 t; +- QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&t)); +- return ((t - hzo) * 1000000) / hz; ++ ticks_t getCurrentTicks() { ++ static LARGE_INTEGER hz = {0}, hzo = {0}; ++ if(!hz.QuadPart) { ++ QueryPerformanceFrequency(&hz); ++ QueryPerformanceCounter(&hzo); ++ } ++ LARGE_INTEGER t; ++ QueryPerformanceCounter(&t); ++ return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart; + } + #else // DOCTEST_PLATFORM_WINDOWS +- UInt64 getCurrentTicks() { ++ ticks_t getCurrentTicks() { + timeval t; + gettimeofday(&t, nullptr); +- return static_cast<UInt64>(t.tv_sec) * 1000000 + static_cast<UInt64>(t.tv_usec); ++ return static_cast<ticks_t>(t.tv_sec) * 1000000 + static_cast<ticks_t>(t.tv_usec); + } + #endif // DOCTEST_PLATFORM_WINDOWS + +@@ -2924,24 +3232,111 @@ + //unsigned int getElapsedMilliseconds() const { + // return static_cast<unsigned int>(getElapsedMicroseconds() / 1000); + //} +- double getElapsedSeconds() const { return getElapsedMicroseconds() / 1000000.0; } ++ double getElapsedSeconds() const { return static_cast<double>(getCurrentTicks() - m_ticks) / 1000000.0; } ++ ++ private: ++ ticks_t m_ticks = 0; ++ }; ++ ++#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS ++ template <typename T> ++ using AtomicOrMultiLaneAtomic = std::atomic<T>; ++#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS ++ // Provides a multilane implementation of an atomic variable that supports add, sub, load, ++ // store. Instead of using a single atomic variable, this splits up into multiple ones, ++ // each sitting on a separate cache line. The goal is to provide a speedup when most ++ // operations are modifying. It achieves this with two properties: ++ // ++ // * Multiple atomics are used, so chance of congestion from the same atomic is reduced. ++ // * Each atomic sits on a separate cache line, so false sharing is reduced. ++ // ++ // The disadvantage is that there is a small overhead due to the use of TLS, and load/store ++ // is slower because all atomics have to be accessed. ++ template <typename T> ++ class MultiLaneAtomic ++ { ++ struct CacheLineAlignedAtomic ++ { ++ std::atomic<T> atomic{}; ++ char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)]; ++ }; ++ CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES]; ++ ++ static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE, ++ "guarantee one atomic takes exactly one cache line"); ++ ++ public: ++ T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; } ++ ++ T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); } ++ ++ T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { ++ return myAtomic().fetch_add(arg, order); ++ } ++ ++ T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { ++ return myAtomic().fetch_sub(arg, order); ++ } ++ ++ operator T() const DOCTEST_NOEXCEPT { return load(); } ++ ++ T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT { ++ auto result = T(); ++ for(auto const& c : m_atomics) { ++ result += c.atomic.load(order); ++ } ++ return result; ++ } ++ ++ T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this] ++ store(desired); ++ return desired; ++ } ++ ++ void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { ++ // first value becomes desired", all others become 0. ++ for(auto& c : m_atomics) { ++ c.atomic.store(desired, order); ++ desired = {}; ++ } ++ } + + private: +- UInt64 m_ticks = 0; ++ // Each thread has a different atomic that it operates on. If more than NumLanes threads ++ // use this, some will use the same atomic. So performance will degrade a bit, but still ++ // everything will work. ++ // ++ // The logic here is a bit tricky. The call should be as fast as possible, so that there ++ // is minimal to no overhead in determining the correct atomic for the current thread. ++ // ++ // 1. A global static counter laneCounter counts continuously up. ++ // 2. Each successive thread will use modulo operation of that counter so it gets an atomic ++ // assigned in a round-robin fashion. ++ // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with ++ // little overhead. ++ std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT { ++ static std::atomic<size_t> laneCounter; ++ DOCTEST_THREAD_LOCAL size_t tlsLaneIdx = ++ laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES; ++ ++ return m_atomics[tlsLaneIdx].atomic; ++ } + }; + ++ template <typename T> ++ using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>; ++#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS ++ + // this holds both parameters from the command line and runtime data for tests + struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats + { +- std::atomic<int> numAssertsCurrentTest_atomic; +- std::atomic<int> numAssertsFailedCurrentTest_atomic; ++ AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic; ++ AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic; + + std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters + + std::vector<IReporter*> reporters_currently_used; + +- const TestCase* currentTest = nullptr; +- + assert_handler ah = nullptr; + + Timer timer; +@@ -2949,10 +3344,11 @@ + std::vector<String> stringifiedContexts; // logging from INFO() due to an exception + + // stuff for subcases +- std::set<SubcaseSignature> subcasesPassed; +- std::set<int> subcasesEnteredLevels; +- int subcasesCurrentLevel; +- bool should_reenter; ++ std::vector<SubcaseSignature> subcasesStack; ++ std::set<decltype(subcasesStack)> subcasesPassed; ++ int subcasesCurrentMaxLevel; ++ bool should_reenter; ++ std::atomic<bool> shouldLogCurrentException; + + void resetRunData() { + numTestCases = 0; +@@ -3002,7 +3398,8 @@ + (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags); + + // if any subcase has failed - the whole test case has failed +- if(failure_flags && !ok_to_fail) ++ testCaseSuccess = !(failure_flags && !ok_to_fail); ++ if(!testCaseSuccess) + numTestCasesFailed++; + } + }; +@@ -3017,6 +3414,21 @@ + #endif // DOCTEST_CONFIG_DISABLE + } // namespace detail + ++char* String::allocate(unsigned sz) { ++ if (sz <= last) { ++ buf[sz] = '\0'; ++ setLast(last - sz); ++ return buf; ++ } else { ++ setOnHeap(); ++ data.size = sz; ++ data.capacity = data.size + 1; ++ data.ptr = new char[data.capacity]; ++ data.ptr[sz] = '\0'; ++ return data.ptr; ++ } ++} ++ + void String::setOnHeap() { *reinterpret_cast<unsigned char*>(&buf[last]) = 128; } + void String::setLast(unsigned in) { buf[last] = char(in); } + +@@ -3024,11 +3436,7 @@ + if(other.isOnStack()) { + memcpy(buf, other.buf, len); + } else { +- setOnHeap(); +- data.size = other.data.size; +- data.capacity = data.size + 1; +- data.ptr = new char[data.capacity]; +- memcpy(data.ptr, other.data.ptr, data.size + 1); ++ memcpy(allocate(other.data.size), other.data.ptr, other.data.size); + } + } + +@@ -3040,22 +3448,18 @@ + String::~String() { + if(!isOnStack()) + delete[] data.ptr; ++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + } + + String::String(const char* in) + : String(in, strlen(in)) {} + + String::String(const char* in, unsigned in_size) { +- if(in_size <= last) { +- memcpy(buf, in, in_size + 1); +- setLast(last - in_size); +- } else { +- setOnHeap(); +- data.size = in_size; +- data.capacity = data.size + 1; +- data.ptr = new char[data.capacity]; +- memcpy(data.ptr, in, in_size + 1); +- } ++ memcpy(allocate(in_size), in, in_size); ++} ++ ++String::String(std::istream& in, unsigned in_size) { ++ in.read(allocate(in_size), in_size); + } + + String::String(const String& other) { copy(other); } +@@ -3079,6 +3483,7 @@ + if(total_size < len) { + // append to the current stack space + memcpy(buf + my_old_size, other.c_str(), other_size + 1); ++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + setLast(last - total_size); + } else { + // alloc new chunk +@@ -3120,8 +3525,6 @@ + return *this; + } + +-String String::operator+(const String& other) const { return String(*this) += other; } +- + String::String(String&& other) { + memcpy(buf, other.buf, len); + other.buf[0] = '\0'; +@@ -3165,7 +3568,7 @@ + + int String::compare(const char* other, bool no_case) const { + if(no_case) +- return stricmp(c_str(), other); ++ return doctest::stricmp(c_str(), other); + return std::strcmp(c_str(), other); + } + +@@ -3173,6 +3576,9 @@ + return compare(other.c_str(), no_case); + } + ++// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) ++String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; } ++ + // clang-format off + bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } + bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } +@@ -3219,6 +3625,10 @@ + case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH"; + case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH"; + ++ case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS"; ++ case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS"; ++ case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS"; ++ + case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; + case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; + case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; +@@ -3268,6 +3678,7 @@ + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") + // depending on the current options this will remove the path of filenames + const char* skipPathFromFilename(const char* file) { ++#ifndef DOCTEST_CONFIG_DISABLE + if(getContextOptions()->no_path_in_filenames) { + auto back = std::strrchr(file, '\\'); + auto forward = std::strrchr(file, '/'); +@@ -3277,41 +3688,26 @@ + return forward + 1; + } + } ++#endif // DOCTEST_CONFIG_DISABLE + return file; + } + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + +-DOCTEST_DEFINE_DEFAULTS(TestCaseData); +-DOCTEST_DEFINE_COPIES(TestCaseData); +- +-DOCTEST_DEFINE_DEFAULTS(AssertData); +- +-DOCTEST_DEFINE_DEFAULTS(MessageData); +- +-SubcaseSignature::SubcaseSignature(const char* name, const char* file, int line) +- : m_name(name) +- , m_file(file) +- , m_line(line) {} +- +-DOCTEST_DEFINE_DEFAULTS(SubcaseSignature); +-DOCTEST_DEFINE_COPIES(SubcaseSignature); +- + bool SubcaseSignature::operator<(const SubcaseSignature& other) const { + if(m_line != other.m_line) + return m_line < other.m_line; + if(std::strcmp(m_file, other.m_file) != 0) + return std::strcmp(m_file, other.m_file) < 0; +- return std::strcmp(m_name, other.m_name) < 0; ++ return m_name.compare(other.m_name) < 0; + } + + IContextScope::IContextScope() = default; + IContextScope::~IContextScope() = default; + +-DOCTEST_DEFINE_DEFAULTS(ContextOptions); +- + #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + String toString(char* in) { return toString(static_cast<const char*>(in)); } ++// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } + #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + String toString(bool in) { return in ? "true" : "false"; } +@@ -3341,7 +3737,7 @@ + String toString(std::nullptr_t) { return "NULL"; } + + #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +-// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 ++// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 + String toString(const std::string& in) { return in.c_str(); } + #endif // VS 2019 + +@@ -3350,8 +3746,6 @@ + , m_scale(1.0) + , m_value(value) {} + +-DOCTEST_DEFINE_COPIES(Approx); +- + Approx Approx::operator()(double value) const { + Approx approx(value); + approx.epsilon(m_epsilon); +@@ -3386,7 +3780,8 @@ + bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } + + String toString(const Approx& in) { +- return String("Approx( ") + doctest::toString(in.m_value) + " )"; ++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) ++ return "Approx( " + doctest::toString(in.m_value) + " )"; + } + const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } + +@@ -3399,17 +3794,15 @@ + void Context::applyCommandLine(int, const char* const*) {} + void Context::addFilter(const char*, const char*) {} + void Context::clearFilters() {} ++void Context::setOption(const char*, bool) {} + void Context::setOption(const char*, int) {} + void Context::setOption(const char*, const char*) {} + bool Context::shouldExit() { return false; } + void Context::setAsDefaultForAssertsOutOfTestCases() {} + void Context::setAssertHandler(detail::assert_handler) {} ++void Context::setCout(std::ostream* out) {} + int Context::run() { return 0; } + +-DOCTEST_DEFINE_DEFAULTS(CurrentTestCaseStats); +- +-DOCTEST_DEFINE_DEFAULTS(TestRunStats); +- + IReporter::~IReporter() = default; + + int IReporter::get_num_active_contexts() { return 0; } +@@ -3435,7 +3828,7 @@ + namespace doctest_detail_test_suite_ns { + // holds the current test suite + doctest::detail::TestSuite& getCurrentTestSuite() { +- static doctest::detail::TestSuite data; ++ static doctest::detail::TestSuite data{}; + return data; + } + } // namespace doctest_detail_test_suite_ns +@@ -3460,8 +3853,6 @@ + for(auto& curr_rep : g_cs->reporters_currently_used) \ + curr_rep->function(__VA_ARGS__) + +- DOCTEST_DEFINE_DEFAULTS(TestFailureException); +- DOCTEST_DEFINE_COPIES(TestFailureException); + bool checkIfShouldThrow(assertType::Enum at) { + if(at & assertType::is_require) //!OCLINT bitwise operator in conditional + return true; +@@ -3476,7 +3867,10 @@ + } + + #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS +- [[noreturn]] void throwException() { throw TestFailureException(); } // NOLINT(cert-err60-cpp) ++ DOCTEST_NORETURN void throwException() { ++ g_cs->shouldLogCurrentException = false; ++ throw TestFailureException(); ++ } // NOLINT(cert-err60-cpp) + #else // DOCTEST_CONFIG_NO_EXCEPTIONS + void throwException() {} + #endif // DOCTEST_CONFIG_NO_EXCEPTIONS +@@ -3487,8 +3881,8 @@ + // matching of a string against a wildcard mask (case sensitivity configurable) taken from + // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing + int wildcmp(const char* str, const char* wild, bool caseSensitive) { +- const char* cp = nullptr; +- const char* mp = nullptr; ++ const char* cp = str; ++ const char* mp = wild; + + while((*str) && (*wild != '*')) { + if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && +@@ -3544,73 +3938,82 @@ + } // namespace + namespace detail { + +- Subcase::Subcase(const char* name, const char* file, int line) +- : m_signature(name, file, line) { +- ContextState* s = g_cs; +- +- // if we have already completed it +- if(s->subcasesPassed.count(m_signature) != 0) +- return; ++ Subcase::Subcase(const String& name, const char* file, int line) ++ : m_signature({name, file, line}) { ++ auto* s = g_cs; + + // check subcase filters +- if(s->subcasesCurrentLevel < s->subcase_filter_levels) { +- if(!matchesAny(m_signature.m_name, s->filters[6], true, s->case_sensitive)) ++ if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) { ++ if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive)) + return; +- if(matchesAny(m_signature.m_name, s->filters[7], false, s->case_sensitive)) ++ if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive)) + return; + } +- ++ + // if a Subcase on the same level has already been entered +- if(s->subcasesEnteredLevels.count(s->subcasesCurrentLevel) != 0) { ++ if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) { + s->should_reenter = true; + return; + } + +- s->subcasesEnteredLevels.insert(s->subcasesCurrentLevel++); ++ // push the current signature to the stack so we can check if the ++ // current stack + the current new subcase have been traversed ++ s->subcasesStack.push_back(m_signature); ++ if(s->subcasesPassed.count(s->subcasesStack) != 0) { ++ // pop - revert to previous stack since we've already passed this ++ s->subcasesStack.pop_back(); ++ return; ++ } ++ ++ s->subcasesCurrentMaxLevel = s->subcasesStack.size(); + m_entered = true; + + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } + ++ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 ++ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") ++ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") ++ + Subcase::~Subcase() { + if(m_entered) { +- ContextState* s = g_cs; +- +- s->subcasesCurrentLevel--; +- // only mark the subcase as passed if no subcases have been skipped +- if(s->should_reenter == false) +- s->subcasesPassed.insert(m_signature); ++ // only mark the subcase stack as passed if no subcases have been skipped ++ if(g_cs->should_reenter == false) ++ g_cs->subcasesPassed.insert(g_cs->subcasesStack); ++ g_cs->subcasesStack.pop_back(); + ++#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) ++ if(std::uncaught_exceptions() > 0 ++#else ++ if(std::uncaught_exception() ++#endif ++ && g_cs->shouldLogCurrentException) { ++ DOCTEST_ITERATE_THROUGH_REPORTERS( ++ test_case_exception, {"exception thrown in subcase - will translate later " ++ "when the whole test case has been exited (cannot " ++ "translate while there is an active exception)", ++ false}); ++ g_cs->shouldLogCurrentException = false; ++ } + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); + } + } + ++ DOCTEST_CLANG_SUPPRESS_WARNING_POP ++ DOCTEST_GCC_SUPPRESS_WARNING_POP ++ DOCTEST_MSVC_SUPPRESS_WARNING_POP ++ + Subcase::operator bool() const { return m_entered; } + + Result::Result(bool passed, const String& decomposition) + : m_passed(passed) + , m_decomp(decomposition) {} + +- DOCTEST_DEFINE_DEFAULTS(Result); +- DOCTEST_DEFINE_COPIES(Result); +- + ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at) + : m_at(at) {} + +- DOCTEST_DEFINE_DEFAULTS(ExpressionDecomposer); +- +- DOCTEST_DEFINE_DEFAULTS(TestSuite); +- DOCTEST_DEFINE_COPIES(TestSuite); +- + TestSuite& TestSuite::operator*(const char* in) { + m_test_suite = in; +- // clear state +- m_description = nullptr; +- m_skip = false; +- m_may_fail = false; +- m_should_fail = false; +- m_expected_failures = 0; +- m_timeout = 0; + return *this; + } + +@@ -3622,6 +4025,8 @@ + m_test_suite = test_suite.m_test_suite; + m_description = test_suite.m_description; + m_skip = test_suite.m_skip; ++ m_no_breaks = test_suite.m_no_breaks; ++ m_no_output = test_suite.m_no_output; + m_may_fail = test_suite.m_may_fail; + m_should_fail = test_suite.m_should_fail; + m_expected_failures = test_suite.m_expected_failures; +@@ -3632,8 +4037,6 @@ + m_template_id = template_id; + } + +- DOCTEST_DEFINE_DEFAULTS(TestCase); +- + TestCase::TestCase(const TestCase& other) + : TestCaseData() { + *this = other; +@@ -3667,25 +4070,31 @@ + } + + bool TestCase::operator<(const TestCase& other) const { ++ // this will be used only to differentiate between test cases - not relevant for sorting + if(m_line != other.m_line) + return m_line < other.m_line; +- const int file_cmp = std::strcmp(m_file, other.m_file); ++ const int name_cmp = strcmp(m_name, other.m_name); ++ if(name_cmp != 0) ++ return name_cmp < 0; ++ const int file_cmp = m_file.compare(other.m_file); + if(file_cmp != 0) + return file_cmp < 0; + return m_template_id < other.m_template_id; + } ++ ++ // all the registered tests ++ std::set<TestCase>& getRegisteredTests() { ++ static std::set<TestCase> data; ++ return data; ++ } + } // namespace detail + namespace { + using namespace detail; + // for sorting tests by file/line + bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) { +-#if DOCTEST_MSVC + // this is needed because MSVC gives different case for drive letters + // for __FILE__ when evaluated in a header and a source file +- const int res = stricmp(lhs->m_file, rhs->m_file); +-#else // MSVC +- const int res = std::strcmp(lhs->m_file, rhs->m_file); +-#endif // MSVC ++ const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC)); + if(res != 0) + return res < 0; + if(lhs->m_line != rhs->m_line) +@@ -3709,39 +4118,10 @@ + return suiteOrderComparator(lhs, rhs); + } + +- // all the registered tests +- std::set<TestCase>& getRegisteredTests() { +- static std::set<TestCase> data; +- return data; +- } +- +-#ifdef DOCTEST_CONFIG_COLORS_WINDOWS +- HANDLE g_stdoutHandle; +- WORD g_origFgAttrs; +- WORD g_origBgAttrs; +- bool g_attrsInitted = false; +- +- int colors_init() { +- if(!g_attrsInitted) { +- g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); +- g_attrsInitted = true; +- CONSOLE_SCREEN_BUFFER_INFO csbiInfo; +- GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo); +- g_origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | +- BACKGROUND_BLUE | BACKGROUND_INTENSITY); +- g_origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | +- FOREGROUND_BLUE | FOREGROUND_INTENSITY); +- } +- return 0; +- } +- +- int dumy_init_console_colors = colors_init(); +-#endif // DOCTEST_CONFIG_COLORS_WINDOWS +- + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + void color_to_stream(std::ostream& s, Color::Enum code) { +- ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS +- ((void)code); // for DOCTEST_CONFIG_COLORS_NONE ++ static_cast<void>(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS ++ static_cast<void>(code); // for DOCTEST_CONFIG_COLORS_NONE + #ifdef DOCTEST_CONFIG_COLORS_ANSI + if(g_no_colors || + (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) +@@ -3771,10 +4151,26 @@ + + #ifdef DOCTEST_CONFIG_COLORS_WINDOWS + if(g_no_colors || +- (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false)) ++ (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false)) + return; + +-#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs) ++ static struct ConsoleHelper { ++ HANDLE stdoutHandle; ++ WORD origFgAttrs; ++ WORD origBgAttrs; ++ ++ ConsoleHelper() { ++ stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); ++ CONSOLE_SCREEN_BUFFER_INFO csbiInfo; ++ GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); ++ origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | ++ BACKGROUND_BLUE | BACKGROUND_INTENSITY); ++ origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | ++ FOREGROUND_BLUE | FOREGROUND_INTENSITY); ++ } ++ } ch; ++ ++#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs) + + // clang-format off + switch (code) { +@@ -3791,7 +4187,7 @@ + case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; + case Color::None: + case Color::Bright: // invalid +- default: DOCTEST_SET_ATTR(g_origFgAttrs); ++ default: DOCTEST_SET_ATTR(ch.origFgAttrs); + } + // clang-format on + #endif // DOCTEST_CONFIG_COLORS_WINDOWS +@@ -3844,9 +4240,33 @@ + return 0; + } + +-#ifdef DOCTEST_PLATFORM_MAC ++#ifdef DOCTEST_IS_DEBUGGER_ACTIVE ++ bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } ++#else // DOCTEST_IS_DEBUGGER_ACTIVE ++#ifdef DOCTEST_PLATFORM_LINUX ++ class ErrnoGuard { ++ public: ++ ErrnoGuard() : m_oldErrno(errno) {} ++ ~ErrnoGuard() { errno = m_oldErrno; } ++ private: ++ int m_oldErrno; ++ }; ++ // See the comments in Catch2 for the reasoning behind this implementation: ++ // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102 ++ bool isDebuggerActive() { ++ ErrnoGuard guard; ++ std::ifstream in("/proc/self/status"); ++ for(std::string line; std::getline(in, line);) { ++ static const int PREFIX_LEN = 11; ++ if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { ++ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; ++ } ++ } ++ return false; ++ } ++#elif defined(DOCTEST_PLATFORM_MAC) + // The following function is taken directly from the following technical note: +- // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html ++ // https://developer.apple.com/library/archive/qa/qa1361/_index.html + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { +@@ -3871,11 +4291,12 @@ + // We're being debugged if the P_TRACED flag is set. + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +-#elif DOCTEST_MSVC || defined(__MINGW32__) ++#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__) + bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } + #else + bool isDebuggerActive() { return false; } + #endif // Platform ++#endif // DOCTEST_IS_DEBUGGER_ACTIVE + + void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { + if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == +@@ -3910,41 +4331,47 @@ + g_infoContexts.push_back(this); + } + ++ ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) { ++ if (other.need_to_destroy) { ++ other.destroy(); ++ } ++ other.need_to_destroy = false; ++ g_infoContexts.push_back(this); ++ } ++ + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") ++ + // destroy cannot be inlined into the destructor because that would mean calling stringify after + // ContextScope has been destroyed (base class destructors run after derived class destructors). + // Instead, ContextScope calls this method directly from its destructor. + void ContextScopeBase::destroy() { ++#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) ++ if(std::uncaught_exceptions() > 0) { ++#else + if(std::uncaught_exception()) { ++#endif + std::ostringstream s; + this->stringify(&s); + g_cs->stringifiedContexts.push_back(s.str().c_str()); + } + g_infoContexts.pop_back(); + } ++ + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP +- + } // namespace detail + namespace { + using namespace detail; + +- std::ostream& file_line_to_stream(std::ostream& s, const char* file, int line, +- const char* tail = "") { +- const auto opt = getContextOptions(); +- s << Color::LightGrey << skipPathFromFilename(file) << (opt->gnu_file_line ? ":" : "(") +- << (opt->no_line_numbers ? 0 : line) // 0 or the real num depending on the option +- << (opt->gnu_file_line ? ":" : "):") << tail; +- return s; +- } +- + #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) + struct FatalConditionHandler + { +- void reset() {} ++ static void reset() {} ++ static void allocateAltStackMem() {} ++ static void freeAltStackMem() {} + }; + #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH + +@@ -3961,43 +4388,113 @@ + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { +- {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"}, +- {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"}, +- {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"}, +- {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"}, ++ {static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), ++ "SIGILL - Illegal instruction signal"}, ++ {static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, ++ {static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), ++ "SIGSEGV - Segmentation violation signal"}, ++ {static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, + }; + + struct FatalConditionHandler + { +- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { +- for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { +- if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { +- reportFatal(signalDefs[i].name); ++ static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) { ++ // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the ++ // console just once no matter how many threads have crashed. ++ static std::mutex mutex; ++ static bool execute = true; ++ { ++ std::lock_guard<std::mutex> lock(mutex); ++ if(execute) { ++ bool reported = false; ++ for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { ++ if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { ++ reportFatal(signalDefs[i].name); ++ reported = true; ++ break; ++ } ++ } ++ if(reported == false) ++ reportFatal("Unhandled SEH exception caught"); ++ if(isDebuggerActive() && !g_cs->no_breaks) ++ DOCTEST_BREAK_INTO_DEBUGGER(); + } ++ execute = false; + } +- // If its not an exception we care about, pass it along. +- // This stops us from eating debugger breaks etc. +- return EXCEPTION_CONTINUE_SEARCH; ++ std::exit(EXIT_FAILURE); + } + ++ static void allocateAltStackMem() {} ++ static void freeAltStackMem() {} ++ + FatalConditionHandler() { + isSet = true; + // 32k seems enough for doctest to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; +- exceptionHandlerHandle = nullptr; +- // Register as first handler in current chain +- exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); ++ // Register an unhandled exception filter ++ previousTop = SetUnhandledExceptionFilter(handleException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); ++ ++ // On Windows uncaught exceptions from another thread, exceptions from ++ // destructors, or calls to std::terminate are not a SEH exception ++ ++ // The terminal handler gets called when: ++ // - std::terminate is called FROM THE TEST RUNNER THREAD ++ // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD ++ original_terminate_handler = std::get_terminate(); ++ std::set_terminate([]() DOCTEST_NOEXCEPT { ++ reportFatal("Terminate handler called"); ++ if(isDebuggerActive() && !g_cs->no_breaks) ++ DOCTEST_BREAK_INTO_DEBUGGER(); ++ std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well ++ }); ++ ++ // SIGABRT is raised when: ++ // - std::terminate is called FROM A DIFFERENT THREAD ++ // - an exception is thrown from a destructor FROM A DIFFERENT THREAD ++ // - an uncaught exception is thrown FROM A DIFFERENT THREAD ++ prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT { ++ if(signal == SIGABRT) { ++ reportFatal("SIGABRT - Abort (abnormal termination) signal"); ++ if(isDebuggerActive() && !g_cs->no_breaks) ++ DOCTEST_BREAK_INTO_DEBUGGER(); ++ std::exit(EXIT_FAILURE); ++ } ++ }); ++ ++ // The following settings are taken from google test, and more ++ // specifically from UnitTest::Run() inside of gtest.cc ++ ++ // the user does not want to see pop-up dialogs about crashes ++ prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | ++ SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); ++ // This forces the abort message to go to stderr in all circumstances. ++ prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR); ++ // In the debug version, Visual Studio pops up a separate dialog ++ // offering a choice to debug the aborted program - we want to disable that. ++ prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); ++ // In debug mode, the Windows CRT can crash with an assertion over invalid ++ // input (e.g. passing an invalid file descriptor). The default handling ++ // for these assertions is to pop up a dialog and wait for user input. ++ // Instead ask the CRT to dump such assertions to stderr non-interactively. ++ prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); ++ prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + } + + static void reset() { + if(isSet) { + // Unregister handler and restore the old guarantee +- RemoveVectoredExceptionHandler(exceptionHandlerHandle); ++ SetUnhandledExceptionFilter(previousTop); + SetThreadStackGuarantee(&guaranteeSize); +- exceptionHandlerHandle = nullptr; ++ std::set_terminate(original_terminate_handler); ++ std::signal(SIGABRT, prev_sigabrt_handler); ++ SetErrorMode(prev_error_mode_1); ++ _set_error_mode(prev_error_mode_2); ++ _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); ++ static_cast<void>(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode)); ++ static_cast<void>(_CrtSetReportFile(_CRT_ASSERT, prev_report_file)); + isSet = false; + } + } +@@ -4005,14 +4502,28 @@ + ~FatalConditionHandler() { reset(); } + + private: ++ static UINT prev_error_mode_1; ++ static int prev_error_mode_2; ++ static unsigned int prev_abort_behavior; ++ static int prev_report_mode; ++ static _HFILE prev_report_file; ++ static void (DOCTEST_CDECL *prev_sigabrt_handler)(int); ++ static std::terminate_handler original_terminate_handler; + static bool isSet; + static ULONG guaranteeSize; +- static PVOID exceptionHandlerHandle; ++ static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; + }; + ++ UINT FatalConditionHandler::prev_error_mode_1; ++ int FatalConditionHandler::prev_error_mode_2; ++ unsigned int FatalConditionHandler::prev_abort_behavior; ++ int FatalConditionHandler::prev_report_mode; ++ _HFILE FatalConditionHandler::prev_report_file; ++ void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int); ++ std::terminate_handler FatalConditionHandler::original_terminate_handler; + bool FatalConditionHandler::isSet = false; + ULONG FatalConditionHandler::guaranteeSize = 0; +- PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; ++ LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; + + #else // DOCTEST_PLATFORM_WINDOWS + +@@ -4033,7 +4544,8 @@ + static bool isSet; + static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)]; + static stack_t oldSigStack; +- static char altStackMem[4 * SIGSTKSZ]; ++ static size_t altStackSize; ++ static char* altStackMem; + + static void handleSignal(int sig) { + const char* name = "<unknown signal>"; +@@ -4049,11 +4561,19 @@ + raise(sig); + } + ++ static void allocateAltStackMem() { ++ altStackMem = new char[altStackSize]; ++ } ++ ++ static void freeAltStackMem() { ++ delete[] altStackMem; ++ } ++ + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; +- sigStack.ss_size = sizeof(altStackMem); ++ sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = {}; +@@ -4078,10 +4598,11 @@ + } + }; + +- bool FatalConditionHandler::isSet = false; ++ bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; +- stack_t FatalConditionHandler::oldSigStack = {}; +- char FatalConditionHandler::altStackMem[] = {}; ++ stack_t FatalConditionHandler::oldSigStack = {}; ++ size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ; ++ char* FatalConditionHandler::altStackMem = nullptr; + + #endif // DOCTEST_PLATFORM_WINDOWS + #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH +@@ -4114,8 +4635,10 @@ + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); + +- while(g_cs->subcasesCurrentLevel--) ++ while(g_cs->subcasesStack.size()) { ++ g_cs->subcasesStack.pop_back(); + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); ++ } + + g_cs->finalizeTestCaseData(); + +@@ -4128,24 +4651,23 @@ + namespace detail { + + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, +- const char* exception_type) { +- m_test_case = g_cs->currentTest; +- m_at = at; +- m_file = file; +- m_line = line; +- m_expr = expr; +- m_failed = true; +- m_threw = false; +- m_threw_as = false; +- m_exception_type = exception_type; ++ const char* exception_type, const char* exception_string) { ++ m_test_case = g_cs->currentTest; ++ m_at = at; ++ m_file = file; ++ m_line = line; ++ m_expr = expr; ++ m_failed = true; ++ m_threw = false; ++ m_threw_as = false; ++ m_exception_type = exception_type; ++ m_exception_string = exception_string; + #if DOCTEST_MSVC + if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC + ++m_expr; + #endif // MSVC + } + +- DOCTEST_DEFINE_DEFAULTS(ResultBuilder); +- + void ResultBuilder::setResult(const Result& res) { + m_decomp = res.m_decomp; + m_failed = !res.m_passed; +@@ -4159,16 +4681,18 @@ + bool ResultBuilder::log() { + if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional + m_failed = !m_threw; ++ } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT ++ m_failed = !m_threw_as || (m_exception != m_exception_string); + } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional + m_failed = !m_threw_as; + } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional +- m_failed = m_exception != m_exception_type; ++ m_failed = m_exception != m_exception_string; + } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional + m_failed = m_threw; + } + + if(m_exception.size()) +- m_exception = String("\"") + m_exception + "\""; ++ m_exception = "\"" + m_exception + "\""; + + if(is_running_in_test) { + addAssert(m_at); +@@ -4180,8 +4704,8 @@ + failed_out_of_a_testing_context(*this); + } + +- return m_failed && isDebuggerActive() && +- !getContextOptions()->no_breaks; // break into debugger ++ return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks && ++ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger + } + + void ResultBuilder::react() const { +@@ -4196,7 +4720,7 @@ + std::abort(); + } + +- void decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, ++ bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, + Result result) { + bool failed = !result.m_passed; + +@@ -4206,58 +4730,53 @@ + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); + DOCTEST_ASSERT_IN_TESTS(result.m_decomp); ++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) ++ return !failed; + } + + MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { +- m_stream = getTlsOss(); ++ m_stream = tlssPush(); + m_file = file; + m_line = line; + m_severity = severity; + } + ++ MessageBuilder::~MessageBuilder() { ++ if (!logged) ++ tlssPop(); ++ } ++ + IExceptionTranslator::IExceptionTranslator() = default; + IExceptionTranslator::~IExceptionTranslator() = default; + + bool MessageBuilder::log() { +- m_string = getTlsOssResult(); ++ if (!logged) { ++ m_string = tlssPop(); ++ logged = true; ++ } ++ + DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this); + + const bool isWarn = m_severity & assertType::is_warn; + +- // warn is just a message in this context so we dont treat it as an assert ++ // warn is just a message in this context so we don't treat it as an assert + if(!isWarn) { + addAssert(m_severity); + addFailedAssert(m_severity); + } + +- return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break ++ return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn && ++ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger + } + + void MessageBuilder::react() { + if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional + throwException(); + } +- +- MessageBuilder::~MessageBuilder() = default; + } // namespace detail + namespace { + using namespace detail; + +- template <typename Ex> +- [[noreturn]] void throw_exception(Ex const& e) { +-#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS +- throw e; +-#else // DOCTEST_CONFIG_NO_EXCEPTIONS +- std::cerr << "doctest will terminate because it needed to throw an exception.\n" +- << "The message was: " << e.what() << '\n'; +- std::terminate(); +-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS +- } +- +-#define DOCTEST_INTERNAL_ERROR(msg) \ +- throw_exception(std::logic_error( \ +- __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) +- + // clang-format off + + // ================================================================================================= +@@ -4287,8 +4806,8 @@ + public: + ScopedElement( XmlWriter* writer ); + +- ScopedElement( ScopedElement&& other ) noexcept; +- ScopedElement& operator=( ScopedElement&& other ) noexcept; ++ ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT; ++ ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT; + + ~ScopedElement(); + +@@ -4404,7 +4923,7 @@ + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes +- // (see: http://www.w3.org/TR/xml/#syntax) ++ // (see: https://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; +@@ -4413,7 +4932,7 @@ + case '&': os << "&"; break; + + case '>': +- // See: http://www.w3.org/TR/xml/#syntax ++ // See: https://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else +@@ -4431,7 +4950,7 @@ + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii +- // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 ++ // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; +@@ -4505,11 +5024,11 @@ + : m_writer( writer ) + {} + +- XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept ++ XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } +- XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { ++ XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT { + if ( m_writer ) { + m_writer->endElement(); + } +@@ -4671,7 +5190,7 @@ + void test_case_start_impl(const TestCaseData& in) { + bool open_ts_tag = false; + if(tc != nullptr) { // we have already opened a test suite +- if(strcmp(tc->m_test_suite, in.m_test_suite) != 0) { ++ if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) { + xml.endElement(); + open_ts_tag = true; + } +@@ -4688,7 +5207,7 @@ + tc = ∈ + xml.startElement("TestCase") + .writeAttribute("name", in.m_name) +- .writeAttribute("filename", skipPathFromFilename(in.m_file)) ++ .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str())) + .writeAttribute("line", line(in.m_line)) + .writeAttribute("description", in.m_description); + +@@ -4716,13 +5235,18 @@ + .writeAttribute("priority", curr.first.first) + .writeAttribute("name", curr.first.second); + } else if(opt.count || opt.list_test_cases) { +- for(unsigned i = 0; i < in.num_data; ++i) +- xml.scopedElement("TestCase").writeAttribute("name", in.data[i]); ++ for(unsigned i = 0; i < in.num_data; ++i) { ++ xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) ++ .writeAttribute("testsuite", in.data[i]->m_test_suite) ++ .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) ++ .writeAttribute("line", line(in.data[i]->m_line)) ++ .writeAttribute("skipped", in.data[i]->m_skip); ++ } + xml.scopedElement("OverallResultsTestCases") + .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); + } else if(opt.list_test_suites) { + for(unsigned i = 0; i < in.num_data; ++i) +- xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]); ++ xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite); + xml.scopedElement("OverallResultsTestCases") + .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); + xml.scopedElement("OverallResultsTestSuites") +@@ -4779,12 +5303,15 @@ + test_case_start_impl(in); + xml.ensureTagClosed(); + } ++ ++ void test_case_reenter(const TestCaseData&) override {} + + void test_case_end(const CurrentTestCaseStats& st) override { + xml.startElement("OverallResultsAsserts") + .writeAttribute("successes", + st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest) +- .writeAttribute("failures", st.numAssertsFailedCurrentTest); ++ .writeAttribute("failures", st.numAssertsFailedCurrentTest) ++ .writeAttribute("test_case_success", st.testCaseSuccess); + if(opt.duration) + xml.writeAttribute("duration", st.seconds); + if(tc->m_expected_failures) +@@ -4803,8 +5330,6 @@ + } + + void subcase_start(const SubcaseSignature& in) override { +- std::lock_guard<std::mutex> lock(mutex); +- + xml.startElement("SubCase") + .writeAttribute("name", in.m_name) + .writeAttribute("filename", skipPathFromFilename(in.m_file)) +@@ -4831,11 +5356,12 @@ + if(rb.m_threw) + xml.scopedElement("Exception").writeText(rb.m_exception.c_str()); + +- if(rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) { ++ if(rb.m_at & assertType::is_throws_as) + xml.scopedElement("ExpectedException").writeText(rb.m_exception_type); +- } else if((rb.m_at & assertType::is_normal) && !rb.m_threw) { ++ if(rb.m_at & assertType::is_throws_with) ++ xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string); ++ if((rb.m_at & assertType::is_normal) && !rb.m_threw) + xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str()); +- } + + log_contexts(); + +@@ -4868,6 +5394,278 @@ + + DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter); + ++ void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) { ++ if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == ++ 0) //!OCLINT bitwise operator in conditional ++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " ++ << Color::None; ++ ++ if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional ++ s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; ++ } else if((rb.m_at & assertType::is_throws_as) && ++ (rb.m_at & assertType::is_throws_with)) { //!OCLINT ++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" ++ << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None; ++ if(rb.m_threw) { ++ if(!rb.m_failed) { ++ s << "threw as expected!\n"; ++ } else { ++ s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; ++ } ++ } else { ++ s << "did NOT throw at all!\n"; ++ } ++ } else if(rb.m_at & ++ assertType::is_throws_as) { //!OCLINT bitwise operator in conditional ++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " ++ << rb.m_exception_type << " ) " << Color::None ++ << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : ++ "threw a DIFFERENT exception: ") : ++ "did NOT throw at all!") ++ << Color::Cyan << rb.m_exception << "\n"; ++ } else if(rb.m_at & ++ assertType::is_throws_with) { //!OCLINT bitwise operator in conditional ++ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" ++ << rb.m_exception_string << "\" ) " << Color::None ++ << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : ++ "threw a DIFFERENT exception: ") : ++ "did NOT throw at all!") ++ << Color::Cyan << rb.m_exception << "\n"; ++ } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional ++ s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan ++ << rb.m_exception << "\n"; ++ } else { ++ s << (rb.m_threw ? "THREW exception: " : ++ (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); ++ if(rb.m_threw) ++ s << rb.m_exception << "\n"; ++ else ++ s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; ++ } ++ } ++ ++ // TODO: ++ // - log_message() ++ // - respond to queries ++ // - honor remaining options ++ // - more attributes in tags ++ struct JUnitReporter : public IReporter ++ { ++ XmlWriter xml; ++ std::mutex mutex; ++ Timer timer; ++ std::vector<String> deepestSubcaseStackNames; ++ ++ struct JUnitTestCaseData ++ { ++ static std::string getCurrentTimestamp() { ++ // Beware, this is not reentrant because of backward compatibility issues ++ // Also, UTC only, again because of backward compatibility (%z is C++11) ++ time_t rawtime; ++ std::time(&rawtime); ++ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); ++ ++ std::tm timeInfo; ++#ifdef DOCTEST_PLATFORM_WINDOWS ++ gmtime_s(&timeInfo, &rawtime); ++#else // DOCTEST_PLATFORM_WINDOWS ++ gmtime_r(&rawtime, &timeInfo); ++#endif // DOCTEST_PLATFORM_WINDOWS ++ ++ char timeStamp[timeStampSize]; ++ const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; ++ ++ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); ++ return std::string(timeStamp); ++ } ++ ++ struct JUnitTestMessage ++ { ++ JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details) ++ : message(_message), type(_type), details(_details) {} ++ ++ JUnitTestMessage(const std::string& _message, const std::string& _details) ++ : message(_message), type(), details(_details) {} ++ ++ std::string message, type, details; ++ }; ++ ++ struct JUnitTestCase ++ { ++ JUnitTestCase(const std::string& _classname, const std::string& _name) ++ : classname(_classname), name(_name), time(0), failures() {} ++ ++ std::string classname, name; ++ double time; ++ std::vector<JUnitTestMessage> failures, errors; ++ }; ++ ++ void add(const std::string& classname, const std::string& name) { ++ testcases.emplace_back(classname, name); ++ } ++ ++ void appendSubcaseNamesToLastTestcase(std::vector<String> nameStack) { ++ for(auto& curr: nameStack) ++ if(curr.size()) ++ testcases.back().name += std::string("/") + curr.c_str(); ++ } ++ ++ void addTime(double time) { ++ if(time < 1e-4) ++ time = 0; ++ testcases.back().time = time; ++ totalSeconds += time; ++ } ++ ++ void addFailure(const std::string& message, const std::string& type, const std::string& details) { ++ testcases.back().failures.emplace_back(message, type, details); ++ ++totalFailures; ++ } ++ ++ void addError(const std::string& message, const std::string& details) { ++ testcases.back().errors.emplace_back(message, details); ++ ++totalErrors; ++ } ++ ++ std::vector<JUnitTestCase> testcases; ++ double totalSeconds = 0; ++ int totalErrors = 0, totalFailures = 0; ++ }; ++ ++ JUnitTestCaseData testCaseData; ++ ++ // caching pointers/references to objects of these types - safe to do ++ const ContextOptions& opt; ++ const TestCaseData* tc = nullptr; ++ ++ JUnitReporter(const ContextOptions& co) ++ : xml(*co.cout) ++ , opt(co) {} ++ ++ unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } ++ ++ // ========================================================================================= ++ // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE ++ // ========================================================================================= ++ ++ void report_query(const QueryData&) override {} ++ ++ void test_run_start() override {} ++ ++ void test_run_end(const TestRunStats& p) override { ++ // remove .exe extension - mainly to have the same output on UNIX and Windows ++ std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); ++#ifdef DOCTEST_PLATFORM_WINDOWS ++ if(binary_name.rfind(".exe") != std::string::npos) ++ binary_name = binary_name.substr(0, binary_name.length() - 4); ++#endif // DOCTEST_PLATFORM_WINDOWS ++ xml.startElement("testsuites"); ++ xml.startElement("testsuite").writeAttribute("name", binary_name) ++ .writeAttribute("errors", testCaseData.totalErrors) ++ .writeAttribute("failures", testCaseData.totalFailures) ++ .writeAttribute("tests", p.numAsserts); ++ if(opt.no_time_in_output == false) { ++ xml.writeAttribute("time", testCaseData.totalSeconds); ++ xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp()); ++ } ++ if(opt.no_version == false) ++ xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR); ++ ++ for(const auto& testCase : testCaseData.testcases) { ++ xml.startElement("testcase") ++ .writeAttribute("classname", testCase.classname) ++ .writeAttribute("name", testCase.name); ++ if(opt.no_time_in_output == false) ++ xml.writeAttribute("time", testCase.time); ++ // This is not ideal, but it should be enough to mimic gtest's junit output. ++ xml.writeAttribute("status", "run"); ++ ++ for(const auto& failure : testCase.failures) { ++ xml.scopedElement("failure") ++ .writeAttribute("message", failure.message) ++ .writeAttribute("type", failure.type) ++ .writeText(failure.details, false); ++ } ++ ++ for(const auto& error : testCase.errors) { ++ xml.scopedElement("error") ++ .writeAttribute("message", error.message) ++ .writeText(error.details); ++ } ++ ++ xml.endElement(); ++ } ++ xml.endElement(); ++ xml.endElement(); ++ } ++ ++ void test_case_start(const TestCaseData& in) override { ++ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); ++ timer.start(); ++ } ++ ++ void test_case_reenter(const TestCaseData& in) override { ++ testCaseData.addTime(timer.getElapsedSeconds()); ++ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); ++ deepestSubcaseStackNames.clear(); ++ ++ timer.start(); ++ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); ++ } ++ ++ void test_case_end(const CurrentTestCaseStats&) override { ++ testCaseData.addTime(timer.getElapsedSeconds()); ++ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); ++ deepestSubcaseStackNames.clear(); ++ } ++ ++ void test_case_exception(const TestCaseException& e) override { ++ std::lock_guard<std::mutex> lock(mutex); ++ testCaseData.addError("exception", e.error_string.c_str()); ++ } ++ ++ void subcase_start(const SubcaseSignature& in) override { ++ deepestSubcaseStackNames.push_back(in.m_name); ++ } ++ ++ void subcase_end() override {} ++ ++ void log_assert(const AssertData& rb) override { ++ if(!rb.m_failed) // report only failures & ignore the `success` option ++ return; ++ ++ std::lock_guard<std::mutex> lock(mutex); ++ ++ std::ostringstream os; ++ os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") ++ << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; ++ ++ fulltext_log_assert_to_stream(os, rb); ++ log_contexts(os); ++ testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); ++ } ++ ++ void log_message(const MessageData&) override {} ++ ++ void test_case_skipped(const TestCaseData&) override {} ++ ++ void log_contexts(std::ostringstream& s) { ++ int num_contexts = get_num_active_contexts(); ++ if(num_contexts) { ++ auto contexts = get_active_contexts(); ++ ++ s << " logged: "; ++ for(int i = 0; i < num_contexts; ++i) { ++ s << (i == 0 ? "" : " "); ++ contexts[i]->stringify(&s); ++ s << std::endl; ++ } ++ } ++ } ++ }; ++ ++ DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); ++ + struct Whitespace + { + int nrSpaces; +@@ -4886,6 +5684,7 @@ + std::ostream& s; + bool hasLoggedCurrentTestStart; + std::vector<SubcaseSignature> subcasesStack; ++ size_t currentSubcaseLevel; + std::mutex mutex; + + // caching pointers/references to objects of these types - safe to do +@@ -4944,23 +5743,40 @@ + s << "\n"; + } + ++ // this was requested to be made virtual so users could override it ++ virtual void file_line_to_stream(const char* file, int line, ++ const char* tail = "") { ++ s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(") ++ << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option ++ << (opt.gnu_file_line ? ":" : "):") << tail; ++ } ++ + void logTestStart() { + if(hasLoggedCurrentTestStart) + return; + + separator_to_stream(); +- file_line_to_stream(s, tc->m_file, tc->m_line, "\n"); ++ file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n"); + if(tc->m_description) + s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n"; + if(tc->m_test_suite && tc->m_test_suite[0] != '\0') + s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n"; + if(strncmp(tc->m_name, " Scenario:", 11) != 0) +- s << Color::None << "TEST CASE: "; ++ s << Color::Yellow << "TEST CASE: "; + s << Color::None << tc->m_name << "\n"; + +- for(auto& curr : subcasesStack) +- if(curr.m_name[0] != '\0') +- s << " " << curr.m_name << "\n"; ++ for(size_t i = 0; i < currentSubcaseLevel; ++i) { ++ if(subcasesStack[i].m_name[0] != '\0') ++ s << " " << subcasesStack[i].m_name << "\n"; ++ } ++ ++ if(currentSubcaseLevel != subcasesStack.size()) { ++ s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None; ++ for(size_t i = 0; i < subcasesStack.size(); ++i) { ++ if(subcasesStack[i].m_name[0] != '\0') ++ s << " " << subcasesStack[i].m_name << "\n"; ++ } ++ } + + s << "\n"; + +@@ -4974,9 +5790,11 @@ + } + + void printIntro() { +- printVersion(); +- s << Color::Cyan << "[doctest] " << Color::None +- << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; ++ if(opt.no_intro == false) { ++ printVersion(); ++ s << Color::Cyan << "[doctest] " << Color::None ++ << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; ++ } + } + + void printHelp() { +@@ -5038,7 +5856,7 @@ + << Whitespace(sizePrefixDisplay*1) << "output filename\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by=<string> " + << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n"; +- s << Whitespace(sizePrefixDisplay*3) << " <string> - by [file/suite/name/rand]\n"; ++ s << Whitespace(sizePrefixDisplay*3) << " <string> - [file/suite/name/rand/none]\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed=<int> " + << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first=<int> " +@@ -5061,12 +5879,18 @@ + << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration=<bool> " + << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n"; ++ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal=<bool> " ++ << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n"; ++ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet=<bool> " ++ << Whitespace(sizePrefixDisplay*1) << "no console output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw=<bool> " + << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode=<bool> " + << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run=<bool> " + << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n"; ++ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro=<bool> " ++ << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version=<bool> " + << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors=<bool> " +@@ -5104,22 +5928,6 @@ + printReporters(getReporters(), "reporters"); + } + +- void list_query_results() { +- separator_to_stream(); +- if(opt.count || opt.list_test_cases) { +- s << Color::Cyan << "[doctest] " << Color::None +- << "unskipped test cases passing the current filters: " +- << g_cs->numTestCasesPassingFilters << "\n"; +- } else if(opt.list_test_suites) { +- s << Color::Cyan << "[doctest] " << Color::None +- << "unskipped test cases passing the current filters: " +- << g_cs->numTestCasesPassingFilters << "\n"; +- s << Color::Cyan << "[doctest] " << Color::None +- << "test suites with unskipped test cases passing the current filters: " +- << g_cs->numTestSuitesPassingFilters << "\n"; +- } +- } +- + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= +@@ -5139,7 +5947,7 @@ + } + + for(unsigned i = 0; i < in.num_data; ++i) +- s << Color::None << in.data[i] << "\n"; ++ s << Color::None << in.data[i]->m_name << "\n"; + + separator_to_stream(); + +@@ -5152,7 +5960,7 @@ + separator_to_stream(); + + for(unsigned i = 0; i < in.num_data; ++i) +- s << Color::None << in.data[i] << "\n"; ++ s << Color::None << in.data[i]->m_test_suite << "\n"; + + separator_to_stream(); + +@@ -5165,30 +5973,40 @@ + } + } + +- void test_run_start() override { printIntro(); } ++ void test_run_start() override { ++ if(!opt.minimal) ++ printIntro(); ++ } + + void test_run_end(const TestRunStats& p) override { ++ if(opt.minimal && p.numTestCasesFailed == 0) ++ return; ++ + separator_to_stream(); ++ s << std::dec; + ++ auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1))); ++ auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1))); ++ auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1))); + const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; +- s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6) ++ s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth) + << p.numTestCasesPassingFilters << " | " + << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : + Color::Green) +- << std::setw(6) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" ++ << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" + << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) +- << std::setw(6) << p.numTestCasesFailed << " failed" << Color::None << " | "; ++ << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |"; + if(opt.no_skipped_summary == false) { + const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; +- s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped ++ s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped + << " skipped" << Color::None; + } + s << "\n"; +- s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6) ++ s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth) + << p.numAsserts << " | " + << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) +- << std::setw(6) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None +- << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6) ++ << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None ++ << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) + << p.numAssertsFailed << " failed" << Color::None << " |\n"; + s << Color::Cyan << "[doctest] " << Color::None + << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) +@@ -5198,9 +6016,18 @@ + void test_case_start(const TestCaseData& in) override { + hasLoggedCurrentTestStart = false; + tc = ∈ ++ subcasesStack.clear(); ++ currentSubcaseLevel = 0; ++ } ++ ++ void test_case_reenter(const TestCaseData&) override { ++ subcasesStack.clear(); + } + + void test_case_end(const CurrentTestCaseStats& st) override { ++ if(tc->m_no_output) ++ return; ++ + // log the preamble of the test case only if there is something + // else to print - something other than that an assert has failed + if(opt.duration || +@@ -5235,9 +6062,13 @@ + } + + void test_case_exception(const TestCaseException& e) override { ++ std::lock_guard<std::mutex> lock(mutex); ++ if(tc->m_no_output) ++ return; ++ + logTestStart(); + +- file_line_to_stream(s, tc->m_file, tc->m_line, " "); ++ file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); + successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require : + assertType::is_check); + s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ") +@@ -5256,71 +6087,41 @@ + } + + void subcase_start(const SubcaseSignature& subc) override { +- std::lock_guard<std::mutex> lock(mutex); + subcasesStack.push_back(subc); ++ ++currentSubcaseLevel; + hasLoggedCurrentTestStart = false; + } + + void subcase_end() override { +- std::lock_guard<std::mutex> lock(mutex); +- subcasesStack.pop_back(); ++ --currentSubcaseLevel; + hasLoggedCurrentTestStart = false; + } + + void log_assert(const AssertData& rb) override { +- if(!rb.m_failed && !opt.success) ++ if((!rb.m_failed && !opt.success) || tc->m_no_output) + return; + + std::lock_guard<std::mutex> lock(mutex); + + logTestStart(); + +- file_line_to_stream(s, rb.m_file, rb.m_line, " "); ++ file_line_to_stream(rb.m_file, rb.m_line, " "); + successOrFailColoredStringToStream(!rb.m_failed, rb.m_at); +- if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == +- 0) //!OCLINT bitwise operator in conditional +- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " +- << Color::None; +- +- if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional +- s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; +- } else if(rb.m_at & +- assertType::is_throws_as) { //!OCLINT bitwise operator in conditional +- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " +- << rb.m_exception_type << " ) " << Color::None +- << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : +- "threw a DIFFERENT exception: ") : +- "did NOT throw at all!") +- << Color::Cyan << rb.m_exception << "\n"; +- } else if(rb.m_at & +- assertType::is_throws_with) { //!OCLINT bitwise operator in conditional +- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" +- << rb.m_exception_type << "\" ) " << Color::None +- << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : +- "threw a DIFFERENT exception: ") : +- "did NOT throw at all!") +- << Color::Cyan << rb.m_exception << "\n"; +- } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional +- s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan +- << rb.m_exception << "\n"; +- } else { +- s << (rb.m_threw ? "THREW exception: " : +- (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); +- if(rb.m_threw) +- s << rb.m_exception << "\n"; +- else +- s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; +- } ++ ++ fulltext_log_assert_to_stream(s, rb); + + log_contexts(); + } + + void log_message(const MessageData& mb) override { ++ if(tc->m_no_output) ++ return; ++ + std::lock_guard<std::mutex> lock(mutex); + + logTestStart(); + +- file_line_to_stream(s, mb.m_file, mb.m_line, " "); ++ file_line_to_stream(mb.m_file, mb.m_line, " "); + s << getSuccessOrFailColor(false, mb.m_severity) + << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity, + "MESSAGE") << ": "; +@@ -5346,14 +6147,17 @@ + bool with_col = g_no_colors; \ + g_no_colors = false; \ + ConsoleReporter::func(arg); \ +- DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ +- oss.str(""); \ ++ if(oss.tellp() != std::streampos{}) { \ ++ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ ++ oss.str(""); \ ++ } \ + g_no_colors = with_col; \ + } + + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in) ++ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in) +@@ -5368,7 +6172,7 @@ + + // the implementation of parseOption() + bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) { +- // going from the end to the begining and stopping on the first occurance from the end ++ // going from the end to the beginning and stopping on the first occurrence from the end + for(int i = argc; i > 0; --i) { + auto index = i - 1; + auto temp = std::strstr(argv[index], pattern); +@@ -5424,18 +6228,42 @@ + std::vector<String>& res) { + String filtersString; + if(parseOption(argc, argv, pattern, &filtersString)) { +- // tokenize with "," as a separator +- // cppcheck-suppress strtokCalled +- DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") +- auto pch = std::strtok(filtersString.c_str(), ","); // modifies the string +- while(pch != nullptr) { +- if(strlen(pch)) +- res.push_back(pch); +- // uses the strtok() internal state to go to the next token +- // cppcheck-suppress strtokCalled +- pch = std::strtok(nullptr, ","); ++ // tokenize with "," as a separator, unless escaped with backslash ++ std::ostringstream s; ++ auto flush = [&s, &res]() { ++ auto string = s.str(); ++ if(string.size() > 0) { ++ res.push_back(string.c_str()); ++ } ++ s.str(""); ++ }; ++ ++ bool seenBackslash = false; ++ const char* current = filtersString.c_str(); ++ const char* end = current + strlen(current); ++ while(current != end) { ++ char character = *current++; ++ if(seenBackslash) { ++ seenBackslash = false; ++ if(character == ',') { ++ s.put(','); ++ continue; ++ } ++ s.put('\\'); ++ } ++ if(character == '\\') { ++ seenBackslash = true; ++ } else if(character == ',') { ++ flush(); ++ } else { ++ s.put(character); ++ } ++ } ++ ++ if(seenBackslash) { ++ s.put('\\'); + } +- DOCTEST_CLANG_SUPPRESS_WARNING_POP ++ flush(); + return true; + } + return false; +@@ -5533,7 +6361,7 @@ + #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ + if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \ + parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \ +- p->var = !!intRes; \ ++ p->var = static_cast<bool>(intRes); \ + else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \ + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \ + p->var = true; \ +@@ -5568,9 +6396,12 @@ + DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false); ++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false); ++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false); ++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false); +@@ -5579,7 +6410,9 @@ + DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); ++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); ++ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); + // clang-format on + + if(withDefaults) { +@@ -5632,9 +6465,15 @@ + curr.clear(); + } + +-// allows the user to override procedurally the int/bool options from the command line ++// allows the user to override procedurally the bool options from the command line ++void Context::setOption(const char* option, bool value) { ++ setOption(option, value ? "true" : "false"); ++} ++ ++// allows the user to override procedurally the int options from the command line + void Context::setOption(const char* option, int value) { + setOption(option, toString(value).c_str()); ++ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + } + + // allows the user to override procedurally the string options from the command line +@@ -5651,6 +6490,31 @@ + + void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; } + ++void Context::setCout(std::ostream* out) { p->cout = out; } ++ ++static class DiscardOStream : public std::ostream ++{ ++private: ++ class : public std::streambuf ++ { ++ private: ++ // allowing some buffering decreases the amount of calls to overflow ++ char buf[1024]; ++ ++ protected: ++ std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; } ++ ++ int_type overflow(int_type ch) override { ++ setp(std::begin(buf), std::end(buf)); ++ return traits_type::not_eof(ch); ++ } ++ } discardBuf; ++ ++public: ++ DiscardOStream() ++ : std::ostream(&discardBuf) {} ++} discardOut; ++ + // the main function that does all the filtering and test running + int Context::run() { + using namespace detail; +@@ -5664,18 +6528,25 @@ + g_no_colors = p->no_colors; + p->resetRunData(); + +- // stdout by default +- p->cout = &std::cout; +- p->cerr = &std::cerr; +- +- // or to a file if specified + std::fstream fstr; +- if(p->out.size()) { +- fstr.open(p->out.c_str(), std::fstream::out); +- p->cout = &fstr; ++ if(p->cout == nullptr) { ++ if(p->quiet) { ++ p->cout = &discardOut; ++ } else if(p->out.size()) { ++ // to a file if specified ++ fstr.open(p->out.c_str(), std::fstream::out); ++ p->cout = &fstr; ++ } else { ++ // stdout by default ++ p->cout = &std::cout; ++ } + } + ++ FatalConditionHandler::allocateAltStackMem(); ++ + auto cleanup_and_return = [&]() { ++ FatalConditionHandler::freeAltStackMem(); ++ + if(fstr.is_open()) + fstr.close(); + +@@ -5710,7 +6581,7 @@ + p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); + + #ifdef DOCTEST_PLATFORM_WINDOWS +- if(isDebuggerActive()) ++ if(isDebuggerActive() && p->no_debug_output == false) + p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); + #endif // DOCTEST_PLATFORM_WINDOWS + +@@ -5747,13 +6618,16 @@ + first[i] = first[idxToSwap]; + first[idxToSwap] = temp; + } ++ } else if(p->order_by.compare("none", true) == 0) { ++ // means no sorting - beneficial for death tests which call into the executable ++ // with a specific test case in mind - we don't want to slow down the startup times + } + } + + std::set<String> testSuitesPassingFilt; + +- bool query_mode = p->count || p->list_test_cases || p->list_test_suites; +- std::vector<String> queryResults; ++ bool query_mode = p->count || p->list_test_cases || p->list_test_suites; ++ std::vector<const TestCaseData*> queryResults; + + if(!query_mode) + DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY); +@@ -5766,9 +6640,9 @@ + if(tc.m_skip && !p->no_skip) + skip_me = true; + +- if(!matchesAny(tc.m_file, p->filters[0], true, p->case_sensitive)) ++ if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive)) + skip_me = true; +- if(matchesAny(tc.m_file, p->filters[1], false, p->case_sensitive)) ++ if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive)) + skip_me = true; + if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive)) + skip_me = true; +@@ -5799,14 +6673,14 @@ + + // print the name of the test and don't execute it + if(p->list_test_cases) { +- queryResults.push_back(tc.m_name); ++ queryResults.push_back(&tc); + continue; + } + + // print the name of the test suite if not done already and don't execute it + if(p->list_test_suites) { + if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') { +- queryResults.push_back(tc.m_test_suite); ++ queryResults.push_back(&tc); + testSuitesPassingFilt.insert(tc.m_test_suite); + p->numTestSuitesPassingFilters++; + } +@@ -5829,12 +6703,16 @@ + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); + + p->timer.start(); ++ ++ bool run_test = true; + + do { + // reset some of the fields for subcases (except for the set of fully passed ones) +- p->should_reenter = false; +- p->subcasesCurrentLevel = 0; +- p->subcasesEnteredLevels.clear(); ++ p->should_reenter = false; ++ p->subcasesCurrentMaxLevel = 0; ++ p->subcasesStack.clear(); ++ ++ p->shouldLogCurrentException = true; + + // reset stuff for logging with INFO() + p->stringifiedContexts.clear(); +@@ -5842,10 +6720,13 @@ + #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + try { + #endif // DOCTEST_CONFIG_NO_EXCEPTIONS ++// MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method) ++DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable + FatalConditionHandler fatalConditionHandler; // Handle signals + // execute the test + tc.m_test(); + fatalConditionHandler.reset(); ++DOCTEST_MSVC_SUPPRESS_WARNING_POP + #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + } catch(const TestFailureException&) { + p->failure_flags |= TestCaseFailureReason::AssertFailure; +@@ -5859,10 +6740,15 @@ + // exit this loop if enough assertions have failed - even if there are more subcases + if(p->abort_after > 0 && + p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) { +- p->should_reenter = false; ++ run_test = false; + p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; + } +- } while(p->should_reenter == true); ++ ++ if(p->should_reenter && run_test) ++ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); ++ if(!p->should_reenter) ++ run_test = false; ++ } while(run_test); + + p->finalizeTestCaseData(); + +@@ -5886,20 +6772,9 @@ + DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata); + } + +- // see these issues on the reasoning for this: +- // - https://github.com/onqtam/doctest/issues/143#issuecomment-414418903 +- // - https://github.com/onqtam/doctest/issues/126 +- auto DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS = []() DOCTEST_NOINLINE +- { std::cout << std::string(); }; +- DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS(); +- + return cleanup_and_return(); + } + +-DOCTEST_DEFINE_DEFAULTS(CurrentTestCaseStats); +- +-DOCTEST_DEFINE_DEFAULTS(TestRunStats); +- + IReporter::~IReporter() = default; + + int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); } +@@ -5935,5 +6810,7 @@ + DOCTEST_MSVC_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + ++DOCTEST_SUPPRESS_COMMON_WARNINGS_POP ++ + #endif // DOCTEST_LIBRARY_IMPLEMENTATION + #endif // DOCTEST_CONFIG_IMPLEMENT |