diff options
Diffstat (limited to 'test/unit/catch.hpp')
| -rw-r--r-- | test/unit/catch.hpp | 13393 | 
1 files changed, 9631 insertions, 3762 deletions
diff --git a/test/unit/catch.hpp b/test/unit/catch.hpp index 362f8693f..0384171ae 100644 --- a/test/unit/catch.hpp +++ b/test/unit/catch.hpp @@ -1,9 +1,9 @@  /* - *  Catch v2.0.1 - *  Generated: 2017-11-03 11:53:39.642003 + *  Catch v2.13.4 + *  Generated: 2020-12-29 14:48:00.116107   *  ----------------------------------------------------------   *  This file has been merged from multiple headers. Please don't edit it directly - *  Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved. + *  Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved.   *   *  Distributed under the Boost Software License, Version 1.0. (See accompanying   *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +13,10 @@  // start catch.hpp +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 4 +  #ifdef __clang__  #    pragma clang system_header  #elif defined __GNUC__ @@ -26,37 +30,45 @@  #       pragma warning(push)  #       pragma warning(disable: 161 1682)  #   else // __ICC -#       pragma clang diagnostic ignored "-Wglobal-constructors" -#       pragma clang diagnostic ignored "-Wvariadic-macros" -#       pragma clang diagnostic ignored "-Wc99-extensions" -#       pragma clang diagnostic ignored "-Wunused-variable"  #       pragma clang diagnostic push  #       pragma clang diagnostic ignored "-Wpadded"  #       pragma clang diagnostic ignored "-Wswitch-enum"  #       pragma clang diagnostic ignored "-Wcovered-switch-default"  #    endif  #elif defined __GNUC__ -#    pragma GCC diagnostic ignored "-Wvariadic-macros" -#    pragma GCC diagnostic ignored "-Wunused-variable" -#    pragma GCC diagnostic ignored "-Wparentheses" +     // Because REQUIREs trigger GCC's -Wparentheses, and because still +     // supported version of g++ have only buggy support for _Pragmas, +     // Wparentheses have to be suppressed globally. +#    pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details  #    pragma GCC diagnostic push +#    pragma GCC diagnostic ignored "-Wunused-variable"  #    pragma GCC diagnostic ignored "-Wpadded"  #endif  // end catch_suppress_warnings.h  #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)  #  define CATCH_IMPL +#  define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS)  #  define CATCH_CONFIG_EXTERNAL_INTERFACES  #  if defined(CATCH_CONFIG_DISABLE_MATCHERS)  #    undef CATCH_CONFIG_DISABLE_MATCHERS  #  endif +#  if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#    define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#  endif  #endif +#if !defined(CATCH_CONFIG_IMPL_ONLY)  // start catch_platform.h  #ifdef __APPLE__  # include <TargetConditionals.h> -# if TARGET_OS_MAC == 1 +# if TARGET_OS_OSX == 1  #  define CATCH_PLATFORM_MAC  # elif TARGET_OS_IPHONE == 1  #  define CATCH_PLATFORM_IPHONE @@ -65,11 +77,12 @@  #elif defined(linux) || defined(__linux) || defined(__linux__)  #  define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)  #  define CATCH_PLATFORM_WINDOWS  #endif  // end catch_platform.h +  #ifdef CATCH_IMPL  #  ifndef CLARA_CONFIG_MAIN  #    define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -77,6 +90,13 @@  #  endif  #endif +// start catch_user_interfaces.h + +namespace Catch { +    unsigned int rngSeed(); +} + +// end catch_user_interfaces.h  // start catch_tag_alias_autoregistrar.h  // start catch_common.h @@ -89,6 +109,7 @@  // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?  // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?  // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?  // ****************  // Note to maintainers: if new toggles are added please document them  // in configuration.md, too @@ -101,37 +122,74 @@  #ifdef __cplusplus -#  if __cplusplus >= 201402L +#  if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)  #    define CATCH_CPP14_OR_GREATER  #  endif +#  if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#    define CATCH_CPP17_OR_GREATER +#  endif +  #endif -#ifdef __clang__ +// We have to avoid both ICC and Clang, because they try to mask themselves +// as gcc, and we want only GCC in this block +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) +#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "GCC diagnostic pop" ) + +#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif -#       define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ -            _Pragma( "clang diagnostic push" ) \ -            _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ -            _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -#       define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ -            _Pragma( "clang diagnostic pop" ) +#if defined(__clang__) + +#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +#  if !defined(__ibmxl__) && !defined(__CUDACC__) +#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +#  endif + +#    define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +         _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ +         _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +#    define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ +         _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +#    define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ +         _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) -#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ -            _Pragma( "clang diagnostic push" ) \ -            _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ -            _Pragma( "clang diagnostic pop" ) +#    define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ +         _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +#    define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ +         _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )  #endif // __clang__  //////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) - -#   if !defined(CATCH_CONFIG_POSIX_SIGNALS) -#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#   endif +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) +    #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) +    #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS  #endif  #ifdef __OS400__ @@ -140,18 +198,47 @@  #endif  //////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#    define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +#    define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +#    define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +////////////////////////////////////////////////////////////////////////////////  // Cygwin  #ifdef __CYGWIN__  // Required for some versions of Cygwin to declare gettimeofday  // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin  #   define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ +           && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# endif  #endif // __CYGWIN__  ////////////////////////////////////////////////////////////////////////////////  // Visual C++ -#ifdef _MSC_VER +#if defined(_MSC_VER) + +#  define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +#  define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  __pragma( warning(pop) )  // Universal Windows platform does not support SEH  // Or console colours (or console at all...) @@ -161,9 +248,41 @@  #    define CATCH_INTERNAL_CONFIG_WINDOWS_SEH  #  endif +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +#  if !defined(__clang__) // Handle Clang masquerading for msvc +#    if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +#      define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#    endif // MSVC_TRADITIONAL +#  endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC  #endif // _MSC_VER  //////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +#  define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +#  define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) +    #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +////////////////////////////////////////////////////////////////////////////////  // Use of __COUNTER__ is suppressed during code analysis in  // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly @@ -174,24 +293,170 @@      #define CATCH_INTERNAL_CONFIG_COUNTER  #endif +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) +    #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +    #define CATCH_INTERNAL_CONFIG_NO_ASYNC +    #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) +  // Check if string_view is available and usable +  #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER) +  #    define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +  #endif + +  // Check if optional is available and usable +  #  if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER) +  #    define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +  #  endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER) + +  // Check if byte is available and usable +  #  if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER) +  #    include <cstddef> +  #    if __cpp_lib_byte > 0 +  #      define CATCH_INTERNAL_CONFIG_CPP17_BYTE +  #    endif +  #  endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER) + +  // Check if variant is available and usable +  #  if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER) +  #    if defined(__clang__) && (__clang_major__ < 8) +         // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 +         // fix should be in clang 8, workaround in libstdc++ 8.2 +  #      include <ciso646> +  #      if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +  #        define CATCH_CONFIG_NO_CPP17_VARIANT +  #      else +  #        define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +  #      endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +  #    else +  #      define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +  #    endif // defined(__clang__) && (__clang_major__ < 8) +  #  endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) +  #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)  #   define CATCH_CONFIG_COUNTER  #endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)  #   define CATCH_CONFIG_WINDOWS_SEH  #endif  // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)  #   define CATCH_CONFIG_POSIX_SIGNALS  #endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +#   define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +#    define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +#  define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +#  define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +#  define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +#  define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +#  define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +#  define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#  define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +#  define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC)  && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +#  define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#  define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +#  define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +#   define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +#   define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif  #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)  #   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#   define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS  #endif  #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)  #   define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#   define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +#   define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +#   define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +#   define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +#   define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR  #endif  // end catch_compiler_capabilities.h @@ -207,6 +472,10 @@  #include <string>  #include <cstdint> +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); +  namespace Catch {      struct CaseSensitive { enum Choice { @@ -228,14 +497,17 @@ namespace Catch {      struct SourceLineInfo {          SourceLineInfo() = delete; -        SourceLineInfo( char const* _file, std::size_t _line ) noexcept; +        SourceLineInfo( char const* _file, std::size_t _line ) noexcept +        :   file( _file ), +            line( _line ) +        {} -        SourceLineInfo( SourceLineInfo const& other )        = default; -        SourceLineInfo( SourceLineInfo && )                  = default; -        SourceLineInfo& operator = ( SourceLineInfo const& ) = default; -        SourceLineInfo& operator = ( SourceLineInfo && )     = default; +        SourceLineInfo( SourceLineInfo const& other )            = default; +        SourceLineInfo& operator = ( SourceLineInfo const& )     = default; +        SourceLineInfo( SourceLineInfo&& )              noexcept = default; +        SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; -        bool empty() const noexcept; +        bool empty() const noexcept { return file[0] == '\0'; }          bool operator == ( SourceLineInfo const& other ) const noexcept;          bool operator < ( SourceLineInfo const& other ) const noexcept; @@ -245,10 +517,10 @@ namespace Catch {      std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); -    // This is just here to avoid compiler warnings with macro constants and boolean literals -    bool isTrue( bool value ); -    bool alwaysTrue(); -    bool alwaysFalse(); +    // Bring in operator<< from global namespace into Catch namespace +    // This is necessary because the overload of operator<< above makes +    // lookup stop at namespace Catch +    using ::operator<<;      // Use this in variadic streaming macros to allow      //    >> +StreamEndStop @@ -275,7 +547,11 @@ namespace Catch {  } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ +    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +    namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ +    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  // end catch_tag_alias_autoregistrar.h  // start catch_test_registry.h @@ -283,7 +559,6 @@ namespace Catch {  // start catch_interfaces_testcase.h  #include <vector> -#include <memory>  namespace Catch { @@ -294,8 +569,6 @@ namespace Catch {          virtual ~ITestInvoker();      }; -    using ITestCasePtr = std::shared_ptr<ITestInvoker>; -      class TestCase;      struct IConfig; @@ -305,6 +578,7 @@ namespace Catch {          virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;      }; +    bool isThrowSafe( TestCase const& testCase, IConfig const& config );      bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );      std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );      std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); @@ -317,74 +591,366 @@ namespace Catch {  #include <cstddef>  #include <string>  #include <iosfwd> +#include <cassert>  namespace Catch { -    class StringData; -      /// A non-owning string class (similar to the forthcoming std::string_view)      /// Note that, because a StringRef may be a substring of another string, -    /// it may not be null terminated. c_str() must return a null terminated -    /// string, however, and so the StringRef will internally take ownership -    /// (taking a copy), if necessary. In theory this ownership is not externally -    /// visible - but it does mean (substring) StringRefs should not be shared between -    /// threads. +    /// it may not be null terminated.      class StringRef { -        friend struct StringRefTestAccess; - +    public:          using size_type = std::size_t; +        using const_iterator = const char*; -        char const* m_start; -        size_type m_size; +    private: +        static constexpr char const* const s_empty = ""; -        char* m_data = nullptr; +        char const* m_start = s_empty; +        size_type m_size = 0; -        void takeOwnership(); +    public: // construction +        constexpr StringRef() noexcept = default; -    public: // construction/ assignment -        StringRef() noexcept; -        StringRef( StringRef const& other ) noexcept; -        StringRef( StringRef&& other ) noexcept;          StringRef( char const* rawChars ) noexcept; -        StringRef( char const* rawChars, size_type size ) noexcept; -        StringRef( std::string const& stdString ) noexcept; -        ~StringRef() noexcept; -        auto operator = ( StringRef other ) noexcept -> StringRef&; -        operator std::string() const; +        constexpr StringRef( char const* rawChars, size_type size ) noexcept +        :   m_start( rawChars ), +            m_size( size ) +        {} -        void swap( StringRef& other ) noexcept; +        StringRef( std::string const& stdString ) noexcept +        :   m_start( stdString.c_str() ), +            m_size( stdString.size() ) +        {} + +        explicit operator std::string() const { +            return std::string(m_start, m_size); +        }      public: // operators          auto operator == ( StringRef const& other ) const noexcept -> bool; -        auto operator != ( StringRef const& other ) const noexcept -> bool; +        auto operator != (StringRef const& other) const noexcept -> bool { +            return !(*this == other); +        } -        auto operator[] ( size_type index ) const noexcept -> char; +        auto operator[] ( size_type index ) const noexcept -> char { +            assert(index < m_size); +            return m_start[index]; +        }      public: // named queries -        auto empty() const noexcept -> bool; -        auto size() const noexcept -> size_type; -        auto numberOfCharacters() const noexcept -> size_type; +        constexpr auto empty() const noexcept -> bool { +            return m_size == 0; +        } +        constexpr auto size() const noexcept -> size_type { +            return m_size; +        } + +        // Returns the current start pointer. If the StringRef is not +        // null-terminated, throws std::domain_exception          auto c_str() const -> char const*;      public: // substrings and searches -        auto substr( size_type start, size_type size ) const noexcept -> StringRef; +        // Returns a substring of [start, start + length). +        // If start + length > size(), then the substring is [start, size()). +        // If start > size(), then the substring is empty. +        auto substr( size_type start, size_type length ) const noexcept -> StringRef; -    private: // ownership queries - may not be consistent between calls -        auto isOwned() const noexcept -> bool; -        auto isSubstring() const noexcept -> bool; +        // Returns the current start pointer. May not be null-terminated.          auto data() const noexcept -> char const*; -    }; -    auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; -    auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; -    auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; +        constexpr auto isNullTerminated() const noexcept -> bool { +            return m_start[m_size] == '\0'; +        } + +    public: // iterators +        constexpr const_iterator begin() const { return m_start; } +        constexpr const_iterator end() const { return m_start + m_size; } +    }; +    auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;      auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; +    constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { +        return StringRef( rawChars, size ); +    }  } // namespace Catch +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { +    return Catch::StringRef( rawChars, size ); +} +  // end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next)  CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...)   f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...)   f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ +    CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ +    template<typename...> struct TypeList {};\ +    template<typename...Ts>\ +    constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\ +    template<template<typename...> class...> struct TemplateTypeList{};\ +    template<template<typename...> class...Cs>\ +    constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\ +    template<typename...>\ +    struct append;\ +    template<typename...>\ +    struct rewrap;\ +    template<template<typename...> class, typename...>\ +    struct create;\ +    template<template<typename...> class, typename>\ +    struct convert;\ +    \ +    template<typename T> \ +    struct append<T> { using type = T; };\ +    template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\ +    struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\ +    template< template<typename...> class L1, typename...E1, typename...Rest>\ +    struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\ +    \ +    template< template<typename...> class Container, template<typename...> class List, typename...elems>\ +    struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\ +    template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\ +    struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\ +    \ +    template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\ +    struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\ +    template<template <typename...> class Final, template <typename...> class List, typename...Ts>\ +    struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; }; + +#define INTERNAL_CATCH_NTTP_1(signature, ...)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \ +    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\ +    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\ +    constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \ +    \ +    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\ +    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\ +    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\ +    template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\ +    struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; }; + +#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    static void TestName() +#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    static void TestName() + +#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    static void TestName() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    static void TestName() + +#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\ +    template<typename Type>\ +    void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\ +    {\ +        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\ +    } + +#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\ +    {\ +        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\ +    } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\ +    template<typename Type>\ +    void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\ +    {\ +        Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\ +    } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ +    void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\ +    {\ +        Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\ +    } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\ +    template<typename TestType> \ +    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \ +        void test();\ +    } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \ +    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \ +        void test();\ +    } + +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\ +    template<typename TestType> \ +    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\ +    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \ +    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_NTTP_0 +#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__) +#else +#define INTERNAL_CATCH_NTTP_0(signature) +#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)) +#endif + +// end catch_preprocessor.hpp +// start catch_meta.hpp + + +#include <type_traits> + +namespace Catch { +    template<typename T> +    struct always_false : std::false_type {}; + +    template <typename> struct true_given : std::true_type {}; +    struct is_callable_tester { +        template <typename Fun, typename... Args> +        true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int); +        template <typename...> +        std::false_type static test(...); +    }; + +    template <typename T> +    struct is_callable; + +    template <typename Fun, typename... Args> +    struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {}; + +#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 +    // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is +    // replaced with std::invoke_result here. +    template <typename Func, typename... U> +    using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>; +#else +    // Keep ::type here because we still support C++11 +    template <typename Func, typename... U> +    using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U...)>::type>::type>::type; +#endif + +} // namespace Catch + +namespace mpl_{ +    struct na; +} + +// end catch_meta.hpp  namespace Catch {  template<typename C> @@ -407,13 +973,13 @@ auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {  }  struct NameAndTags { -    NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept; +    NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;      StringRef name;      StringRef tags;  };  struct AutoReg : NonCopyable { -    AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; +    AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;      ~AutoReg();  }; @@ -424,64 +990,507 @@ struct AutoReg : NonCopyable {          static void TestName()      #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \          namespace{                        \ -            struct TestName : ClassName { \ +            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \                  void test();              \              };                            \          }                                 \          void TestName::test() +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... )  \ +        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... )    \ +        namespace{                                                                                  \ +            namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                      \ +            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ +        }                                                                                           \ +        }                                                                                           \ +        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ +            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) +    #else +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ +            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) ) +    #endif + +    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ +            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) +    #else +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ +            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) ) +    #endif +    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \ +            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) +    #else +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \ +            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) ) +    #endif + +    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \ +            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) +    #else +        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \ +            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) +    #endif  #endif      ///////////////////////////////////////////////////////////////////////////////      #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \          static void TestName(); \ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \          CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ -        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ -        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ +        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \          static void TestName()      #define INTERNAL_CATCH_TESTCASE( ... ) \          INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )      ///////////////////////////////////////////////////////////////////////////////      #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \          CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \          namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ -        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION      ///////////////////////////////////////////////////////////////////////////////      #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \          CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \          namespace{ \ -            struct TestName : ClassName{ \ +            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \                  void test(); \              }; \              Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \          } \ -        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \          void TestName::test()      #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \          INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )      ///////////////////////////////////////////////////////////////////////////////      #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \          CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ -        Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ -        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +        Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +    /////////////////////////////////////////////////////////////////////////////// +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ +        INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ +        namespace {\ +        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\ +            INTERNAL_CATCH_TYPE_GEN\ +            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ +            INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\ +            template<typename...Types> \ +            struct TestName{\ +                TestName(){\ +                    int index = 0;                                    \ +                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ +                    using expander = int[];\ +                    (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ +                }\ +            };\ +            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +            TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\ +            return 0;\ +        }();\ +        }\ +        }\ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ +        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) +#else +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ +        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) +#else +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) ) +#endif + +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION                      \ +        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                      \ +        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS                \ +        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS              \ +        template<typename TestType> static void TestFuncName();       \ +        namespace {\ +        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                     \ +            INTERNAL_CATCH_TYPE_GEN                                                  \ +            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))         \ +            template<typename... Types>                               \ +            struct TestName {                                         \ +                void reg_tests() {                                          \ +                    int index = 0;                                    \ +                    using expander = int[];                           \ +                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ +                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ +                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ +                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\ +                }                                                     \ +            };                                                        \ +            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ +                using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \ +                TestInit t;                                           \ +                t.reg_tests();                                        \ +                return 0;                                             \ +            }();                                                      \ +        }                                                             \ +        }                                                             \ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION                       \ +        template<typename TestType>                                   \ +        static void TestFuncName() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ +        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__) +#else +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\ +        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__) +#else +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) ) +#endif + +    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ +        template<typename TestType> static void TestFunc();       \ +        namespace {\ +        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\ +        INTERNAL_CATCH_TYPE_GEN\ +        template<typename... Types>                               \ +        struct TestName {                                         \ +            void reg_tests() {                                          \ +                int index = 0;                                    \ +                using expander = int[];                           \ +                (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ +            }                                                     \ +        };\ +        static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ +                using TestInit = typename convert<TestName, TmplList>::type; \ +                TestInit t;                                           \ +                t.reg_tests();                                        \ +                return 0;                                             \ +            }();                                                      \ +        }}\ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION                       \ +        template<typename TestType>                                   \ +        static void TestFunc() + +    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \ +        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList ) + +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ +        namespace {\ +        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ +            INTERNAL_CATCH_TYPE_GEN\ +            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ +            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ +            INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\ +            template<typename...Types> \ +            struct TestNameClass{\ +                TestNameClass(){\ +                    int index = 0;                                    \ +                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ +                    using expander = int[];\ +                    (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ +                }\ +            };\ +            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +                TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\ +                return 0;\ +        }();\ +        }\ +        }\ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ +        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) +#else +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \ +        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) +#else +    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) +#endif + +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ +        template<typename TestType> \ +            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \ +                void test();\ +            };\ +        namespace {\ +        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\ +            INTERNAL_CATCH_TYPE_GEN                  \ +            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ +            template<typename...Types>\ +            struct TestNameClass{\ +                void reg_tests(){\ +                    int index = 0;\ +                    using expander = int[];\ +                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ +                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ +                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ +                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \ +                }\ +            };\ +            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +                using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\ +                TestInit t;\ +                t.reg_tests();\ +                return 0;\ +            }(); \ +        }\ +        }\ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +        template<typename TestType> \ +        void TestName<TestType>::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ +        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ ) +#else +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\ +        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ ) +#else +    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\ +        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) ) +#endif + +    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ +        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ +        template<typename TestType> \ +        struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \ +            void test();\ +        };\ +        namespace {\ +        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ +            INTERNAL_CATCH_TYPE_GEN\ +            template<typename...Types>\ +            struct TestNameClass{\ +                void reg_tests(){\ +                    int index = 0;\ +                    using expander = int[];\ +                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ +                }\ +            };\ +            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +                using TestInit = typename convert<TestNameClass, TmplList>::type;\ +                TestInit t;\ +                t.reg_tests();\ +                return 0;\ +            }(); \ +        }}\ +        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +        template<typename TestType> \ +        void TestName<TestType>::test() + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \ +        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList )  // end catch_test_registry.h  // start catch_capture.hpp  // start catch_assertionhandler.h +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + +    // ResultWas::OfType enum +    struct ResultWas { enum OfType { +        Unknown = -1, +        Ok = 0, +        Info = 1, +        Warning = 2, + +        FailureBit = 0x10, + +        ExpressionFailed = FailureBit | 1, +        ExplicitFailure = FailureBit | 2, + +        Exception = 0x100 | FailureBit, + +        ThrewException = Exception | 1, +        DidntThrowException = Exception | 2, + +        FatalErrorCondition = 0x200 | FailureBit + +    }; }; + +    bool isOk( ResultWas::OfType resultType ); +    bool isJustInfo( int flags ); + +    // ResultDisposition::Flags enum +    struct ResultDisposition { enum Flags { +        Normal = 0x01, + +        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues +        FalseTest = 0x04,           // Prefix expression with ! +        SuppressFail = 0x08         // Failures are reported but do not fail the test +    }; }; + +    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + +    bool shouldContinueOnFailure( int flags ); +    inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } +    bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + +    struct AssertionInfo +    { +        StringRef macroName; +        SourceLineInfo lineInfo; +        StringRef capturedExpression; +        ResultDisposition::Flags resultDisposition; + +        // We want to delete this constructor but a compiler bug in 4.8 means +        // the struct is then treated as non-aggregate +        //AssertionInfo() = delete; +    }; + +} // end namespace Catch + +// end catch_assertioninfo.h  // start catch_decomposer.h  // start catch_tostring.h -#include <sstream>  #include <vector>  #include <cstddef>  #include <type_traits>  #include <string> +// start catch_stream.h + +#include <iosfwd> +#include <cstddef> +#include <ostream> + +namespace Catch { + +    std::ostream& cout(); +    std::ostream& cerr(); +    std::ostream& clog(); + +    class StringRef; + +    struct IStream { +        virtual ~IStream(); +        virtual std::ostream& stream() const = 0; +    }; + +    auto makeStream( StringRef const &filename ) -> IStream const*; + +    class ReusableStringStream : NonCopyable { +        std::size_t m_index; +        std::ostream* m_oss; +    public: +        ReusableStringStream(); +        ~ReusableStringStream(); + +        auto str() const -> std::string; + +        template<typename T> +        auto operator << ( T const& value ) -> ReusableStringStream& { +            *m_oss << value; +            return *this; +        } +        auto get() -> std::ostream& { return *m_oss; } +    }; +} + +// end catch_stream.h +// start catch_interfaces_enum_values_registry.h + +#include <vector> + +namespace Catch { + +    namespace Detail { +        struct EnumInfo { +            StringRef m_name; +            std::vector<std::pair<int, StringRef>> m_values; + +            ~EnumInfo(); + +            StringRef lookup( int value ) const; +        }; +    } // namespace Detail + +    struct IMutableEnumValuesRegistry { +        virtual ~IMutableEnumValuesRegistry(); + +        virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0; + +        template<typename E> +        Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) { +            static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); +            std::vector<int> intValues; +            intValues.reserve( values.size() ); +            for( auto enumValue : values ) +                intValues.push_back( static_cast<int>( enumValue ) ); +            return registerEnum( enumName, allEnums, intValues ); +        } +    }; + +} // Catch + +// end catch_interfaces_enum_values_registry.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include <string_view> +#endif  #ifdef __OBJC__  // start catch_objc_arc.hpp @@ -534,14 +1543,7 @@ inline id performOptionalSelector( id obj, SEL sel ) {  #pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless  #endif -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); -  namespace Catch { -    // Bring in operator<< from global namespace into Catch namespace -    using ::operator<<; -      namespace Detail {          extern const std::string unprintableString; @@ -555,9 +1557,9 @@ namespace Catch {          template<typename T>          class IsStreamInsertable { -            template<typename SS, typename TT> +            template<typename Stream, typename U>              static auto test(int) -                -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type()); +                -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());              template<typename, typename>              static auto test(...)->std::false_type; @@ -566,25 +1568,66 @@ namespace Catch {              static const bool value = decltype(test<std::ostream, const T&>(0))::value;          }; +        template<typename E> +        std::string convertUnknownEnumToString( E e ); + +        template<typename T> +        typename std::enable_if< +            !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value, +        std::string>::type convertUnstreamable( T const& ) { +            return Detail::unprintableString; +        } +        template<typename T> +        typename std::enable_if< +            !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value, +         std::string>::type convertUnstreamable(T const& ex) { +            return ex.what(); +        } + +        template<typename T> +        typename std::enable_if< +            std::is_enum<T>::value +        , std::string>::type convertUnstreamable( T const& value ) { +            return convertUnknownEnumToString( value ); +        } + +#if defined(_MANAGED) +        //! Convert a CLR string to a utf8 std::string +        template<typename T> +        std::string clrReferenceToString( T^ ref ) { +            if (ref == nullptr) +                return std::string("null"); +            auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); +            cli::pin_ptr<System::Byte> p = &bytes[0]; +            return std::string(reinterpret_cast<char const *>(p), bytes->Length); +        } +#endif +      } // namespace Detail      // If we decide for C++14, change these to enable_if_ts -    template <typename T> +    template <typename T, typename = void>      struct StringMaker {          template <typename Fake = T>          static          typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type -            convert(const Fake& t) { -                std::ostringstream sstr; -                sstr << t; -                return sstr.str(); +            convert(const Fake& value) { +                ReusableStringStream rss; +                // NB: call using the function-like syntax to avoid ambiguity with +                // user-defined templated operator<< under clang. +                rss.operator<<(value); +                return rss.str();          }          template <typename Fake = T>          static          typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type -            convert(const Fake&) { -                return Detail::unprintableString; +            convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) +            return Detail::convertUnstreamable(value); +#else +            return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif          }      }; @@ -597,6 +1640,18 @@ namespace Catch {              return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e);          } +        template<typename E> +        std::string convertUnknownEnumToString( E e ) { +            return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e)); +        } + +#if defined(_MANAGED) +        template <typename T> +        std::string stringify( T^ e ) { +            return ::Catch::StringMaker<T^>::convert(e); +        } +#endif +      } // namespace Detail      // Some predefined specializations @@ -605,10 +1660,13 @@ namespace Catch {      struct StringMaker<std::string> {          static std::string convert(const std::string& str);      }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW      template<> -    struct StringMaker<std::wstring> { -        static std::string convert(const std::wstring& wstr); +    struct StringMaker<std::string_view> { +        static std::string convert(std::string_view str);      }; +#endif      template<>      struct StringMaker<char const *> { @@ -618,6 +1676,20 @@ namespace Catch {      struct StringMaker<char *> {          static std::string convert(char * str);      }; + +#ifdef CATCH_CONFIG_WCHAR +    template<> +    struct StringMaker<std::wstring> { +        static std::string convert(const std::wstring& wstr); +    }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +    template<> +    struct StringMaker<std::wstring_view> { +        static std::string convert(std::wstring_view str); +    }; +# endif +      template<>      struct StringMaker<wchar_t const *> {          static std::string convert(wchar_t const * str); @@ -626,26 +1698,35 @@ namespace Catch {      struct StringMaker<wchar_t *> {          static std::string convert(wchar_t * str);      }; +#endif +    // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, +    //      while keeping string semantics?      template<int SZ>      struct StringMaker<char[SZ]> { -        static std::string convert(const char* str) { +        static std::string convert(char const* str) {              return ::Catch::Detail::stringify(std::string{ str });          }      };      template<int SZ>      struct StringMaker<signed char[SZ]> { -        static std::string convert(const char* str) { -            return ::Catch::Detail::stringify(std::string{ str }); +        static std::string convert(signed char const* str) { +            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });          }      };      template<int SZ>      struct StringMaker<unsigned char[SZ]> { -        static std::string convert(const char* str) { -            return ::Catch::Detail::stringify(std::string{ str }); +        static std::string convert(unsigned char const* str) { +            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });          }      }; +#if defined(CATCH_CONFIG_CPP17_BYTE) +    template<> +    struct StringMaker<std::byte> { +        static std::string convert(std::byte value); +    }; +#endif // defined(CATCH_CONFIG_CPP17_BYTE)      template<>      struct StringMaker<int> {          static std::string convert(int value); @@ -697,10 +1778,13 @@ namespace Catch {      template<>      struct StringMaker<float> {          static std::string convert(float value); +        static int precision;      }; +      template<>      struct StringMaker<double> {          static std::string convert(double value); +        static int precision;      };      template <typename T> @@ -726,35 +1810,30 @@ namespace Catch {          }      }; +#if defined(_MANAGED) +    template <typename T> +    struct StringMaker<T^> { +        static std::string convert( T^ ref ) { +            return ::Catch::Detail::clrReferenceToString(ref); +        } +    }; +#endif +      namespace Detail { -        template<typename InputIterator> -        std::string rangeToString(InputIterator first, InputIterator last) { -            std::ostringstream oss; -            oss << "{ "; +        template<typename InputIterator, typename Sentinel = InputIterator> +        std::string rangeToString(InputIterator first, Sentinel last) { +            ReusableStringStream rss; +            rss << "{ ";              if (first != last) { -                oss << ::Catch::Detail::stringify(*first); +                rss << ::Catch::Detail::stringify(*first);                  for (++first; first != last; ++first) -                    oss << ", " << ::Catch::Detail::stringify(*first); +                    rss << ", " << ::Catch::Detail::stringify(*first);              } -            oss << " }"; -            return oss.str(); +            rss << " }"; +            return rss.str();          }      } -    template<typename T, typename Allocator> -    struct StringMaker<std::vector<T, Allocator> > { -        static std::string convert( std::vector<T,Allocator> const& v ) { -            return ::Catch::Detail::rangeToString( v.begin(), v.end() ); -        } -    }; - -    template<typename T> -    struct EnumStringMaker { -        static std::string convert(const T& t) { -            return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t)); -        } -    }; -  #ifdef __OBJC__      template<>      struct StringMaker<NSString*> { @@ -788,7 +1867,9 @@ namespace Catch {  #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)  #  define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER  #  define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +#  define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER  #  define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#  define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER  #endif  // Separate std::pair specialization @@ -798,18 +1879,36 @@ namespace Catch {      template<typename T1, typename T2>      struct StringMaker<std::pair<T1, T2> > {          static std::string convert(const std::pair<T1, T2>& pair) { -            std::ostringstream oss; -            oss << "{ " +            ReusableStringStream rss; +            rss << "{ "                  << ::Catch::Detail::stringify(pair.first)                  << ", "                  << ::Catch::Detail::stringify(pair.second)                  << " }"; -            return oss.str(); +            return rss.str();          }      };  }  #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include <optional> +namespace Catch { +    template<typename T> +    struct StringMaker<std::optional<T> > { +        static std::string convert(const std::optional<T>& optional) { +            ReusableStringStream rss; +            if (optional.has_value()) { +                rss << ::Catch::Detail::stringify(*optional); +            } else { +                rss << "{ }"; +            } +            return rss.str(); +        } +    }; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +  // Separate std::tuple specialization  #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)  #include <tuple> @@ -841,22 +1940,121 @@ namespace Catch {      template<typename ...Types>      struct StringMaker<std::tuple<Types...>> {          static std::string convert(const std::tuple<Types...>& tuple) { -            std::ostringstream os; -            os << '{'; -            Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, os); -            os << " }"; -            return os.str(); +            ReusableStringStream rss; +            rss << '{'; +            Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get()); +            rss << " }"; +            return rss.str();          }      };  }  #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include <variant> +namespace Catch { +    template<> +    struct StringMaker<std::monostate> { +        static std::string convert(const std::monostate&) { +            return "{ }"; +        } +    }; + +    template<typename... Elements> +    struct StringMaker<std::variant<Elements...>> { +        static std::string convert(const std::variant<Elements...>& variant) { +            if (variant.valueless_by_exception()) { +                return "{valueless variant}"; +            } else { +                return std::visit( +                    [](const auto& value) { +                        return ::Catch::Detail::stringify(value); +                    }, +                    variant +                ); +            } +        } +    }; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { +    // Import begin/ end from std here +    using std::begin; +    using std::end; + +    namespace detail { +        template <typename...> +        struct void_type { +            using type = void; +        }; + +        template <typename T, typename = void> +        struct is_range_impl : std::false_type { +        }; + +        template <typename T> +        struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type { +        }; +    } // namespace detail + +    template <typename T> +    struct is_range : detail::is_range_impl<T> { +    }; + +#if defined(_MANAGED) // Managed types are never ranges +    template <typename T> +    struct is_range<T^> { +        static const bool value = false; +    }; +#endif + +    template<typename Range> +    std::string rangeToString( Range const& range ) { +        return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); +    } + +    // Handle vector<bool> specially +    template<typename Allocator> +    std::string rangeToString( std::vector<bool, Allocator> const& v ) { +        ReusableStringStream rss; +        rss << "{ "; +        bool first = true; +        for( bool b : v ) { +            if( first ) +                first = false; +            else +                rss << ", "; +            rss << ::Catch::Detail::stringify( b ); +        } +        rss << " }"; +        return rss.str(); +    } + +    template<typename R> +    struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> { +        static std::string convert( R const& range ) { +            return rangeToString( range ); +        } +    }; + +    template <typename T, int SZ> +    struct StringMaker<T[SZ]> { +        static std::string convert(T const(&arr)[SZ]) { +            return rangeToString(arr); +        } +    }; + +} // namespace Catch +  // Separate std::chrono::duration specialization  #if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)  #include <ctime>  #include <ratio>  #include <chrono> +namespace Catch { +  template <class Ratio>  struct ratio_string {      static std::string symbol(); @@ -864,69 +2062,68 @@ struct ratio_string {  template <class Ratio>  std::string ratio_string<Ratio>::symbol() { -    std::ostringstream oss; -    oss << '[' << Ratio::num << '/' +    Catch::ReusableStringStream rss; +    rss << '[' << Ratio::num << '/'          << Ratio::den << ']'; -    return oss.str(); +    return rss.str();  }  template <>  struct ratio_string<std::atto> { -    static std::string symbol() { return "a"; } +    static std::string symbol();  };  template <>  struct ratio_string<std::femto> { -    static std::string symbol() { return "f"; } +    static std::string symbol();  };  template <>  struct ratio_string<std::pico> { -    static std::string symbol() { return "p"; } +    static std::string symbol();  };  template <>  struct ratio_string<std::nano> { -    static std::string symbol() { return "n"; } +    static std::string symbol();  };  template <>  struct ratio_string<std::micro> { -    static std::string symbol() { return "u"; } +    static std::string symbol();  };  template <>  struct ratio_string<std::milli> { -    static std::string symbol() { return "m"; } +    static std::string symbol();  }; -namespace Catch {      ////////////      // std::chrono::duration specializations      template<typename Value, typename Ratio>      struct StringMaker<std::chrono::duration<Value, Ratio>> {          static std::string convert(std::chrono::duration<Value, Ratio> const& duration) { -            std::ostringstream oss; -            oss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; -            return oss.str(); +            ReusableStringStream rss; +            rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; +            return rss.str();          }      };      template<typename Value>      struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {          static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) { -            std::ostringstream oss; -            oss << duration.count() << " s"; -            return oss.str(); +            ReusableStringStream rss; +            rss << duration.count() << " s"; +            return rss.str();          }      };      template<typename Value>      struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {          static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) { -            std::ostringstream oss; -            oss << duration.count() << " m"; -            return oss.str(); +            ReusableStringStream rss; +            rss << duration.count() << " m"; +            return rss.str();          }      };      template<typename Value>      struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {          static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) { -            std::ostringstream oss; -            oss << duration.count() << " h"; -            return oss.str(); +            ReusableStringStream rss; +            rss << duration.count() << " h"; +            return rss.str();          }      }; @@ -967,12 +2164,24 @@ namespace Catch {  }  #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ +namespace Catch { \ +    template<> struct StringMaker<enumName> { \ +        static std::string convert( enumName value ) { \ +            static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ +            return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \ +        } \ +    }; \ +} + +#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) +  #ifdef _MSC_VER  #pragma warning(pop)  #endif  // end catch_tostring.h -#include <ostream> +#include <iosfwd>  #ifdef _MSC_VER  #pragma warning(push) @@ -980,32 +2189,38 @@ namespace Catch {  #pragma warning(disable:4018) // more "signed/unsigned mismatch"  #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)  #pragma warning(disable:4180) // qualifier applied to function type has no meaning +#pragma warning(disable:4800) // Forcing result to true or false  #endif  namespace Catch {      struct ITransientExpression { -        virtual auto isBinaryExpression() const -> bool = 0; -        virtual auto getResult() const -> bool = 0; +        auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } +        auto getResult() const -> bool { return m_result; }          virtual void streamReconstructedExpression( std::ostream &os ) const = 0; -        // We don't actually need a virtual destructore, but many static analysers +        ITransientExpression( bool isBinaryExpression, bool result ) +        :   m_isBinaryExpression( isBinaryExpression ), +            m_result( result ) +        {} + +        // We don't actually need a virtual destructor, but many static analysers          // complain if it's not here :-(          virtual ~ITransientExpression(); + +        bool m_isBinaryExpression; +        bool m_result; +      };      void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );      template<typename LhsT, typename RhsT>      class BinaryExpr  : public ITransientExpression { -        bool m_result;          LhsT m_lhs;          StringRef m_op;          RhsT m_rhs; -        auto isBinaryExpression() const -> bool override { return true; } -        auto getResult() const -> bool override { return m_result; } -          void streamReconstructedExpression( std::ostream &os ) const override {              formatReconstructedExpression                      ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); @@ -1013,31 +2228,87 @@ namespace Catch {      public:          BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) -        :   m_result( comparisonResult ), +        :   ITransientExpression{ true, comparisonResult },              m_lhs( lhs ),              m_op( op ),              m_rhs( rhs )          {} + +        template<typename T> +        auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename T> +        auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename T> +        auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename T> +        auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename T> +        auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename T> +        auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename T> +        auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename T> +        auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<T>::value, +            "chained comparisons are not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        }      };      template<typename LhsT>      class UnaryExpr : public ITransientExpression {          LhsT m_lhs; -        auto isBinaryExpression() const -> bool override { return false; } -        auto getResult() const -> bool override { return m_lhs ? true : false; } -          void streamReconstructedExpression( std::ostream &os ) const override {              os << Catch::Detail::stringify( m_lhs );          }      public: -        UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {} +        explicit UnaryExpr( LhsT lhs ) +        :   ITransientExpression{ false, static_cast<bool>(lhs) }, +            m_lhs( lhs ) +        {}      };      // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)      template<typename LhsT, typename RhsT> -    auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; }; +    auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }      template<typename T>      auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }      template<typename T> @@ -1048,7 +2319,7 @@ namespace Catch {      auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }      template<typename LhsT, typename RhsT> -    auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; +    auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }      template<typename T>      auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }      template<typename T> @@ -1062,43 +2333,69 @@ namespace Catch {      class ExprLhs {          LhsT m_lhs;      public: -        ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} +        explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}          template<typename RhsT>          auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { -            return BinaryExpr<LhsT, RhsT const&>( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); +            return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };          }          auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { -            return BinaryExpr<LhsT, bool>( m_lhs == rhs, m_lhs, "==", rhs ); +            return { m_lhs == rhs, m_lhs, "==", rhs };          }          template<typename RhsT>          auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { -            return BinaryExpr<LhsT, RhsT const&>( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); +            return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };          }          auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { -            return BinaryExpr<LhsT, bool>( m_lhs != rhs, m_lhs, "!=", rhs ); +            return { m_lhs != rhs, m_lhs, "!=", rhs };          }          template<typename RhsT>          auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { -            return BinaryExpr<LhsT, RhsT const&>( m_lhs > rhs, m_lhs, ">", rhs ); +            return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs };          }          template<typename RhsT>          auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { -            return BinaryExpr<LhsT, RhsT const&>( m_lhs < rhs, m_lhs, "<", rhs ); +            return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs };          }          template<typename RhsT>          auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { -            return BinaryExpr<LhsT, RhsT const&>( m_lhs >= rhs, m_lhs, ">=", rhs ); +            return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs };          }          template<typename RhsT>          auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { -            return BinaryExpr<LhsT, RhsT const&>( m_lhs <= rhs, m_lhs, "<=", rhs ); +            return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs }; +        } +        template <typename RhsT> +        auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const { +            return { static_cast<bool>(m_lhs | rhs), m_lhs, "|", rhs }; +        } +        template <typename RhsT> +        auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const { +            return { static_cast<bool>(m_lhs & rhs), m_lhs, "&", rhs }; +        } +        template <typename RhsT> +        auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const { +            return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^", rhs }; +        } + +        template<typename RhsT> +        auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<RhsT>::value, +            "operator&& is not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it"); +        } + +        template<typename RhsT> +        auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { +            static_assert(always_false<RhsT>::value, +            "operator|| is not supported inside assertions, " +            "wrap the expression inside parentheses, or decompose it");          }          auto makeUnaryExpr() const -> UnaryExpr<LhsT> { -            return UnaryExpr<LhsT>( m_lhs ); +            return UnaryExpr<LhsT>{ m_lhs };          }      }; @@ -1112,10 +2409,11 @@ namespace Catch {      struct Decomposer {          template<typename T>          auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { -            return ExprLhs<T const&>( lhs ); +            return ExprLhs<T const&>{ lhs };          } +          auto operator <=( bool value ) -> ExprLhs<bool> { -            return ExprLhs<bool>( value ); +            return ExprLhs<bool>{ value };          }      }; @@ -1126,79 +2424,104 @@ namespace Catch {  #endif  // end catch_decomposer.h -// start catch_assertioninfo.h +// start catch_interfaces_capture.h -// start catch_result_type.h +#include <string> +#include <chrono>  namespace Catch { -    // ResultWas::OfType enum -    struct ResultWas { enum OfType { -        Unknown = -1, -        Ok = 0, -        Info = 1, -        Warning = 2, - -        FailureBit = 0x10, - -        ExpressionFailed = FailureBit | 1, -        ExplicitFailure = FailureBit | 2, +    class AssertionResult; +    struct AssertionInfo; +    struct SectionInfo; +    struct SectionEndInfo; +    struct MessageInfo; +    struct MessageBuilder; +    struct Counts; +    struct AssertionReaction; +    struct SourceLineInfo; -        Exception = 0x100 | FailureBit, +    struct ITransientExpression; +    struct IGeneratorTracker; -        ThrewException = Exception | 1, -        DidntThrowException = Exception | 2, +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +    struct BenchmarkInfo; +    template <typename Duration = std::chrono::duration<double, std::nano>> +    struct BenchmarkStats; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -        FatalErrorCondition = 0x200 | FailureBit +    struct IResultCapture { -    }; }; +        virtual ~IResultCapture(); -    bool isOk( ResultWas::OfType resultType ); -    bool isJustInfo( int flags ); +        virtual bool sectionStarted(    SectionInfo const& sectionInfo, +                                        Counts& assertions ) = 0; +        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; +        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; -    // ResultDisposition::Flags enum -    struct ResultDisposition { enum Flags { -        Normal = 0x01, +        virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; -        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues -        FalseTest = 0x04,           // Prefix expression with ! -        SuppressFail = 0x08         // Failures are reported but do not fail the test -    }; }; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +        virtual void benchmarkPreparing( std::string const& name ) = 0; +        virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; +        virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; +        virtual void benchmarkFailed( std::string const& error ) = 0; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); +        virtual void pushScopedMessage( MessageInfo const& message ) = 0; +        virtual void popScopedMessage( MessageInfo const& message ) = 0; -    bool shouldContinueOnFailure( int flags ); -    bool isFalseTest( int flags ); -    bool shouldSuppressFailure( int flags ); +        virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; -} // end namespace Catch +        virtual void handleFatalErrorCondition( StringRef message ) = 0; -// end catch_result_type.h -namespace Catch { +        virtual void handleExpr +                (   AssertionInfo const& info, +                    ITransientExpression const& expr, +                    AssertionReaction& reaction ) = 0; +        virtual void handleMessage +                (   AssertionInfo const& info, +                    ResultWas::OfType resultType, +                    StringRef const& message, +                    AssertionReaction& reaction ) = 0; +        virtual void handleUnexpectedExceptionNotThrown +                (   AssertionInfo const& info, +                    AssertionReaction& reaction ) = 0; +        virtual void handleUnexpectedInflightException +                (   AssertionInfo const& info, +                    std::string const& message, +                    AssertionReaction& reaction ) = 0; +        virtual void handleIncomplete +                (   AssertionInfo const& info ) = 0; +        virtual void handleNonExpr +                (   AssertionInfo const &info, +                    ResultWas::OfType resultType, +                    AssertionReaction &reaction ) = 0; -    struct AssertionInfo -    { -        StringRef macroName; -        SourceLineInfo lineInfo; -        StringRef capturedExpression; -        ResultDisposition::Flags resultDisposition; +        virtual bool lastAssertionPassed() = 0; +        virtual void assertionPassed() = 0; -        // We want to delete this constructor but a compiler bug in 4.8 means -        // the struct is then treated as non-aggregate -        //AssertionInfo() = delete; +        // Deprecated, do not use: +        virtual std::string getCurrentTestName() const = 0; +        virtual const AssertionResult* getLastResult() const = 0; +        virtual void exceptionEarlyReported() = 0;      }; -} // end namespace Catch +    IResultCapture& getResultCapture(); +} -// end catch_assertioninfo.h +// end catch_interfaces_capture.h  namespace Catch {      struct TestFailureException{};      struct AssertionResultData; +    struct IResultCapture; +    class RunContext;      class LazyExpression {          friend class AssertionHandler;          friend struct AssertionStats; +        friend class RunContext;          ITransientExpression const* m_transientExpression = nullptr;          bool m_isNegated; @@ -1212,41 +2535,51 @@ namespace Catch {          friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;      }; +    struct AssertionReaction { +        bool shouldDebugBreak = false; +        bool shouldThrow = false; +    }; +      class AssertionHandler {          AssertionInfo m_assertionInfo; -        bool m_shouldDebugBreak = false; -        bool m_shouldThrow = false; -        bool m_inExceptionGuard = false; +        AssertionReaction m_reaction; +        bool m_completed = false; +        IResultCapture& m_resultCapture;      public:          AssertionHandler -            (   StringRef macroName, +            (   StringRef const& macroName,                  SourceLineInfo const& lineInfo,                  StringRef capturedExpression,                  ResultDisposition::Flags resultDisposition ); -        ~AssertionHandler(); - -        void handle( ITransientExpression const& expr ); +        ~AssertionHandler() { +            if ( !m_completed ) { +                m_resultCapture.handleIncomplete( m_assertionInfo ); +            } +        }          template<typename T> -        void handle( ExprLhs<T> const& expr ) { -            handle( expr.makeUnaryExpr() ); +        void handleExpr( ExprLhs<T> const& expr ) { +            handleExpr( expr.makeUnaryExpr() );          } -        void handle( ResultWas::OfType resultType ); -        void handle( ResultWas::OfType resultType, StringRef const& message ); -        void handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ); -        void handle( AssertionResultData const& resultData, ITransientExpression const* expr ); +        void handleExpr( ITransientExpression const& expr ); + +        void handleMessage(ResultWas::OfType resultType, StringRef const& message); + +        void handleExceptionThrownAsExpected(); +        void handleUnexpectedExceptionNotThrown(); +        void handleExceptionNotThrownAsExpected(); +        void handleThrowingCallSkipped(); +        void handleUnexpectedInflightException(); -        auto shouldDebugBreak() const -> bool; +        void complete(); +        void setCompleted(); + +        // query          auto allowThrows() const -> bool; -        void reactWithDebugBreak() const; -        void reactWithoutDebugBreak() const; -        void useActiveException(); -        void setExceptionGuard(); -        void unsetExceptionGuard();      }; -    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); +    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString );  } // namespace Catch @@ -1254,16 +2587,16 @@ namespace Catch {  // start catch_message.h  #include <string> -#include <sstream> +#include <vector>  namespace Catch {      struct MessageInfo { -        MessageInfo(    std::string const& _macroName, +        MessageInfo(    StringRef const& _macroName,                          SourceLineInfo const& _lineInfo,                          ResultWas::OfType _type ); -        std::string macroName; +        StringRef macroName;          std::string message;          SourceLineInfo lineInfo;          ResultWas::OfType type; @@ -1283,12 +2616,11 @@ namespace Catch {              return *this;          } -        // !TBD reuse a global/ thread-local stream -        std::ostringstream m_stream; +        ReusableStringStream m_stream;      };      struct MessageBuilder : MessageStream { -        MessageBuilder( std::string const& macroName, +        MessageBuilder( StringRef const& macroName,                          SourceLineInfo const& lineInfo,                          ResultWas::OfType type ); @@ -1303,98 +2635,40 @@ namespace Catch {      class ScopedMessage {      public: -        ScopedMessage( MessageBuilder const& builder ); +        explicit ScopedMessage( MessageBuilder const& builder ); +        ScopedMessage( ScopedMessage& duplicate ) = delete; +        ScopedMessage( ScopedMessage&& old );          ~ScopedMessage();          MessageInfo m_info; +        bool m_moved;      }; -} // end namespace Catch - -// end catch_message.h -// start catch_interfaces_capture.h - -#include <string> - -namespace Catch { - -    class AssertionResult; -    struct AssertionInfo; -    struct SectionInfo; -    struct SectionEndInfo; -    struct MessageInfo; -    struct Counts; -    struct BenchmarkInfo; -    struct BenchmarkStats; - -    struct IResultCapture { - -        virtual ~IResultCapture(); - -        virtual void assertionStarting( AssertionInfo const& info ) = 0; -        virtual void assertionEnded( AssertionResult const& result ) = 0; -        virtual bool sectionStarted(    SectionInfo const& sectionInfo, -                                        Counts& assertions ) = 0; -        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; -        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - -        virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; -        virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; - -        virtual void pushScopedMessage( MessageInfo const& message ) = 0; -        virtual void popScopedMessage( MessageInfo const& message ) = 0; - -        virtual std::string getCurrentTestName() const = 0; -        virtual const AssertionResult* getLastResult() const = 0; +    class Capturer { +        std::vector<MessageInfo> m_messages; +        IResultCapture& m_resultCapture = getResultCapture(); +        size_t m_captured = 0; +    public: +        Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); +        ~Capturer(); -        virtual void exceptionEarlyReported() = 0; +        void captureValue( size_t index, std::string const& value ); -        virtual void handleFatalErrorCondition( StringRef message ) = 0; +        template<typename T> +        void captureValues( size_t index, T const& value ) { +            captureValue( index, Catch::Detail::stringify( value ) ); +        } -        virtual bool lastAssertionPassed() = 0; -        virtual void assertionPassed() = 0; -        virtual void assertionRun() = 0; +        template<typename T, typename... Ts> +        void captureValues( size_t index, T const& value, Ts const&... values ) { +            captureValue( index, Catch::Detail::stringify(value) ); +            captureValues( index+1, values... ); +        }      }; -    IResultCapture& getResultCapture(); -} - -// end catch_interfaces_capture.h -// start catch_debugger.h - -namespace Catch { -    bool isDebuggerActive(); -} - -#ifdef CATCH_PLATFORM_MAC - -    #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ - -#elif defined(CATCH_PLATFORM_LINUX) -    // If we can use inline assembler, do it because this allows us to break -    // directly at the location of the failing check instead of breaking inside -    // raise() called from it, i.e. one stack frame below. -    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) -        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ -    #else // Fall back to the generic way. -        #include <signal.h> - -        #define CATCH_TRAP() raise(SIGTRAP) -    #endif -#elif defined(_MSC_VER) -    #define CATCH_TRAP() __debugbreak() -#elif defined(__MINGW32__) -    extern "C" __declspec(dllimport) void __stdcall DebugBreak(); -    #define CATCH_TRAP() DebugBreak() -#endif - -#ifdef CATCH_TRAP -    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } -#else -    #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); -#endif +} // end namespace Catch -// end catch_debugger.h +// end catch_message.h  #if !defined(CATCH_CONFIG_DISABLE)  #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) @@ -1403,50 +2677,36 @@ namespace Catch {    #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"  #endif -#if defined(CATCH_CONFIG_FAST_COMPILE) -/////////////////////////////////////////////////////////////////////////////// -// We can speedup compilation significantly by breaking into debugger lower in -// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER -// macro in each assertion -#define INTERNAL_CATCH_REACT( handler ) \ -    handler.reactWithDebugBreak(); +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)  ///////////////////////////////////////////////////////////////////////////////  // Another way to speed-up compilation is to omit local try-catch for REQUIRE*  // macros. -// This can potentially cause false negative, if the test code catches -// the exception before it propagates back up to the runner. -#define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard(); -#define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard(); +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer )  #else // CATCH_CONFIG_FAST_COMPILE -/////////////////////////////////////////////////////////////////////////////// -// In the event of a failure works out if the debugger needs to be invoked -// and/or an exception thrown and takes appropriate action. -// This needs to be done as a macro so the debugger will stop in the user -// source code rather than in Catch library code -#define INTERNAL_CATCH_REACT( handler ) \ -    if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ -    handler.reactWithoutDebugBreak(); - -#define INTERNAL_CATCH_TRY( capturer ) try -#define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); } +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }  #endif +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); +  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ -        INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ +        CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ +        INTERNAL_CATCH_TRY { \ +            CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \              CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ -            catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ -            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ +            catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ +            CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \          } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::isTrue( false && static_cast<bool>( !!(__VA_ARGS__) ) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look -    // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. +    } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ @@ -1461,83 +2721,92 @@ namespace Catch {  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \          try { \              static_cast<void>(__VA_ARGS__); \ -            catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ +            catchAssertionHandler.handleExceptionNotThrownAsExpected(); \          } \          catch( ... ) { \ -            catchAssertionHandler.useActiveException(); \ +            catchAssertionHandler.handleUnexpectedInflightException(); \          } \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::alwaysFalse() ) +    } while( false )  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \          if( catchAssertionHandler.allowThrows() ) \              try { \                  static_cast<void>(__VA_ARGS__); \ -                catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ +                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \              } \              catch( ... ) { \ -                catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ +                catchAssertionHandler.handleExceptionThrownAsExpected(); \              } \          else \ -            catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ +            catchAssertionHandler.handleThrowingCallSkipped(); \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::alwaysFalse() ) +    } while( false )  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \          if( catchAssertionHandler.allowThrows() ) \              try { \                  static_cast<void>(expr); \ -                catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ +                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \              } \              catch( exceptionType const& ) { \ -                catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ +                catchAssertionHandler.handleExceptionThrownAsExpected(); \              } \              catch( ... ) { \ -                catchAssertionHandler.useActiveException(); \ +                catchAssertionHandler.handleUnexpectedInflightException(); \              } \          else \ -            catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ +            catchAssertionHandler.handleThrowingCallSkipped(); \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::alwaysFalse() ) +    } while( false )  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ -        catchAssertionHandler.handle( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ +        catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::alwaysFalse() ) +    } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ +    auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ +    varName.captureValues( 0, __VA_ARGS__ )  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_INFO( macroName, log ) \ -    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; +    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ +    Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )  ///////////////////////////////////////////////////////////////////////////////  // Although this is matcher-based, it can be used with just a string  #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \          if( catchAssertionHandler.allowThrows() ) \              try { \                  static_cast<void>(__VA_ARGS__); \ -                catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ +                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \              } \              catch( ... ) { \ -                handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ +                Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \              } \          else \ -            catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ +            catchAssertionHandler.handleThrowingCallSkipped(); \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::alwaysFalse() ) +    } while( false )  #endif // CATCH_CONFIG_DISABLE @@ -1572,6 +2841,7 @@ namespace Catch {          Totals delta( Totals const& prevTotals ) const; +        int error = 0;          Counts assertions;          Counts testCases;      }; @@ -1585,17 +2855,20 @@ namespace Catch {      struct SectionInfo {          SectionInfo              (   SourceLineInfo const& _lineInfo, +                std::string const& _name ); + +        // Deprecated +        SectionInfo +            (   SourceLineInfo const& _lineInfo,                  std::string const& _name, -                std::string const& _description = std::string() ); +                std::string const& ) : SectionInfo( _lineInfo, _name ) {}          std::string name; -        std::string description; +        std::string description; // !Deprecated: this will always be empty          SourceLineInfo lineInfo;      };      struct SectionEndInfo { -        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); -          SectionInfo sectionInfo;          Counts prevAssertions;          double durationInSeconds; @@ -1617,8 +2890,8 @@ namespace Catch {          uint64_t m_nanoseconds = 0;      public:          void start(); -        auto getElapsedNanoseconds() const -> unsigned int; -        auto getElapsedMicroseconds() const -> unsigned int; +        auto getElapsedNanoseconds() const -> uint64_t; +        auto getElapsedMicroseconds() const -> uint64_t;          auto getElapsedMilliseconds() const -> unsigned int;          auto getElapsedSeconds() const -> double;      }; @@ -1649,56 +2922,19 @@ namespace Catch {  } // end namespace Catch -    #define INTERNAL_CATCH_SECTION( ... ) \ -        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#define INTERNAL_CATCH_SECTION( ... ) \ +    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +    CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ +    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ +    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -// end catch_section.h -// start catch_benchmark.h +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ +    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +    CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ +    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ +    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#include <cstdint> -#include <string> - -namespace Catch { - -    class BenchmarkLooper { - -        std::string m_name; -        std::size_t m_count = 0; -        std::size_t m_iterationsToRun = 1; -        uint64_t m_resolution; -        Timer m_timer; - -        static auto getResolution() -> uint64_t; -    public: -        // Keep most of this inline as it's on the code path that is being timed -        BenchmarkLooper( StringRef name ) -        :   m_name( name ), -            m_resolution( getResolution() ) -        { -            reportStart(); -            m_timer.start(); -        } - -        explicit operator bool() { -            if( m_count < m_iterationsToRun ) -                return true; -            return needsMoreIterations(); -        } - -        void increment() { -            ++m_count; -        } - -        void reportStart(); -        auto needsMoreIterations() -> bool; -    }; - -} // end namespace Catch - -#define BENCHMARK( name ) \ -    for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) - -// end catch_benchmark.h +// end catch_section.h  // start catch_interfaces_exception.h  // start catch_interfaces_registry_hub.h @@ -1715,6 +2951,8 @@ namespace Catch {      struct IReporterRegistry;      struct IReporterFactory;      struct ITagAliasRegistry; +    struct IMutableEnumValuesRegistry; +      class StartupExceptionRegistry;      using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; @@ -1725,8 +2963,7 @@ namespace Catch {          virtual IReporterRegistry const& getReporterRegistry() const = 0;          virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;          virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; - -        virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; +        virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;          virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;      }; @@ -1739,9 +2976,10 @@ namespace Catch {          virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;          virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;          virtual void registerStartupException() noexcept = 0; +        virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;      }; -    IRegistryHub& getRegistryHub(); +    IRegistryHub const& getRegistryHub();      IMutableRegistryHub& getMutableRegistryHub();      void cleanUp();      std::string translateActiveException(); @@ -1785,6 +3023,9 @@ namespace Catch {              {}              std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +                return ""; +#else                  try {                      if( it == itEnd )                          std::rethrow_exception(std::current_exception()); @@ -1794,6 +3035,7 @@ namespace Catch {                  catch( T& ex ) {                      return m_translateFunction( ex );                  } +#endif              }          protected: @@ -1812,7 +3054,10 @@ namespace Catch {  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \      static std::string translatorName( signature ); \ -    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ +    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ +    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ +    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ +    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \      static std::string translatorName( signature )  #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) @@ -1820,21 +3065,6 @@ namespace Catch {  // end catch_interfaces_exception.h  // start catch_approx.h -// start catch_enforce.h - -#include <sstream> -#include <stdexcept> - -#define CATCH_PREPARE_EXCEPTION( type, msg ) \ -    type( static_cast<std::ostringstream&&>( std::ostringstream() << msg ).str() ) -#define CATCH_INTERNAL_ERROR( msg ) \ -    throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); -#define CATCH_ERROR( msg ) \ -    throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg ) -#define CATCH_ENFORCE( condition, msg ) \ -    do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) - -// end catch_enforce.h  #include <type_traits>  namespace Catch { @@ -1843,18 +3073,26 @@ namespace Detail {      class Approx {      private:          bool equalityComparisonImpl(double other) const; +        // Validates the new margin (margin >= 0) +        // out-of-line to avoid including stdexcept in the header +        void setMargin(double margin); +        // Validates the new epsilon (0 < epsilon < 1) +        // out-of-line to avoid including stdexcept in the header +        void setEpsilon(double epsilon);      public:          explicit Approx ( double value );          static Approx custom(); +        Approx operator-() const; +          template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>          Approx operator()( T const& value ) {              Approx approx( static_cast<double>(value) ); -            approx.epsilon( m_epsilon ); -            approx.margin( m_margin ); -            approx.scale( m_scale ); +            approx.m_epsilon = m_epsilon; +            approx.m_margin = m_margin; +            approx.m_scale = m_scale;              return approx;          } @@ -1906,20 +3144,14 @@ namespace Detail {          template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>          Approx& epsilon( T const& newEpsilon ) {              double epsilonAsDouble = static_cast<double>(newEpsilon); -            CATCH_ENFORCE(epsilonAsDouble >= 0 && epsilonAsDouble <= 1.0, -                          "Invalid Approx::epsilon: " << epsilonAsDouble -                          << ", Approx::epsilon has to be between 0 and 1"); -            m_epsilon = epsilonAsDouble; +            setEpsilon(epsilonAsDouble);              return *this;          }          template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>          Approx& margin( T const& newMargin ) {              double marginAsDouble = static_cast<double>(newMargin); -            CATCH_ENFORCE(marginAsDouble >= 0, -                          "Invalid Approx::margin: " << marginAsDouble -                          << ", Approx::Margin has to be non-negative."); -            m_margin = marginAsDouble; +            setMargin(marginAsDouble);              return *this;          } @@ -1937,7 +3169,12 @@ namespace Detail {          double m_scale;          double m_value;      }; -} +} // end namespace Detail + +namespace literals { +    Detail::Approx operator "" _a(long double val); +    Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals  template<>  struct StringMaker<Catch::Detail::Approx> { @@ -1951,6 +3188,7 @@ struct StringMaker<Catch::Detail::Approx> {  #include <string>  #include <iosfwd> +#include <vector>  namespace Catch { @@ -1961,7 +3199,13 @@ namespace Catch {      bool contains( std::string const& s, std::string const& infix );      void toLowerInPlace( std::string& s );      std::string toLower( std::string const& s ); +    //! Returns a new string without whitespace at the start/end      std::string trim( std::string const& str ); +    //! Returns a substring of the original ref without whitespace. Beware lifetimes! +    StringRef trim(StringRef ref); + +    // !!! Be aware, returns refs into original string - make sure original string outlives them +    std::vector<StringRef> splitStringRef( StringRef str, char delimiter );      bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );      struct pluralise { @@ -2004,21 +3248,35 @@ namespace Matchers {              mutable std::string m_cachedToString;          }; +#ifdef __clang__ +#    pragma clang diagnostic push +#    pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif +          template<typename ObjectT>          struct MatcherMethod {              virtual bool match( ObjectT const& arg ) const = 0;          }; -        template<typename PtrT> -        struct MatcherMethod<PtrT*> { -            virtual bool match( PtrT* arg ) const = 0; + +#if defined(__OBJC__) +        // Hack to fix Catch GH issue #1661. Could use id for generic Object support. +        // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation +        template<> +        struct MatcherMethod<NSString*> { +            virtual bool match( NSString* arg ) const = 0;          }; +#endif -        template<typename ObjectT, typename ComparatorT = ObjectT> -        struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> { +#ifdef __clang__ +#    pragma clang diagnostic pop +#endif -            MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const; -            MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const; -            MatchNotOf<ComparatorT> operator ! () const; +        template<typename T> +        struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> { + +            MatchAllOf<T> operator && ( MatcherBase const& other ) const; +            MatchAnyOf<T> operator || ( MatcherBase const& other ) const; +            MatchNotOf<T> operator ! () const;          };          template<typename ArgT> @@ -2046,9 +3304,10 @@ namespace Matchers {                  return description;              } -            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) { -                m_matchers.push_back( &other ); -                return *this; +            MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) { +                auto copy(*this); +                copy.m_matchers.push_back( &other ); +                return copy;              }              std::vector<MatcherBase<ArgT> const*> m_matchers; @@ -2079,9 +3338,10 @@ namespace Matchers {                  return description;              } -            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) { -                m_matchers.push_back( &other ); -                return *this; +            MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) { +                auto copy(*this); +                copy.m_matchers.push_back( &other ); +                return copy;              }              std::vector<MatcherBase<ArgT> const*> m_matchers; @@ -2102,17 +3362,17 @@ namespace Matchers {              MatcherBase<ArgT> const& m_underlyingMatcher;          }; -        template<typename ObjectT, typename ComparatorT> -        MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const { -            return MatchAllOf<ComparatorT>() && *this && other; +        template<typename T> +        MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const { +            return MatchAllOf<T>() && *this && other;          } -        template<typename ObjectT, typename ComparatorT> -        MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const { -            return MatchAnyOf<ComparatorT>() || *this || other; +        template<typename T> +        MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const { +            return MatchAnyOf<T>() || *this || other;          } -        template<typename ObjectT, typename ComparatorT> -        MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const { -            return MatchNotOf<ComparatorT>( *this ); +        template<typename T> +        MatchNotOf<T> MatcherBase<T>::operator ! () const { +            return MatchNotOf<T>( *this );          }      } // namespace Impl @@ -2125,6 +3385,142 @@ using Matchers::Impl::MatcherBase;  } // namespace Catch  // end catch_matchers.h +// start catch_matchers_exception.hpp + +namespace Catch { +namespace Matchers { +namespace Exception { + +class ExceptionMessageMatcher : public MatcherBase<std::exception> { +    std::string m_message; +public: + +    ExceptionMessageMatcher(std::string const& message): +        m_message(message) +    {} + +    bool match(std::exception const& ex) const override; + +    std::string describe() const override; +}; + +} // namespace Exception + +Exception::ExceptionMessageMatcher Message(std::string const& message); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_exception.hpp +// start catch_matchers_floating.h + +namespace Catch { +namespace Matchers { + +    namespace Floating { + +        enum class FloatingPointKind : uint8_t; + +        struct WithinAbsMatcher : MatcherBase<double> { +            WithinAbsMatcher(double target, double margin); +            bool match(double const& matchee) const override; +            std::string describe() const override; +        private: +            double m_target; +            double m_margin; +        }; + +        struct WithinUlpsMatcher : MatcherBase<double> { +            WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); +            bool match(double const& matchee) const override; +            std::string describe() const override; +        private: +            double m_target; +            uint64_t m_ulps; +            FloatingPointKind m_type; +        }; + +        // Given IEEE-754 format for floats and doubles, we can assume +        // that float -> double promotion is lossless. Given this, we can +        // assume that if we do the standard relative comparison of +        // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get +        // the same result if we do this for floats, as if we do this for +        // doubles that were promoted from floats. +        struct WithinRelMatcher : MatcherBase<double> { +            WithinRelMatcher(double target, double epsilon); +            bool match(double const& matchee) const override; +            std::string describe() const override; +        private: +            double m_target; +            double m_epsilon; +        }; + +    } // namespace Floating + +    // The following functions create the actual matcher objects. +    // This allows the types to be inferred +    Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); +    Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); +    Floating::WithinAbsMatcher WithinAbs(double target, double margin); +    Floating::WithinRelMatcher WithinRel(double target, double eps); +    // defaults epsilon to 100*numeric_limits<double>::epsilon() +    Floating::WithinRelMatcher WithinRel(double target); +    Floating::WithinRelMatcher WithinRel(float target, float eps); +    // defaults epsilon to 100*numeric_limits<float>::epsilon() +    Floating::WithinRelMatcher WithinRel(float target); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include <functional> +#include <string> + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { +    std::string finalizeDescription(const std::string& desc); +} + +template <typename T> +class PredicateMatcher : public MatcherBase<T> { +    std::function<bool(T const&)> m_predicate; +    std::string m_description; +public: + +    PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr) +        :m_predicate(std::move(elem)), +        m_description(Detail::finalizeDescription(descr)) +    {} + +    bool match( T const& item ) const override { +        return m_predicate(item); +    } + +    std::string describe() const override { +        return m_description; +    } +}; + +} // namespace Generic + +    // The following functions create the actual matcher objects. +    // The user has to explicitly specify type to the function, because +    // inferring std::function<bool(T const&)> is hard (but possible) and +    // requires a lot of TMP. +    template<typename T> +    Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") { +        return Generic::PredicateMatcher<T>(predicate, description); +    } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp  // start catch_matchers_string.h  #include <string> @@ -2169,6 +3565,16 @@ namespace Matchers {              bool match( std::string const& source ) const override;          }; +        struct RegexMatcher : MatcherBase<std::string> { +            RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); +            bool match( std::string const& matchee ) const override; +            std::string describe() const override; + +        private: +            std::string m_regex; +            CaseSensitive::Choice m_caseSensitivity; +        }; +      } // namespace StdString      // The following functions create the actual matcher objects. @@ -2178,6 +3584,7 @@ namespace Matchers {      StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );      StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );      StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); +    StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );  } // namespace Matchers  } // namespace Catch @@ -2185,17 +3592,18 @@ namespace Matchers {  // end catch_matchers_string.h  // start catch_matchers_vector.h +#include <algorithm> +  namespace Catch {  namespace Matchers {      namespace Vector { - -        template<typename T> -        struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> { +        template<typename T, typename Alloc> +        struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> {              ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} -            bool match(std::vector<T> const &v) const override { +            bool match(std::vector<T, Alloc> const &v) const override {                  for (auto const& el : v) {                      if (el == m_comparator) {                          return true; @@ -2211,12 +3619,12 @@ namespace Matchers {              T const& m_comparator;          }; -        template<typename T> -        struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > { +        template<typename T, typename AllocComp, typename AllocMatch> +        struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> { -            ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} +            ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {} -            bool match(std::vector<T> const &v) const override { +            bool match(std::vector<T, AllocMatch> const &v) const override {                  // !TBD: see note in EqualsMatcher                  if (m_comparator.size() > v.size())                      return false; @@ -2238,18 +3646,18 @@ namespace Matchers {                  return "Contains: " + ::Catch::Detail::stringify( m_comparator );              } -            std::vector<T> const& m_comparator; +            std::vector<T, AllocComp> const& m_comparator;          }; -        template<typename T> -        struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > { +        template<typename T, typename AllocComp, typename AllocMatch> +        struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> { -            EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} +            EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {} -            bool match(std::vector<T> const &v) const override { +            bool match(std::vector<T, AllocMatch> const &v) const override {                  // !TBD: This currently works if all elements can be compared using !=                  // - a more general approach would be via a compare template that defaults -                // to using !=. but could be specialised for, e.g. std::vector<T> etc +                // to using !=. but could be specialised for, e.g. std::vector<T, Alloc> etc                  // - then just call that directly                  if (m_comparator.size() != v.size())                      return false; @@ -2261,7 +3669,60 @@ namespace Matchers {              std::string describe() const override {                  return "Equals: " + ::Catch::Detail::stringify( m_comparator );              } -            std::vector<T> const& m_comparator; +            std::vector<T, AllocComp> const& m_comparator; +        }; + +        template<typename T, typename AllocComp, typename AllocMatch> +        struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> { + +            ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {} + +            bool match(std::vector<T, AllocMatch> const &v) const override { +                if (m_comparator.size() != v.size()) +                    return false; +                for (std::size_t i = 0; i < v.size(); ++i) +                    if (m_comparator[i] != approx(v[i])) +                        return false; +                return true; +            } +            std::string describe() const override { +                return "is approx: " + ::Catch::Detail::stringify( m_comparator ); +            } +            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> +            ApproxMatcher& epsilon( T const& newEpsilon ) { +                approx.epsilon(newEpsilon); +                return *this; +            } +            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> +            ApproxMatcher& margin( T const& newMargin ) { +                approx.margin(newMargin); +                return *this; +            } +            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> +            ApproxMatcher& scale( T const& newScale ) { +                approx.scale(newScale); +                return *this; +            } + +            std::vector<T, AllocComp> const& m_comparator; +            mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); +        }; + +        template<typename T, typename AllocComp, typename AllocMatch> +        struct UnorderedEqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> { +            UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target) : m_target(target) {} +            bool match(std::vector<T, AllocMatch> const& vec) const override { +                if (m_target.size() != vec.size()) { +                    return false; +                } +                return std::is_permutation(m_target.begin(), m_target.end(), vec.begin()); +            } + +            std::string describe() const override { +                return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); +            } +        private: +            std::vector<T, AllocComp> const& m_target;          };      } // namespace Vector @@ -2269,19 +3730,29 @@ namespace Matchers {      // The following functions create the actual matcher objects.      // This allows the types to be inferred -    template<typename T> -    Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) { -        return Vector::ContainsMatcher<T>( comparator ); +    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> +    Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) { +        return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator );      } -    template<typename T> -    Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) { -        return Vector::ContainsElementMatcher<T>( comparator ); +    template<typename T, typename Alloc = std::allocator<T>> +    Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) { +        return Vector::ContainsElementMatcher<T, Alloc>( comparator );      } -    template<typename T> -    Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) { -        return Vector::EqualsMatcher<T>( comparator ); +    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> +    Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) { +        return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator ); +    } + +    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> +    Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) { +        return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator ); +    } + +    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> +    Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) { +        return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target );      }  } // namespace Matchers @@ -2295,18 +3766,14 @@ namespace Catch {          ArgT const& m_arg;          MatcherT m_matcher;          StringRef m_matcherString; -        bool m_result;      public: -        MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) -        :   m_arg( arg ), +        MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) +        :   ITransientExpression{ true, matcher.match( arg ) }, +            m_arg( arg ),              m_matcher( matcher ), -            m_matcherString( matcherString ), -            m_result( matcher.match( arg ) ) +            m_matcherString( matcherString )          {} -        auto isBinaryExpression() const -> bool  override { return true; } -        auto getResult() const -> bool override { return m_result; } -          void streamReconstructedExpression( std::ostream &os ) const override {              auto matcherAsString = m_matcher.toString();              os << Catch::Detail::stringify( m_arg ) << ' '; @@ -2319,10 +3786,10 @@ namespace Catch {      using StringMatcher = Matchers::Impl::MatcherBase<std::string>; -    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString  ); +    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  );      template<typename ArgT, typename MatcherT> -    auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString  ) -> MatchExpr<ArgT, MatcherT> { +    auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> {          return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );      } @@ -2331,35 +3798,949 @@ namespace Catch {  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ -        INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ -            catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ +        INTERNAL_CATCH_TRY { \ +            catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \          } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::alwaysFalse() ) +    } while( false )  ///////////////////////////////////////////////////////////////////////////////  #define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \      do { \ -        Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ +        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \          if( catchAssertionHandler.allowThrows() ) \              try { \                  static_cast<void>(__VA_ARGS__ ); \ -                catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \ +                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \              } \              catch( exceptionType const& ex ) { \ -                catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ +                catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \              } \              catch( ... ) { \ -                catchAssertionHandler.useActiveException(); \ +                catchAssertionHandler.handleUnexpectedInflightException(); \              } \          else \ -            catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ +            catchAssertionHandler.handleThrowingCallSkipped(); \          INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -    } while( Catch::alwaysFalse() ) +    } while( false )  // end catch_capture_matchers.h  #endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include <memory> + +namespace Catch { + +    namespace Generators { +        class GeneratorUntypedBase { +        public: +            GeneratorUntypedBase() = default; +            virtual ~GeneratorUntypedBase(); +            // Attempts to move the generator to the next element +             // +             // Returns true iff the move succeeded (and a valid element +             // can be retrieved). +            virtual bool next() = 0; +        }; +        using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>; + +    } // namespace Generators + +    struct IGeneratorTracker { +        virtual ~IGeneratorTracker(); +        virtual auto hasGenerator() const -> bool = 0; +        virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; +        virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; +    }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include <exception> + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +    template <typename Ex> +    [[noreturn]] +    void throw_exception(Ex const& e) { +        throw e; +    } +#else // ^^ Exceptions are enabled //  Exceptions are disabled vv +    [[noreturn]] +    void throw_exception(std::exception const& e); +#endif + +    [[noreturn]] +    void throw_logic_error(std::string const& msg); +    [[noreturn]] +    void throw_domain_error(std::string const& msg); +    [[noreturn]] +    void throw_runtime_error(std::string const& msg); + +} // namespace Catch; + +#define CATCH_MAKE_MSG(...) \ +    (Catch::ReusableStringStream() << __VA_ARGS__).str() + +#define CATCH_INTERNAL_ERROR(...) \ +    Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__)) + +#define CATCH_ERROR(...) \ +    Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ )) + +#define CATCH_RUNTIME_ERROR(...) \ +    Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ )) + +#define CATCH_ENFORCE( condition, ... ) \ +    do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false) + +// end catch_enforce.h +#include <memory> +#include <vector> +#include <cassert> + +#include <utility> +#include <exception> + +namespace Catch { + +class GeneratorException : public std::exception { +    const char* const m_msg = ""; + +public: +    GeneratorException(const char* msg): +        m_msg(msg) +    {} + +    const char* what() const noexcept override final; +}; + +namespace Generators { + +    // !TBD move this into its own location? +    namespace pf{ +        template<typename T, typename... Args> +        std::unique_ptr<T> make_unique( Args&&... args ) { +            return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +        } +    } + +    template<typename T> +    struct IGenerator : GeneratorUntypedBase { +        virtual ~IGenerator() = default; + +        // Returns the current element of the generator +        // +        // \Precondition The generator is either freshly constructed, +        // or the last call to `next()` returned true +        virtual T const& get() const = 0; +        using type = T; +    }; + +    template<typename T> +    class SingleValueGenerator final : public IGenerator<T> { +        T m_value; +    public: +        SingleValueGenerator(T&& value) : m_value(std::move(value)) {} + +        T const& get() const override { +            return m_value; +        } +        bool next() override { +            return false; +        } +    }; + +    template<typename T> +    class FixedValuesGenerator final : public IGenerator<T> { +        static_assert(!std::is_same<T, bool>::value, +            "FixedValuesGenerator does not support bools because of std::vector<bool>" +            "specialization, use SingleValue Generator instead."); +        std::vector<T> m_values; +        size_t m_idx = 0; +    public: +        FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} + +        T const& get() const override { +            return m_values[m_idx]; +        } +        bool next() override { +            ++m_idx; +            return m_idx < m_values.size(); +        } +    }; + +    template <typename T> +    class GeneratorWrapper final { +        std::unique_ptr<IGenerator<T>> m_generator; +    public: +        GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): +            m_generator(std::move(generator)) +        {} +        T const& get() const { +            return m_generator->get(); +        } +        bool next() { +            return m_generator->next(); +        } +    }; + +    template <typename T> +    GeneratorWrapper<T> value(T&& value) { +        return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); +    } +    template <typename T> +    GeneratorWrapper<T> values(std::initializer_list<T> values) { +        return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values)); +    } + +    template<typename T> +    class Generators : public IGenerator<T> { +        std::vector<GeneratorWrapper<T>> m_generators; +        size_t m_current = 0; + +        void populate(GeneratorWrapper<T>&& generator) { +            m_generators.emplace_back(std::move(generator)); +        } +        void populate(T&& val) { +            m_generators.emplace_back(value(std::forward<T>(val))); +        } +        template<typename U> +        void populate(U&& val) { +            populate(T(std::forward<U>(val))); +        } +        template<typename U, typename... Gs> +        void populate(U&& valueOrGenerator, Gs &&... moreGenerators) { +            populate(std::forward<U>(valueOrGenerator)); +            populate(std::forward<Gs>(moreGenerators)...); +        } + +    public: +        template <typename... Gs> +        Generators(Gs &&... moreGenerators) { +            m_generators.reserve(sizeof...(Gs)); +            populate(std::forward<Gs>(moreGenerators)...); +        } + +        T const& get() const override { +            return m_generators[m_current].get(); +        } + +        bool next() override { +            if (m_current >= m_generators.size()) { +                return false; +            } +            const bool current_status = m_generators[m_current].next(); +            if (!current_status) { +                ++m_current; +            } +            return m_current < m_generators.size(); +        } +    }; + +    template<typename... Ts> +    GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) { +        return values<std::tuple<Ts...>>( tuples ); +    } + +    // Tag type to signal that a generator sequence should convert arguments to a specific type +    template <typename T> +    struct as {}; + +    template<typename T, typename... Gs> +    auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> { +        return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...); +    } +    template<typename T> +    auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { +        return Generators<T>(std::move(generator)); +    } +    template<typename T, typename... Gs> +    auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> { +        return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); +    } +    template<typename T, typename U, typename... Gs> +    auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> { +        return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); +    } + +    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + +    template<typename L> +    // Note: The type after -> is weird, because VS2015 cannot parse +    //       the expression used in the typedef inside, when it is in +    //       return type. Yeah. +    auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { +        using UnderlyingType = typename decltype(generatorExpression())::type; + +        IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo ); +        if (!tracker.hasGenerator()) { +            tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); +        } + +        auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); +        return generator.get(); +    } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ +    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ +                                 CATCH_INTERNAL_LINEINFO, \ +                                 [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) +#define GENERATE_COPY( ... ) \ +    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ +                                 CATCH_INTERNAL_LINEINFO, \ +                                 [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) +#define GENERATE_REF( ... ) \ +    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ +                                 CATCH_INTERNAL_LINEINFO, \ +                                 [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { +namespace Generators { + +    template <typename T> +    class TakeGenerator : public IGenerator<T> { +        GeneratorWrapper<T> m_generator; +        size_t m_returned = 0; +        size_t m_target; +    public: +        TakeGenerator(size_t target, GeneratorWrapper<T>&& generator): +            m_generator(std::move(generator)), +            m_target(target) +        { +            assert(target != 0 && "Empty generators are not allowed"); +        } +        T const& get() const override { +            return m_generator.get(); +        } +        bool next() override { +            ++m_returned; +            if (m_returned >= m_target) { +                return false; +            } + +            const auto success = m_generator.next(); +            // If the underlying generator does not contain enough values +            // then we cut short as well +            if (!success) { +                m_returned = m_target; +            } +            return success; +        } +    }; + +    template <typename T> +    GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) { +        return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator))); +    } + +    template <typename T, typename Predicate> +    class FilterGenerator : public IGenerator<T> { +        GeneratorWrapper<T> m_generator; +        Predicate m_predicate; +    public: +        template <typename P = Predicate> +        FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator): +            m_generator(std::move(generator)), +            m_predicate(std::forward<P>(pred)) +        { +            if (!m_predicate(m_generator.get())) { +                // It might happen that there are no values that pass the +                // filter. In that case we throw an exception. +                auto has_initial_value = next(); +                if (!has_initial_value) { +                    Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); +                } +            } +        } + +        T const& get() const override { +            return m_generator.get(); +        } + +        bool next() override { +            bool success = m_generator.next(); +            if (!success) { +                return false; +            } +            while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); +            return success; +        } +    }; + +    template <typename T, typename Predicate> +    GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) { +        return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator)))); +    } + +    template <typename T> +    class RepeatGenerator : public IGenerator<T> { +        static_assert(!std::is_same<T, bool>::value, +            "RepeatGenerator currently does not support bools" +            "because of std::vector<bool> specialization"); +        GeneratorWrapper<T> m_generator; +        mutable std::vector<T> m_returned; +        size_t m_target_repeats; +        size_t m_current_repeat = 0; +        size_t m_repeat_index = 0; +    public: +        RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator): +            m_generator(std::move(generator)), +            m_target_repeats(repeats) +        { +            assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); +        } + +        T const& get() const override { +            if (m_current_repeat == 0) { +                m_returned.push_back(m_generator.get()); +                return m_returned.back(); +            } +            return m_returned[m_repeat_index]; +        } + +        bool next() override { +            // There are 2 basic cases: +            // 1) We are still reading the generator +            // 2) We are reading our own cache + +            // In the first case, we need to poke the underlying generator. +            // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache +            if (m_current_repeat == 0) { +                const auto success = m_generator.next(); +                if (!success) { +                    ++m_current_repeat; +                } +                return m_current_repeat < m_target_repeats; +            } + +            // In the second case, we need to move indices forward and check that we haven't run up against the end +            ++m_repeat_index; +            if (m_repeat_index == m_returned.size()) { +                m_repeat_index = 0; +                ++m_current_repeat; +            } +            return m_current_repeat < m_target_repeats; +        } +    }; + +    template <typename T> +    GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) { +        return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator))); +    } + +    template <typename T, typename U, typename Func> +    class MapGenerator : public IGenerator<T> { +        // TBD: provide static assert for mapping function, for friendly error message +        GeneratorWrapper<U> m_generator; +        Func m_function; +        // To avoid returning dangling reference, we have to save the values +        T m_cache; +    public: +        template <typename F2 = Func> +        MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) : +            m_generator(std::move(generator)), +            m_function(std::forward<F2>(function)), +            m_cache(m_function(m_generator.get())) +        {} + +        T const& get() const override { +            return m_cache; +        } +        bool next() override { +            const auto success = m_generator.next(); +            if (success) { +                m_cache = m_function(m_generator.get()); +            } +            return success; +        } +    }; + +    template <typename Func, typename U, typename T = FunctionReturnType<Func, U>> +    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) { +        return GeneratorWrapper<T>( +            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator)) +        ); +    } + +    template <typename T, typename U, typename Func> +    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) { +        return GeneratorWrapper<T>( +            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator)) +        ); +    } + +    template <typename T> +    class ChunkGenerator final : public IGenerator<std::vector<T>> { +        std::vector<T> m_chunk; +        size_t m_chunk_size; +        GeneratorWrapper<T> m_generator; +        bool m_used_up = false; +    public: +        ChunkGenerator(size_t size, GeneratorWrapper<T> generator) : +            m_chunk_size(size), m_generator(std::move(generator)) +        { +            m_chunk.reserve(m_chunk_size); +            if (m_chunk_size != 0) { +                m_chunk.push_back(m_generator.get()); +                for (size_t i = 1; i < m_chunk_size; ++i) { +                    if (!m_generator.next()) { +                        Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); +                    } +                    m_chunk.push_back(m_generator.get()); +                } +            } +        } +        std::vector<T> const& get() const override { +            return m_chunk; +        } +        bool next() override { +            m_chunk.clear(); +            for (size_t idx = 0; idx < m_chunk_size; ++idx) { +                if (!m_generator.next()) { +                    return false; +                } +                m_chunk.push_back(m_generator.get()); +            } +            return true; +        } +    }; + +    template <typename T> +    GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) { +        return GeneratorWrapper<std::vector<T>>( +            pf::make_unique<ChunkGenerator<T>>(size, std::move(generator)) +        ); +    } + +} // namespace Generators +} // namespace Catch + +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp + +// start catch_context.h + +#include <memory> + +namespace Catch { + +    struct IResultCapture; +    struct IRunner; +    struct IConfig; +    struct IMutableContext; + +    using IConfigPtr = std::shared_ptr<IConfig const>; + +    struct IContext +    { +        virtual ~IContext(); + +        virtual IResultCapture* getResultCapture() = 0; +        virtual IRunner* getRunner() = 0; +        virtual IConfigPtr const& getConfig() const = 0; +    }; + +    struct IMutableContext : IContext +    { +        virtual ~IMutableContext(); +        virtual void setResultCapture( IResultCapture* resultCapture ) = 0; +        virtual void setRunner( IRunner* runner ) = 0; +        virtual void setConfig( IConfigPtr const& config ) = 0; + +    private: +        static IMutableContext *currentContext; +        friend IMutableContext& getCurrentMutableContext(); +        friend void cleanUpContext(); +        static void createContext(); +    }; + +    inline IMutableContext& getCurrentMutableContext() +    { +        if( !IMutableContext::currentContext ) +            IMutableContext::createContext(); +        // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) +        return *IMutableContext::currentContext; +    } + +    inline IContext& getCurrentContext() +    { +        return getCurrentMutableContext(); +    } + +    void cleanUpContext(); + +    class SimplePcg32; +    SimplePcg32& rng(); +} + +// end catch_context.h +// start catch_interfaces_config.h + +// start catch_option.hpp + +namespace Catch { + +    // An optional type +    template<typename T> +    class Option { +    public: +        Option() : nullableValue( nullptr ) {} +        Option( T const& _value ) +        : nullableValue( new( storage ) T( _value ) ) +        {} +        Option( Option const& _other ) +        : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) +        {} + +        ~Option() { +            reset(); +        } + +        Option& operator= ( Option const& _other ) { +            if( &_other != this ) { +                reset(); +                if( _other ) +                    nullableValue = new( storage ) T( *_other ); +            } +            return *this; +        } +        Option& operator = ( T const& _value ) { +            reset(); +            nullableValue = new( storage ) T( _value ); +            return *this; +        } + +        void reset() { +            if( nullableValue ) +                nullableValue->~T(); +            nullableValue = nullptr; +        } + +        T& operator*() { return *nullableValue; } +        T const& operator*() const { return *nullableValue; } +        T* operator->() { return nullableValue; } +        const T* operator->() const { return nullableValue; } + +        T valueOr( T const& defaultValue ) const { +            return nullableValue ? *nullableValue : defaultValue; +        } + +        bool some() const { return nullableValue != nullptr; } +        bool none() const { return nullableValue == nullptr; } + +        bool operator !() const { return nullableValue == nullptr; } +        explicit operator bool() const { +            return some(); +        } + +    private: +        T *nullableValue; +        alignas(alignof(T)) char storage[sizeof(T)]; +    }; + +} // end namespace Catch + +// end catch_option.hpp +#include <chrono> +#include <iosfwd> +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + +    enum class Verbosity { +        Quiet = 0, +        Normal, +        High +    }; + +    struct WarnAbout { enum What { +        Nothing = 0x00, +        NoAssertions = 0x01, +        NoTests = 0x02 +    }; }; + +    struct ShowDurations { enum OrNot { +        DefaultForReporter, +        Always, +        Never +    }; }; +    struct RunTests { enum InWhatOrder { +        InDeclarationOrder, +        InLexicographicalOrder, +        InRandomOrder +    }; }; +    struct UseColour { enum YesOrNo { +        Auto, +        Yes, +        No +    }; }; +    struct WaitForKeypress { enum When { +        Never, +        BeforeStart = 1, +        BeforeExit = 2, +        BeforeStartAndExit = BeforeStart | BeforeExit +    }; }; + +    class TestSpec; + +    struct IConfig : NonCopyable { + +        virtual ~IConfig(); + +        virtual bool allowThrows() const = 0; +        virtual std::ostream& stream() const = 0; +        virtual std::string name() const = 0; +        virtual bool includeSuccessfulResults() const = 0; +        virtual bool shouldDebugBreak() const = 0; +        virtual bool warnAboutMissingAssertions() const = 0; +        virtual bool warnAboutNoTests() const = 0; +        virtual int abortAfter() const = 0; +        virtual bool showInvisibles() const = 0; +        virtual ShowDurations::OrNot showDurations() const = 0; +        virtual double minDuration() const = 0; +        virtual TestSpec const& testSpec() const = 0; +        virtual bool hasTestFilters() const = 0; +        virtual std::vector<std::string> const& getTestsOrTags() const = 0; +        virtual RunTests::InWhatOrder runOrder() const = 0; +        virtual unsigned int rngSeed() const = 0; +        virtual UseColour::YesOrNo useColour() const = 0; +        virtual std::vector<std::string> const& getSectionsToRun() const = 0; +        virtual Verbosity verbosity() const = 0; + +        virtual bool benchmarkNoAnalysis() const = 0; +        virtual int benchmarkSamples() const = 0; +        virtual double benchmarkConfidenceInterval() const = 0; +        virtual unsigned int benchmarkResamples() const = 0; +        virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; +    }; + +    using IConfigPtr = std::shared_ptr<IConfig const>; +} + +// end catch_interfaces_config.h +// start catch_random_number_generator.h + +#include <cstdint> + +namespace Catch { + +    // This is a simple implementation of C++11 Uniform Random Number +    // Generator. It does not provide all operators, because Catch2 +    // does not use it, but it should behave as expected inside stdlib's +    // distributions. +    // The implementation is based on the PCG family (http://pcg-random.org) +    class SimplePcg32 { +        using state_type = std::uint64_t; +    public: +        using result_type = std::uint32_t; +        static constexpr result_type (min)() { +            return 0; +        } +        static constexpr result_type (max)() { +            return static_cast<result_type>(-1); +        } + +        // Provide some default initial state for the default constructor +        SimplePcg32():SimplePcg32(0xed743cc4U) {} + +        explicit SimplePcg32(result_type seed_); + +        void seed(result_type seed_); +        void discard(uint64_t skip); + +        result_type operator()(); + +    private: +        friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs); +        friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs); + +        // In theory we also need operator<< and operator>> +        // In practice we do not use them, so we will skip them for now + +        std::uint64_t m_state; +        // This part of the state determines which "stream" of the numbers +        // is chosen -- we take it as a constant for Catch2, so we only +        // need to deal with seeding the main state. +        // Picked by reading 8 bytes from `/dev/random` :-) +        static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL; +    }; + +} // end namespace Catch + +// end catch_random_number_generator.h +#include <random> + +namespace Catch { +namespace Generators { + +template <typename Float> +class RandomFloatingGenerator final : public IGenerator<Float> { +    Catch::SimplePcg32& m_rng; +    std::uniform_real_distribution<Float> m_dist; +    Float m_current_number; +public: + +    RandomFloatingGenerator(Float a, Float b): +        m_rng(rng()), +        m_dist(a, b) { +        static_cast<void>(next()); +    } + +    Float const& get() const override { +        return m_current_number; +    } +    bool next() override { +        m_current_number = m_dist(m_rng); +        return true; +    } +}; + +template <typename Integer> +class RandomIntegerGenerator final : public IGenerator<Integer> { +    Catch::SimplePcg32& m_rng; +    std::uniform_int_distribution<Integer> m_dist; +    Integer m_current_number; +public: + +    RandomIntegerGenerator(Integer a, Integer b): +        m_rng(rng()), +        m_dist(a, b) { +        static_cast<void>(next()); +    } + +    Integer const& get() const override { +        return m_current_number; +    } +    bool next() override { +        m_current_number = m_dist(m_rng); +        return true; +    } +}; + +// TODO: Ideally this would be also constrained against the various char types, +//       but I don't expect users to run into that in practice. +template <typename T> +typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, +GeneratorWrapper<T>>::type +random(T a, T b) { +    return GeneratorWrapper<T>( +        pf::make_unique<RandomIntegerGenerator<T>>(a, b) +    ); +} + +template <typename T> +typename std::enable_if<std::is_floating_point<T>::value, +GeneratorWrapper<T>>::type +random(T a, T b) { +    return GeneratorWrapper<T>( +        pf::make_unique<RandomFloatingGenerator<T>>(a, b) +    ); +} + +template <typename T> +class RangeGenerator final : public IGenerator<T> { +    T m_current; +    T m_end; +    T m_step; +    bool m_positive; + +public: +    RangeGenerator(T const& start, T const& end, T const& step): +        m_current(start), +        m_end(end), +        m_step(step), +        m_positive(m_step > T(0)) +    { +        assert(m_current != m_end && "Range start and end cannot be equal"); +        assert(m_step != T(0) && "Step size cannot be zero"); +        assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); +    } + +    RangeGenerator(T const& start, T const& end): +        RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) +    {} + +    T const& get() const override { +        return m_current; +    } + +    bool next() override { +        m_current += m_step; +        return (m_positive) ? (m_current < m_end) : (m_current > m_end); +    } +}; + +template <typename T> +GeneratorWrapper<T> range(T const& start, T const& end, T const& step) { +    static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric"); +    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step)); +} + +template <typename T> +GeneratorWrapper<T> range(T const& start, T const& end) { +    static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer"); +    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end)); +} + +template <typename T> +class IteratorGenerator final : public IGenerator<T> { +    static_assert(!std::is_same<T, bool>::value, +        "IteratorGenerator currently does not support bools" +        "because of std::vector<bool> specialization"); + +    std::vector<T> m_elems; +    size_t m_current = 0; +public: +    template <typename InputIterator, typename InputSentinel> +    IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) { +        if (m_elems.empty()) { +            Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values")); +        } +    } + +    T const& get() const override { +        return m_elems[m_current]; +    } + +    bool next() override { +        ++m_current; +        return m_current != m_elems.size(); +    } +}; + +template <typename InputIterator, +          typename InputSentinel, +          typename ResultType = typename std::iterator_traits<InputIterator>::value_type> +GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) { +    return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to)); +} + +template <typename Container, +          typename ResultType = typename Container::value_type> +GeneratorWrapper<ResultType> from_range(Container const& cnt) { +    return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end())); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp  // These files are included here so the single_include script doesn't put them  // in the conditionally compiled sections @@ -2416,7 +4797,7 @@ namespace Catch {      class TestCase : public TestCaseInfo {      public: -        TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); +        TestCase( ITestInvoker* testCase, TestCaseInfo&& info );          TestCase withName( std::string const& _newName ) const; @@ -2433,8 +4814,7 @@ namespace Catch {      TestCase makeTestCase(  ITestInvoker* testCase,                              std::string const& className, -                            std::string const& name, -                            std::string const& description, +                            NameAndTags const& nameAndTags,                              SourceLineInfo const& lineInfo );  } @@ -2537,7 +4917,7 @@ namespace Catch {                          std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );                          const char* className = class_getName( cls ); -                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); +                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) );                          noTestMethods++;                      }                  } @@ -2560,7 +4940,7 @@ namespace Catch {                      arcSafeRelease( m_substr );                  } -                bool match( NSString* arg ) const override { +                bool match( NSString* str ) const override {                      return false;                  } @@ -2583,7 +4963,7 @@ namespace Catch {              struct Contains : StringHolder {                  Contains( NSString* substr ) : StringHolder( substr ){} -                bool match( NSString* str ) const { +                bool match( NSString* str ) const override {                      return  (str != nil || m_substr == nil ) &&                              [str rangeOfString:m_substr].location != NSNotFound;                  } @@ -2659,7 +5039,8 @@ return @ desc; \  // end catch_objc.hpp  #endif -#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// Benchmarking needs the externally-facing parts of reporters to work +#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING)  // start catch_external_interfaces.h  // start catch_reporter_bases.hpp @@ -2701,7 +5082,7 @@ namespace Catch          virtual bool matches( std::string const& str ) const;      private: -        std::string adjustCase( std::string const& str ) const; +        std::string normaliseString( std::string const& str ) const;          CaseSensitive::Choice m_caseSensitivity;          WildcardPosition m_wildcard = NoWildcard;          std::string m_pattern; @@ -2715,36 +5096,40 @@ namespace Catch  namespace Catch { +    struct IConfig; +      class TestSpec { -        struct Pattern { +        class Pattern { +        public: +            explicit Pattern( std::string const& name );              virtual ~Pattern();              virtual bool matches( TestCaseInfo const& testCase ) const = 0; +            std::string const& name() const; +        private: +            std::string const m_name;          };          using PatternPtr = std::shared_ptr<Pattern>;          class NamePattern : public Pattern {          public: -            NamePattern( std::string const& name ); -            virtual ~NamePattern(); -            virtual bool matches( TestCaseInfo const& testCase ) const override; +            explicit NamePattern( std::string const& name, std::string const& filterString ); +            bool matches( TestCaseInfo const& testCase ) const override;          private:              WildcardPattern m_wildcardPattern;          };          class TagPattern : public Pattern {          public: -            TagPattern( std::string const& tag ); -            virtual ~TagPattern(); -            virtual bool matches( TestCaseInfo const& testCase ) const override; +            explicit TagPattern( std::string const& tag, std::string const& filterString ); +            bool matches( TestCaseInfo const& testCase ) const override;          private:              std::string m_tag;          };          class ExcludedPattern : public Pattern {          public: -            ExcludedPattern( PatternPtr const& underlyingPattern ); -            virtual ~ExcludedPattern(); -            virtual bool matches( TestCaseInfo const& testCase ) const override; +            explicit ExcludedPattern( PatternPtr const& underlyingPattern ); +            bool matches( TestCaseInfo const& testCase ) const override;          private:              PatternPtr m_underlyingPattern;          }; @@ -2753,15 +5138,25 @@ namespace Catch {              std::vector<PatternPtr> m_patterns;              bool matches( TestCaseInfo const& testCase ) const; +            std::string name() const;          };      public: +        struct FilterMatch { +            std::string name; +            std::vector<TestCase const*> tests; +        }; +        using Matches = std::vector<FilterMatch>; +        using vectorStrings = std::vector<std::string>; +          bool hasFilters() const;          bool matches( TestCaseInfo const& testCase ) const; +        Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const; +        const vectorStrings & getInvalidArgs() const;      private:          std::vector<Filter> m_filters; - +        std::vector<std::string> m_invalidArgs;          friend class TestSpecParser;      };  } @@ -2796,9 +5191,13 @@ namespace Catch {      class TestSpecParser {          enum Mode{ None, Name, QuotedName, Tag, EscapedName };          Mode m_mode = None; +        Mode lastMode = None;          bool m_exclusion = false; -        std::size_t m_start = std::string::npos, m_pos = 0; +        std::size_t m_pos = 0; +        std::size_t m_realPatternPos = 0;          std::string m_arg; +        std::string m_substring; +        std::string m_patternName;          std::vector<std::size_t> m_escapeChars;          TestSpec::Filter m_currentFilter;          TestSpec m_testSpec; @@ -2811,32 +5210,32 @@ namespace Catch {          TestSpec testSpec();      private: -        void visitChar( char c ); -        void startNewMode( Mode mode, std::size_t start ); +        bool visitChar( char c ); +        void startNewMode( Mode mode ); +        bool processNoneChar( char c ); +        void processNameChar( char c ); +        bool processOtherChar( char c ); +        void endMode();          void escape(); -        std::string subString() const; +        bool isControlChar( char c ) const; +        void saveLastMode(); +        void revertBackToLastMode(); +        void addFilter(); +        bool separate(); -        template<typename T> -        void addPattern() { -            std::string token = subString(); -            for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) -                token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); -            m_escapeChars.clear(); -            if( startsWith( token, "exclude:" ) ) { -                m_exclusion = true; -                token = token.substr( 8 ); -            } -            if( !token.empty() ) { -                TestSpec::PatternPtr pattern = std::make_shared<T>( token ); -                if( m_exclusion ) -                    pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern ); -                m_currentFilter.m_patterns.push_back( pattern ); -            } -            m_exclusion = false; -            m_mode = None; +        // Handles common preprocessing of the pattern for name/tag patterns +        std::string preprocessPattern(); +        // Adds the current pattern as a test name +        void addNamePattern(); +        // Adds the current pattern as a tag +        void addTagPattern(); + +        inline void addCharToPattern(char c) { +            m_substring += c; +            m_patternName += c; +            m_realPatternPos++;          } -        void addFilter();      };      TestSpec parseTestSpec( std::string const& arg ); @@ -2847,140 +5246,7 @@ namespace Catch {  #endif  // end catch_test_spec_parser.h -// start catch_interfaces_config.h - -#include <iosfwd> -#include <string> -#include <vector> -#include <memory> - -namespace Catch { - -    enum class Verbosity { -        Quiet = 0, -        Normal, -        High -    }; - -    struct WarnAbout { enum What { -        Nothing = 0x00, -        NoAssertions = 0x01 -    }; }; - -    struct ShowDurations { enum OrNot { -        DefaultForReporter, -        Always, -        Never -    }; }; -    struct RunTests { enum InWhatOrder { -        InDeclarationOrder, -        InLexicographicalOrder, -        InRandomOrder -    }; }; -    struct UseColour { enum YesOrNo { -        Auto, -        Yes, -        No -    }; }; -    struct WaitForKeypress { enum When { -        Never, -        BeforeStart = 1, -        BeforeExit = 2, -        BeforeStartAndExit = BeforeStart | BeforeExit -    }; }; - -    class TestSpec; - -    struct IConfig : NonCopyable { - -        virtual ~IConfig(); - -        virtual bool allowThrows() const = 0; -        virtual std::ostream& stream() const = 0; -        virtual std::string name() const = 0; -        virtual bool includeSuccessfulResults() const = 0; -        virtual bool shouldDebugBreak() const = 0; -        virtual bool warnAboutMissingAssertions() const = 0; -        virtual int abortAfter() const = 0; -        virtual bool showInvisibles() const = 0; -        virtual ShowDurations::OrNot showDurations() const = 0; -        virtual TestSpec const& testSpec() const = 0; -        virtual RunTests::InWhatOrder runOrder() const = 0; -        virtual unsigned int rngSeed() const = 0; -        virtual int benchmarkResolutionMultiple() const = 0; -        virtual UseColour::YesOrNo useColour() const = 0; -        virtual std::vector<std::string> const& getSectionsToRun() const = 0; -        virtual Verbosity verbosity() const = 0; -    }; - -    using IConfigPtr = std::shared_ptr<IConfig const>; -} - -// end catch_interfaces_config.h  // Libstdc++ doesn't like incomplete classes for unique_ptr -// start catch_stream.h - -// start catch_streambuf.h - -#include <streambuf> - -namespace Catch { - -    class StreamBufBase : public std::streambuf { -    public: -        virtual ~StreamBufBase(); -    }; -} - -// end catch_streambuf.h -#include <streambuf> -#include <ostream> -#include <fstream> -#include <memory> - -namespace Catch { - -    std::ostream& cout(); -    std::ostream& cerr(); -    std::ostream& clog(); - -    struct IStream { -        virtual ~IStream(); -        virtual std::ostream& stream() const = 0; -    }; - -    class FileStream : public IStream { -        mutable std::ofstream m_ofs; -    public: -        FileStream( std::string const& filename ); -        ~FileStream() override = default; -    public: // IStream -        std::ostream& stream() const override; -    }; - -    class CoutStream : public IStream { -        mutable std::ostream m_os; -    public: -        CoutStream(); -        ~CoutStream() override = default; - -    public: // IStream -        std::ostream& stream() const override; -    }; - -    class DebugOutStream : public IStream { -        std::unique_ptr<StreamBufBase> m_streamBuf; -        mutable std::ostream m_os; -    public: -        DebugOutStream(); -        ~DebugOutStream() override = default; - -    public: // IStream -        std::ostream& stream() const override; -    }; -} - -// end catch_stream.h  #include <memory>  #include <vector> @@ -3010,11 +5276,17 @@ namespace Catch {          int abortAfter = -1;          unsigned int rngSeed = 0; -        int benchmarkResolutionMultiple = 100; + +        bool benchmarkNoAnalysis = false; +        unsigned int benchmarkSamples = 100; +        double benchmarkConfidenceInterval = 0.95; +        unsigned int benchmarkResamples = 100000; +        std::chrono::milliseconds::rep benchmarkWarmupTime = 100;          Verbosity verbosity = Verbosity::Normal;          WarnAbout::What warnings = WarnAbout::Nothing;          ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; +        double minDuration = -1;          RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder;          UseColour::YesOrNo useColour = UseColour::Auto;          WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; @@ -3022,8 +5294,12 @@ namespace Catch {          std::string outputFilename;          std::string name;          std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif +        std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER -        std::vector<std::string> reporterNames;          std::vector<std::string> testsOrTags;          std::vector<std::string> sectionsToRun;      }; @@ -3043,11 +5319,13 @@ namespace Catch {          bool listReporters() const;          std::string getProcessName() const; +        std::string const& getReporterName() const; -        std::vector<std::string> const& getReporterNames() const; +        std::vector<std::string> const& getTestsOrTags() const override;          std::vector<std::string> const& getSectionsToRun() const override; -        virtual TestSpec const& testSpec() const override; +        TestSpec const& testSpec() const override; +        bool hasTestFilters() const override;          bool showHelp() const; @@ -3057,15 +5335,21 @@ namespace Catch {          std::string name() const override;          bool includeSuccessfulResults() const override;          bool warnAboutMissingAssertions() const override; +        bool warnAboutNoTests() const override;          ShowDurations::OrNot showDurations() const override; +        double minDuration() const override;          RunTests::InWhatOrder runOrder() const override;          unsigned int rngSeed() const override; -        int benchmarkResolutionMultiple() const override;          UseColour::YesOrNo useColour() const override;          bool shouldDebugBreak() const override;          int abortAfter() const override;          bool showInvisibles() const override;          Verbosity verbosity() const override; +        bool benchmarkNoAnalysis() const override; +        int benchmarkSamples() const override; +        double benchmarkConfidenceInterval() const override; +        unsigned int benchmarkResamples() const override; +        std::chrono::milliseconds benchmarkWarmupTime() const override;      private: @@ -3074,6 +5358,7 @@ namespace Catch {          std::unique_ptr<IStream const> m_stream;          TestSpec m_testSpec; +        bool m_hasTestFilters = false;      };  } // end namespace Catch @@ -3115,7 +5400,7 @@ namespace Catch {          std::string getExpandedExpression() const;          std::string getMessage() const;          SourceLineInfo getSourceInfo() const; -        std::string getTestMacroName() const; +        StringRef getTestMacroName() const;      //protected:          AssertionInfo m_info; @@ -3125,76 +5410,59 @@ namespace Catch {  } // end namespace Catch  // end catch_assertionresult.h -// start catch_option.hpp +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_estimate.hpp -namespace Catch { + // Statistics estimates -    // An optional type -    template<typename T> -    class Option { -    public: -        Option() : nullableValue( nullptr ) {} -        Option( T const& _value ) -        : nullableValue( new( storage ) T( _value ) ) -        {} -        Option( Option const& _other ) -        : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) -        {} -        ~Option() { -            reset(); -        } - -        Option& operator= ( Option const& _other ) { -            if( &_other != this ) { -                reset(); -                if( _other ) -                    nullableValue = new( storage ) T( *_other ); +namespace Catch { +    namespace Benchmark { +        template <typename Duration> +        struct Estimate { +            Duration point; +            Duration lower_bound; +            Duration upper_bound; +            double confidence_interval; + +            template <typename Duration2> +            operator Estimate<Duration2>() const { +                return { point, lower_bound, upper_bound, confidence_interval };              } -            return *this; -        } -        Option& operator = ( T const& _value ) { -            reset(); -            nullableValue = new( storage ) T( _value ); -            return *this; -        } - -        void reset() { -            if( nullableValue ) -                nullableValue->~T(); -            nullableValue = nullptr; -        } - -        T& operator*() { return *nullableValue; } -        T const& operator*() const { return *nullableValue; } -        T* operator->() { return nullableValue; } -        const T* operator->() const { return nullableValue; } - -        T valueOr( T const& defaultValue ) const { -            return nullableValue ? *nullableValue : defaultValue; -        } +        }; +    } // namespace Benchmark +} // namespace Catch -        bool some() const { return nullableValue != nullptr; } -        bool none() const { return nullableValue == nullptr; } +// end catch_estimate.hpp +// start catch_outlier_classification.hpp -        bool operator !() const { return nullableValue == nullptr; } -        explicit operator bool() const { -            return some(); -        } +// Outlier information -    private: -        T *nullableValue; -        alignas(alignof(T)) char storage[sizeof(T)]; -    }; +namespace Catch { +    namespace Benchmark { +        struct OutlierClassification { +            int samples_seen = 0; +            int low_severe = 0;     // more than 3 times IQR below Q1 +            int low_mild = 0;       // 1.5 to 3 times IQR below Q1 +            int high_mild = 0;      // 1.5 to 3 times IQR above Q3 +            int high_severe = 0;    // more than 3 times IQR above Q3 + +            int total() const { +                return low_severe + low_mild + high_mild + high_severe; +            } +        }; +    } // namespace Benchmark +} // namespace Catch -} // end namespace Catch +// end catch_outlier_classification.hpp +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -// end catch_option.hpp  #include <string>  #include <iosfwd>  #include <map>  #include <set>  #include <memory> +#include <algorithm>  namespace Catch { @@ -3213,6 +5481,7 @@ namespace Catch {      struct ReporterPreferences {          bool shouldRedirectStdOut = false; +        bool shouldReportAllAssertions = false;      };      template<typename T> @@ -3250,8 +5519,8 @@ namespace Catch {          AssertionStats( AssertionStats const& )              = default;          AssertionStats( AssertionStats && )                  = default; -        AssertionStats& operator = ( AssertionStats const& ) = default; -        AssertionStats& operator = ( AssertionStats && )     = default; +        AssertionStats& operator = ( AssertionStats const& ) = delete; +        AssertionStats& operator = ( AssertionStats && )     = delete;          virtual ~AssertionStats();          AssertionResult assertionResult; @@ -3329,14 +5598,43 @@ namespace Catch {          bool aborting;      }; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)      struct BenchmarkInfo {          std::string name; +        double estimatedDuration; +        int iterations; +        int samples; +        unsigned int resamples; +        double clockResolution; +        double clockCost;      }; + +    template <class Duration>      struct BenchmarkStats {          BenchmarkInfo info; -        std::size_t iterations; -        uint64_t elapsedTimeInNanoseconds; + +        std::vector<Duration> samples; +        Benchmark::Estimate<Duration> mean; +        Benchmark::Estimate<Duration> standardDeviation; +        Benchmark::OutlierClassification outliers; +        double outlierVariance; + +        template <typename Duration2> +        operator BenchmarkStats<Duration2>() const { +            std::vector<Duration2> samples2; +            samples2.reserve(samples.size()); +            std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); +            return { +                info, +                std::move(samples2), +                mean, +                standardDeviation, +                outliers, +                outlierVariance, +            }; +        }      }; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING      struct IStreamingReporter {          virtual ~IStreamingReporter() = default; @@ -3349,23 +5647,26 @@ namespace Catch {          virtual void noMatchingTestCases( std::string const& spec ) = 0; +        virtual void reportInvalidArguments(std::string const&) {} +          virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;          virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;          virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;          virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; -        // *** experimental *** +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +        virtual void benchmarkPreparing( std::string const& ) {}          virtual void benchmarkStarting( BenchmarkInfo const& ) {} +        virtual void benchmarkEnded( BenchmarkStats<> const& ) {} +        virtual void benchmarkFailed( std::string const& ) {} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING          virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;          // The return value indicates if the messages buffer should be cleared:          virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; -        // *** experimental *** -        virtual void benchmarkEnded( BenchmarkStats const& ) {} -          virtual void sectionEnded( SectionStats const& sectionStats ) = 0;          virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;          virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; @@ -3397,8 +5698,6 @@ namespace Catch {          virtual Listeners const& getListeners() const = 0;      }; -    void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); -  } // end namespace Catch  // end catch_interfaces_reporter.h @@ -3406,8 +5705,9 @@ namespace Catch {  #include <cstring>  #include <cfloat>  #include <cstdio> -#include <assert.h> +#include <cassert>  #include <memory> +#include <ostream>  namespace Catch {      void prepareExpandedExpression(AssertionResult& result); @@ -3415,6 +5715,11 @@ namespace Catch {      // Returns double formatted as %.3f (format expected on output)      std::string getFormattedDuration( double duration ); +    //! Should the reporter show +    bool shouldShowDuration( IConfig const& config, double duration ); + +    std::string serializeFilters( std::vector<std::string> const& container ); +      template<typename DerivedT>      struct StreamingReporterBase : IStreamingReporter { @@ -3423,7 +5728,8 @@ namespace Catch {              stream( _config.stream() )          {              m_reporterPrefs.shouldRedirectStdOut = false; -            CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); +            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) +                CATCH_ERROR( "Verbosity level not supported by this reporter" );          }          ReporterPreferences getPreferences() const override { @@ -3438,9 +5744,12 @@ namespace Catch {          void noMatchingTestCases(std::string const&) override {} +        void reportInvalidArguments(std::string const&) override {} +          void testRunStarting(TestRunInfo const& _testRunInfo) override {              currentTestRunInfo = _testRunInfo;          } +          void testGroupStarting(GroupInfo const& _groupInfo) override {              currentGroupInfo = _groupInfo;          } @@ -3536,7 +5845,8 @@ namespace Catch {              stream( _config.stream() )          {              m_reporterPrefs.shouldRedirectStdOut = false; -            CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); +            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) +                CATCH_ERROR( "Verbosity level not supported by this reporter" );          }          ~CumulativeReporterBase() override = default; @@ -3652,6 +5962,8 @@ namespace Catch {      struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> {          TestEventListenerBase( ReporterConfig const& _config ); +        static std::set<Verbosity> getSupportedVerbosities(); +          void assertionStarting(AssertionInfo const&) override;          bool assertionEnded(AssertionStats const&) override;      }; @@ -3681,10 +5993,11 @@ namespace Catch {              BrightGreen = Bright | Green,              LightGrey = Bright | Grey,              BrightWhite = Bright | White, +            BrightYellow = Bright | Yellow,              // By intention              FileName = LightGrey, -            Warning = Yellow, +            Warning = BrightYellow,              ResultError = BrightRed,              ResultSuccess = BrightGreen,              ResultExpectedFailure = Warning, @@ -3693,7 +6006,7 @@ namespace Catch {              Success = Green,              OriginalExpression = Cyan, -            ReconstructedExpression = Yellow, +            ReconstructedExpression = BrightYellow,              SecondaryText = LightGrey,              Headers = White @@ -3727,18 +6040,18 @@ namespace Catch {          class ReporterFactory : public IReporterFactory { -            virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { +            IStreamingReporterPtr create( ReporterConfig const& config ) const override {                  return std::unique_ptr<T>( new T( config ) );              } -            virtual std::string getDescription() const override { +            std::string getDescription() const override {                  return T::getDescription();              }          };      public: -        ReporterRegistrar( std::string const& name ) { +        explicit ReporterRegistrar( std::string const& name ) {              getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );          }      }; @@ -3748,10 +6061,10 @@ namespace Catch {          class ListenerFactory : public IReporterFactory { -            virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { +            IStreamingReporterPtr create( ReporterConfig const& config ) const override {                  return std::unique_ptr<T>( new T( config ) );              } -            virtual std::string getDescription() const override { +            std::string getDescription() const override {                  return std::string();              }          }; @@ -3767,14 +6080,16 @@ namespace Catch {  #if !defined(CATCH_CONFIG_DISABLE)  #define CATCH_REGISTER_REPORTER( name, reporterType ) \ +    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION         \      CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS          \      namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \ -    CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  #define CATCH_REGISTER_LISTENER( listenerType ) \ -     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS   \ -     namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \ -     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION   \ +    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS    \ +    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \ +    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  #else // CATCH_CONFIG_DISABLE  #define CATCH_REGISTER_REPORTER(name, reporterType) @@ -3783,9 +6098,1349 @@ namespace Catch {  #endif // CATCH_CONFIG_DISABLE  // end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + +    struct CompactReporter : StreamingReporterBase<CompactReporter> { + +        using StreamingReporterBase::StreamingReporterBase; + +        ~CompactReporter() override; + +        static std::string getDescription(); + +        void noMatchingTestCases(std::string const& spec) override; + +        void assertionStarting(AssertionInfo const&) override; + +        bool assertionEnded(AssertionStats const& _assertionStats) override; + +        void sectionEnded(SectionStats const& _sectionStats) override; + +        void testRunEnded(TestRunStats const& _testRunStats) override; + +    }; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch +                              // Note that 4062 (not all labels are handled +                              // and default is missing) is enabled +#endif + +namespace Catch { +    // Fwd decls +    struct SummaryColumn; +    class TablePrinter; + +    struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> { +        std::unique_ptr<TablePrinter> m_tablePrinter; + +        ConsoleReporter(ReporterConfig const& config); +        ~ConsoleReporter() override; +        static std::string getDescription(); + +        void noMatchingTestCases(std::string const& spec) override; + +        void reportInvalidArguments(std::string const&arg) override; + +        void assertionStarting(AssertionInfo const&) override; + +        bool assertionEnded(AssertionStats const& _assertionStats) override; + +        void sectionStarting(SectionInfo const& _sectionInfo) override; +        void sectionEnded(SectionStats const& _sectionStats) override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +        void benchmarkPreparing(std::string const& name) override; +        void benchmarkStarting(BenchmarkInfo const& info) override; +        void benchmarkEnded(BenchmarkStats<> const& stats) override; +        void benchmarkFailed(std::string const& error) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +        void testCaseEnded(TestCaseStats const& _testCaseStats) override; +        void testGroupEnded(TestGroupStats const& _testGroupStats) override; +        void testRunEnded(TestRunStats const& _testRunStats) override; +        void testRunStarting(TestRunInfo const& _testRunInfo) override; +    private: + +        void lazyPrint(); + +        void lazyPrintWithoutClosingBenchmarkTable(); +        void lazyPrintRunInfo(); +        void lazyPrintGroupInfo(); +        void printTestCaseAndSectionHeader(); + +        void printClosedHeader(std::string const& _name); +        void printOpenHeader(std::string const& _name); + +        // if string has a : in first line will set indent to follow it on +        // subsequent lines +        void printHeaderString(std::string const& _string, std::size_t indent = 0); + +        void printTotals(Totals const& totals); +        void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row); + +        void printTotalsDivider(Totals const& totals); +        void printSummaryDivider(); +        void printTestFilters(); + +    private: +        bool m_headerPrinted = false; +    }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include <vector> + +namespace Catch { +    enum class XmlFormatting { +        None = 0x00, +        Indent = 0x01, +        Newline = 0x02, +    }; + +    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs); +    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs); + +    class XmlEncode { +    public: +        enum ForWhat { ForTextNodes, ForAttributes }; + +        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + +        void encodeTo( std::ostream& os ) const; + +        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + +    private: +        std::string m_str; +        ForWhat m_forWhat; +    }; + +    class XmlWriter { +    public: + +        class ScopedElement { +        public: +            ScopedElement( XmlWriter* writer, XmlFormatting fmt ); + +            ScopedElement( ScopedElement&& other ) noexcept; +            ScopedElement& operator=( ScopedElement&& other ) noexcept; + +            ~ScopedElement(); + +            ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent ); + +            template<typename T> +            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { +                m_writer->writeAttribute( name, attribute ); +                return *this; +            } + +        private: +            mutable XmlWriter* m_writer = nullptr; +            XmlFormatting m_fmt; +        }; + +        XmlWriter( std::ostream& os = Catch::cout() ); +        ~XmlWriter(); + +        XmlWriter( XmlWriter const& ) = delete; +        XmlWriter& operator=( XmlWriter const& ) = delete; + +        XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + +        ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + +        XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + +        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + +        XmlWriter& writeAttribute( std::string const& name, bool attribute ); + +        template<typename T> +        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { +            ReusableStringStream rss; +            rss << attribute; +            return writeAttribute( name, rss.str() ); +        } + +        XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + +        XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + +        void writeStylesheetRef( std::string const& url ); + +        XmlWriter& writeBlankLine(); + +        void ensureTagClosed(); + +    private: + +        void applyFormatting(XmlFormatting fmt); + +        void writeDeclaration(); + +        void newlineIfNecessary(); + +        bool m_tagIsOpen = false; +        bool m_needsNewline = false; +        std::vector<std::string> m_tags; +        std::string m_indent; +        std::ostream& m_os; +    }; + +} + +// end catch_xmlwriter.h +namespace Catch { + +    class JunitReporter : public CumulativeReporterBase<JunitReporter> { +    public: +        JunitReporter(ReporterConfig const& _config); + +        ~JunitReporter() override; + +        static std::string getDescription(); + +        void noMatchingTestCases(std::string const& /*spec*/) override; + +        void testRunStarting(TestRunInfo const& runInfo) override; + +        void testGroupStarting(GroupInfo const& groupInfo) override; + +        void testCaseStarting(TestCaseInfo const& testCaseInfo) override; +        bool assertionEnded(AssertionStats const& assertionStats) override; + +        void testCaseEnded(TestCaseStats const& testCaseStats) override; + +        void testGroupEnded(TestGroupStats const& testGroupStats) override; + +        void testRunEndedCumulative() override; + +        void writeGroup(TestGroupNode const& groupNode, double suiteTime); + +        void writeTestCase(TestCaseNode const& testCaseNode); + +        void writeSection(std::string const& className, +                          std::string const& rootName, +                          SectionNode const& sectionNode); + +        void writeAssertions(SectionNode const& sectionNode); +        void writeAssertion(AssertionStats const& stats); + +        XmlWriter xml; +        Timer suiteTimer; +        std::string stdOutForSuite; +        std::string stdErrForSuite; +        unsigned int unexpectedExceptions = 0; +        bool m_okToFail = false; +    }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { +    class XmlReporter : public StreamingReporterBase<XmlReporter> { +    public: +        XmlReporter(ReporterConfig const& _config); + +        ~XmlReporter() override; + +        static std::string getDescription(); + +        virtual std::string getStylesheetRef() const; + +        void writeSourceInfo(SourceLineInfo const& sourceInfo); + +    public: // StreamingReporterBase + +        void noMatchingTestCases(std::string const& s) override; + +        void testRunStarting(TestRunInfo const& testInfo) override; + +        void testGroupStarting(GroupInfo const& groupInfo) override; + +        void testCaseStarting(TestCaseInfo const& testInfo) override; + +        void sectionStarting(SectionInfo const& sectionInfo) override; + +        void assertionStarting(AssertionInfo const&) override; + +        bool assertionEnded(AssertionStats const& assertionStats) override; + +        void sectionEnded(SectionStats const& sectionStats) override; + +        void testCaseEnded(TestCaseStats const& testCaseStats) override; + +        void testGroupEnded(TestGroupStats const& testGroupStats) override; + +        void testRunEnded(TestRunStats const& testRunStats) override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +        void benchmarkPreparing(std::string const& name) override; +        void benchmarkStarting(BenchmarkInfo const&) override; +        void benchmarkEnded(BenchmarkStats<> const&) override; +        void benchmarkFailed(std::string const&) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +    private: +        Timer m_testCaseTimer; +        XmlWriter m_xml; +        int m_sectionDepth = 0; +    }; + +} // end namespace Catch + +// end catch_reporter_xml.h +  // end catch_external_interfaces.h  #endif +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_benchmarking_all.hpp + +// A proxy header that includes all of the benchmarking headers to allow +// concise include of the benchmarking features. You should prefer the +// individual includes in standard use. + +// start catch_benchmark.hpp + + // Benchmark + +// start catch_chronometer.hpp + +// User-facing chronometer + + +// start catch_clock.hpp + +// Clocks + + +#include <chrono> +#include <ratio> + +namespace Catch { +    namespace Benchmark { +        template <typename Clock> +        using ClockDuration = typename Clock::duration; +        template <typename Clock> +        using FloatDuration = std::chrono::duration<double, typename Clock::period>; + +        template <typename Clock> +        using TimePoint = typename Clock::time_point; + +        using default_clock = std::chrono::steady_clock; + +        template <typename Clock> +        struct now { +            TimePoint<Clock> operator()() const { +                return Clock::now(); +            } +        }; + +        using fp_seconds = std::chrono::duration<double, std::ratio<1>>; +    } // namespace Benchmark +} // namespace Catch + +// end catch_clock.hpp +// start catch_optimizer.hpp + + // Hinting the optimizer + + +#if defined(_MSC_VER) +#   include <atomic> // atomic_thread_fence +#endif + +namespace Catch { +    namespace Benchmark { +#if defined(__GNUC__) || defined(__clang__) +        template <typename T> +        inline void keep_memory(T* p) { +            asm volatile("" : : "g"(p) : "memory"); +        } +        inline void keep_memory() { +            asm volatile("" : : : "memory"); +        } + +        namespace Detail { +            inline void optimizer_barrier() { keep_memory(); } +        } // namespace Detail +#elif defined(_MSC_VER) + +#pragma optimize("", off) +        template <typename T> +        inline void keep_memory(T* p) { +            // thanks @milleniumbug +            *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p); +        } +        // TODO equivalent keep_memory() +#pragma optimize("", on) + +        namespace Detail { +            inline void optimizer_barrier() { +                std::atomic_thread_fence(std::memory_order_seq_cst); +            } +        } // namespace Detail + +#endif + +        template <typename T> +        inline void deoptimize_value(T&& x) { +            keep_memory(&x); +        } + +        template <typename Fn, typename... Args> +        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type { +            deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...))); +        } + +        template <typename Fn, typename... Args> +        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type { +            std::forward<Fn>(fn) (std::forward<Args...>(args...)); +        } +    } // namespace Benchmark +} // namespace Catch + +// end catch_optimizer.hpp +// start catch_complete_invoke.hpp + +// Invoke with a special case for void + + +#include <type_traits> +#include <utility> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename T> +            struct CompleteType { using type = T; }; +            template <> +            struct CompleteType<void> { struct type {}; }; + +            template <typename T> +            using CompleteType_t = typename CompleteType<T>::type; + +            template <typename Result> +            struct CompleteInvoker { +                template <typename Fun, typename... Args> +                static Result invoke(Fun&& fun, Args&&... args) { +                    return std::forward<Fun>(fun)(std::forward<Args>(args)...); +                } +            }; +            template <> +            struct CompleteInvoker<void> { +                template <typename Fun, typename... Args> +                static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) { +                    std::forward<Fun>(fun)(std::forward<Args>(args)...); +                    return {}; +                } +            }; + +            // invoke and not return void :( +            template <typename Fun, typename... Args> +            CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) { +                return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...); +            } + +            const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; +        } // namespace Detail + +        template <typename Fun> +        Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) { +            CATCH_TRY{ +                return Detail::complete_invoke(std::forward<Fun>(fun)); +            } CATCH_CATCH_ALL{ +                getResultCapture().benchmarkFailed(translateActiveException()); +                CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg); +            } +        } +    } // namespace Benchmark +} // namespace Catch + +// end catch_complete_invoke.hpp +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            struct ChronometerConcept { +                virtual void start() = 0; +                virtual void finish() = 0; +                virtual ~ChronometerConcept() = default; +            }; +            template <typename Clock> +            struct ChronometerModel final : public ChronometerConcept { +                void start() override { started = Clock::now(); } +                void finish() override { finished = Clock::now(); } + +                ClockDuration<Clock> elapsed() const { return finished - started; } + +                TimePoint<Clock> started; +                TimePoint<Clock> finished; +            }; +        } // namespace Detail + +        struct Chronometer { +        public: +            template <typename Fun> +            void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); } + +            int runs() const { return k; } + +            Chronometer(Detail::ChronometerConcept& meter, int k) +                : impl(&meter) +                , k(k) {} + +        private: +            template <typename Fun> +            void measure(Fun&& fun, std::false_type) { +                measure([&fun](int) { return fun(); }, std::true_type()); +            } + +            template <typename Fun> +            void measure(Fun&& fun, std::true_type) { +                Detail::optimizer_barrier(); +                impl->start(); +                for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i); +                impl->finish(); +                Detail::optimizer_barrier(); +            } + +            Detail::ChronometerConcept* impl; +            int k; +        }; +    } // namespace Benchmark +} // namespace Catch + +// end catch_chronometer.hpp +// start catch_environment.hpp + +// Environment information + + +namespace Catch { +    namespace Benchmark { +        template <typename Duration> +        struct EnvironmentEstimate { +            Duration mean; +            OutlierClassification outliers; + +            template <typename Duration2> +            operator EnvironmentEstimate<Duration2>() const { +                return { mean, outliers }; +            } +        }; +        template <typename Clock> +        struct Environment { +            using clock_type = Clock; +            EnvironmentEstimate<FloatDuration<Clock>> clock_resolution; +            EnvironmentEstimate<FloatDuration<Clock>> clock_cost; +        }; +    } // namespace Benchmark +} // namespace Catch + +// end catch_environment.hpp +// start catch_execution_plan.hpp + + // Execution plan + + +// start catch_benchmark_function.hpp + + // Dumb std::function implementation for consistent call overhead + + +#include <cassert> +#include <type_traits> +#include <utility> +#include <memory> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename T> +            using Decay = typename std::decay<T>::type; +            template <typename T, typename U> +            struct is_related +                : std::is_same<Decay<T>, Decay<U>> {}; + +            /// We need to reinvent std::function because every piece of code that might add overhead +            /// in a measurement context needs to have consistent performance characteristics so that we +            /// can account for it in the measurement. +            /// Implementations of std::function with optimizations that aren't always applicable, like +            /// small buffer optimizations, are not uncommon. +            /// This is effectively an implementation of std::function without any such optimizations; +            /// it may be slow, but it is consistently slow. +            struct BenchmarkFunction { +            private: +                struct callable { +                    virtual void call(Chronometer meter) const = 0; +                    virtual callable* clone() const = 0; +                    virtual ~callable() = default; +                }; +                template <typename Fun> +                struct model : public callable { +                    model(Fun&& fun) : fun(std::move(fun)) {} +                    model(Fun const& fun) : fun(fun) {} + +                    model<Fun>* clone() const override { return new model<Fun>(*this); } + +                    void call(Chronometer meter) const override { +                        call(meter, is_callable<Fun(Chronometer)>()); +                    } +                    void call(Chronometer meter, std::true_type) const { +                        fun(meter); +                    } +                    void call(Chronometer meter, std::false_type) const { +                        meter.measure(fun); +                    } + +                    Fun fun; +                }; + +                struct do_nothing { void operator()() const {} }; + +                template <typename T> +                BenchmarkFunction(model<T>* c) : f(c) {} + +            public: +                BenchmarkFunction() +                    : f(new model<do_nothing>{ {} }) {} + +                template <typename Fun, +                    typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0> +                    BenchmarkFunction(Fun&& fun) +                    : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {} + +                BenchmarkFunction(BenchmarkFunction&& that) +                    : f(std::move(that.f)) {} + +                BenchmarkFunction(BenchmarkFunction const& that) +                    : f(that.f->clone()) {} + +                BenchmarkFunction& operator=(BenchmarkFunction&& that) { +                    f = std::move(that.f); +                    return *this; +                } + +                BenchmarkFunction& operator=(BenchmarkFunction const& that) { +                    f.reset(that.f->clone()); +                    return *this; +                } + +                void operator()(Chronometer meter) const { f->call(meter); } + +            private: +                std::unique_ptr<callable> f; +            }; +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +// end catch_benchmark_function.hpp +// start catch_repeat.hpp + +// repeat algorithm + + +#include <type_traits> +#include <utility> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename Fun> +            struct repeater { +                void operator()(int k) const { +                    for (int i = 0; i < k; ++i) { +                        fun(); +                    } +                } +                Fun fun; +            }; +            template <typename Fun> +            repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) { +                return { std::forward<Fun>(fun) }; +            } +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +// end catch_repeat.hpp +// start catch_run_for_at_least.hpp + +// Run a function for a minimum amount of time + + +// start catch_measure.hpp + +// Measure + + +// start catch_timing.hpp + +// Timing + + +#include <tuple> +#include <type_traits> + +namespace Catch { +    namespace Benchmark { +        template <typename Duration, typename Result> +        struct Timing { +            Duration elapsed; +            Result result; +            int iterations; +        }; +        template <typename Clock, typename Func, typename... Args> +        using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>; +    } // namespace Benchmark +} // namespace Catch + +// end catch_timing.hpp +#include <utility> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename Clock, typename Fun, typename... Args> +            TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) { +                auto start = Clock::now(); +                auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...); +                auto end = Clock::now(); +                auto delta = end - start; +                return { delta, std::forward<decltype(r)>(r), 1 }; +            } +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +// end catch_measure.hpp +#include <utility> +#include <type_traits> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename Clock, typename Fun> +            TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) { +                return Detail::measure<Clock>(fun, iters); +            } +            template <typename Clock, typename Fun> +            TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) { +                Detail::ChronometerModel<Clock> meter; +                auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); + +                return { meter.elapsed(), std::move(result), iters }; +            } + +            template <typename Clock, typename Fun> +            using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type; + +            struct optimized_away_error : std::exception { +                const char* what() const noexcept override { +                    return "could not measure benchmark, maybe it was optimized away"; +                } +            }; + +            template <typename Clock, typename Fun> +            TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) { +                auto iters = seed; +                while (iters < (1 << 30)) { +                    auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>()); + +                    if (Timing.elapsed >= how_long) { +                        return { Timing.elapsed, std::move(Timing.result), iters }; +                    } +                    iters *= 2; +                } +                throw optimized_away_error{}; +            } +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +// end catch_run_for_at_least.hpp +#include <algorithm> + +namespace Catch { +    namespace Benchmark { +        template <typename Duration> +        struct ExecutionPlan { +            int iterations_per_sample; +            Duration estimated_duration; +            Detail::BenchmarkFunction benchmark; +            Duration warmup_time; +            int warmup_iterations; + +            template <typename Duration2> +            operator ExecutionPlan<Duration2>() const { +                return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations }; +            } + +            template <typename Clock> +            std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { +                // warmup a bit +                Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{})); + +                std::vector<FloatDuration<Clock>> times; +                times.reserve(cfg.benchmarkSamples()); +                std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { +                    Detail::ChronometerModel<Clock> model; +                    this->benchmark(Chronometer(model, iterations_per_sample)); +                    auto sample_time = model.elapsed() - env.clock_cost.mean; +                    if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero(); +                    return sample_time / iterations_per_sample; +                }); +                return times; +            } +        }; +    } // namespace Benchmark +} // namespace Catch + +// end catch_execution_plan.hpp +// start catch_estimate_clock.hpp + + // Environment measurement + + +// start catch_stats.hpp + +// Statistical analysis tools + + +#include <algorithm> +#include <functional> +#include <vector> +#include <iterator> +#include <numeric> +#include <tuple> +#include <cmath> +#include <utility> +#include <cstddef> +#include <random> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            using sample = std::vector<double>; + +            double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last); + +            template <typename Iterator> +            OutlierClassification classify_outliers(Iterator first, Iterator last) { +                std::vector<double> copy(first, last); + +                auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); +                auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); +                auto iqr = q3 - q1; +                auto los = q1 - (iqr * 3.); +                auto lom = q1 - (iqr * 1.5); +                auto him = q3 + (iqr * 1.5); +                auto his = q3 + (iqr * 3.); + +                OutlierClassification o; +                for (; first != last; ++first) { +                    auto&& t = *first; +                    if (t < los) ++o.low_severe; +                    else if (t < lom) ++o.low_mild; +                    else if (t > his) ++o.high_severe; +                    else if (t > him) ++o.high_mild; +                    ++o.samples_seen; +                } +                return o; +            } + +            template <typename Iterator> +            double mean(Iterator first, Iterator last) { +                auto count = last - first; +                double sum = std::accumulate(first, last, 0.); +                return sum / count; +            } + +            template <typename URng, typename Iterator, typename Estimator> +            sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) { +                auto n = last - first; +                std::uniform_int_distribution<decltype(n)> dist(0, n - 1); + +                sample out; +                out.reserve(resamples); +                std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { +                    std::vector<double> resampled; +                    resampled.reserve(n); +                    std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; }); +                    return estimator(resampled.begin(), resampled.end()); +                }); +                std::sort(out.begin(), out.end()); +                return out; +            } + +            template <typename Estimator, typename Iterator> +            sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { +                auto n = last - first; +                auto second = std::next(first); +                sample results; +                results.reserve(n); + +                for (auto it = first; it != last; ++it) { +                    std::iter_swap(it, first); +                    results.push_back(estimator(second, last)); +                } + +                return results; +            } + +            inline double normal_cdf(double x) { +                return std::erfc(-x / std::sqrt(2.0)) / 2.0; +            } + +            double erfc_inv(double x); + +            double normal_quantile(double p); + +            template <typename Iterator, typename Estimator> +            Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { +                auto n_samples = last - first; + +                double point = estimator(first, last); +                // Degenerate case with a single sample +                if (n_samples == 1) return { point, point, point, confidence_level }; + +                sample jack = jackknife(estimator, first, last); +                double jack_mean = mean(jack.begin(), jack.end()); +                double sum_squares, sum_cubes; +                std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> { +                    auto d = jack_mean - x; +                    auto d2 = d * d; +                    auto d3 = d2 * d; +                    return { sqcb.first + d2, sqcb.second + d3 }; +                }); + +                double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); +                int n = static_cast<int>(resample.size()); +                double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n; +                // degenerate case with uniform samples +                if (prob_n == 0) return { point, point, point, confidence_level }; + +                double bias = normal_quantile(prob_n); +                double z1 = normal_quantile((1. - confidence_level) / 2.); + +                auto cumn = [n](double x) -> int { +                    return std::lround(normal_cdf(x) * n); }; +                auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; +                double b1 = bias + z1; +                double b2 = bias - z1; +                double a1 = a(b1); +                double a2 = a(b2); +                auto lo = std::max(cumn(a1), 0); +                auto hi = std::min(cumn(a2), n - 1); + +                return { point, resample[lo], resample[hi], confidence_level }; +            } + +            double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n); + +            struct bootstrap_analysis { +                Estimate<double> mean; +                Estimate<double> standard_deviation; +                double outlier_variance; +            }; + +            bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last); +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +// end catch_stats.hpp +#include <algorithm> +#include <iterator> +#include <tuple> +#include <vector> +#include <cmath> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename Clock> +            std::vector<double> resolution(int k) { +                std::vector<TimePoint<Clock>> times; +                times.reserve(k + 1); +                std::generate_n(std::back_inserter(times), k + 1, now<Clock>{}); + +                std::vector<double> deltas; +                deltas.reserve(k); +                std::transform(std::next(times.begin()), times.end(), times.begin(), +                    std::back_inserter(deltas), +                    [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); }); + +                return deltas; +            } + +            const auto warmup_iterations = 10000; +            const auto warmup_time = std::chrono::milliseconds(100); +            const auto minimum_ticks = 1000; +            const auto warmup_seed = 10000; +            const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); +            const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); +            const auto clock_cost_estimation_tick_limit = 100000; +            const auto clock_cost_estimation_time = std::chrono::milliseconds(10); +            const auto clock_cost_estimation_iterations = 10000; + +            template <typename Clock> +            int warmup() { +                return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>) +                    .iterations; +            } +            template <typename Clock> +            EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) { +                auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>) +                    .result; +                return { +                    FloatDuration<Clock>(mean(r.begin(), r.end())), +                    classify_outliers(r.begin(), r.end()), +                }; +            } +            template <typename Clock> +            EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { +                auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit)); +                auto time_clock = [](int k) { +                    return Detail::measure<Clock>([k] { +                        for (int i = 0; i < k; ++i) { +                            volatile auto ignored = Clock::now(); +                            (void)ignored; +                        } +                    }).elapsed; +                }; +                time_clock(1); +                int iters = clock_cost_estimation_iterations; +                auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock); +                std::vector<double> times; +                int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed)); +                times.reserve(nsamples); +                std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { +                    return static_cast<double>((time_clock(r.iterations) / r.iterations).count()); +                }); +                return { +                    FloatDuration<Clock>(mean(times.begin(), times.end())), +                    classify_outliers(times.begin(), times.end()), +                }; +            } + +            template <typename Clock> +            Environment<FloatDuration<Clock>> measure_environment() { +                static Environment<FloatDuration<Clock>>* env = nullptr; +                if (env) { +                    return *env; +                } + +                auto iters = Detail::warmup<Clock>(); +                auto resolution = Detail::estimate_clock_resolution<Clock>(iters); +                auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean); + +                env = new Environment<FloatDuration<Clock>>{ resolution, cost }; +                return *env; +            } +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +// end catch_estimate_clock.hpp +// start catch_analyse.hpp + + // Run and analyse one benchmark + + +// start catch_sample_analysis.hpp + +// Benchmark results + + +#include <algorithm> +#include <vector> +#include <string> +#include <iterator> + +namespace Catch { +    namespace Benchmark { +        template <typename Duration> +        struct SampleAnalysis { +            std::vector<Duration> samples; +            Estimate<Duration> mean; +            Estimate<Duration> standard_deviation; +            OutlierClassification outliers; +            double outlier_variance; + +            template <typename Duration2> +            operator SampleAnalysis<Duration2>() const { +                std::vector<Duration2> samples2; +                samples2.reserve(samples.size()); +                std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); +                return { +                    std::move(samples2), +                    mean, +                    standard_deviation, +                    outliers, +                    outlier_variance, +                }; +            } +        }; +    } // namespace Benchmark +} // namespace Catch + +// end catch_sample_analysis.hpp +#include <algorithm> +#include <iterator> +#include <vector> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename Duration, typename Iterator> +            SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) { +                if (!cfg.benchmarkNoAnalysis()) { +                    std::vector<double> samples; +                    samples.reserve(last - first); +                    std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); + +                    auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); +                    auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); + +                    auto wrap_estimate = [](Estimate<double> e) { +                        return Estimate<Duration> { +                            Duration(e.point), +                                Duration(e.lower_bound), +                                Duration(e.upper_bound), +                                e.confidence_interval, +                        }; +                    }; +                    std::vector<Duration> samples2; +                    samples2.reserve(samples.size()); +                    std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); +                    return { +                        std::move(samples2), +                        wrap_estimate(analysis.mean), +                        wrap_estimate(analysis.standard_deviation), +                        outliers, +                        analysis.outlier_variance, +                    }; +                } else { +                    std::vector<Duration> samples; +                    samples.reserve(last - first); + +                    Duration mean = Duration(0); +                    int i = 0; +                    for (auto it = first; it < last; ++it, ++i) { +                        samples.push_back(Duration(*it)); +                        mean += Duration(*it); +                    } +                    mean /= i; + +                    return { +                        std::move(samples), +                        Estimate<Duration>{mean, mean, mean, 0.0}, +                        Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0}, +                        OutlierClassification{}, +                        0.0 +                    }; +                } +            } +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +// end catch_analyse.hpp +#include <algorithm> +#include <functional> +#include <string> +#include <vector> +#include <cmath> + +namespace Catch { +    namespace Benchmark { +        struct Benchmark { +            Benchmark(std::string &&name) +                : name(std::move(name)) {} + +            template <class FUN> +            Benchmark(std::string &&name, FUN &&func) +                : fun(std::move(func)), name(std::move(name)) {} + +            template <typename Clock> +            ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { +                auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; +                auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime())); +                auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun); +                int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed)); +                return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; +            } + +            template <typename Clock = default_clock> +            void run() { +                IConfigPtr cfg = getCurrentContext().getConfig(); + +                auto env = Detail::measure_environment<Clock>(); + +                getResultCapture().benchmarkPreparing(name); +                CATCH_TRY{ +                    auto plan = user_code([&] { +                        return prepare<Clock>(*cfg, env); +                    }); + +                    BenchmarkInfo info { +                        name, +                        plan.estimated_duration.count(), +                        plan.iterations_per_sample, +                        cfg->benchmarkSamples(), +                        cfg->benchmarkResamples(), +                        env.clock_resolution.mean.count(), +                        env.clock_cost.mean.count() +                    }; + +                    getResultCapture().benchmarkStarting(info); + +                    auto samples = user_code([&] { +                        return plan.template run<Clock>(*cfg, env); +                    }); + +                    auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); +                    BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; +                    getResultCapture().benchmarkEnded(stats); + +                } CATCH_CATCH_ALL{ +                    if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow. +                        std::rethrow_exception(std::current_exception()); +                } +            } + +            // sets lambda to be used in fun *and* executes benchmark! +            template <typename Fun, +                typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0> +                Benchmark & operator=(Fun func) { +                fun = Detail::BenchmarkFunction(func); +                run(); +                return *this; +            } + +            explicit operator bool() { +                return true; +            } + +        private: +            Detail::BenchmarkFunction fun; +            std::string name; +        }; +    } +} // namespace Catch + +#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 +#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 + +#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ +    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ +        BenchmarkName = [&](int benchmarkIndex) + +#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ +    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ +        BenchmarkName = [&] + +// end catch_benchmark.hpp +// start catch_constructor.hpp + +// Constructor and destructor helpers + + +#include <type_traits> + +namespace Catch { +    namespace Benchmark { +        namespace Detail { +            template <typename T, bool Destruct> +            struct ObjectStorage +            { +                using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type; + +                ObjectStorage() : data() {} + +                ObjectStorage(const ObjectStorage& other) +                { +                    new(&data) T(other.stored_object()); +                } + +                ObjectStorage(ObjectStorage&& other) +                { +                    new(&data) T(std::move(other.stored_object())); +                } + +                ~ObjectStorage() { destruct_on_exit<T>(); } + +                template <typename... Args> +                void construct(Args&&... args) +                { +                    new (&data) T(std::forward<Args>(args)...); +                } + +                template <bool AllowManualDestruction = !Destruct> +                typename std::enable_if<AllowManualDestruction>::type destruct() +                { +                    stored_object().~T(); +                } + +            private: +                // If this is a constructor benchmark, destruct the underlying object +                template <typename U> +                void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); } +                // Otherwise, don't +                template <typename U> +                void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { } + +                T& stored_object() { +                    return *static_cast<T*>(static_cast<void*>(&data)); +                } + +                T const& stored_object() const { +                    return *static_cast<T*>(static_cast<void*>(&data)); +                } + +                TStorage data; +            }; +        } + +        template <typename T> +        using storage_for = Detail::ObjectStorage<T, true>; + +        template <typename T> +        using destructable_object = Detail::ObjectStorage<T, false>; +    } +} + +// end catch_constructor.hpp +// end catch_benchmarking_all.hpp +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY +  #ifdef CATCH_IMPL  // start catch_impl.hpp @@ -3809,23 +7464,37 @@ namespace TestCaseTracking {          SourceLineInfo location;          NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); +        friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { +            return lhs.name == rhs.name +                && lhs.location == rhs.location; +        }      }; -    struct ITracker; +    class ITracker;      using ITrackerPtr = std::shared_ptr<ITracker>; -    struct ITracker { -        virtual ~ITracker(); +    class  ITracker { +        NameAndLocation m_nameAndLocation; + +    public: +        ITracker(NameAndLocation const& nameAndLoc) : +            m_nameAndLocation(nameAndLoc) +        {}          // static queries -        virtual NameAndLocation const& nameAndLocation() const = 0; +        NameAndLocation const& nameAndLocation() const { +            return m_nameAndLocation; +        } + +        virtual ~ITracker();          // dynamic queries          virtual bool isComplete() const = 0; // Successfully completed or failed          virtual bool isSuccessfullyCompleted() const = 0;          virtual bool isOpen() const = 0; // Started but not complete          virtual bool hasChildren() const = 0; +        virtual bool hasStarted() const = 0;          virtual ITracker& parent() = 0; @@ -3840,7 +7509,7 @@ namespace TestCaseTracking {          // Debug/ checking          virtual bool isSectionTracker() const = 0; -        virtual bool isIndexTracker() const = 0; +        virtual bool isGeneratorTracker() const = 0;      };      class TrackerContext { @@ -3857,8 +7526,6 @@ namespace TestCaseTracking {      public: -        static TrackerContext& instance(); -          ITracker& startRun();          void endRun(); @@ -3881,15 +7548,7 @@ namespace TestCaseTracking {              Failed          }; -        class TrackerHasName { -            NameAndLocation m_nameAndLocation; -        public: -            TrackerHasName( NameAndLocation const& nameAndLocation ); -            bool operator ()( ITrackerPtr const& tracker ) const; -        }; -          using Children = std::vector<ITrackerPtr>; -        NameAndLocation m_nameAndLocation;          TrackerContext& m_ctx;          ITracker* m_parent;          Children m_children; @@ -3898,11 +7557,13 @@ namespace TestCaseTracking {      public:          TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); -        NameAndLocation const& nameAndLocation() const override;          bool isComplete() const override;          bool isSuccessfullyCompleted() const override;          bool isOpen() const override;          bool hasChildren() const override; +        bool hasStarted() const override { +            return m_runState != NotStarted; +        }          void addChild( ITrackerPtr const& child ) override; @@ -3912,7 +7573,7 @@ namespace TestCaseTracking {          void openChild() override;          bool isSectionTracker() const override; -        bool isIndexTracker() const override; +        bool isGeneratorTracker() const override;          void open(); @@ -3927,33 +7588,24 @@ namespace TestCaseTracking {      class SectionTracker : public TrackerBase {          std::vector<std::string> m_filters; +        std::string m_trimmed_name;      public:          SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );          bool isSectionTracker() const override; +        bool isComplete() const override; +          static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );          void tryOpen();          void addInitialFilters( std::vector<std::string> const& filters );          void addNextFilters( std::vector<std::string> const& filters ); -    }; - -    class IndexTracker : public TrackerBase { -        int m_size; -        int m_index = -1; -    public: -        IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); - -        bool isIndexTracker() const override; -        void close() override; - -        static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); - -        int index() const; - -        void moveNext(); +        //! Returns filters active in this tracker +        std::vector<std::string> const& getFilters() const; +        //! Returns whitespace-trimmed name of the tracked section +        std::string const& trimmedName() const;      };  } // namespace TestCaseTracking @@ -3961,7 +7613,6 @@ namespace TestCaseTracking {  using TestCaseTracking::ITracker;  using TestCaseTracking::TrackerContext;  using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker;  } // namespace Catch @@ -3973,11 +7624,223 @@ namespace Catch {      struct LeakDetector {          LeakDetector(); +        ~LeakDetector();      };  }  // end catch_leak_detector.h  // Cpp files will be included in the single-header file here +// start catch_stats.cpp + +// Statistical analysis tools + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + +#include <cassert> +#include <random> + +#if defined(CATCH_CONFIG_USE_ASYNC) +#include <future> +#endif + +namespace { +    double erf_inv(double x) { +        // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 +        double w, p; + +        w = -log((1.0 - x) * (1.0 + x)); + +        if (w < 6.250000) { +            w = w - 3.125000; +            p = -3.6444120640178196996e-21; +            p = -1.685059138182016589e-19 + p * w; +            p = 1.2858480715256400167e-18 + p * w; +            p = 1.115787767802518096e-17 + p * w; +            p = -1.333171662854620906e-16 + p * w; +            p = 2.0972767875968561637e-17 + p * w; +            p = 6.6376381343583238325e-15 + p * w; +            p = -4.0545662729752068639e-14 + p * w; +            p = -8.1519341976054721522e-14 + p * w; +            p = 2.6335093153082322977e-12 + p * w; +            p = -1.2975133253453532498e-11 + p * w; +            p = -5.4154120542946279317e-11 + p * w; +            p = 1.051212273321532285e-09 + p * w; +            p = -4.1126339803469836976e-09 + p * w; +            p = -2.9070369957882005086e-08 + p * w; +            p = 4.2347877827932403518e-07 + p * w; +            p = -1.3654692000834678645e-06 + p * w; +            p = -1.3882523362786468719e-05 + p * w; +            p = 0.0001867342080340571352 + p * w; +            p = -0.00074070253416626697512 + p * w; +            p = -0.0060336708714301490533 + p * w; +            p = 0.24015818242558961693 + p * w; +            p = 1.6536545626831027356 + p * w; +        } else if (w < 16.000000) { +            w = sqrt(w) - 3.250000; +            p = 2.2137376921775787049e-09; +            p = 9.0756561938885390979e-08 + p * w; +            p = -2.7517406297064545428e-07 + p * w; +            p = 1.8239629214389227755e-08 + p * w; +            p = 1.5027403968909827627e-06 + p * w; +            p = -4.013867526981545969e-06 + p * w; +            p = 2.9234449089955446044e-06 + p * w; +            p = 1.2475304481671778723e-05 + p * w; +            p = -4.7318229009055733981e-05 + p * w; +            p = 6.8284851459573175448e-05 + p * w; +            p = 2.4031110387097893999e-05 + p * w; +            p = -0.0003550375203628474796 + p * w; +            p = 0.00095328937973738049703 + p * w; +            p = -0.0016882755560235047313 + p * w; +            p = 0.0024914420961078508066 + p * w; +            p = -0.0037512085075692412107 + p * w; +            p = 0.005370914553590063617 + p * w; +            p = 1.0052589676941592334 + p * w; +            p = 3.0838856104922207635 + p * w; +        } else { +            w = sqrt(w) - 5.000000; +            p = -2.7109920616438573243e-11; +            p = -2.5556418169965252055e-10 + p * w; +            p = 1.5076572693500548083e-09 + p * w; +            p = -3.7894654401267369937e-09 + p * w; +            p = 7.6157012080783393804e-09 + p * w; +            p = -1.4960026627149240478e-08 + p * w; +            p = 2.9147953450901080826e-08 + p * w; +            p = -6.7711997758452339498e-08 + p * w; +            p = 2.2900482228026654717e-07 + p * w; +            p = -9.9298272942317002539e-07 + p * w; +            p = 4.5260625972231537039e-06 + p * w; +            p = -1.9681778105531670567e-05 + p * w; +            p = 7.5995277030017761139e-05 + p * w; +            p = -0.00021503011930044477347 + p * w; +            p = -0.00013871931833623122026 + p * w; +            p = 1.0103004648645343977 + p * w; +            p = 4.8499064014085844221 + p * w; +        } +        return p * x; +    } + +    double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) { +        auto m = Catch::Benchmark::Detail::mean(first, last); +        double variance = std::accumulate(first, last, 0., [m](double a, double b) { +            double diff = b - m; +            return a + diff * diff; +            }) / (last - first); +            return std::sqrt(variance); +    } + +} + +namespace Catch { +    namespace Benchmark { +        namespace Detail { + +            double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) { +                auto count = last - first; +                double idx = (count - 1) * k / static_cast<double>(q); +                int j = static_cast<int>(idx); +                double g = idx - j; +                std::nth_element(first, first + j, last); +                auto xj = first[j]; +                if (g == 0) return xj; + +                auto xj1 = *std::min_element(first + (j + 1), last); +                return xj + g * (xj1 - xj); +            } + +            double erfc_inv(double x) { +                return erf_inv(1.0 - x); +            } + +            double normal_quantile(double p) { +                static const double ROOT_TWO = std::sqrt(2.0); + +                double result = 0.0; +                assert(p >= 0 && p <= 1); +                if (p < 0 || p > 1) { +                    return result; +                } + +                result = -erfc_inv(2.0 * p); +                // result *= normal distribution standard deviation (1.0) * sqrt(2) +                result *= /*sd * */ ROOT_TWO; +                // result += normal disttribution mean (0) +                return result; +            } + +            double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) { +                double sb = stddev.point; +                double mn = mean.point / n; +                double mg_min = mn / 2.; +                double sg = std::min(mg_min / 4., sb / std::sqrt(n)); +                double sg2 = sg * sg; +                double sb2 = sb * sb; + +                auto c_max = [n, mn, sb2, sg2](double x) -> double { +                    double k = mn - x; +                    double d = k * k; +                    double nd = n * d; +                    double k0 = -n * nd; +                    double k1 = sb2 - n * sg2 + nd; +                    double det = k1 * k1 - 4 * sg2 * k0; +                    return (int)(-2. * k0 / (k1 + std::sqrt(det))); +                }; + +                auto var_out = [n, sb2, sg2](double c) { +                    double nc = n - c; +                    return (nc / n) * (sb2 - nc * sg2); +                }; + +                return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2; +            } + +            bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) { +                CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +                CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +                static std::random_device entropy; +                CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +                auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ + +                auto mean = &Detail::mean<std::vector<double>::iterator>; +                auto stddev = &standard_deviation; + +#if defined(CATCH_CONFIG_USE_ASYNC) +                auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) { +                    auto seed = entropy(); +                    return std::async(std::launch::async, [=] { +                        std::mt19937 rng(seed); +                        auto resampled = resample(rng, n_resamples, first, last, f); +                        return bootstrap(confidence_level, first, last, resampled, f); +                    }); +                }; + +                auto mean_future = Estimate(mean); +                auto stddev_future = Estimate(stddev); + +                auto mean_estimate = mean_future.get(); +                auto stddev_estimate = stddev_future.get(); +#else +                auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) { +                    auto seed = entropy(); +                    std::mt19937 rng(seed); +                    auto resampled = resample(rng, n_resamples, first, last, f); +                    return bootstrap(confidence_level, first, last, resampled, f); +                }; + +                auto mean_estimate = Estimate(mean); +                auto stddev_estimate = Estimate(stddev); +#endif // CATCH_USE_ASYNC + +                double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); + +                return { mean_estimate, stddev_estimate, outlier_variance }; +            } +        } // namespace Detail +    } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING +// end catch_stats.cpp  // start catch_approx.cpp  #include <cmath> @@ -4007,20 +7870,50 @@ namespace Detail {          return Approx( 0 );      } +    Approx Approx::operator-() const { +        auto temp(*this); +        temp.m_value = -temp.m_value; +        return temp; +    } +      std::string Approx::toString() const { -        std::ostringstream oss; -        oss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; -        return oss.str(); +        ReusableStringStream rss; +        rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; +        return rss.str();      }      bool Approx::equalityComparisonImpl(const double other) const {          // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value          // Thanks to Richard Harris for his help refining the scaled margin value -        return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); +        return marginComparison(m_value, other, m_margin) +            || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); +    } + +    void Approx::setMargin(double newMargin) { +        CATCH_ENFORCE(newMargin >= 0, +            "Invalid Approx::margin: " << newMargin << '.' +            << " Approx::Margin has to be non-negative."); +        m_margin = newMargin; +    } + +    void Approx::setEpsilon(double newEpsilon) { +        CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, +            "Invalid Approx::epsilon: " << newEpsilon << '.' +            << " Approx::epsilon has to be in [0, 1]"); +        m_epsilon = newEpsilon;      }  } // end namespace Detail +namespace literals { +    Detail::Approx operator "" _a(long double val) { +        return Detail::Approx(val); +    } +    Detail::Approx operator "" _a(unsigned long long val) { +        return Detail::Approx(val); +    } +} // end namespace literals +  std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) {      return value.toString();  } @@ -4029,48 +7922,286 @@ std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx co  // end catch_approx.cpp  // start catch_assertionhandler.cpp -// start catch_context.h - -#include <memory> +// start catch_debugger.h  namespace Catch { +    bool isDebuggerActive(); +} -    struct IResultCapture; -    struct IRunner; -    struct IConfig; +#ifdef CATCH_PLATFORM_MAC -    using IConfigPtr = std::shared_ptr<IConfig const>; +    #if defined(__i386__) || defined(__x86_64__) +        #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ +    #elif defined(__aarch64__) +        #define CATCH_TRAP()  __asm__(".inst 0xd4200000") +    #endif -    struct IContext -    { -        virtual ~IContext(); +#elif defined(CATCH_PLATFORM_IPHONE) + +    // use inline assembler +    #if defined(__i386__) || defined(__x86_64__) +        #define CATCH_TRAP()  __asm__("int $3") +    #elif defined(__aarch64__) +        #define CATCH_TRAP()  __asm__(".inst 0xd4200000") +    #elif defined(__arm__) && !defined(__thumb__) +        #define CATCH_TRAP()  __asm__(".inst 0xe7f001f0") +    #elif defined(__arm__) &&  defined(__thumb__) +        #define CATCH_TRAP()  __asm__(".inst 0xde01") +    #endif -        virtual IResultCapture* getResultCapture() = 0; -        virtual IRunner* getRunner() = 0; -        virtual IConfigPtr getConfig() const = 0; +#elif defined(CATCH_PLATFORM_LINUX) +    // If we can use inline assembler, do it because this allows us to break +    // directly at the location of the failing check instead of breaking inside +    // raise() called from it, i.e. one stack frame below. +    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) +        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ +    #else // Fall back to the generic way. +        #include <signal.h> + +        #define CATCH_TRAP() raise(SIGTRAP) +    #endif +#elif defined(_MSC_VER) +    #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) +    extern "C" __declspec(dllimport) void __stdcall DebugBreak(); +    #define CATCH_TRAP() DebugBreak() +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER +    #ifdef CATCH_TRAP +        #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }() +    #else +        #define CATCH_BREAK_INTO_DEBUGGER() []{}() +    #endif +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +#  define CATCH_DEFINED_NOMINMAX +#  define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +#  define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#  define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include <AfxWin.h> +#else +#include <windows.h> +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +#  undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#  undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + +    struct FatalConditionHandler { + +        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); +        FatalConditionHandler(); +        static void reset(); +        ~FatalConditionHandler(); + +    private: +        static bool isSet; +        static ULONG guaranteeSize; +        static PVOID exceptionHandlerHandle;      }; -    struct IMutableContext : IContext -    { -        virtual ~IMutableContext(); -        virtual void setResultCapture( IResultCapture* resultCapture ) = 0; -        virtual void setRunner( IRunner* runner ) = 0; -        virtual void setConfig( IConfigPtr const& config ) = 0; +} // namespace Catch + +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) + +#include <signal.h> + +namespace Catch { + +    struct FatalConditionHandler { + +        static bool isSet; +        static struct sigaction oldSigActions[]; +        static stack_t oldSigStack; +        static char altStackMem[]; + +        static void handleSignal( int sig ); + +        FatalConditionHandler(); +        ~FatalConditionHandler(); +        static void reset();      }; -    IContext& getCurrentContext(); -    IMutableContext& getCurrentMutableContext(); -    void cleanUpContext(); +} // namespace Catch + +#else + +namespace Catch { +    struct FatalConditionHandler { +        void reset(); +    };  } -// end catch_context.h -#include <cassert> +#endif + +// end catch_fatal_condition.h +#include <string>  namespace Catch { -    auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { -        expr.streamReconstructedExpression( os ); -        return os; +    struct IMutableContext; + +    /////////////////////////////////////////////////////////////////////////// + +    class RunContext : public IResultCapture, public IRunner { + +    public: +        RunContext( RunContext const& ) = delete; +        RunContext& operator =( RunContext const& ) = delete; + +        explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + +        ~RunContext() override; + +        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); +        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + +        Totals runTest(TestCase const& testCase); + +        IConfigPtr config() const; +        IStreamingReporter& reporter() const; + +    public: // IResultCapture + +        // Assertion handlers +        void handleExpr +                (   AssertionInfo const& info, +                    ITransientExpression const& expr, +                    AssertionReaction& reaction ) override; +        void handleMessage +                (   AssertionInfo const& info, +                    ResultWas::OfType resultType, +                    StringRef const& message, +                    AssertionReaction& reaction ) override; +        void handleUnexpectedExceptionNotThrown +                (   AssertionInfo const& info, +                    AssertionReaction& reaction ) override; +        void handleUnexpectedInflightException +                (   AssertionInfo const& info, +                    std::string const& message, +                    AssertionReaction& reaction ) override; +        void handleIncomplete +                (   AssertionInfo const& info ) override; +        void handleNonExpr +                (   AssertionInfo const &info, +                    ResultWas::OfType resultType, +                    AssertionReaction &reaction ) override; + +        bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; + +        void sectionEnded( SectionEndInfo const& endInfo ) override; +        void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + +        auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +        void benchmarkPreparing( std::string const& name ) override; +        void benchmarkStarting( BenchmarkInfo const& info ) override; +        void benchmarkEnded( BenchmarkStats<> const& stats ) override; +        void benchmarkFailed( std::string const& error ) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +        void pushScopedMessage( MessageInfo const& message ) override; +        void popScopedMessage( MessageInfo const& message ) override; + +        void emplaceUnscopedMessage( MessageBuilder const& builder ) override; + +        std::string getCurrentTestName() const override; + +        const AssertionResult* getLastResult() const override; + +        void exceptionEarlyReported() override; + +        void handleFatalErrorCondition( StringRef message ) override; + +        bool lastAssertionPassed() override; + +        void assertionPassed() override; + +    public: +        // !TBD We need to do this another way! +        bool aborting() const final; + +    private: + +        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); +        void invokeActiveTestCase(); + +        void resetAssertionInfo(); +        bool testForMissingAssertions( Counts& assertions ); + +        void assertionEnded( AssertionResult const& result ); +        void reportExpr +                (   AssertionInfo const &info, +                    ResultWas::OfType resultType, +                    ITransientExpression const *expr, +                    bool negated ); + +        void populateReaction( AssertionReaction& reaction ); + +    private: + +        void handleUnfinishedSections(); + +        TestRunInfo m_runInfo; +        IMutableContext& m_context; +        TestCase const* m_activeTestCase = nullptr; +        ITracker* m_testCaseTracker = nullptr; +        Option<AssertionResult> m_lastResult; + +        IConfigPtr m_config; +        Totals m_totals; +        IStreamingReporterPtr m_reporter; +        std::vector<MessageInfo> m_messages; +        std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */ +        AssertionInfo m_lastAssertionInfo; +        std::vector<SectionEndInfo> m_unfinishedSections; +        std::vector<ITracker*> m_activeSections; +        TrackerContext m_trackerContext; +        bool m_lastAssertionPassed = false; +        bool m_shouldReportUnexpected = true; +        bool m_includeSuccessfulResults; +    }; + +    void seedRng(IConfig const& config); +    unsigned int rngSeed(); +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + +    namespace { +        auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { +            expr.streamReconstructedExpression( os ); +            return os; +        }      }      LazyExpression::LazyExpression( bool isNegated ) @@ -4100,95 +8231,69 @@ namespace Catch {      }      AssertionHandler::AssertionHandler -        (   StringRef macroName, +        (   StringRef const& macroName,              SourceLineInfo const& lineInfo,              StringRef capturedExpression,              ResultDisposition::Flags resultDisposition ) -    :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } -    { -        getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); -    } -    AssertionHandler::~AssertionHandler() { -        if ( m_inExceptionGuard ) { -            handle( ResultWas::ThrewException, "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE" ); -            getCurrentContext().getResultCapture()->exceptionEarlyReported(); -        } -    } - -    void AssertionHandler::handle( ITransientExpression const& expr ) { - -        bool negated = isFalseTest( m_assertionInfo.resultDisposition ); -        bool result = expr.getResult() != negated; +    :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, +        m_resultCapture( getResultCapture() ) +    {} -        handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated ); -    } -    void AssertionHandler::handle( ResultWas::OfType resultType ) { -        handle( resultType, nullptr, false ); +    void AssertionHandler::handleExpr( ITransientExpression const& expr ) { +        m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );      } -    void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) { -        AssertionResultData data( resultType, LazyExpression( false ) ); -        data.message = message; -        handle( data, nullptr ); +    void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { +        m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );      } -    void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) { -        AssertionResultData data( resultType, LazyExpression( negated ) ); -        handle( data, expr ); -    } -    void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) { -        getResultCapture().assertionRun(); +    auto AssertionHandler::allowThrows() const -> bool { +        return getCurrentContext().getConfig()->allowThrows(); +    } -        AssertionResult assertionResult{ m_assertionInfo, resultData }; -        assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; +    void AssertionHandler::complete() { +        setCompleted(); +        if( m_reaction.shouldDebugBreak ) { -        getResultCapture().assertionEnded( assertionResult ); +            // If you find your debugger stopping you here then go one level up on the +            // call-stack for the code that caused it (typically a failed assertion) -        if( !assertionResult.isOk() ) { -            m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak(); -            m_shouldThrow = -                    getCurrentContext().getRunner()->aborting() || -                    (m_assertionInfo.resultDisposition & ResultDisposition::Normal); +            // (To go back to the test and change execution, jump over the throw, next) +            CATCH_BREAK_INTO_DEBUGGER(); +        } +        if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +            throw Catch::TestFailureException(); +#else +            CATCH_ERROR( "Test failure requires aborting test!" ); +#endif          }      } - -    auto AssertionHandler::allowThrows() const -> bool { -        return getCurrentContext().getConfig()->allowThrows(); +    void AssertionHandler::setCompleted() { +        m_completed = true;      } -    auto AssertionHandler::shouldDebugBreak() const -> bool { -        return m_shouldDebugBreak; +    void AssertionHandler::handleUnexpectedInflightException() { +        m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );      } -    void AssertionHandler::reactWithDebugBreak() const { -        if (m_shouldDebugBreak) { -            /////////////////////////////////////////////////////////////////// -            // To inspect the state during test, you need to go one level up the callstack -            // To go back to the test and change execution, jump over the reactWithoutDebugBreak() call -            /////////////////////////////////////////////////////////////////// -            CATCH_BREAK_INTO_DEBUGGER(); -        } -        reactWithoutDebugBreak(); + +    void AssertionHandler::handleExceptionThrownAsExpected() { +        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);      } -    void AssertionHandler::reactWithoutDebugBreak() const { -        if( m_shouldThrow ) -            throw Catch::TestFailureException(); +    void AssertionHandler::handleExceptionNotThrownAsExpected() { +        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);      } -    void AssertionHandler::useActiveException() { -        handle( ResultWas::ThrewException, Catch::translateActiveException() ); +    void AssertionHandler::handleUnexpectedExceptionNotThrown() { +        m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );      } -    void AssertionHandler::setExceptionGuard() { -        assert( m_inExceptionGuard == false ); -        m_inExceptionGuard = true; -    } -    void AssertionHandler::unsetExceptionGuard() { -        assert( m_inExceptionGuard == true ); -        m_inExceptionGuard = false; +    void AssertionHandler::handleThrowingCallSkipped() { +        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);      }      // This is the overload that takes a string and infers the Equals matcher from it      // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp -    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString  ) { +    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString  ) {          handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );      } @@ -4205,10 +8310,9 @@ namespace Catch {          if( reconstructedExpression.empty() ) {              if( lazyExpression ) { -                // !TBD Use stringstream for now, but rework above to pass stream in -                std::ostringstream oss; -                oss << lazyExpression; -                reconstructedExpression = oss.str(); +                ReusableStringStream rss; +                rss << lazyExpression; +                reconstructedExpression = rss.str();              }          }          return reconstructedExpression; @@ -4234,7 +8338,7 @@ namespace Catch {      }      bool AssertionResult::hasExpression() const { -        return m_info.capturedExpression[0] != 0; +        return !m_info.capturedExpression.empty();      }      bool AssertionResult::hasMessage() const { @@ -4242,16 +8346,22 @@ namespace Catch {      }      std::string AssertionResult::getExpression() const { -        if( isFalseTest( m_info.resultDisposition ) ) -            return "!(" + std::string(m_info.capturedExpression) + ")"; -        else -            return m_info.capturedExpression; +        // Possibly overallocating by 3 characters should be basically free +        std::string expr; expr.reserve(m_info.capturedExpression.size() + 3); +        if (isFalseTest(m_info.resultDisposition)) { +            expr += "!("; +        } +        expr += m_info.capturedExpression; +        if (isFalseTest(m_info.resultDisposition)) { +            expr += ')'; +        } +        return expr;      }      std::string AssertionResult::getExpressionInMacro() const {          std::string expr; -        if( m_info.macroName[0] == 0 ) -            expr = m_info.capturedExpression; +        if( m_info.macroName.empty() ) +            expr = static_cast<std::string>(m_info.capturedExpression);          else {              expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );              expr += m_info.macroName; @@ -4280,38 +8390,12 @@ namespace Catch {          return m_info.lineInfo;      } -    std::string AssertionResult::getTestMacroName() const { +    StringRef AssertionResult::getTestMacroName() const {          return m_info.macroName;      }  } // end namespace Catch  // end catch_assertionresult.cpp -// start catch_benchmark.cpp - -namespace Catch { - -    auto BenchmarkLooper::getResolution() -> uint64_t { -        return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); -    } - -    void BenchmarkLooper::reportStart() { -        getResultCapture().benchmarkStarting( { m_name } ); -    } -    auto BenchmarkLooper::needsMoreIterations() -> bool { -        auto elapsed = m_timer.getElapsedNanoseconds(); - -        // Exponentially increasing iterations until we're confident in our timer resolution -        if( elapsed < m_resolution ) { -            m_iterationsToRun *= 10; -            return true; -        } - -        getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); -        return false; -    } - -} // end namespace Catch -// end catch_benchmark.cpp  // start catch_capture_matchers.cpp  namespace Catch { @@ -4319,12 +8403,12 @@ namespace Catch {      using StringMatcher = Matchers::Impl::MatcherBase<std::string>;      // This is the general overload that takes a any string matcher -    // There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers +    // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers      // the Equals matcher (so the header does not mention matchers) -    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString  ) { +    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  ) {          std::string exceptionMessage = Catch::translateActiveException();          MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString ); -        handler.handle( expr ); +        handler.handleExpr( expr );      }  } // namespace Catch @@ -4350,8 +8434,14 @@ namespace Catch {  #endif  // start clara.hpp -// v1.0-develop.2 -// See https://github.com/philsquared/Clara +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5  #ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH @@ -4362,14 +8452,23 @@ namespace Catch {  #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH  #endif +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include(<optional>) && __cplusplus >= 201703L +#include <optional> +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif +  // ----------- #included from clara_textflow.hpp -----------  // TextFlowCpp  //  // A single-header library for wrapping and laying out basic text, by Phil Nash  // -// This work is licensed under the BSD 2-Clause license. -// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)  //  // This project is hosted at https://github.com/philsquared/textflowcpp @@ -4383,314 +8482,328 @@ namespace Catch {  #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80  #endif -namespace Catch { namespace clara { namespace TextFlow { - -    inline auto isWhitespace( char c ) -> bool { -        static std::string chars = " \t\n\r"; -        return chars.find( c ) != std::string::npos; -    } -    inline auto isBreakableBefore( char c ) -> bool { -        static std::string chars = "[({<|"; -        return chars.find( c ) != std::string::npos; -    } -    inline auto isBreakableAfter( char c ) -> bool { -        static std::string chars = "])}>.,:;*+-=&/\\"; -        return chars.find( c ) != std::string::npos; -    } - -    class Columns; - -    class Column { -        std::vector<std::string> m_strings; -        size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; -        size_t m_indent = 0; -        size_t m_initialIndent = std::string::npos; - -    public: -        class iterator { -            friend Column; - -            Column const& m_column; -            size_t m_stringIndex = 0; -            size_t m_pos = 0; - -            size_t m_len = 0; -            size_t m_end = 0; -            bool m_suffix = false; - -            iterator( Column const& column, size_t stringIndex ) -            :   m_column( column ), -                m_stringIndex( stringIndex ) -            {} - -            auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } - -            auto isBoundary( size_t at ) const -> bool { -                assert( at > 0 ); -                assert( at <= line().size() ); - -                return at == line().size() || -                       ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || -                       isBreakableBefore( line()[at] ) || -                       isBreakableAfter( line()[at-1] ); -            } - -            void calcLength() { -                assert( m_stringIndex < m_column.m_strings.size() ); - -                m_suffix = false; -                auto width = m_column.m_width-indent(); -                m_end = m_pos; -                while( m_end < line().size() && line()[m_end] != '\n' ) -                    ++m_end; - -                if( m_end < m_pos + width ) { -                    m_len = m_end - m_pos; -                } -                else { -                    size_t len = width; -                    while (len > 0 && !isBoundary(m_pos + len)) -                        --len; -                    while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) -                        --len; - -                    if (len > 0) { -                        m_len = len; -                    } else { -                        m_suffix = true; -                        m_len = width - 1; -                    } -                } -            } - -            auto indent() const -> size_t { -                auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; -                return initial == std::string::npos ? m_column.m_indent : initial; -            } - -            auto addIndentAndSuffix(std::string const &plain) const -> std::string { -                return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); -            } - -        public: -            explicit iterator( Column const& column ) : m_column( column ) { -                assert( m_column.m_width > m_column.m_indent ); -                assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); -                calcLength(); -                if( m_len == 0 ) -                    m_stringIndex++; // Empty string -            } - -            auto operator *() const -> std::string { -                assert( m_stringIndex < m_column.m_strings.size() ); -                assert( m_pos <= m_end ); -                if( m_pos + m_column.m_width < m_end ) -                    return addIndentAndSuffix(line().substr(m_pos, m_len)); -                else -                    return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); -            } - -            auto operator ++() -> iterator& { -                m_pos += m_len; -                if( m_pos < line().size() && line()[m_pos] == '\n' ) -                    m_pos += 1; -                else -                    while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) -                        ++m_pos; - -                if( m_pos == line().size() ) { -                    m_pos = 0; -                    ++m_stringIndex; -                } -                if( m_stringIndex < m_column.m_strings.size() ) -                    calcLength(); -                return *this; -            } -            auto operator ++(int) -> iterator { -                iterator prev( *this ); -                operator++(); -                return prev; -            } - -            auto operator ==( iterator const& other ) const -> bool { -                return -                    m_pos == other.m_pos && -                    m_stringIndex == other.m_stringIndex && -                    &m_column == &other.m_column; -            } -            auto operator !=( iterator const& other ) const -> bool { -                return !operator==( other ); -            } -        }; -        using const_iterator = iterator; - -        explicit Column( std::string const& text ) { m_strings.push_back( text ); } - -        auto width( size_t newWidth ) -> Column& { -            assert( newWidth > 0 ); -            m_width = newWidth; -            return *this; -        } -        auto indent( size_t newIndent ) -> Column& { -            m_indent = newIndent; -            return *this; -        } -        auto initialIndent( size_t newIndent ) -> Column& { -            m_initialIndent = newIndent; -            return *this; -        } - -        auto width() const -> size_t { return m_width; } -        auto begin() const -> iterator { return iterator( *this ); } -        auto end() const -> iterator { return { *this, m_strings.size() }; } - -        inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { -            bool first = true; -            for( auto line : col ) { -                if( first ) -                    first = false; -                else -                    os << "\n"; -                os <<  line; -            } -            return os; -        } - -        auto operator + ( Column const& other ) -> Columns; - -        auto toString() const -> std::string { -            std::ostringstream oss; -            oss << *this; -            return oss.str(); -        } -    }; - -    class Spacer : public Column { - -    public: -        explicit Spacer( size_t spaceWidth ) : Column( "" ) { -            width( spaceWidth ); -        } -    }; +namespace Catch { +namespace clara { +namespace TextFlow { -    class Columns { -        std::vector<Column> m_columns; +inline auto isWhitespace(char c) -> bool { +	static std::string chars = " \t\n\r"; +	return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { +	static std::string chars = "[({<|"; +	return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { +	static std::string chars = "])}>.,:;*+-=&/\\"; +	return chars.find(c) != std::string::npos; +} -    public: +class Columns; -        class iterator { -            friend Columns; -            struct EndTag {}; +class Column { +	std::vector<std::string> m_strings; +	size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; +	size_t m_indent = 0; +	size_t m_initialIndent = std::string::npos; -            std::vector<Column> const& m_columns; -            std::vector<Column::iterator> m_iterators; -            size_t m_activeIterators; +public: +	class iterator { +		friend Column; + +		Column const& m_column; +		size_t m_stringIndex = 0; +		size_t m_pos = 0; + +		size_t m_len = 0; +		size_t m_end = 0; +		bool m_suffix = false; + +		iterator(Column const& column, size_t stringIndex) +			: m_column(column), +			m_stringIndex(stringIndex) {} + +		auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + +		auto isBoundary(size_t at) const -> bool { +			assert(at > 0); +			assert(at <= line().size()); + +			return at == line().size() || +				(isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || +				isBreakableBefore(line()[at]) || +				isBreakableAfter(line()[at - 1]); +		} + +		void calcLength() { +			assert(m_stringIndex < m_column.m_strings.size()); + +			m_suffix = false; +			auto width = m_column.m_width - indent(); +			m_end = m_pos; +			if (line()[m_pos] == '\n') { +				++m_end; +			} +			while (m_end < line().size() && line()[m_end] != '\n') +				++m_end; + +			if (m_end < m_pos + width) { +				m_len = m_end - m_pos; +			} else { +				size_t len = width; +				while (len > 0 && !isBoundary(m_pos + len)) +					--len; +				while (len > 0 && isWhitespace(line()[m_pos + len - 1])) +					--len; + +				if (len > 0) { +					m_len = len; +				} else { +					m_suffix = true; +					m_len = width - 1; +				} +			} +		} + +		auto indent() const -> size_t { +			auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; +			return initial == std::string::npos ? m_column.m_indent : initial; +		} + +		auto addIndentAndSuffix(std::string const &plain) const -> std::string { +			return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); +		} + +	public: +		using difference_type = std::ptrdiff_t; +		using value_type = std::string; +		using pointer = value_type * ; +		using reference = value_type & ; +		using iterator_category = std::forward_iterator_tag; + +		explicit iterator(Column const& column) : m_column(column) { +			assert(m_column.m_width > m_column.m_indent); +			assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); +			calcLength(); +			if (m_len == 0) +				m_stringIndex++; // Empty string +		} + +		auto operator *() const -> std::string { +			assert(m_stringIndex < m_column.m_strings.size()); +			assert(m_pos <= m_end); +			return addIndentAndSuffix(line().substr(m_pos, m_len)); +		} + +		auto operator ++() -> iterator& { +			m_pos += m_len; +			if (m_pos < line().size() && line()[m_pos] == '\n') +				m_pos += 1; +			else +				while (m_pos < line().size() && isWhitespace(line()[m_pos])) +					++m_pos; + +			if (m_pos == line().size()) { +				m_pos = 0; +				++m_stringIndex; +			} +			if (m_stringIndex < m_column.m_strings.size()) +				calcLength(); +			return *this; +		} +		auto operator ++(int) -> iterator { +			iterator prev(*this); +			operator++(); +			return prev; +		} + +		auto operator ==(iterator const& other) const -> bool { +			return +				m_pos == other.m_pos && +				m_stringIndex == other.m_stringIndex && +				&m_column == &other.m_column; +		} +		auto operator !=(iterator const& other) const -> bool { +			return !operator==(other); +		} +	}; +	using const_iterator = iterator; + +	explicit Column(std::string const& text) { m_strings.push_back(text); } + +	auto width(size_t newWidth) -> Column& { +		assert(newWidth > 0); +		m_width = newWidth; +		return *this; +	} +	auto indent(size_t newIndent) -> Column& { +		m_indent = newIndent; +		return *this; +	} +	auto initialIndent(size_t newIndent) -> Column& { +		m_initialIndent = newIndent; +		return *this; +	} -            iterator( Columns const& columns, EndTag ) -            :   m_columns( columns.m_columns ), -                m_activeIterators( 0 ) -            { -                m_iterators.reserve( m_columns.size() ); +	auto width() const -> size_t { return m_width; } +	auto begin() const -> iterator { return iterator(*this); } +	auto end() const -> iterator { return { *this, m_strings.size() }; } + +	inline friend std::ostream& operator << (std::ostream& os, Column const& col) { +		bool first = true; +		for (auto line : col) { +			if (first) +				first = false; +			else +				os << "\n"; +			os << line; +		} +		return os; +	} -                for( auto const& col : m_columns ) -                    m_iterators.push_back( col.end() ); -            } +	auto operator + (Column const& other)->Columns; -        public: -            explicit iterator( Columns const& columns ) -            :   m_columns( columns.m_columns ), -                m_activeIterators( m_columns.size() ) -            { -                m_iterators.reserve( m_columns.size() ); +	auto toString() const -> std::string { +		std::ostringstream oss; +		oss << *this; +		return oss.str(); +	} +}; -                for( auto const& col : m_columns ) -                    m_iterators.push_back( col.begin() ); -            } +class Spacer : public Column { -            auto operator ==( iterator const& other ) const -> bool { -                return m_iterators == other.m_iterators; -            } -            auto operator !=( iterator const& other ) const -> bool { -                return m_iterators != other.m_iterators; -            } -            auto operator *() const -> std::string { -                std::string row, padding; +public: +	explicit Spacer(size_t spaceWidth) : Column("") { +		width(spaceWidth); +	} +}; -                for( size_t i = 0; i < m_columns.size(); ++i ) { -                    auto width = m_columns[i].width(); -                    if( m_iterators[i] != m_columns[i].end() ) { -                        std::string col = *m_iterators[i]; -                        row += padding + col; -                        if( col.size() < width ) -                            padding = std::string( width - col.size(), ' ' ); -                        else -                            padding = ""; -                    } -                    else { -                        padding += std::string( width, ' ' ); -                    } -                } -                return row; -            } -            auto operator ++() -> iterator& { -                for( size_t i = 0; i < m_columns.size(); ++i ) { -                    if (m_iterators[i] != m_columns[i].end()) -                        ++m_iterators[i]; -                } -                return *this; -            } -            auto operator ++(int) -> iterator { -                iterator prev( *this ); -                operator++(); -                return prev; -            } -        }; -        using const_iterator = iterator; +class Columns { +	std::vector<Column> m_columns; -        auto begin() const -> iterator { return iterator( *this ); } -        auto end() const -> iterator { return { *this, iterator::EndTag() }; } +public: -        auto operator += ( Column const& col ) -> Columns& { -            m_columns.push_back( col ); -            return *this; -        } -        auto operator + ( Column const& col ) -> Columns { -            Columns combined = *this; -            combined += col; -            return combined; -        } +	class iterator { +		friend Columns; +		struct EndTag {}; + +		std::vector<Column> const& m_columns; +		std::vector<Column::iterator> m_iterators; +		size_t m_activeIterators; + +		iterator(Columns const& columns, EndTag) +			: m_columns(columns.m_columns), +			m_activeIterators(0) { +			m_iterators.reserve(m_columns.size()); + +			for (auto const& col : m_columns) +				m_iterators.push_back(col.end()); +		} + +	public: +		using difference_type = std::ptrdiff_t; +		using value_type = std::string; +		using pointer = value_type * ; +		using reference = value_type & ; +		using iterator_category = std::forward_iterator_tag; + +		explicit iterator(Columns const& columns) +			: m_columns(columns.m_columns), +			m_activeIterators(m_columns.size()) { +			m_iterators.reserve(m_columns.size()); + +			for (auto const& col : m_columns) +				m_iterators.push_back(col.begin()); +		} + +		auto operator ==(iterator const& other) const -> bool { +			return m_iterators == other.m_iterators; +		} +		auto operator !=(iterator const& other) const -> bool { +			return m_iterators != other.m_iterators; +		} +		auto operator *() const -> std::string { +			std::string row, padding; + +			for (size_t i = 0; i < m_columns.size(); ++i) { +				auto width = m_columns[i].width(); +				if (m_iterators[i] != m_columns[i].end()) { +					std::string col = *m_iterators[i]; +					row += padding + col; +					if (col.size() < width) +						padding = std::string(width - col.size(), ' '); +					else +						padding = ""; +				} else { +					padding += std::string(width, ' '); +				} +			} +			return row; +		} +		auto operator ++() -> iterator& { +			for (size_t i = 0; i < m_columns.size(); ++i) { +				if (m_iterators[i] != m_columns[i].end()) +					++m_iterators[i]; +			} +			return *this; +		} +		auto operator ++(int) -> iterator { +			iterator prev(*this); +			operator++(); +			return prev; +		} +	}; +	using const_iterator = iterator; + +	auto begin() const -> iterator { return iterator(*this); } +	auto end() const -> iterator { return { *this, iterator::EndTag() }; } + +	auto operator += (Column const& col) -> Columns& { +		m_columns.push_back(col); +		return *this; +	} +	auto operator + (Column const& col) -> Columns { +		Columns combined = *this; +		combined += col; +		return combined; +	} -        inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { +	inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) { + +		bool first = true; +		for (auto line : cols) { +			if (first) +				first = false; +			else +				os << "\n"; +			os << line; +		} +		return os; +	} -            bool first = true; -            for( auto line : cols ) { -                if( first ) -                    first = false; -                else -                    os << "\n"; -                os << line; -            } -            return os; -        } +	auto toString() const -> std::string { +		std::ostringstream oss; +		oss << *this; +		return oss.str(); +	} +}; -        auto toString() const -> std::string { -            std::ostringstream oss; -            oss << *this; -            return oss.str(); -        } -    }; +inline auto Column::operator + (Column const& other) -> Columns { +	Columns cols; +	cols += *this; +	cols += other; +	return cols; +} +} -    inline auto Column::operator + ( Column const& other ) -> Columns { -        Columns cols; -        cols += *this; -        cols += other; -        return cols; -    } -}}} // namespace Catch::clara::TextFlow +} +}  // ----------- end of #include from clara_textflow.hpp -----------  // ........... back in clara.hpp +#include <cctype> +#include <string>  #include <memory>  #include <set>  #include <algorithm> @@ -4714,7 +8827,7 @@ namespace detail {      template<typename ClassT, typename ReturnT, typename ArgT>      struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {          static const bool isValid = true; -        using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;; +        using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;          using ReturnType = ReturnT;      }; @@ -4727,11 +8840,9 @@ namespace detail {          std::vector<std::string> m_args;      public: -        Args( int argc, char *argv[] ) { -            m_exeName = argv[0]; -            for( int i = 1; i < argc; ++i ) -                m_args.push_back( argv[i] ); -        } +        Args( int argc, char const* const* argv ) +            : m_exeName(argv[0]), +              m_args(argv + 1, argv + argc) {}          Args( std::initializer_list<std::string> args )          :   m_exeName( *args.begin() ), @@ -4878,7 +8989,7 @@ namespace detail {              return *this;          } -        ~ResultValueBase() { +        ~ResultValueBase() override {              if( m_type == Ok )                  m_value.~T();          } @@ -4916,16 +9027,14 @@ namespace detail {          auto errorMessage() const -> std::string { return m_errorMessage; }      protected: -        virtual void enforceOk() const { -            // !TBD: If no exceptions, std::terminate here or something -            switch( m_type ) { -                case ResultBase::LogicError: -                    throw std::logic_error( m_errorMessage ); -                case ResultBase::RuntimeError: -                    throw std::runtime_error( m_errorMessage ); -                case ResultBase::Ok: -                    break; -            } +        void enforceOk() const override { + +            // Errors shouldn't reach this point, but if they do +            // the actual error message will be in m_errorMessage +            assert( m_type != ResultBase::LogicError ); +            assert( m_type != ResultBase::RuntimeError ); +            if( m_type != ResultBase::Ok ) +                std::abort();          }          std::string m_errorMessage; // Only populated if resultType is an error @@ -4986,7 +9095,7 @@ namespace detail {      }      inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {          std::string srcLC = source; -        std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } ); +        std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower(c) ); } );          if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")              target = true;          else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") @@ -4995,47 +9104,43 @@ namespace detail {              return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );          return ParserResult::ok( ParseResultType::Matched );      } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE +    template<typename T> +    inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult { +        T temp; +        auto result = convertInto( source, temp ); +        if( result ) +            target = std::move(temp); +        return result; +    } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + +    struct NonCopyable { +        NonCopyable() = default; +        NonCopyable( NonCopyable const & ) = delete; +        NonCopyable( NonCopyable && ) = delete; +        NonCopyable &operator=( NonCopyable const & ) = delete; +        NonCopyable &operator=( NonCopyable && ) = delete; +    }; -    struct BoundRefBase { -        BoundRefBase() = default; -        BoundRefBase( BoundRefBase const & ) = delete; -        BoundRefBase( BoundRefBase && ) = delete; -        BoundRefBase &operator=( BoundRefBase const & ) = delete; -        BoundRefBase &operator=( BoundRefBase && ) = delete; - -        virtual ~BoundRefBase() = default; - -        virtual auto isFlag() const -> bool = 0; +    struct BoundRef : NonCopyable { +        virtual ~BoundRef() = default;          virtual auto isContainer() const -> bool { return false; } -        virtual auto setValue( std::string const &arg ) -> ParserResult = 0; -        virtual auto setFlag( bool flag ) -> ParserResult = 0; +        virtual auto isFlag() const -> bool { return false; }      }; - -    struct BoundValueRefBase : BoundRefBase { -        auto isFlag() const -> bool override { return false; } - -        auto setFlag( bool ) -> ParserResult override { -            return ParserResult::logicError( "Flags can only be set on boolean fields" ); -        } +    struct BoundValueRefBase : BoundRef { +        virtual auto setValue( std::string const &arg ) -> ParserResult = 0;      }; - -    struct BoundFlagRefBase : BoundRefBase { -        auto isFlag() const -> bool override { return true; } - -        auto setValue( std::string const &arg ) -> ParserResult override { -            bool flag; -            auto result = convertInto( arg, flag ); -            if( result ) -                setFlag( flag ); -            return result; -        } +    struct BoundFlagRefBase : BoundRef { +        virtual auto setFlag( bool flag ) -> ParserResult = 0; +        virtual auto isFlag() const -> bool { return true; }      };      template<typename T> -    struct BoundRef : BoundValueRefBase { +    struct BoundValueRef : BoundValueRefBase {          T &m_ref; -        explicit BoundRef( T &ref ) : m_ref( ref ) {} +        explicit BoundValueRef( T &ref ) : m_ref( ref ) {}          auto setValue( std::string const &arg ) -> ParserResult override {              return convertInto( arg, m_ref ); @@ -5043,10 +9148,10 @@ namespace detail {      };      template<typename T> -    struct BoundRef<std::vector<T>> : BoundValueRefBase { +    struct BoundValueRef<std::vector<T>> : BoundValueRefBase {          std::vector<T> &m_ref; -        explicit BoundRef( std::vector<T> &ref ) : m_ref( ref ) {} +        explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}          auto isContainer() const -> bool override { return true; } @@ -5091,12 +9196,12 @@ namespace detail {      template<typename ArgType, typename L>      inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { -        ArgType temp; +        ArgType temp{};          auto result = convertInto( arg, temp );          return !result             ? result             : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp ); -    }; +    }      template<typename L>      struct BoundLambda : BoundValueRefBase { @@ -5145,6 +9250,9 @@ namespace detail {      public:          template<typename T>          auto operator|( T const &other ) const -> Parser; + +		template<typename T> +        auto operator+( T const &other ) const -> Parser;      };      // Common code and state for Args and Opts @@ -5152,16 +9260,16 @@ namespace detail {      class ParserRefImpl : public ComposableParserImpl<DerivedT> {      protected:          Optionality m_optionality = Optionality::Optional; -        std::shared_ptr<BoundRefBase> m_ref; +        std::shared_ptr<BoundRef> m_ref;          std::string m_hint;          std::string m_description; -        explicit ParserRefImpl( std::shared_ptr<BoundRefBase> const &ref ) : m_ref( ref ) {} +        explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}      public:          template<typename T>          ParserRefImpl( T &ref, std::string const &hint ) -        :   m_ref( std::make_shared<BoundRef<T>>( ref ) ), +        :   m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),              m_hint( hint )          {} @@ -5202,10 +9310,10 @@ namespace detail {      class ExeName : public ComposableParserImpl<ExeName> {          std::shared_ptr<std::string> m_name; -        std::shared_ptr<BoundRefBase> m_ref; +        std::shared_ptr<BoundValueRefBase> m_ref;          template<typename LambdaT> -        static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundRefBase> { +        static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {              return std::make_shared<BoundLambda<LambdaT>>( lambda) ;          } @@ -5213,7 +9321,7 @@ namespace detail {          ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}          explicit ExeName( std::string &ref ) : ExeName() { -            m_ref = std::make_shared<BoundRef<std::string>>( ref ); +            m_ref = std::make_shared<BoundValueRef<std::string>>( ref );          }          template<typename LambdaT> @@ -5256,7 +9364,10 @@ namespace detail {              if( token.type != TokenType::Argument )                  return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); -            auto result = m_ref->setValue( remainingTokens->token ); +            assert( !m_ref->isFlag() ); +            auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() ); + +            auto result = valueRef->setValue( remainingTokens->token );              if( !result )                  return InternalParseResult( result );              else @@ -5330,19 +9441,21 @@ namespace detail {                  auto const &token = *remainingTokens;                  if( isMatch(token.token ) ) {                      if( m_ref->isFlag() ) { -                        auto result = m_ref->setFlag( true ); +                        auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() ); +                        auto result = flagRef->setFlag( true );                          if( !result )                              return InternalParseResult( result );                          if( result.value() == ParseResultType::ShortCircuitAll )                              return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );                      } else { +                        auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );                          ++remainingTokens;                          if( !remainingTokens )                              return InternalParseResult::runtimeError( "Expected argument following " + token.token );                          auto const &argToken = *remainingTokens;                          if( argToken.type != TokenType::Argument )                              return InternalParseResult::runtimeError( "Expected argument following " + token.token ); -                        auto result = m_ref->setValue( argToken.token ); +                        auto result = valueRef->setValue( argToken.token );                          if( !result )                              return InternalParseResult( result );                          if( result.value() == ParseResultType::ShortCircuitAll ) @@ -5418,6 +9531,12 @@ namespace detail {              return Parser( *this ) |= other;          } +        // Forward deprecated interface with '+' instead of '|' +        template<typename T> +        auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } +        template<typename T> +        auto operator+( T const &other ) const -> Parser { return operator|( other ); } +          auto getHelpColumns() const -> std::vector<HelpColumns> {              std::vector<HelpColumns> cols;              for (auto const &o : m_options) { @@ -5457,6 +9576,8 @@ namespace detail {              for( auto const &cols : rows )                  optWidth = (std::max)(optWidth, cols.left.size() + 2); +            optWidth = (std::min)(optWidth, consoleWidth/2); +              for( auto const &cols : rows ) {                  auto row =                          TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + @@ -5596,9 +9717,19 @@ namespace Catch {          using namespace clara;          auto const setWarning = [&]( std::string const& warning ) { -                if( warning != "NoAssertions" ) +                auto warningSet = [&]() { +                    if( warning == "NoAssertions" ) +                        return WarnAbout::NoAssertions; + +                    if ( warning == "NoTests" ) +                        return WarnAbout::NoTests; + +                    return WarnAbout::Nothing; +                }(); + +                if (warningSet == WarnAbout::Nothing)                      return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); -                config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions ); +                config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet );                  return ParserResult::ok( ParseResultType::Matched );              };          auto const loadTestNamesFromFile = [&]( std::string const& filename ) { @@ -5612,9 +9743,14 @@ namespace Catch {                      if( !line.empty() && !startsWith( line, '#' ) ) {                          if( !startsWith( line, '"' ) )                              line = '"' + line + '"'; -                        config.testsOrTags.push_back( line + ',' ); +                        config.testsOrTags.push_back( line ); +                        config.testsOrTags.emplace_back( "," );                      }                  } +                //Remove comma in the end +                if(!config.testsOrTags.empty()) +                    config.testsOrTags.erase( config.testsOrTags.end()-1 ); +                  return ParserResult::ok( ParseResultType::Matched );              };          auto const setTestOrder = [&]( std::string const& order ) { @@ -5649,14 +9785,16 @@ namespace Catch {              };          auto const setWaitForKeypress = [&]( std::string const& keypress ) {                  auto keypressLc = toLower( keypress ); -                if( keypressLc == "start" ) +                if (keypressLc == "never") +                    config.waitForKeypress = WaitForKeypress::Never; +                else if( keypressLc == "start" )                      config.waitForKeypress = WaitForKeypress::BeforeStart;                  else if( keypressLc == "exit" )                      config.waitForKeypress = WaitForKeypress::BeforeExit;                  else if( keypressLc == "both" )                      config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;                  else -                    return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); +                    return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );              return ParserResult::ok( ParseResultType::Matched );              };          auto const setVerbosity = [&]( std::string const& verbosity ) { @@ -5671,6 +9809,18 @@ namespace Catch {                  return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" );              return ParserResult::ok( ParseResultType::Matched );          }; +        auto const setReporter = [&]( std::string const& reporter ) { +            IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + +            auto lcReporter = toLower( reporter ); +            auto result = factories.find( lcReporter ); + +            if( factories.end() != result ) +                config.reporterName = lcReporter; +            else +                return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); +            return ParserResult::ok( ParseResultType::Matched ); +        };          auto cli              = ExeName( config.processName ) @@ -5696,7 +9846,7 @@ namespace Catch {              | Opt( config.outputFilename, "filename" )                  ["-o"]["--out"]                  ( "output filename" ) -            | Opt( config.reporterNames, "name" ) +            | Opt( setReporter, "name" )                  ["-r"]["--reporter"]                  ( "reporter to use (defaults to console)" )              | Opt( config.name, "name" ) @@ -5714,6 +9864,9 @@ namespace Catch {              | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )                  ["-d"]["--durations"]                  ( "show test durations" ) +            | Opt( config.minDuration, "seconds" ) +                ["-D"]["--min-duration"] +                ( "show test durations for tests taking at least the given number of seconds" )              | Opt( loadTestNamesFromFile, "filename" )                  ["-f"]["--input-file"]                  ( "load test names to run from a file" ) @@ -5744,13 +9897,24 @@ namespace Catch {              | Opt( config.libIdentify )                  ["--libidentify"]                  ( "report name and version according to libidentify standard" ) -            | Opt( setWaitForKeypress, "start|exit|both" ) +            | Opt( setWaitForKeypress, "never|start|exit|both" )                  ["--wait-for-keypress"]                  ( "waits for a keypress before exiting" ) -            | Opt( config.benchmarkResolutionMultiple, "multiplier" ) -                ["--benchmark-resolution-multiple"] -                ( "multiple of clock resolution to run benchmarks" ) - +            | Opt( config.benchmarkSamples, "samples" ) +                ["--benchmark-samples"] +                ( "number of samples to collect (default: 100)" ) +            | Opt( config.benchmarkResamples, "resamples" ) +                ["--benchmark-resamples"] +                ( "number of resamples for the bootstrap (default: 100000)" ) +            | Opt( config.benchmarkConfidenceInterval, "confidence interval" ) +                ["--benchmark-confidence-interval"] +                ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" ) +            | Opt( config.benchmarkNoAnalysis ) +                ["--benchmark-no-analysis"] +                ( "perform only measurements; do not perform any analysis" ) +            | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" ) +                ["--benchmark-warmup-time"] +                ( "amount of time in milliseconds spent on warming up each test (default: 100)" )              | Arg( config.testsOrTags, "test name|pattern|tags" )                  ( "which test or tests to use" ); @@ -5766,18 +9930,13 @@ namespace Catch {  namespace Catch { -    SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) noexcept -    :   file( _file ), -        line( _line ) -    {} -    bool SourceLineInfo::empty() const noexcept { -        return file[0] == '\0'; -    }      bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {          return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);      }      bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { -        return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); +        // We can assume that the same file will usually have the same pointer. +        // Thus, if the pointers are the same, there is no point in calling the strcmp +        return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));      }      std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { @@ -5789,10 +9948,6 @@ namespace Catch {          return os;      } -    bool isTrue( bool value ){ return value; } -    bool alwaysTrue() { return true; } -    bool alwaysFalse() { return false; } -      std::string StreamEndStop::operator+() const {          return std::string();      } @@ -5810,12 +9965,25 @@ namespace Catch {      :   m_data( data ),          m_stream( openStream() )      { -        if( !data.testsOrTags.empty() ) { -            TestSpecParser parser( ITagAliasRegistry::get() ); -            for( auto const& testOrTags : data.testsOrTags ) -                parser.parse( testOrTags ); -            m_testSpec = parser.testSpec(); +        // We need to trim filter specs to avoid trouble with superfluous +        // whitespace (esp. important for bdd macros, as those are manually +        // aligned with whitespace). + +        for (auto& elem : m_data.testsOrTags) { +            elem = trim(elem);          } +        for (auto& elem : m_data.sectionsToRun) { +            elem = trim(elem); +        } + +        TestSpecParser parser(ITagAliasRegistry::get()); +        if (!m_data.testsOrTags.empty()) { +            m_hasTestFilters = true; +            for (auto const& testOrTags : m_data.testsOrTags) { +                parser.parse(testOrTags); +            } +        } +        m_testSpec = parser.testSpec();      }      std::string const& Config::getFilename() const { @@ -5828,11 +9996,13 @@ namespace Catch {      bool Config::listReporters() const      { return m_data.listReporters; }      std::string Config::getProcessName() const { return m_data.processName; } +    std::string const& Config::getReporterName() const { return m_data.reporterName; } -    std::vector<std::string> const& Config::getReporterNames() const { return m_data.reporterNames; } +    std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }      std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }      TestSpec const& Config::testSpec() const { return m_testSpec; } +    bool Config::hasTestFilters() const { return m_hasTestFilters; }      bool Config::showHelp() const { return m_data.showHelp; } @@ -5841,28 +10011,26 @@ namespace Catch {      std::ostream& Config::stream() const               { return m_stream->stream(); }      std::string Config::name() const                   { return m_data.name.empty() ? m_data.processName : m_data.name; }      bool Config::includeSuccessfulResults() const      { return m_data.showSuccessfulTests; } -    bool Config::warnAboutMissingAssertions() const    { return m_data.warnings & WarnAbout::NoAssertions; } +    bool Config::warnAboutMissingAssertions() const    { return !!(m_data.warnings & WarnAbout::NoAssertions); } +    bool Config::warnAboutNoTests() const              { return !!(m_data.warnings & WarnAbout::NoTests); }      ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } +    double Config::minDuration() const                 { return m_data.minDuration; }      RunTests::InWhatOrder Config::runOrder() const     { return m_data.runOrder; }      unsigned int Config::rngSeed() const               { return m_data.rngSeed; } -    int Config::benchmarkResolutionMultiple() const    { return m_data.benchmarkResolutionMultiple; }      UseColour::YesOrNo Config::useColour() const       { return m_data.useColour; }      bool Config::shouldDebugBreak() const              { return m_data.shouldDebugBreak; }      int Config::abortAfter() const                     { return m_data.abortAfter; }      bool Config::showInvisibles() const                { return m_data.showInvisibles; }      Verbosity Config::verbosity() const                { return m_data.verbosity; } +    bool Config::benchmarkNoAnalysis() const                      { return m_data.benchmarkNoAnalysis; } +    int Config::benchmarkSamples() const                          { return m_data.benchmarkSamples; } +    double Config::benchmarkConfidenceInterval() const            { return m_data.benchmarkConfidenceInterval; } +    unsigned int Config::benchmarkResamples() const               { return m_data.benchmarkResamples; } +    std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } +      IStream const* Config::openStream() { -        if( m_data.outputFilename.empty() ) -            return new CoutStream(); -        else if( m_data.outputFilename[0] == '%' ) { -            if( m_data.outputFilename == "%debug" ) -                return new DebugOutStream(); -            else -                CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" ); -        } -        else -            return new FileStream( m_data.outputFilename ); +        return Catch::makeStream(m_data.outputFilename);      }  } // end namespace Catch @@ -5889,36 +10057,8 @@ namespace Catch {  }  // end catch_errno_guard.h -// start catch_windows_h_proxy.h - - -#if defined(CATCH_PLATFORM_WINDOWS) - -#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -#  define CATCH_DEFINED_NOMINMAX -#  define NOMINMAX -#endif -#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -#  define CATCH_DEFINED_WIN32_LEAN_AND_MEAN -#  define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __AFXDLL -#include <AfxWin.h> -#else -#include <windows.h> -#endif - -#ifdef CATCH_DEFINED_NOMINMAX -#  undef NOMINMAX -#endif -#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN -#  undef WIN32_LEAN_AND_MEAN -#endif - -#endif // defined(CATCH_PLATFORM_WINDOWS) +#include <sstream> -// end catch_windows_h_proxy.h  namespace Catch {      namespace { @@ -5928,7 +10068,7 @@ namespace Catch {          };          struct NoColourImpl : IColourImpl { -            void use( Colour::Code ) {} +            void use( Colour::Code ) override {}              static IColourImpl* instance() {                  static NoColourImpl s_instance; @@ -5962,7 +10102,7 @@ namespace {              originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );          } -        virtual void use( Colour::Code _colourCode ) override { +        void use( Colour::Code _colourCode ) override {              switch( _colourCode ) {                  case Colour::None:      return setTextAttribute( originalForegroundAttributes );                  case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); @@ -5977,8 +10117,12 @@ namespace {                  case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );                  case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );                  case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); +                case Colour::BrightYellow:  return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );                  case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + +                default: +                    CATCH_ERROR( "Unknown colour requested" );              }          } @@ -6021,7 +10165,7 @@ namespace {      // https://github.com/philsquared/Catch/pull/131      class PosixColourImpl : public IColourImpl {      public: -        virtual void use( Colour::Code _colourCode ) override { +        void use( Colour::Code _colourCode ) override {              switch( _colourCode ) {                  case Colour::None:                  case Colour::White:     return setColour( "[0m" ); @@ -6036,8 +10180,10 @@ namespace {                  case Colour::BrightRed:     return setColour( "[1;31m" );                  case Colour::BrightGreen:   return setColour( "[1;32m" );                  case Colour::BrightWhite:   return setColour( "[1;37m" ); +                case Colour::BrightYellow:  return setColour( "[1;33m" );                  case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); +                default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );              }          }          static IColourImpl* instance() { @@ -6047,16 +10193,22 @@ namespace {      private:          void setColour( const char* _escapeCode ) { -            Catch::cout() << '\033' << _escapeCode; +            getCurrentContext().getConfig()->stream() +                << '\033' << _escapeCode;          }      };      bool useColourOnPlatform() {          return -#ifdef CATCH_PLATFORM_MAC +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)              !isDebuggerActive() &&  #endif -            isatty(STDOUT_FILENO); +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) +            isatty(STDOUT_FILENO) +#else +            false +#endif +            ;      }      IColourImpl* platformColourInstance() {          ErrnoGuard guard; @@ -6089,13 +10241,13 @@ namespace Catch {  namespace Catch {      Colour::Colour( Code _colourCode ) { use( _colourCode ); } -    Colour::Colour( Colour&& rhs ) noexcept { -        m_moved = rhs.m_moved; -        rhs.m_moved = true; +    Colour::Colour( Colour&& other ) noexcept { +        m_moved = other.m_moved; +        other.m_moved = true;      } -    Colour& Colour::operator=( Colour&& rhs ) noexcept { -        m_moved = rhs.m_moved; -        rhs.m_moved  = true; +    Colour& Colour::operator=( Colour&& other ) noexcept { +        m_moved = other.m_moved; +        other.m_moved  = true;          return *this;      } @@ -6103,7 +10255,13 @@ namespace Catch {      void Colour::use( Code _colourCode ) {          static IColourImpl* impl = platformColourInstance(); -        impl->use( _colourCode ); +        // Strictly speaking, this cannot possibly happen. +        // However, under some conditions it does happen (see #1626), +        // and this change is small enough that we can let practicality +        // triumph over purity in this case. +        if (impl != nullptr) { +            impl->use( _colourCode ); +        }      }      std::ostream& operator << ( std::ostream& os, Colour const& ) { @@ -6124,27 +10282,27 @@ namespace Catch {      class Context : public IMutableContext, NonCopyable {      public: // IContext -        virtual IResultCapture* getResultCapture() override { +        IResultCapture* getResultCapture() override {              return m_resultCapture;          } -        virtual IRunner* getRunner() override { +        IRunner* getRunner() override {              return m_runner;          } -        virtual IConfigPtr getConfig() const override { +        IConfigPtr const& getConfig() const override {              return m_config;          } -        virtual ~Context() override; +        ~Context() override;      public: // IMutableContext -        virtual void setResultCapture( IResultCapture* resultCapture ) override { +        void setResultCapture( IResultCapture* resultCapture ) override {              m_resultCapture = resultCapture;          } -        virtual void setRunner( IRunner* runner ) override { +        void setRunner( IRunner* runner ) override {              m_runner = runner;          } -        virtual void setConfig( IConfigPtr const& config ) override { +        void setConfig( IConfigPtr const& config ) override {              m_config = config;          } @@ -6156,25 +10314,26 @@ namespace Catch {          IResultCapture* m_resultCapture = nullptr;      }; -    namespace { -        Context* currentContext = nullptr; -    } -    IMutableContext& getCurrentMutableContext() { -        if( !currentContext ) -            currentContext = new Context(); -        return *currentContext; -    } -    IContext& getCurrentContext() { -        return getCurrentMutableContext(); +    IMutableContext *IMutableContext::currentContext = nullptr; + +    void IMutableContext::createContext() +    { +        currentContext = new Context();      }      void cleanUpContext() { -        delete currentContext; -        currentContext = nullptr; +        delete IMutableContext::currentContext; +        IMutableContext::currentContext = nullptr;      }      IContext::~IContext() = default;      IMutableContext::~IMutableContext() = default;      Context::~Context() = default; + +    SimplePcg32& rng() { +        static SimplePcg32 s_rng; +        return s_rng; +    } +  }  // end catch_context.cpp  // start catch_debug_console.cpp @@ -6188,41 +10347,58 @@ namespace Catch {  }  // end catch_debug_console.h -#ifdef CATCH_PLATFORM_WINDOWS +#if defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#include <android/log.h> + +    namespace Catch { +        void writeToDebugConsole( std::string const& text ) { +            __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() ); +        } +    } + +#elif defined(CATCH_PLATFORM_WINDOWS)      namespace Catch {          void writeToDebugConsole( std::string const& text ) {              ::OutputDebugStringA( text.c_str() );          }      } +  #else +      namespace Catch {          void writeToDebugConsole( std::string const& text ) {              // !TBD: Need a version for Mac/ XCode and other IDEs              Catch::cout() << text;          }      } +  #endif // Platform  // end catch_debug_console.cpp  // start catch_debugger.cpp -#ifdef CATCH_PLATFORM_MAC +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) -    #include <assert.h> -    #include <stdbool.h> -    #include <sys/types.h> -    #include <unistd.h> -    #include <sys/sysctl.h> +#  include <cassert> +#  include <sys/types.h> +#  include <unistd.h> +#  include <cstddef> +#  include <ostream> -    namespace Catch { +#ifdef __apple_build_version__ +    // These headers will only compile with AppleClang (XCode) +    // For other compilers (Clang, GCC, ... ) we need to exclude them +#  include <sys/sysctl.h> +#endif +    namespace Catch { +        #ifdef __apple_build_version__          // 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(){ -              int                 mib[4];              struct kinfo_proc   info;              std::size_t         size; @@ -6252,6 +10428,12 @@ namespace Catch {              return ( (info.kp_proc.p_flag & P_TRACED) != 0 );          } +        #else +        bool isDebuggerActive() { +            // We need to find another way to determine this for non-appleclang compilers on macOS +            return false; +        } +        #endif      } // namespace Catch  #elif defined(CATCH_PLATFORM_LINUX) @@ -6320,6 +10502,129 @@ namespace Catch {      }  }  // end catch_decomposer.cpp +// start catch_enforce.cpp + +#include <stdexcept> + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) +    [[noreturn]] +    void throw_exception(std::exception const& e) { +        Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" +                      << "The message was: " << e.what() << '\n'; +        std::terminate(); +    } +#endif + +    [[noreturn]] +    void throw_logic_error(std::string const& msg) { +        throw_exception(std::logic_error(msg)); +    } + +    [[noreturn]] +    void throw_domain_error(std::string const& msg) { +        throw_exception(std::domain_error(msg)); +    } + +    [[noreturn]] +    void throw_runtime_error(std::string const& msg) { +        throw_exception(std::runtime_error(msg)); +    } + +} // namespace Catch; +// end catch_enforce.cpp +// start catch_enum_values_registry.cpp +// start catch_enum_values_registry.h + +#include <vector> +#include <memory> + +namespace Catch { + +    namespace Detail { + +        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ); + +        class EnumValuesRegistry : public IMutableEnumValuesRegistry { + +            std::vector<std::unique_ptr<EnumInfo>> m_enumInfos; + +            EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override; +        }; + +        std::vector<StringRef> parseEnums( StringRef enums ); + +    } // Detail + +} // Catch + +// end catch_enum_values_registry.h + +#include <map> +#include <cassert> + +namespace Catch { + +    IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} + +    namespace Detail { + +        namespace { +            // Extracts the actual name part of an enum instance +            // In other words, it returns the Blue part of Bikeshed::Colour::Blue +            StringRef extractInstanceName(StringRef enumInstance) { +                // Find last occurence of ":" +                size_t name_start = enumInstance.size(); +                while (name_start > 0 && enumInstance[name_start - 1] != ':') { +                    --name_start; +                } +                return enumInstance.substr(name_start, enumInstance.size() - name_start); +            } +        } + +        std::vector<StringRef> parseEnums( StringRef enums ) { +            auto enumValues = splitStringRef( enums, ',' ); +            std::vector<StringRef> parsed; +            parsed.reserve( enumValues.size() ); +            for( auto const& enumValue : enumValues ) { +                parsed.push_back(trim(extractInstanceName(enumValue))); +            } +            return parsed; +        } + +        EnumInfo::~EnumInfo() {} + +        StringRef EnumInfo::lookup( int value ) const { +            for( auto const& valueToName : m_values ) { +                if( valueToName.first == value ) +                    return valueToName.second; +            } +            return "{** unexpected enum value **}"_sr; +        } + +        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { +            std::unique_ptr<EnumInfo> enumInfo( new EnumInfo ); +            enumInfo->m_name = enumName; +            enumInfo->m_values.reserve( values.size() ); + +            const auto valueNames = Catch::Detail::parseEnums( allValueNames ); +            assert( valueNames.size() == values.size() ); +            std::size_t i = 0; +            for( auto value : values ) +                enumInfo->m_values.emplace_back(value, valueNames[i++]); + +            return enumInfo; +        } + +        EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { +            m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); +            return *m_enumInfos.back(); +        } + +    } // Detail +} // Catch + +// end catch_enum_values_registry.cpp  // start catch_errno_guard.cpp  #include <cerrno> @@ -6343,7 +10648,7 @@ namespace Catch {      public:          ~ExceptionTranslatorRegistry();          virtual void registerTranslator( const IExceptionTranslator* translator ); -        virtual std::string translateActiveException() const override; +        std::string translateActiveException() const override;          std::string tryTranslators() const;      private: @@ -6365,6 +10670,7 @@ namespace Catch {          m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) );      } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)      std::string ExceptionTranslatorRegistry::translateActiveException() const {          try {  #ifdef __OBJC__ @@ -6376,6 +10682,17 @@ namespace Catch {                  return Catch::Detail::stringify( [exception description] );              }  #else +            // Compiling a mixed mode project with MSVC means that CLR +            // exceptions will be caught in (...) as well. However, these +            // do not fill-in std::current_exception and thus lead to crash +            // when attempting rethrow. +            // /EHa switch also causes structured exceptions to be caught +            // here, but they fill-in current_exception properly, so +            // at worst the output should be a little weird, instead of +            // causing a crash. +            if (std::current_exception() == nullptr) { +                return "Non C++ exception. Possibly a CLR exception."; +            }              return tryTranslators();  #endif          } @@ -6397,87 +10714,34 @@ namespace Catch {      }      std::string ExceptionTranslatorRegistry::tryTranslators() const { -        if( m_translators.empty() ) +        if (m_translators.empty()) {              std::rethrow_exception(std::current_exception()); -        else -            return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); +        } else { +            return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); +        }      } -} -// end catch_exception_translator_registry.cpp -// start catch_fatal_condition.cpp - -// start catch_fatal_condition.h - -#include <string> - -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// - -#  if !defined ( CATCH_CONFIG_WINDOWS_SEH ) -namespace Catch { -    struct FatalConditionHandler { -        void reset(); -    }; -} - -#  else // CATCH_CONFIG_WINDOWS_SEH is defined - -namespace Catch { - -    struct FatalConditionHandler { - -        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); -        FatalConditionHandler(); -        static void reset(); -        ~FatalConditionHandler(); - -    private: -        static bool isSet; -        static ULONG guaranteeSize; -        static PVOID exceptionHandlerHandle; -    }; - -} // namespace Catch - -#  endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// +#else // ^^ Exceptions are enabled // Exceptions are disabled vv +    std::string ExceptionTranslatorRegistry::translateActiveException() const { +        CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +    } -#  if !defined(CATCH_CONFIG_POSIX_SIGNALS) +    std::string ExceptionTranslatorRegistry::tryTranslators() const { +        CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +    } +#endif -namespace Catch { -    struct FatalConditionHandler { -        void reset(); -    };  } +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp -#  else // CATCH_CONFIG_POSIX_SIGNALS is defined - -#include <signal.h> - -namespace Catch { - -    struct FatalConditionHandler { - -        static bool isSet; -        static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; -        static stack_t oldSigStack; -        static char altStackMem[]; - -        static void handleSignal( int sig ); - -        FatalConditionHandler(); -        ~FatalConditionHandler(); -        static void reset(); -    }; - -} // namespace Catch - -#  endif // CATCH_CONFIG_POSIX_SIGNALS +#if defined(__GNUC__) +#    pragma GCC diagnostic push +#    pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif -#endif // not Windows +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) -// end catch_fatal_condition.h  namespace {      // Report the error condition      void reportFatal( char const * const message ) { @@ -6485,15 +10749,9 @@ namespace {      }  } -#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// - -#  if !defined ( CATCH_CONFIG_WINDOWS_SEH ) - -namespace Catch { -    void FatalConditionHandler::reset() {} -} +#endif // signals/SEH handling -#  else // CATCH_CONFIG_WINDOWS_SEH is defined +#if defined( CATCH_CONFIG_WINDOWS_SEH )  namespace Catch {      struct SignalDefs { DWORD id; const char* name; }; @@ -6502,10 +10760,10 @@ namespace Catch {      // Windows can easily distinguish between SO and SigSegV,      // but SigInt, SigTerm, etc are handled differently.      static 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" },      };      LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { @@ -6533,7 +10791,6 @@ namespace Catch {      void FatalConditionHandler::reset() {          if (isSet) { -            // Unregister handler and restore the old guarantee              RemoveVectoredExceptionHandler(exceptionHandlerHandle);              SetThreadStackGuarantee(&guaranteeSize);              exceptionHandlerHandle = nullptr; @@ -6551,19 +10808,7 @@ PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;  } // namespace Catch -#  endif // CATCH_CONFIG_WINDOWS_SEH - -#else // Not Windows - assumed to be POSIX compatible ////////////////////////// - -#  if !defined(CATCH_CONFIG_POSIX_SIGNALS) - -namespace Catch { -    void FatalConditionHandler::reset() {} -} - -#  else // CATCH_CONFIG_POSIX_SIGNALS is defined - -#include <signal.h> +#elif defined( CATCH_CONFIG_POSIX_SIGNALS )  namespace Catch { @@ -6571,6 +10816,11 @@ namespace Catch {          int id;          const char* name;      }; + +    // 32kb for the alternate stack seems to be sufficient. However, this value +    // is experimentally determined, so that's not guaranteed. +    static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; +      static SignalDefs signalDefs[] = {          { SIGINT,  "SIGINT - Terminal interrupt signal" },          { SIGILL,  "SIGILL - Illegal instruction signal" }, @@ -6597,7 +10847,7 @@ namespace Catch {          isSet = true;          stack_t sigStack;          sigStack.ss_sp = altStackMem; -        sigStack.ss_size = SIGSTKSZ; +        sigStack.ss_size = sigStackSize;          sigStack.ss_flags = 0;          sigaltstack(&sigStack, &oldSigStack);          struct sigaction sa = { }; @@ -6628,14 +10878,46 @@ namespace Catch {      bool FatalConditionHandler::isSet = false;      struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};      stack_t FatalConditionHandler::oldSigStack = {}; -    char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; +    char FatalConditionHandler::altStackMem[sigStackSize] = {};  } // namespace Catch -#  endif // CATCH_CONFIG_POSIX_SIGNALS +#else + +namespace Catch { +    void FatalConditionHandler::reset() {} +} -#endif // not Windows +#endif // signals/SEH handling + +#if defined(__GNUC__) +#    pragma GCC diagnostic pop +#endif  // end catch_fatal_condition.cpp +// start catch_generators.cpp + +#include <limits> +#include <set> + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +const char* GeneratorException::what() const noexcept { +    return m_msg; +} + +namespace Generators { + +    GeneratorUntypedBase::~GeneratorUntypedBase() {} + +    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { +        return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); +    } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp  // start catch_interfaces_capture.cpp  namespace Catch { @@ -6664,16 +10946,21 @@ namespace Catch {  // end catch_interfaces_registry_hub.cpp  // start catch_interfaces_reporter.cpp -// start catch_reporter_multi.h +// start catch_reporter_listening.h  namespace Catch { -    class MultipleReporters : public IStreamingReporter { +    class ListeningReporter : public IStreamingReporter {          using Reporters = std::vector<IStreamingReporterPtr>; -        Reporters m_reporters; +        Reporters m_listeners; +        IStreamingReporterPtr m_reporter = nullptr; +        ReporterPreferences m_preferences;      public: -        void add( IStreamingReporterPtr&& reporter ); +        ListeningReporter(); + +        void addListener( IStreamingReporterPtr&& listener ); +        void addReporter( IStreamingReporterPtr&& reporter );      public: // IStreamingReporter @@ -6681,10 +10968,16 @@ namespace Catch {          void noMatchingTestCases( std::string const& spec ) override; +        void reportInvalidArguments(std::string const&arg) override; +          static std::set<Verbosity> getSupportedVerbosities(); +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +        void benchmarkPreparing(std::string const& name) override;          void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; -        void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; +        void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override; +        void benchmarkFailed(std::string const&) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING          void testRunStarting( TestRunInfo const& testRunInfo ) override;          void testGroupStarting( GroupInfo const& groupInfo ) override; @@ -6706,7 +10999,7 @@ namespace Catch {  } // end namespace Catch -// end catch_reporter_multi.h +// end catch_reporter_listening.h  namespace Catch {      ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) @@ -6807,27 +11100,6 @@ namespace Catch {      IReporterFactory::~IReporterFactory() = default;      IReporterRegistry::~IReporterRegistry() = default; -    void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { - -        if( !existingReporter ) { -            existingReporter = std::move( additionalReporter ); -            return; -        } - -        MultipleReporters* multi = nullptr; - -        if( existingReporter->isMulti() ) { -            multi = static_cast<MultipleReporters*>( existingReporter.get() ); -        } -        else { -            auto newMulti = std::unique_ptr<MultipleReporters>( new MultipleReporters ); -            newMulti->add( std::move( existingReporter ) ); -            multi = newMulti.get(); -            existingReporter = std::move( newMulti ); -        } -        multi->add( std::move( additionalReporter ) ); -    } -  } // end namespace Catch  // end catch_interfaces_reporter.cpp  // start catch_interfaces_runner.cpp @@ -6845,28 +11117,31 @@ namespace Catch {  // end catch_interfaces_testcase.cpp  // start catch_leak_detector.cpp -namespace Catch { -  #ifdef CATCH_CONFIG_WINDOWS_CRTDBG  #include <crtdbg.h> -	LeakDetector::LeakDetector() { -		int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); -		flag |= _CRTDBG_LEAK_CHECK_DF; -		flag |= _CRTDBG_ALLOC_MEM_DF; -		_CrtSetDbgFlag(flag); -		_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); -		_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); -		// Change this to leaking allocation's number to break there -		_CrtSetBreakAlloc(-1); -	} +namespace Catch { + +    LeakDetector::LeakDetector() { +        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); +        flag |= _CRTDBG_LEAK_CHECK_DF; +        flag |= _CRTDBG_ALLOC_MEM_DF; +        _CrtSetDbgFlag(flag); +        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); +        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); +        // Change this to leaking allocation's number to break there +        _CrtSetBreakAlloc(-1); +    } +}  #else -    LeakDetector::LeakDetector(){} +    Catch::LeakDetector::LeakDetector() {}  #endif +Catch::LeakDetector::~LeakDetector() { +    Catch::cleanUp();  }  // end catch_leak_detector.cpp  // start catch_list.cpp @@ -6891,9 +11166,9 @@ namespace Catch {      std::size_t listTags( Config const& config ); -    std::size_t listReporters( Config const& /*config*/ ); +    std::size_t listReporters(); -    Option<std::size_t> list( Config const& config ); +    Option<std::size_t> list( std::shared_ptr<Config> const& config );  } // end namespace Catch @@ -6912,12 +11187,11 @@ namespace Catch {  namespace Catch {      std::size_t listTests( Config const& config ) { -        TestSpec testSpec = config.testSpec(); -        if( config.testSpec().hasFilters() ) +        TestSpec const& testSpec = config.testSpec(); +        if( config.hasTestFilters() )              Catch::cout() << "Matching test cases:\n";          else {              Catch::cout() << "All available test cases:\n"; -            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();          }          auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); @@ -6939,7 +11213,7 @@ namespace Catch {                  Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n";          } -        if( !config.testSpec().hasFilters() ) +        if( !config.hasTestFilters() )              Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl;          else              Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; @@ -6947,9 +11221,7 @@ namespace Catch {      }      std::size_t listTestsNamesOnly( Config const& config ) { -        TestSpec testSpec = config.testSpec(); -        if( !config.testSpec().hasFilters() ) -            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); +        TestSpec const& testSpec = config.testSpec();          std::size_t matchedTests = 0;          std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );          for( auto const& testCaseInfo : matchedTestCases ) { @@ -6971,19 +11243,27 @@ namespace Catch {      }      std::string TagInfo::all() const { -        std::string out; -        for( auto const& spelling : spellings ) -            out += "[" + spelling + "]"; +        size_t size = 0; +        for (auto const& spelling : spellings) { +            // Add 2 for the brackes +            size += spelling.size() + 2; +        } + +        std::string out; out.reserve(size); +        for (auto const& spelling : spellings) { +            out += '['; +            out += spelling; +            out += ']'; +        }          return out;      }      std::size_t listTags( Config const& config ) { -        TestSpec testSpec = config.testSpec(); -        if( config.testSpec().hasFilters() ) +        TestSpec const& testSpec = config.testSpec(); +        if( config.hasTestFilters() )              Catch::cout() << "Tags for matching test cases:\n";          else {              Catch::cout() << "All available tags:\n"; -            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();          }          std::map<std::string, TagInfo> tagCounts; @@ -7000,19 +11280,20 @@ namespace Catch {          }          for( auto const& tagCount : tagCounts ) { -            std::ostringstream oss; -            oss << "  " << std::setw(2) << tagCount.second.count << "  "; +            ReusableStringStream rss; +            rss << "  " << std::setw(2) << tagCount.second.count << "  "; +            auto str = rss.str();              auto wrapper = Column( tagCount.second.all() )                                                      .initialIndent( 0 ) -                                                    .indent( oss.str().size() ) +                                                    .indent( str.size() )                                                      .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); -            Catch::cout() << oss.str() << wrapper << '\n'; +            Catch::cout() << str << wrapper << '\n';          }          Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;          return tagCounts.size();      } -    std::size_t listReporters( Config const& /*config*/ ) { +    std::size_t listReporters() {          Catch::cout() << "Available reporters:\n";          IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();          std::size_t maxNameLen = 0; @@ -7034,16 +11315,17 @@ namespace Catch {          return factories.size();      } -    Option<std::size_t> list( Config const& config ) { +    Option<std::size_t> list( std::shared_ptr<Config> const& config ) {          Option<std::size_t> listedCount; -        if( config.listTests() ) -            listedCount = listedCount.valueOr(0) + listTests( config ); -        if( config.listTestNamesOnly() ) -            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); -        if( config.listTags() ) -            listedCount = listedCount.valueOr(0) + listTags( config ); -        if( config.listReporters() ) -            listedCount = listedCount.valueOr(0) + listReporters( config ); +        getCurrentMutableContext().setConfig( config ); +        if( config->listTests() ) +            listedCount = listedCount.valueOr(0) + listTests( *config ); +        if( config->listTestNamesOnly() ) +            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config ); +        if( config->listTags() ) +            listedCount = listedCount.valueOr(0) + listTags( *config ); +        if( config->listReporters() ) +            listedCount = listedCount.valueOr(0) + listReporters();          return listedCount;      } @@ -7071,8 +11353,290 @@ using Matchers::Impl::MatcherBase;  } // namespace Catch  // end catch_matchers.cpp +// start catch_matchers_exception.cpp + +namespace Catch { +namespace Matchers { +namespace Exception { + +bool ExceptionMessageMatcher::match(std::exception const& ex) const { +    return ex.what() == m_message; +} + +std::string ExceptionMessageMatcher::describe() const { +    return "exception message matches \"" + m_message + "\""; +} + +} +Exception::ExceptionMessageMatcher Message(std::string const& message) { +    return Exception::ExceptionMessageMatcher(message); +} + +// namespace Exception +} // namespace Matchers +} // namespace Catch +// end catch_matchers_exception.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { +    bool isnan(float f); +    bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include <string> + +namespace Catch { +    template <typename T> +    std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) +        return std::to_string(t); +#else +        ReusableStringStream rss; +        rss << t; +        return rss.str(); +#endif +    } +} // end namespace Catch + +// end catch_to_string.hpp +#include <algorithm> +#include <cmath> +#include <cstdlib> +#include <cstdint> +#include <cstring> +#include <sstream> +#include <type_traits> +#include <iomanip> +#include <limits> + +namespace Catch { +namespace { + +    int32_t convert(float f) { +        static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); +        int32_t i; +        std::memcpy(&i, &f, sizeof(f)); +        return i; +    } + +    int64_t convert(double d) { +        static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); +        int64_t i; +        std::memcpy(&i, &d, sizeof(d)); +        return i; +    } + +    template <typename FP> +    bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { +        // Comparison with NaN should always be false. +        // This way we can rule it out before getting into the ugly details +        if (Catch::isnan(lhs) || Catch::isnan(rhs)) { +            return false; +        } + +        auto lc = convert(lhs); +        auto rc = convert(rhs); + +        if ((lc < 0) != (rc < 0)) { +            // Potentially we can have +0 and -0 +            return lhs == rhs; +        } + +        auto ulpDiff = std::abs(lc - rc); +        return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff; +    } + +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + +    float nextafter(float x, float y) { +        return ::nextafterf(x, y); +    } + +    double nextafter(double x, double y) { +        return ::nextafter(x, y); +    } + +#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ + +template <typename FP> +FP step(FP start, FP direction, uint64_t steps) { +    for (uint64_t i = 0; i < steps; ++i) { +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +        start = Catch::nextafter(start, direction); +#else +        start = std::nextafter(start, direction); +#endif +    } +    return start; +} + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { +    return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +template <typename FloatingPoint> +void write(std::ostream& out, FloatingPoint num) { +    out << std::scientific +        << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1) +        << num; +} + +} // end anonymous namespace + +namespace Matchers { +namespace Floating { + +    enum class FloatingPointKind : uint8_t { +        Float, +        Double +    }; + +    WithinAbsMatcher::WithinAbsMatcher(double target, double margin) +        :m_target{ target }, m_margin{ margin } { +        CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' +            << " Margin has to be non-negative."); +    } + +    // Performs equivalent check of std::fabs(lhs - rhs) <= margin +    // But without the subtraction to allow for INFINITY in comparison +    bool WithinAbsMatcher::match(double const& matchee) const { +        return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); +    } + +    std::string WithinAbsMatcher::describe() const { +        return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); +    } + +    WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType) +        :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { +        CATCH_ENFORCE(m_type == FloatingPointKind::Double +                   || m_ulps < (std::numeric_limits<uint32_t>::max)(), +            "Provided ULP is impossibly large for a float comparison."); +    } + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + +    bool WithinUlpsMatcher::match(double const& matchee) const { +        switch (m_type) { +        case FloatingPointKind::Float: +            return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps); +        case FloatingPointKind::Double: +            return almostEqualUlps<double>(matchee, m_target, m_ulps); +        default: +            CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); +        } +    } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +    std::string WithinUlpsMatcher::describe() const { +        std::stringstream ret; + +        ret << "is within " << m_ulps << " ULPs of "; + +        if (m_type == FloatingPointKind::Float) { +            write(ret, static_cast<float>(m_target)); +            ret << 'f'; +        } else { +            write(ret, m_target); +        } + +        ret << " (["; +        if (m_type == FloatingPointKind::Double) { +            write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps)); +            ret << ", "; +            write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps)); +        } else { +            // We have to cast INFINITY to float because of MinGW, see #1782 +            write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps)); +            ret << ", "; +            write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps)); +        } +        ret << "])"; + +        return ret.str(); +    } + +    WithinRelMatcher::WithinRelMatcher(double target, double epsilon): +        m_target(target), +        m_epsilon(epsilon){ +        CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon <  0 does not make sense."); +        CATCH_ENFORCE(m_epsilon  < 1., "Relative comparison with epsilon >= 1 does not make sense."); +    } + +    bool WithinRelMatcher::match(double const& matchee) const { +        const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); +        return marginComparison(matchee, m_target, +                                std::isinf(relMargin)? 0 : relMargin); +    } + +    std::string WithinRelMatcher::describe() const { +        Catch::ReusableStringStream sstr; +        sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; +        return sstr.str(); +    } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { +    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { +    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { +    return Floating::WithinAbsMatcher(target, margin); +} + +Floating::WithinRelMatcher WithinRel(double target, double eps) { +    return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(double target) { +    return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100); +} + +Floating::WithinRelMatcher WithinRel(float target, float eps) { +    return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(float target) { +    return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { +    if (desc.empty()) { +        return "matches undescribed predicate"; +    } else { +        return "matches predicate: \"" + desc + '"'; +    } +} +// end catch_matchers_generic.cpp  // start catch_matchers_string.cpp +#include <regex> +  namespace Catch {  namespace Matchers { @@ -7134,6 +11698,21 @@ namespace Matchers {              return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );          } +        RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + +        bool RegexMatcher::match(std::string const& matchee) const { +            auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway +            if (m_caseSensitivity == CaseSensitive::Choice::No) { +                flags |= std::regex::icase; +            } +            auto reg = std::regex(m_regex, flags); +            return std::regex_match(matchee, reg); +        } + +        std::string RegexMatcher::describe() const { +            return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); +        } +      } // namespace StdString      StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { @@ -7149,14 +11728,28 @@ namespace Matchers {          return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );      } +    StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { +        return StdString::RegexMatcher(regex, caseSensitivity); +    } +  } // namespace Matchers  } // namespace Catch  // end catch_matchers_string.cpp  // start catch_message.cpp +// start catch_uncaught_exceptions.h +  namespace Catch { +    bool uncaught_exceptions(); +} // end namespace Catch -    MessageInfo::MessageInfo(   std::string const& _macroName, +// end catch_uncaught_exceptions.h +#include <cassert> +#include <stack> + +namespace Catch { + +    MessageInfo::MessageInfo(   StringRef const& _macroName,                                  SourceLineInfo const& _lineInfo,                                  ResultWas::OfType _type )      :   macroName( _macroName ), @@ -7178,7 +11771,7 @@ namespace Catch {      //////////////////////////////////////////////////////////////////////////// -    Catch::MessageBuilder::MessageBuilder( std::string const& macroName, +    Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,                                             SourceLineInfo const& lineInfo,                                             ResultWas::OfType type )          :m_info(macroName, lineInfo, type) {} @@ -7186,72 +11779,428 @@ namespace Catch {      ////////////////////////////////////////////////////////////////////////////      ScopedMessage::ScopedMessage( MessageBuilder const& builder ) -    : m_info( builder.m_info ) +    : m_info( builder.m_info ), m_moved()      {          m_info.message = builder.m_stream.str();          getResultCapture().pushScopedMessage( m_info );      } +    ScopedMessage::ScopedMessage( ScopedMessage&& old ) +    : m_info( old.m_info ), m_moved() +    { +        old.m_moved = true; +    } +      ScopedMessage::~ScopedMessage() { -        if ( !std::uncaught_exception() ){ +        if ( !uncaught_exceptions() && !m_moved ){              getResultCapture().popScopedMessage(m_info);          }      } +    Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { +        auto trimmed = [&] (size_t start, size_t end) { +            while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) { +                ++start; +            } +            while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) { +                --end; +            } +            return names.substr(start, end - start + 1); +        }; +        auto skipq = [&] (size_t start, char quote) { +            for (auto i = start + 1; i < names.size() ; ++i) { +                if (names[i] == quote) +                    return i; +                if (names[i] == '\\') +                    ++i; +            } +            CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); +        }; + +        size_t start = 0; +        std::stack<char> openings; +        for (size_t pos = 0; pos < names.size(); ++pos) { +            char c = names[pos]; +            switch (c) { +            case '[': +            case '{': +            case '(': +            // It is basically impossible to disambiguate between +            // comparison and start of template args in this context +//            case '<': +                openings.push(c); +                break; +            case ']': +            case '}': +            case ')': +//           case '>': +                openings.pop(); +                break; +            case '"': +            case '\'': +                pos = skipq(pos, c); +                break; +            case ',': +                if (start != pos && openings.empty()) { +                    m_messages.emplace_back(macroName, lineInfo, resultType); +                    m_messages.back().message = static_cast<std::string>(trimmed(start, pos)); +                    m_messages.back().message += " := "; +                    start = pos; +                } +            } +        } +        assert(openings.empty() && "Mismatched openings"); +        m_messages.emplace_back(macroName, lineInfo, resultType); +        m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1)); +        m_messages.back().message += " := "; +    } +    Capturer::~Capturer() { +        if ( !uncaught_exceptions() ){ +            assert( m_captured == m_messages.size() ); +            for( size_t i = 0; i < m_captured; ++i  ) +                m_resultCapture.popScopedMessage( m_messages[i] ); +        } +    } + +    void Capturer::captureValue( size_t index, std::string const& value ) { +        assert( index < m_messages.size() ); +        m_messages[index].message += value; +        m_resultCapture.pushScopedMessage( m_messages[index] ); +        m_captured++; +    } +  } // end namespace Catch  // end catch_message.cpp -// start catch_random_number_generator.cpp +// start catch_output_redirect.cpp -// start catch_random_number_generator.h +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H -#include <algorithm> +#include <cstdio> +#include <iosfwd> +#include <string>  namespace Catch { -    struct IConfig; +    class RedirectedStream { +        std::ostream& m_originalStream; +        std::ostream& m_redirectionStream; +        std::streambuf* m_prevBuf; -    void seedRng( IConfig const& config ); +    public: +        RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); +        ~RedirectedStream(); +    }; -    unsigned int rngSeed(); +    class RedirectedStdOut { +        ReusableStringStream m_rss; +        RedirectedStream m_cout; +    public: +        RedirectedStdOut(); +        auto str() const -> std::string; +    }; -    struct RandomNumberGenerator { -        using result_type = unsigned int; +    // StdErr has two constituent streams in C++, std::cerr and std::clog +    // This means that we need to redirect 2 streams into 1 to keep proper +    // order of writes +    class RedirectedStdErr { +        ReusableStringStream m_rss; +        RedirectedStream m_cerr; +        RedirectedStream m_clog; +    public: +        RedirectedStdErr(); +        auto str() const -> std::string; +    }; -        static constexpr result_type (min)() { return 0; } -        static constexpr result_type (max)() { return 1000000; } +    class RedirectedStreams { +    public: +        RedirectedStreams(RedirectedStreams const&) = delete; +        RedirectedStreams& operator=(RedirectedStreams const&) = delete; +        RedirectedStreams(RedirectedStreams&&) = delete; +        RedirectedStreams& operator=(RedirectedStreams&&) = delete; -        result_type operator()( result_type n ) const; -        result_type operator()() const; +        RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); +        ~RedirectedStreams(); +    private: +        std::string& m_redirectedCout; +        std::string& m_redirectedCerr; +        RedirectedStdOut m_redirectedStdOut; +        RedirectedStdErr m_redirectedStdErr; +    }; -        template<typename V> -        static void shuffle( V& vector ) { -            RandomNumberGenerator rng; -            std::shuffle( vector.begin(), vector.end(), rng ); -        } +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +    // Windows's implementation of std::tmpfile is terrible (it tries +    // to create a file inside system folder, thus requiring elevated +    // privileges for the binary), so we have to use tmpnam(_s) and +    // create the file ourselves there. +    class TempFile { +    public: +        TempFile(TempFile const&) = delete; +        TempFile& operator=(TempFile const&) = delete; +        TempFile(TempFile&&) = delete; +        TempFile& operator=(TempFile&&) = delete; + +        TempFile(); +        ~TempFile(); + +        std::FILE* getFile(); +        std::string getContents(); + +    private: +        std::FILE* m_file = nullptr; +    #if defined(_MSC_VER) +        char m_buffer[L_tmpnam] = { 0 }; +    #endif      }; -} +    class OutputRedirect { +    public: +        OutputRedirect(OutputRedirect const&) = delete; +        OutputRedirect& operator=(OutputRedirect const&) = delete; +        OutputRedirect(OutputRedirect&&) = delete; +        OutputRedirect& operator=(OutputRedirect&&) = delete; -// end catch_random_number_generator.h -#include <cstdlib> +        OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); +        ~OutputRedirect(); + +    private: +        int m_originalStdout = -1; +        int m_originalStderr = -1; +        TempFile m_stdoutFile; +        TempFile m_stderrFile; +        std::string& m_stdoutDest; +        std::string& m_stderrDest; +    }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include <cstdio> +#include <cstring> +#include <fstream> +#include <sstream> +#include <stdexcept> + +#if defined(CATCH_CONFIG_NEW_CAPTURE) +    #if defined(_MSC_VER) +    #include <io.h>      //_dup and _dup2 +    #define dup _dup +    #define dup2 _dup2 +    #define fileno _fileno +    #else +    #include <unistd.h>  // dup and dup2 +    #endif +#endif  namespace Catch { -    void seedRng( IConfig const& config ) { -        if( config.rngSeed() != 0 ) -            std::srand( config.rngSeed() ); +    RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) +    :   m_originalStream( originalStream ), +        m_redirectionStream( redirectionStream ), +        m_prevBuf( m_originalStream.rdbuf() ) +    { +        m_originalStream.rdbuf( m_redirectionStream.rdbuf() );      } -    unsigned int rngSeed() { -        return getCurrentContext().getConfig()->rngSeed(); + +    RedirectedStream::~RedirectedStream() { +        m_originalStream.rdbuf( m_prevBuf ); +    } + +    RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} +    auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + +    RedirectedStdErr::RedirectedStdErr() +    :   m_cerr( Catch::cerr(), m_rss.get() ), +        m_clog( Catch::clog(), m_rss.get() ) +    {} +    auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + +    RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) +    :   m_redirectedCout(redirectedCout), +        m_redirectedCerr(redirectedCerr) +    {} + +    RedirectedStreams::~RedirectedStreams() { +        m_redirectedCout += m_redirectedStdOut.str(); +        m_redirectedCerr += m_redirectedStdErr.str(); +    } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) +    TempFile::TempFile() { +        if (tmpnam_s(m_buffer)) { +            CATCH_RUNTIME_ERROR("Could not get a temp filename"); +        } +        if (fopen_s(&m_file, m_buffer, "w+")) { +            char buffer[100]; +            if (strerror_s(buffer, errno)) { +                CATCH_RUNTIME_ERROR("Could not translate errno to a string"); +            } +            CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); +        } +    } +#else +    TempFile::TempFile() { +        m_file = std::tmpfile(); +        if (!m_file) { +            CATCH_RUNTIME_ERROR("Could not create a temp file."); +        } +    } + +#endif + +    TempFile::~TempFile() { +         // TBD: What to do about errors here? +         std::fclose(m_file); +         // We manually create the file on Windows only, on Linux +         // it will be autodeleted +#if defined(_MSC_VER) +         std::remove(m_buffer); +#endif +    } + +    FILE* TempFile::getFile() { +        return m_file; +    } + +    std::string TempFile::getContents() { +        std::stringstream sstr; +        char buffer[100] = {}; +        std::rewind(m_file); +        while (std::fgets(buffer, sizeof(buffer), m_file)) { +            sstr << buffer; +        } +        return sstr.str(); +    } + +    OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : +        m_originalStdout(dup(1)), +        m_originalStderr(dup(2)), +        m_stdoutDest(stdout_dest), +        m_stderrDest(stderr_dest) { +        dup2(fileno(m_stdoutFile.getFile()), 1); +        dup2(fileno(m_stderrFile.getFile()), 2); +    } + +    OutputRedirect::~OutputRedirect() { +        Catch::cout() << std::flush; +        fflush(stdout); +        // Since we support overriding these streams, we flush cerr +        // even though std::cerr is unbuffered +        Catch::cerr() << std::flush; +        Catch::clog() << std::flush; +        fflush(stderr); + +        dup2(m_originalStdout, 1); +        dup2(m_originalStderr, 2); + +        m_stdoutDest += m_stdoutFile.getContents(); +        m_stderrDest += m_stderrFile.getContents(); +    } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) +    #if defined(_MSC_VER) +    #undef dup +    #undef dup2 +    #undef fileno +    #endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include <cmath> + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) +    bool isnan(float f) { +        return std::isnan(f); +    } +    bool isnan(double d) { +        return std::isnan(d); +    } +#else +    // For now we only use this for embarcadero +    bool isnan(float f) { +        return std::_isnan(f); +    } +    bool isnan(double d) { +        return std::_isnan(d); +    } +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + +namespace { + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4146) // we negate uint32 during the rotate +#endif +        // Safe rotr implementation thanks to John Regehr +        uint32_t rotate_right(uint32_t val, uint32_t count) { +            const uint32_t mask = 31; +            count &= mask; +            return (val >> count) | (val << (-count & mask)); +        } + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +} + +    SimplePcg32::SimplePcg32(result_type seed_) { +        seed(seed_); +    } + +    void SimplePcg32::seed(result_type seed_) { +        m_state = 0; +        (*this)(); +        m_state += seed_; +        (*this)(); +    } + +    void SimplePcg32::discard(uint64_t skip) { +        // We could implement this to run in O(log n) steps, but this +        // should suffice for our use case. +        for (uint64_t s = 0; s < skip; ++s) { +            static_cast<void>((*this)()); +        }      } -    RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { -        return std::rand() % n; +    SimplePcg32::result_type SimplePcg32::operator()() { +        // prepare the output value +        const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u); +        const auto output = rotate_right(xorshifted, m_state >> 59u); + +        // advance state +        m_state = m_state * 6364136223846793005ULL + s_inc; + +        return output;      } -    RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { -        return std::rand() % (max)(); + +    bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { +        return lhs.m_state == rhs.m_state;      } +    bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { +        return lhs.m_state != rhs.m_state; +    }  }  // end catch_random_number_generator.cpp  // start catch_registry_hub.cpp @@ -7269,6 +12218,8 @@ namespace Catch {      struct IConfig;      std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ); + +    bool isThrowSafe( TestCase const& testCase, IConfig const& config );      bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );      void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ); @@ -7303,7 +12254,7 @@ namespace Catch {          void invoke() const override;      }; -    std::string extractClassName( std::string const& classOrQualifiedMethodName ); +    std::string extractClassName( StringRef const& classOrQualifiedMethodName );      /////////////////////////////////////////////////////////////////////////// @@ -7381,16 +12332,53 @@ namespace Catch {  namespace Catch {      class StartupExceptionRegistry { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)      public:          void add(std::exception_ptr const& exception) noexcept;          std::vector<std::exception_ptr> const& getExceptions() const noexcept;      private:          std::vector<std::exception_ptr> m_exceptions; +#endif      };  } // end namespace Catch  // end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + +    struct ISingleton { +        virtual ~ISingleton(); +    }; + +    void addSingleton( ISingleton* singleton ); +    void cleanupSingletons(); + +    template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT> +    class Singleton : SingletonImplT, public ISingleton { + +        static auto getInternal() -> Singleton* { +            static Singleton* s_instance = nullptr; +            if( !s_instance ) { +                s_instance = new Singleton; +                addSingleton( s_instance ); +            } +            return s_instance; +        } + +    public: +        static auto get() -> InterfaceT const& { +            return *getInternal(); +        } +        static auto getMutable() -> MutableInterfaceT& { +            return *getInternal(); +        } +    }; + +} // namespace Catch + +// end catch_singletons.hpp  namespace Catch {      namespace { @@ -7406,7 +12394,7 @@ namespace Catch {              ITestCaseRegistry const& getTestCaseRegistry() const override {                  return m_testCaseRegistry;              } -            IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() override { +            IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {                  return m_exceptionTranslatorRegistry;              }              ITagAliasRegistry const& getTagAliasRegistry() const override { @@ -7433,7 +12421,14 @@ namespace Catch {                  m_tagAliasRegistry.add( alias, tag, lineInfo );              }              void registerStartupException() noexcept override { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)                  m_exceptionRegistry.add(std::current_exception()); +#else +                CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif +            } +            IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { +                return m_enumValuesRegistry;              }          private: @@ -7442,26 +12437,20 @@ namespace Catch {              ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;              TagAliasRegistry m_tagAliasRegistry;              StartupExceptionRegistry m_exceptionRegistry; +            Detail::EnumValuesRegistry m_enumValuesRegistry;          }; - -        // Single, global, instance -        RegistryHub*& getTheRegistryHub() { -            static RegistryHub* theRegistryHub = nullptr; -            if( !theRegistryHub ) -                theRegistryHub = new RegistryHub(); -            return theRegistryHub; -        }      } -    IRegistryHub& getRegistryHub() { -        return *getTheRegistryHub(); +    using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>; + +    IRegistryHub const& getRegistryHub() { +        return RegistryHubSingleton::get();      }      IMutableRegistryHub& getMutableRegistryHub() { -        return *getTheRegistryHub(); +        return RegistryHubSingleton::getMutable();      }      void cleanUp() { -        delete getTheRegistryHub(); -        getTheRegistryHub() = nullptr; +        cleanupSingletons();          cleanUpContext();      }      std::string translateActiveException() { @@ -7515,164 +12504,146 @@ namespace Catch {      }      bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } -    bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; }      bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }  } // end namespace Catch  // end catch_result_type.cpp  // start catch_run_context.cpp -// start catch_run_context.h -#include <string> +#include <cassert> +#include <algorithm> +#include <sstream>  namespace Catch { -    struct IMutableContext; - -    class StreamRedirect { - -    public: -        StreamRedirect(std::ostream& stream, std::string& targetString); - -        ~StreamRedirect(); - -    private: -        std::ostream& m_stream; -        std::streambuf* m_prevBuf; -        std::ostringstream m_oss; -        std::string& m_targetString; -    }; - -    // StdErr has two constituent streams in C++, std::cerr and std::clog -    // This means that we need to redirect 2 streams into 1 to keep proper -    // order of writes and cannot use StreamRedirect on its own -    class StdErrRedirect { -    public: -        StdErrRedirect(std::string& targetString); -        ~StdErrRedirect(); -    private: -        std::streambuf* m_cerrBuf; -        std::streambuf* m_clogBuf; -        std::ostringstream m_oss; -        std::string& m_targetString; -    }; - -    /////////////////////////////////////////////////////////////////////////// - -    class RunContext : public IResultCapture, public IRunner { - -    public: -        RunContext( RunContext const& ) = delete; -        RunContext& operator =( RunContext const& ) = delete; - -        explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter); - -        virtual ~RunContext(); - -        void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount); -        void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount); - -        Totals runTest(TestCase const& testCase); - -        IConfigPtr config() const; -        IStreamingReporter& reporter() const; - -    private: // IResultCapture - -        void assertionStarting(AssertionInfo const& info) override; -        void assertionEnded(AssertionResult const& result) override; - -        bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; -        bool testForMissingAssertions(Counts& assertions); - -        void sectionEnded(SectionEndInfo const& endInfo) override; -        void sectionEndedEarly(SectionEndInfo const& endInfo) override; - -        void benchmarkStarting( BenchmarkInfo const& info ) override; -        void benchmarkEnded( BenchmarkStats const& stats ) override; - -        void pushScopedMessage(MessageInfo const& message) override; -        void popScopedMessage(MessageInfo const& message) override; - -        std::string getCurrentTestName() const override; - -        const AssertionResult* getLastResult() const override; - -        void exceptionEarlyReported() override; - -        void handleFatalErrorCondition( StringRef message ) override; - -        bool lastAssertionPassed() override; - -        void assertionPassed() override; - -        void assertionRun() override; - -    public: -        // !TBD We need to do this another way! -        bool aborting() const override; - -    private: - -        void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr); -        void invokeActiveTestCase(); - -    private: - -        void handleUnfinishedSections(); +    namespace Generators { +        struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { +            GeneratorBasePtr m_generator; -        TestRunInfo m_runInfo; -        IMutableContext& m_context; -        TestCase const* m_activeTestCase = nullptr; -        ITracker* m_testCaseTracker; -        Option<AssertionResult> m_lastResult; - -        IConfigPtr m_config; -        Totals m_totals; -        IStreamingReporterPtr m_reporter; -        std::vector<MessageInfo> m_messages; -        AssertionInfo m_lastAssertionInfo; -        std::vector<SectionEndInfo> m_unfinishedSections; -        std::vector<ITracker*> m_activeSections; -        TrackerContext m_trackerContext; -        std::size_t m_prevPassed = 0; -        bool m_shouldReportUnexpected = true; -    }; - -    IResultCapture& getResultCapture(); - -} // end namespace Catch - -// end catch_run_context.h +            GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) +            :   TrackerBase( nameAndLocation, ctx, parent ) +            {} +            ~GeneratorTracker(); + +            static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { +                std::shared_ptr<GeneratorTracker> tracker; + +                ITracker& currentTracker = ctx.currentTracker(); +                // Under specific circumstances, the generator we want +                // to acquire is also the current tracker. If this is +                // the case, we have to avoid looking through current +                // tracker's children, and instead return the current +                // tracker. +                // A case where this check is important is e.g. +                //     for (int i = 0; i < 5; ++i) { +                //         int n = GENERATE(1, 2); +                //     } +                // +                // without it, the code above creates 5 nested generators. +                if (currentTracker.nameAndLocation() == nameAndLocation) { +                    auto thisTracker = currentTracker.parent().findChild(nameAndLocation); +                    assert(thisTracker); +                    assert(thisTracker->isGeneratorTracker()); +                    tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker); +                } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { +                    assert( childTracker ); +                    assert( childTracker->isGeneratorTracker() ); +                    tracker = std::static_pointer_cast<GeneratorTracker>( childTracker ); +                } else { +                    tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, ¤tTracker ); +                    currentTracker.addChild( tracker ); +                } -#include <cassert> -#include <algorithm> +                if( !tracker->isComplete() ) { +                    tracker->open(); +                } -namespace Catch { +                return *tracker; +            } -    StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString) -        : m_stream(stream), -        m_prevBuf(stream.rdbuf()), -        m_targetString(targetString) { -        stream.rdbuf(m_oss.rdbuf()); -    } +            // TrackerBase interface +            bool isGeneratorTracker() const override { return true; } +            auto hasGenerator() const -> bool override { +                return !!m_generator; +            } +            void close() override { +                TrackerBase::close(); +                // If a generator has a child (it is followed by a section) +                // and none of its children have started, then we must wait +                // until later to start consuming its values. +                // This catches cases where `GENERATE` is placed between two +                // `SECTION`s. +                // **The check for m_children.empty cannot be removed**. +                // doing so would break `GENERATE` _not_ followed by `SECTION`s. +                const bool should_wait_for_child = [&]() { +                    // No children -> nobody to wait for +                    if ( m_children.empty() ) { +                        return false; +                    } +                    // If at least one child started executing, don't wait +                    if ( std::find_if( +                             m_children.begin(), +                             m_children.end(), +                             []( TestCaseTracking::ITrackerPtr tracker ) { +                                 return tracker->hasStarted(); +                             } ) != m_children.end() ) { +                        return false; +                    } -    StreamRedirect::~StreamRedirect() { -        m_targetString += m_oss.str(); -        m_stream.rdbuf(m_prevBuf); -    } +                    // No children have started. We need to check if they _can_ +                    // start, and thus we should wait for them, or they cannot +                    // start (due to filters), and we shouldn't wait for them +                    auto* parent = m_parent; +                    // This is safe: there is always at least one section +                    // tracker in a test case tracking tree +                    while ( !parent->isSectionTracker() ) { +                        parent = &( parent->parent() ); +                    } +                    assert( parent && +                            "Missing root (test case) level section" ); + +                    auto const& parentSection = +                        static_cast<SectionTracker&>( *parent ); +                    auto const& filters = parentSection.getFilters(); +                    // No filters -> no restrictions on running sections +                    if ( filters.empty() ) { +                        return true; +                    } -    StdErrRedirect::StdErrRedirect(std::string & targetString) -        :m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), -        m_targetString(targetString) { -        cerr().rdbuf(m_oss.rdbuf()); -        clog().rdbuf(m_oss.rdbuf()); -    } +                    for ( auto const& child : m_children ) { +                        if ( child->isSectionTracker() && +                             std::find( filters.begin(), +                                        filters.end(), +                                        static_cast<SectionTracker&>( *child ) +                                            .trimmedName() ) != +                                 filters.end() ) { +                            return true; +                        } +                    } +                    return false; +                }(); + +                // This check is a bit tricky, because m_generator->next() +                // has a side-effect, where it consumes generator's current +                // value, but we do not want to invoke the side-effect if +                // this generator is still waiting for any child to start. +                if ( should_wait_for_child || +                     ( m_runState == CompletedSuccessfully && +                       m_generator->next() ) ) { +                    m_children.clear(); +                    m_runState = Executing; +                } +            } -    StdErrRedirect::~StdErrRedirect() { -        m_targetString += m_oss.str(); -        cerr().rdbuf(m_cerrBuf); -        clog().rdbuf(m_clogBuf); +            // IGeneratorTracker interface +            auto getGenerator() const -> GeneratorBasePtr const& override { +                return m_generator; +            } +            void setGenerator( GeneratorBasePtr&& generator ) override { +                m_generator = std::move( generator ); +            } +        }; +        GeneratorTracker::~GeneratorTracker() {}      }      RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) @@ -7680,7 +12651,8 @@ namespace Catch {          m_context(getCurrentMutableContext()),          m_config(_config),          m_reporter(std::move(reporter)), -        m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal } +        m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, +        m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )      {          m_context.setRunner(this);          m_context.setConfig(m_config); @@ -7706,7 +12678,7 @@ namespace Catch {          std::string redirectedCout;          std::string redirectedCerr; -        TestCaseInfo testInfo = testCase.getTestCaseInfo(); +        auto const& testInfo = testCase.getTestCaseInfo();          m_reporter->testCaseStarting(testInfo); @@ -7748,27 +12720,36 @@ namespace Catch {          return *m_reporter;      } -    void RunContext::assertionStarting(AssertionInfo const& info) { -        m_reporter->assertionStarting( info ); -    }      void RunContext::assertionEnded(AssertionResult const & result) {          if (result.getResultType() == ResultWas::Ok) {              m_totals.assertions.passed++; +            m_lastAssertionPassed = true;          } else if (!result.isOk()) { +            m_lastAssertionPassed = false;              if( m_activeTestCase->getTestCaseInfo().okToFail() )                  m_totals.assertions.failedButOk++;              else                  m_totals.assertions.failed++;          } +        else { +            m_lastAssertionPassed = true; +        }          // We have no use for the return value (whether messages should be cleared), because messages were made scoped          // and should be let to clear themselves out.          static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); +        if (result.getResultType() != ResultWas::Warning) +            m_messageScopes.clear(); +          // Reset working state -        m_lastAssertionInfo = { "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition }; +        resetAssertionInfo();          m_lastResult = result;      } +    void RunContext::resetAssertionInfo() { +        m_lastAssertionInfo.macroName = StringRef(); +        m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; +    }      bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {          ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); @@ -7784,6 +12765,13 @@ namespace Catch {          return true;      } +    auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { +        using namespace Generators; +        GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, +                                                              TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) ); +        m_lastAssertionInfo.lineInfo = lineInfo; +        return tracker; +    }      bool RunContext::testForMissingAssertions(Counts& assertions) {          if (assertions.total() != 0) @@ -7808,6 +12796,7 @@ namespace Catch {          m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));          m_messages.clear(); +        m_messageScopes.clear();      }      void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { @@ -7819,12 +12808,21 @@ namespace Catch {          m_unfinishedSections.push_back(endInfo);      } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +    void RunContext::benchmarkPreparing(std::string const& name) { +        m_reporter->benchmarkPreparing(name); +    }      void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {          m_reporter->benchmarkStarting( info );      } -    void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { +    void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {          m_reporter->benchmarkEnded( stats );      } +    void RunContext::benchmarkFailed(std::string const & error) { +        m_reporter->benchmarkFailed(error); +    } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING      void RunContext::pushScopedMessage(MessageInfo const & message) {          m_messages.push_back(message); @@ -7834,6 +12832,10 @@ namespace Catch {          m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());      } +    void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { +        m_messageScopes.emplace_back( builder ); +    } +      std::string RunContext::getCurrentTestName() const {          return m_activeTestCase              ? m_activeTestCase->getTestCaseInfo().name @@ -7855,16 +12857,16 @@ namespace Catch {          // Don't rebuild the result -- the stringification itself can cause more fatal errors          // Instead, fake a result data.          AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); -        tempResult.message = message; +        tempResult.message = static_cast<std::string>(message);          AssertionResult result(m_lastAssertionInfo, tempResult); -        getResultCapture().assertionEnded(result); +        assertionEnded(result);          handleUnfinishedSections();          // Recreate section for test case (as we will lose the one that was in scope)          auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); -        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); +        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);          Counts assertions;          assertions.failed = 1; @@ -7887,64 +12889,67 @@ namespace Catch {      }      bool RunContext::lastAssertionPassed() { -         return m_totals.assertions.passed == (m_prevPassed + 1); +         return m_lastAssertionPassed;      }      void RunContext::assertionPassed() { +        m_lastAssertionPassed = true;          ++m_totals.assertions.passed; -        m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; -        m_lastAssertionInfo.macroName = ""; -    } - -    void RunContext::assertionRun() { -        m_prevPassed = m_totals.assertions.passed; +        resetAssertionInfo(); +        m_messageScopes.clear();      }      bool RunContext::aborting() const { -        return m_totals.assertions.failed == static_cast<std::size_t>(m_config->abortAfter()); +        return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());      }      void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {          auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); -        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); +        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);          m_reporter->sectionStarting(testCaseSection);          Counts prevAssertions = m_totals.assertions;          double duration = 0;          m_shouldReportUnexpected = true; -        try { -            m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; +        m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; -            seedRng(*m_config); +        seedRng(*m_config); -            Timer timer; -            timer.start(); +        Timer timer; +        CATCH_TRY {              if (m_reporter->getPreferences().shouldRedirectStdOut) { -                StreamRedirect coutRedir(cout(), redirectedCout); -                StdErrRedirect errRedir(redirectedCerr); +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +                RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + +                timer.start();                  invokeActiveTestCase(); +#else +                OutputRedirect r(redirectedCout, redirectedCerr); +                timer.start(); +                invokeActiveTestCase(); +#endif              } else { +                timer.start();                  invokeActiveTestCase();              }              duration = timer.getElapsedSeconds(); -        } catch (TestFailureException&) { +        } CATCH_CATCH_ANON (TestFailureException&) {              // This just means the test was aborted due to failure -        } catch (...) { +        } CATCH_CATCH_ALL {              // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions              // are reported without translation at the point of origin. -            if (m_shouldReportUnexpected) { -                AssertionHandler -                    ( m_lastAssertionInfo.macroName, -                      m_lastAssertionInfo.lineInfo, -                      m_lastAssertionInfo.capturedExpression, -                      m_lastAssertionInfo.resultDisposition ).useActiveException(); +            if( m_shouldReportUnexpected ) { +                AssertionReaction dummyReaction; +                handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );              }          } +        Counts assertions = m_totals.assertions - prevAssertions; +        bool missingAssertions = testForMissingAssertions(assertions); +          m_testCaseTracker->close();          handleUnfinishedSections();          m_messages.clear(); +        m_messageScopes.clear(); -        Counts assertions = m_totals.assertions - prevAssertions; -        bool missingAssertions = testForMissingAssertions(assertions);          SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);          m_reporter->sectionEnded(testCaseSectionStats);      } @@ -7966,12 +12971,130 @@ namespace Catch {          m_unfinishedSections.clear();      } +    void RunContext::handleExpr( +        AssertionInfo const& info, +        ITransientExpression const& expr, +        AssertionReaction& reaction +    ) { +        m_reporter->assertionStarting( info ); + +        bool negated = isFalseTest( info.resultDisposition ); +        bool result = expr.getResult() != negated; + +        if( result ) { +            if (!m_includeSuccessfulResults) { +                assertionPassed(); +            } +            else { +                reportExpr(info, ResultWas::Ok, &expr, negated); +            } +        } +        else { +            reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); +            populateReaction( reaction ); +        } +    } +    void RunContext::reportExpr( +            AssertionInfo const &info, +            ResultWas::OfType resultType, +            ITransientExpression const *expr, +            bool negated ) { + +        m_lastAssertionInfo = info; +        AssertionResultData data( resultType, LazyExpression( negated ) ); + +        AssertionResult assertionResult{ info, data }; +        assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + +        assertionEnded( assertionResult ); +    } + +    void RunContext::handleMessage( +            AssertionInfo const& info, +            ResultWas::OfType resultType, +            StringRef const& message, +            AssertionReaction& reaction +    ) { +        m_reporter->assertionStarting( info ); + +        m_lastAssertionInfo = info; + +        AssertionResultData data( resultType, LazyExpression( false ) ); +        data.message = static_cast<std::string>(message); +        AssertionResult assertionResult{ m_lastAssertionInfo, data }; +        assertionEnded( assertionResult ); +        if( !assertionResult.isOk() ) +            populateReaction( reaction ); +    } +    void RunContext::handleUnexpectedExceptionNotThrown( +            AssertionInfo const& info, +            AssertionReaction& reaction +    ) { +        handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); +    } + +    void RunContext::handleUnexpectedInflightException( +            AssertionInfo const& info, +            std::string const& message, +            AssertionReaction& reaction +    ) { +        m_lastAssertionInfo = info; + +        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); +        data.message = message; +        AssertionResult assertionResult{ info, data }; +        assertionEnded( assertionResult ); +        populateReaction( reaction ); +    } + +    void RunContext::populateReaction( AssertionReaction& reaction ) { +        reaction.shouldDebugBreak = m_config->shouldDebugBreak(); +        reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); +    } + +    void RunContext::handleIncomplete( +            AssertionInfo const& info +    ) { +        m_lastAssertionInfo = info; + +        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); +        data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; +        AssertionResult assertionResult{ info, data }; +        assertionEnded( assertionResult ); +    } +    void RunContext::handleNonExpr( +            AssertionInfo const &info, +            ResultWas::OfType resultType, +            AssertionReaction &reaction +    ) { +        m_lastAssertionInfo = info; + +        AssertionResultData data( resultType, LazyExpression( false ) ); +        AssertionResult assertionResult{ info, data }; +        assertionEnded( assertionResult ); + +        if( !assertionResult.isOk() ) +            populateReaction( reaction ); +    } +      IResultCapture& getResultCapture() {          if (auto* capture = getCurrentContext().getResultCapture())              return *capture;          else              CATCH_INTERNAL_ERROR("No result capture instance");      } + +    void seedRng(IConfig const& config) { +        if (config.rngSeed() != 0) { +            std::srand(config.rngSeed()); +            rng().seed(config.rngSeed()); +        } +    } + +    unsigned int rngSeed() { +        return getCurrentContext().getConfig()->rngSeed(); +    } +  }  // end catch_run_context.cpp  // start catch_section.cpp @@ -7985,22 +13108,15 @@ namespace Catch {          m_timer.start();      } -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 -#endif      Section::~Section() {          if( m_sectionIncluded ) { -            SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); -            if( std::uncaught_exception() ) +            SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; +            if( uncaught_exceptions() )                  getResultCapture().sectionEndedEarly( endInfo );              else                  getResultCapture().sectionEnded( endInfo );          }      } -#if defined(_MSC_VER) -#pragma warning(pop) -#endif      // This indicates whether the section should be executed or not      Section::operator bool() const { @@ -8015,17 +13131,11 @@ namespace Catch {      SectionInfo::SectionInfo          (   SourceLineInfo const& _lineInfo, -            std::string const& _name, -            std::string const& _description ) +            std::string const& _name )      :   name( _name ), -        description( _description ),          lineInfo( _lineInfo )      {} -    SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) -    : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) -    {} -  } // end namespace Catch  // end catch_section_info.cpp  // start catch_session.cpp @@ -8045,14 +13155,23 @@ namespace Catch {          void showHelp() const;          void libIdentify(); -        int applyCommandLine( int argc, char* argv[] ); +        int applyCommandLine( int argc, char const * const * argv ); +    #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) +        int applyCommandLine( int argc, wchar_t const * const * argv ); +    #endif          void useConfigData( ConfigData const& configData ); -        int run( int argc, char* argv[] ); -    #if defined(WIN32) && defined(UNICODE) -        int run( int argc, wchar_t* const argv[] ); -    #endif +        template<typename CharT> +        int run(int argc, CharT const * const argv[]) { +            if (m_startupExceptions) +                return 1; +            int returnCode = applyCommandLine(argc, argv); +            if (returnCode == 0) +                returnCode = run(); +            return returnCode; +        } +          int run();          clara::Parser const& cli() const; @@ -8104,109 +13223,138 @@ namespace Catch {  // end catch_version.h  #include <cstdlib>  #include <iomanip> +#include <set> +#include <iterator> -namespace { -    const int MaxExitCode = 255; -    using Catch::IStreamingReporterPtr; -    using Catch::IConfigPtr; -    using Catch::Config; - -    IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { -        auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); -        CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); +namespace Catch { -        return reporter; -    } +    namespace { +        const int MaxExitCode = 255; -#ifndef CATCH_CONFIG_DEFAULT_REPORTER -#define CATCH_CONFIG_DEFAULT_REPORTER "console" -#endif +        IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { +            auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); +            CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); -    IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { -        auto const& reporterNames = config->getReporterNames(); -        if (reporterNames.empty()) -            return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); +            return reporter; +        } -        IStreamingReporterPtr reporter; -        for (auto const& name : reporterNames) -            addReporter(reporter, createReporter(name, config)); -        return reporter; -    } +        IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { +            if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { +                return createReporter(config->getReporterName(), config); +            } -#undef CATCH_CONFIG_DEFAULT_REPORTER +            // On older platforms, returning std::unique_ptr<ListeningReporter> +            // when the return type is std::unique_ptr<IStreamingReporter> +            // doesn't compile without a std::move call. However, this causes +            // a warning on newer platforms. Thus, we have to work around +            // it a bit and downcast the pointer manually. +            auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter); +            auto& multi = static_cast<ListeningReporter&>(*ret); +            auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); +            for (auto const& listener : listeners) { +                multi.addListener(listener->create(Catch::ReporterConfig(config))); +            } +            multi.addReporter(createReporter(config->getReporterName(), config)); +            return ret; +        } -    void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { -        auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); -        for (auto const& listener : listeners) -            addReporter(reporters, listener->create(Catch::ReporterConfig(config))); -    } +        class TestGroup { +        public: +            explicit TestGroup(std::shared_ptr<Config> const& config) +            : m_config{config} +            , m_context{config, makeReporter(config)} +            { +                auto const& allTestCases = getAllTestCasesSorted(*m_config); +                m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); +                auto const& invalidArgs = m_config->testSpec().getInvalidArgs(); + +                if (m_matches.empty() && invalidArgs.empty()) { +                    for (auto const& test : allTestCases) +                        if (!test.isHidden()) +                            m_tests.emplace(&test); +                } else { +                    for (auto const& match : m_matches) +                        m_tests.insert(match.tests.begin(), match.tests.end()); +                } +            } -    Catch::Totals runTests(std::shared_ptr<Config> const& config) { -        using namespace Catch; -        IStreamingReporterPtr reporter = makeReporter(config); -        addListeners(reporter, config); +            Totals execute() { +                auto const& invalidArgs = m_config->testSpec().getInvalidArgs(); +                Totals totals; +                m_context.testGroupStarting(m_config->name(), 1, 1); +                for (auto const& testCase : m_tests) { +                    if (!m_context.aborting()) +                        totals += m_context.runTest(*testCase); +                    else +                        m_context.reporter().skipTest(*testCase); +                } -        RunContext context(config, std::move(reporter)); +                for (auto const& match : m_matches) { +                    if (match.tests.empty()) { +                        m_context.reporter().noMatchingTestCases(match.name); +                        totals.error = -1; +                    } +                } -        Totals totals; +                if (!invalidArgs.empty()) { +                    for (auto const& invalidArg: invalidArgs) +                         m_context.reporter().reportInvalidArguments(invalidArg); +                } -        context.testGroupStarting(config->name(), 1, 1); +                m_context.testGroupEnded(m_config->name(), totals, 1, 1); +                return totals; +            } -        TestSpec testSpec = config->testSpec(); -        if (!testSpec.hasFilters()) -            testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests +        private: +            using Tests = std::set<TestCase const*>; -        auto const& allTestCases = getAllTestCasesSorted(*config); -        for (auto const& testCase : allTestCases) { -            if (!context.aborting() && matchTest(testCase, testSpec, *config)) -                totals += context.runTest(testCase); -            else -                context.reporter().skipTest(testCase); -        } +            std::shared_ptr<Config> m_config; +            RunContext m_context; +            Tests m_tests; +            TestSpec::Matches m_matches; +        }; -        context.testGroupEnded(config->name(), totals, 1, 1); -        return totals; -    } +        void applyFilenamesAsTags(Catch::IConfig const& config) { +            auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); +            for (auto& testCase : tests) { +                auto tags = testCase.tags; -    void applyFilenamesAsTags(Catch::IConfig const& config) { -        using namespace Catch; -        auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); -        for (auto& testCase : tests) { -            auto tags = testCase.tags; +                std::string filename = testCase.lineInfo.file; +                auto lastSlash = filename.find_last_of("\\/"); +                if (lastSlash != std::string::npos) { +                    filename.erase(0, lastSlash); +                    filename[0] = '#'; +                } -            std::string filename = testCase.lineInfo.file; -            auto lastSlash = filename.find_last_of("\\/"); -            if (lastSlash != std::string::npos) { -                filename.erase(0, lastSlash); -                filename[0] = '#'; -            } +                auto lastDot = filename.find_last_of('.'); +                if (lastDot != std::string::npos) { +                    filename.erase(lastDot); +                } -            auto lastDot = filename.find_last_of('.'); -            if (lastDot != std::string::npos) { -                filename.erase(lastDot); +                tags.push_back(std::move(filename)); +                setTags(testCase, tags);              } - -            tags.push_back(std::move(filename)); -            setTags(testCase, tags);          } -    } - -} -namespace Catch { +    } // anon namespace      Session::Session() {          static bool alreadyInstantiated = false;          if( alreadyInstantiated ) { -            try         { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } -            catch(...)  { getMutableRegistryHub().registerStartupException(); } +            CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } +            CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }          } +        // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)          const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();          if ( !exceptions.empty() ) { +            config(); +            getCurrentMutableContext().setConfig(m_config); +              m_startupExceptions = true;              Colour colourGuard( Colour::Red ); -            Catch::cerr() << "Errors occured during startup!" << '\n'; +            Catch::cerr() << "Errors occurred during startup!" << '\n';              // iterate over all exceptions and notify user              for ( const auto& ex_ptr : exceptions ) {                  try { @@ -8216,6 +13364,7 @@ namespace Catch {                  }              }          } +#endif          alreadyInstantiated = true;          m_cli = makeCommandLineParser( m_configData ); @@ -8232,18 +13381,20 @@ namespace Catch {      }      void Session::libIdentify() {          Catch::cout() -                << std::left << std::setw(16) << "description: " << "A Catch test executable\n" +                << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"                  << std::left << std::setw(16) << "category: " << "testframework\n"                  << std::left << std::setw(16) << "framework: " << "Catch Test\n"                  << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;      } -    int Session::applyCommandLine( int argc, char* argv[] ) { +    int Session::applyCommandLine( int argc, char const * const * argv ) {          if( m_startupExceptions )              return 1;          auto result = m_cli.parse( clara::Args( argc, argv ) );          if( !result ) { +            config(); +            getCurrentMutableContext().setConfig(m_config);              Catch::cerr()                  << Colour( Colour::Red )                  << "\nError(s) in input:\n" @@ -8261,34 +13412,20 @@ namespace Catch {          return 0;      } -    void Session::useConfigData( ConfigData const& configData ) { -        m_configData = configData; -        m_config.reset(); -    } - -    int Session::run( int argc, char* argv[] ) { -        if( m_startupExceptions ) -            return 1; -        int returnCode = applyCommandLine( argc, argv ); -        if( returnCode == 0 ) -            returnCode = run(); -        return returnCode; -    } - -#if defined(WIN32) && defined(UNICODE) -    int Session::run( int argc, wchar_t* const argv[] ) { +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) +    int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {          char **utf8Argv = new char *[ argc ];          for ( int i = 0; i < argc; ++i ) { -            int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); +            int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );              utf8Argv[ i ] = new char[ bufSize ]; -            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); +            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );          } -        int returnCode = run( argc, utf8Argv ); +        int returnCode = applyCommandLine( argc, utf8Argv );          for ( int i = 0; i < argc; ++i )              delete [] utf8Argv[ i ]; @@ -8298,6 +13435,12 @@ namespace Catch {          return returnCode;      }  #endif + +    void Session::useConfigData( ConfigData const& configData ) { +        m_configData = configData; +        m_config.reset(); +    } +      int Session::run() {          if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {              Catch::cout() << "...waiting for enter/ return before starting" << std::endl; @@ -8330,11 +13473,11 @@ namespace Catch {          if( m_startupExceptions )              return 1; -        if( m_configData.showHelp || m_configData.libIdentify ) +        if (m_configData.showHelp || m_configData.libIdentify) {              return 0; +        } -        try -        { +        CATCH_TRY {              config(); // Force config to be constructed              seedRng( *m_config ); @@ -8343,27 +13486,68 @@ namespace Catch {                  applyFilenamesAsTags( *m_config );              // Handle list request -            if( Option<std::size_t> listed = list( config() ) ) +            if( Option<std::size_t> listed = list( m_config ) )                  return static_cast<int>( *listed ); -            return (std::min)( MaxExitCode, static_cast<int>( runTests( m_config ).assertions.failed ) ); +            TestGroup tests { m_config }; +            auto const totals = tests.execute(); + +            if( m_config->warnAboutNoTests() && totals.error == -1 ) +                return 2; + +            // Note that on unices only the lower 8 bits are usually used, clamping +            // the return value to 255 prevents false negative when some multiple +            // of 256 tests has failed +            return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));          } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)          catch( std::exception& ex ) {              Catch::cerr() << ex.what() << std::endl;              return MaxExitCode;          } +#endif      }  } // end namespace Catch  // end catch_session.cpp +// start catch_singletons.cpp + +#include <vector> + +namespace Catch { + +    namespace { +        static auto getSingletons() -> std::vector<ISingleton*>*& { +            static std::vector<ISingleton*>* g_singletons = nullptr; +            if( !g_singletons ) +                g_singletons = new std::vector<ISingleton*>(); +            return g_singletons; +        } +    } + +    ISingleton::~ISingleton() {} + +    void addSingleton(ISingleton* singleton ) { +        getSingletons()->push_back( singleton ); +    } +    void cleanupSingletons() { +        auto& singletons = getSingletons(); +        for( auto singleton : *singletons ) +            delete singleton; +        delete singletons; +        singletons = nullptr; +    } + +} // namespace Catch +// end catch_singletons.cpp  // start catch_startup_exception_registry.cpp +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)  namespace Catch { -    void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { -        try { +void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { +        CATCH_TRY {              m_exceptions.push_back(exception); -        } -        catch(...) { +        } CATCH_CATCH_ALL {              // If we run out of memory during start-up there's really not a lot more we can do about it              std::terminate();          } @@ -8374,118 +13558,195 @@ namespace Catch {      }  } // end namespace Catch +#endif  // end catch_startup_exception_registry.cpp  // start catch_stream.cpp -#include <stdexcept>  #include <cstdio>  #include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <memory>  namespace Catch { -    template<typename WriterF, std::size_t bufferSize=256> -    class StreamBufImpl : public StreamBufBase { -        char data[bufferSize]; -        WriterF m_writer; +    Catch::IStream::~IStream() = default; -    public: -        StreamBufImpl() { -            setp( data, data + sizeof(data) ); -        } +    namespace Detail { namespace { +        template<typename WriterF, std::size_t bufferSize=256> +        class StreamBufImpl : public std::streambuf { +            char data[bufferSize]; +            WriterF m_writer; -        ~StreamBufImpl() noexcept { -            StreamBufImpl::sync(); -        } +        public: +            StreamBufImpl() { +                setp( data, data + sizeof(data) ); +            } -    private: -        int overflow( int c ) override { -            sync(); +            ~StreamBufImpl() noexcept { +                StreamBufImpl::sync(); +            } -            if( c != EOF ) { -                if( pbase() == epptr() ) -                    m_writer( std::string( 1, static_cast<char>( c ) ) ); -                else -                    sputc( static_cast<char>( c ) ); +        private: +            int overflow( int c ) override { +                sync(); + +                if( c != EOF ) { +                    if( pbase() == epptr() ) +                        m_writer( std::string( 1, static_cast<char>( c ) ) ); +                    else +                        sputc( static_cast<char>( c ) ); +                } +                return 0;              } -            return 0; -        } -        int sync() override { -            if( pbase() != pptr() ) { -                m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); -                setp( pbase(), epptr() ); +            int sync() override { +                if( pbase() != pptr() ) { +                    m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); +                    setp( pbase(), epptr() ); +                } +                return 0;              } -            return 0; -        } -    }; +        }; -    /////////////////////////////////////////////////////////////////////////// +        /////////////////////////////////////////////////////////////////////////// -    Catch::IStream::~IStream() = default; +        struct OutputDebugWriter { -    FileStream::FileStream( std::string const& filename ) { -        m_ofs.open( filename.c_str() ); -        CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); -    } +            void operator()( std::string const&str ) { +                writeToDebugConsole( str ); +            } +        }; + +        /////////////////////////////////////////////////////////////////////////// + +        class FileStream : public IStream { +            mutable std::ofstream m_ofs; +        public: +            FileStream( StringRef filename ) { +                m_ofs.open( filename.c_str() ); +                CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); +            } +            ~FileStream() override = default; +        public: // IStream +            std::ostream& stream() const override { +                return m_ofs; +            } +        }; + +        /////////////////////////////////////////////////////////////////////////// + +        class CoutStream : public IStream { +            mutable std::ostream m_os; +        public: +            // Store the streambuf from cout up-front because +            // cout may get redirected when running tests +            CoutStream() : m_os( Catch::cout().rdbuf() ) {} +            ~CoutStream() override = default; + +        public: // IStream +            std::ostream& stream() const override { return m_os; } +        }; + +        /////////////////////////////////////////////////////////////////////////// + +        class DebugOutStream : public IStream { +            std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; +            mutable std::ostream m_os; +        public: +            DebugOutStream() +            :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), +                m_os( m_streamBuf.get() ) +            {} + +            ~DebugOutStream() override = default; + +        public: // IStream +            std::ostream& stream() const override { return m_os; } +        }; -    std::ostream& FileStream::stream() const { -        return m_ofs; +    }} // namespace anon::detail + +    /////////////////////////////////////////////////////////////////////////// + +    auto makeStream( StringRef const &filename ) -> IStream const* { +        if( filename.empty() ) +            return new Detail::CoutStream(); +        else if( filename[0] == '%' ) { +            if( filename == "%debug" ) +                return new Detail::DebugOutStream(); +            else +                CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); +        } +        else +            return new Detail::FileStream( filename );      } -    struct OutputDebugWriter { +    // This class encapsulates the idea of a pool of ostringstreams that can be reused. +    struct StringStreams { +        std::vector<std::unique_ptr<std::ostringstream>> m_streams; +        std::vector<std::size_t> m_unused; +        std::ostringstream m_referenceStream; // Used for copy state/ flags from -        void operator()( std::string const&str ) { -            writeToDebugConsole( str ); +        auto add() -> std::size_t { +            if( m_unused.empty() ) { +                m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) ); +                return m_streams.size()-1; +            } +            else { +                auto index = m_unused.back(); +                m_unused.pop_back(); +                return index; +            } +        } + +        void release( std::size_t index ) { +            m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state +            m_unused.push_back(index);          }      }; -    DebugOutStream::DebugOutStream() -    :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), -        m_os( m_streamBuf.get() ) +    ReusableStringStream::ReusableStringStream() +    :   m_index( Singleton<StringStreams>::getMutable().add() ), +        m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )      {} -    std::ostream& DebugOutStream::stream() const { -        return m_os; +    ReusableStringStream::~ReusableStringStream() { +        static_cast<std::ostringstream*>( m_oss )->str(""); +        m_oss->clear(); +        Singleton<StringStreams>::getMutable().release( m_index );      } -    // Store the streambuf from cout up-front because -    // cout may get redirected when running tests -    CoutStream::CoutStream() -    :   m_os( Catch::cout().rdbuf() ) -    {} - -    std::ostream& CoutStream::stream() const { -        return m_os; +    auto ReusableStringStream::str() const -> std::string { +        return static_cast<std::ostringstream*>( m_oss )->str();      } +    /////////////////////////////////////////////////////////////////////////// +  #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions -    std::ostream& cout() { -        return std::cout; -    } -    std::ostream& cerr() { -        return std::cerr; -    } -    std::ostream& clog() { -        return std::clog; -    } +    std::ostream& cout() { return std::cout; } +    std::ostream& cerr() { return std::cerr; } +    std::ostream& clog() { return std::clog; }  #endif  }  // end catch_stream.cpp -// start catch_streambuf.cpp - -namespace Catch { -    StreamBufBase::~StreamBufBase() = default; -} -// end catch_streambuf.cpp  // start catch_string_manip.cpp  #include <algorithm>  #include <ostream>  #include <cstring>  #include <cctype> +#include <vector>  namespace Catch { +    namespace { +        char toLowerCh(char c) { +            return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) ); +        } +    } +      bool startsWith( std::string const& s, std::string const& prefix ) {          return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());      } @@ -8501,9 +13762,6 @@ namespace Catch {      bool contains( std::string const& s, std::string const& infix ) {          return s.find( infix ) != std::string::npos;      } -    char toLowerCh(char c) { -        return static_cast<char>( std::tolower( c ) ); -    }      void toLowerInPlace( std::string& s ) {          std::transform( s.begin(), s.end(), s.begin(), toLowerCh );      } @@ -8520,6 +13778,18 @@ namespace Catch {          return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();      } +    StringRef trim(StringRef ref) { +        const auto is_ws = [](char c) { +            return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +        }; +        size_t real_begin = 0; +        while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } +        size_t real_end = ref.size(); +        while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } + +        return ref.substr(real_begin, real_end - real_begin); +    } +      bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {          bool replaced = false;          std::size_t i = str.find( replaceThis ); @@ -8534,6 +13804,21 @@ namespace Catch {          return replaced;      } +    std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) { +        std::vector<StringRef> subStrings; +        std::size_t start = 0; +        for(std::size_t pos = 0; pos < str.size(); ++pos ) { +            if( str[pos] == delimiter ) { +                if( pos - start > 1 ) +                    subStrings.push_back( str.substr( start, pos-start ) ); +                start = pos+1; +            } +        } +        if( start < str.size() ) +            subStrings.push_back( str.substr( start, str.size()-start ) ); +        return subStrings; +    } +      pluralise::pluralise( std::size_t count, std::string const& label )      :   m_count( count ),          m_label( label ) @@ -8550,168 +13835,46 @@ namespace Catch {  // end catch_string_manip.cpp  // start catch_stringref.cpp -#if defined(__clang__) -#    pragma clang diagnostic push -#    pragma clang diagnostic ignored "-Wexit-time-destructors" -#endif - +#include <algorithm>  #include <ostream> -#include <cassert>  #include <cstring> +#include <cstdint>  namespace Catch { - -    auto getEmptyStringRef() -> StringRef { -        static StringRef s_emptyStringRef(""); -        return s_emptyStringRef; -    } - -    StringRef::StringRef() noexcept -    :   StringRef( getEmptyStringRef() ) -    {} - -    StringRef::StringRef( StringRef const& other ) noexcept -    :   m_start( other.m_start ), -        m_size( other.m_size ) -    {} - -    StringRef::StringRef( StringRef&& other ) noexcept -    :   m_start( other.m_start ), -        m_size( other.m_size ), -        m_data( other.m_data ) -    { -        other.m_data = nullptr; -    } -      StringRef::StringRef( char const* rawChars ) noexcept -    :   m_start( rawChars ), -        m_size( static_cast<size_type>( std::strlen( rawChars ) ) ) -    { -        assert( rawChars != nullptr ); -    } - -    StringRef::StringRef( char const* rawChars, size_type size ) noexcept -    :   m_start( rawChars ), -        m_size( size ) -    { -        size_type rawSize = rawChars == nullptr ? 0 : static_cast<size_type>( std::strlen( rawChars ) ); -        if( rawSize < size ) -            m_size = rawSize; -    } - -    StringRef::StringRef( std::string const& stdString ) noexcept -    :   m_start( stdString.c_str() ), -        m_size( stdString.size() ) +    : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )      {} -    StringRef::~StringRef() noexcept { -        delete[] m_data; -    } - -    auto StringRef::operator = ( StringRef other ) noexcept -> StringRef& { -        swap( other ); -        return *this; -    } -    StringRef::operator std::string() const { -        return std::string( m_start, m_size ); -    } - -    void StringRef::swap( StringRef& other ) noexcept { -        std::swap( m_start, other.m_start ); -        std::swap( m_size, other.m_size ); -        std::swap( m_data, other.m_data ); -    } -      auto StringRef::c_str() const -> char const* { -        if( isSubstring() ) -           const_cast<StringRef*>( this )->takeOwnership(); +        CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");          return m_start;      }      auto StringRef::data() const noexcept -> char const* {          return m_start;      } -    auto StringRef::isOwned() const noexcept -> bool { -        return m_data != nullptr; -    } -    auto StringRef::isSubstring() const noexcept -> bool { -        return m_start[m_size] != '\0'; -    } - -    void StringRef::takeOwnership() { -        if( !isOwned() ) { -            m_data = new char[m_size+1]; -            memcpy( m_data, m_start, m_size ); -            m_data[m_size] = '\0'; -            m_start = m_data; -        } -    }      auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { -        if( start < m_size ) -            return StringRef( m_start+start, size ); -        else +        if (start < m_size) { +            return StringRef(m_start + start, (std::min)(m_size - start, size)); +        } else {              return StringRef(); +        }      }      auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { -        return -            size() == other.size() && -            (std::strncmp( m_start, other.m_start, size() ) == 0); -    } -    auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { -        return !operator==( other ); -    } - -    auto StringRef::operator[](size_type index) const noexcept -> char { -        return m_start[index]; -    } - -    auto StringRef::empty() const noexcept -> bool { -        return m_size == 0; -    } - -    auto StringRef::size() const noexcept -> size_type { -        return m_size; -    } -    auto StringRef::numberOfCharacters() const noexcept -> size_type { -        size_type noChars = m_size; -        // Make adjustments for uft encodings -        for( size_type i=0; i < m_size; ++i ) { -            char c = m_start[i]; -            if( ( c & 0b11000000 ) == 0b11000000 ) { -                if( ( c & 0b11100000 ) == 0b11000000 ) -                    noChars--; -                else if( ( c & 0b11110000 ) == 0b11100000 ) -                    noChars-=2; -                else if( ( c & 0b11111000 ) == 0b11110000 ) -                    noChars-=3; -            } -        } -        return noChars; +        return m_size == other.m_size +            && (std::memcmp( m_start, other.m_start, m_size ) == 0);      } -    auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { -        std::string str; -        str.reserve( lhs.size() + rhs.size() ); -        str += lhs; -        str += rhs; -        return str; -    } -    auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { -        return std::string( lhs ) + std::string( rhs ); -    } -    auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { -        return std::string( lhs ) + std::string( rhs ); +    auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { +        return os.write(str.data(), str.size());      } -    auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { -        return os << str.c_str(); +    auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { +        lhs.append(rhs.data(), rhs.size()); +        return lhs;      }  } // namespace Catch - -#if defined(__clang__) -#    pragma clang diagnostic pop -#endif  // end catch_stringref.cpp  // start catch_tag_alias.cpp @@ -8724,9 +13887,9 @@ namespace Catch {  namespace Catch {      RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { -        try { +        CATCH_TRY {              getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); -        } catch (...) { +        } CATCH_CATCH_ALL {              // Do not throw when constructing global objects, instead register the exception to be processed later              getMutableRegistryHub().registerStartupException();          } @@ -8736,6 +13899,8 @@ namespace Catch {  // end catch_tag_alias_autoregistrar.cpp  // start catch_tag_alias_registry.cpp +#include <sstream> +  namespace Catch {      TagAliasRegistry::~TagAliasRegistry() {} @@ -8784,40 +13949,42 @@ namespace Catch {  #include <cctype>  #include <exception>  #include <algorithm> +#include <sstream>  namespace Catch { -    TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { -        if( startsWith( tag, '.' ) || -            tag == "!hide" ) -            return TestCaseInfo::IsHidden; -        else if( tag == "!throws" ) -            return TestCaseInfo::Throws; -        else if( tag == "!shouldfail" ) -            return TestCaseInfo::ShouldFail; -        else if( tag == "!mayfail" ) -            return TestCaseInfo::MayFail; -        else if( tag == "!nonportable" ) -            return TestCaseInfo::NonPortable; -        else if( tag == "!benchmark" ) -            return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); -        else -            return TestCaseInfo::None; -    } -    bool isReservedTag( std::string const& tag ) { -        return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); -    } -    void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { -        CATCH_ENFORCE( !isReservedTag(tag), -                      "Tag name: [" << tag << "] is not allowed.\n" -                      << "Tag names starting with non alpha-numeric characters are reserved\n" -                      << _lineInfo ); +    namespace { +        TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { +            if( startsWith( tag, '.' ) || +                tag == "!hide" ) +                return TestCaseInfo::IsHidden; +            else if( tag == "!throws" ) +                return TestCaseInfo::Throws; +            else if( tag == "!shouldfail" ) +                return TestCaseInfo::ShouldFail; +            else if( tag == "!mayfail" ) +                return TestCaseInfo::MayFail; +            else if( tag == "!nonportable" ) +                return TestCaseInfo::NonPortable; +            else if( tag == "!benchmark" ) +                return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); +            else +                return TestCaseInfo::None; +        } +        bool isReservedTag( std::string const& tag ) { +            return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) ); +        } +        void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { +            CATCH_ENFORCE( !isReservedTag(tag), +                          "Tag name: [" << tag << "] is not allowed.\n" +                          << "Tag names starting with non alphanumeric characters are reserved\n" +                          << _lineInfo ); +        }      }      TestCase makeTestCase(  ITestInvoker* _testCase,                              std::string const& _className, -                            std::string const& _name, -                            std::string const& _descOrTags, +                            NameAndTags const& nameAndTags,                              SourceLineInfo const& _lineInfo )      {          bool isHidden = false; @@ -8826,7 +13993,7 @@ namespace Catch {          std::vector<std::string> tags;          std::string desc, tag;          bool inTag = false; -        for (char c : _descOrTags) { +        for (char c : nameAndTags.tags) {              if( !inTag ) {                  if( c == '[' )                      inTag = true; @@ -8841,6 +14008,12 @@ namespace Catch {                      else if( prop == TestCaseInfo::None )                          enforceNotReservedTag( tag, _lineInfo ); +                    // Merged hide tags like `[.approvals]` should be added as +                    // `[.][approvals]`. The `[.]` is added at later point, so +                    // we only strip the prefix +                    if (startsWith(tag, '.') && tag.size() > 1) { +                        tag.erase(0, 1); +                    }                      tags.push_back( tag );                      tag.clear();                      inTag = false; @@ -8850,11 +14023,12 @@ namespace Catch {              }          }          if( isHidden ) { -            tags.push_back( "." ); +            // Add all "hidden" tags to make them behave identically +            tags.insert( tags.end(), { ".", "!hide" } );          } -        TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); -        return TestCase( _testCase, info ); +        TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo ); +        return TestCase( _testCase, std::move(info) );      }      void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { @@ -8914,7 +14088,7 @@ namespace Catch {          return ret;      } -    TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} +    TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {}      TestCase TestCase::withName( std::string const& _newName ) const {          TestCase other( *this ); @@ -8945,30 +14119,89 @@ namespace Catch {  // end catch_test_case_info.cpp  // start catch_test_case_registry_impl.cpp +#include <algorithm>  #include <sstream>  namespace Catch { -    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { +    namespace { +        struct TestHasher { +            using hash_t = uint64_t; + +            explicit TestHasher( hash_t hashSuffix ): +                m_hashSuffix{ hashSuffix } {} + +            uint32_t operator()( TestCase const& t ) const { +                // FNV-1a hash with multiplication fold. +                const hash_t prime = 1099511628211u; +                hash_t hash = 14695981039346656037u; +                for ( const char c : t.name ) { +                    hash ^= c; +                    hash *= prime; +                } +                hash ^= m_hashSuffix; +                hash *= prime; +                const uint32_t low{ static_cast<uint32_t>( hash ) }; +                const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) }; +                return low * high; +            } -        std::vector<TestCase> sorted = unsortedTestCases; +        private: +            hash_t m_hashSuffix; +        }; +    } // end unnamed namespace +    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {          switch( config.runOrder() ) { -            case RunTests::InLexicographicalOrder: -                std::sort( sorted.begin(), sorted.end() ); -                break; -            case RunTests::InRandomOrder: -                seedRng( config ); -                RandomNumberGenerator::shuffle( sorted ); -                break;              case RunTests::InDeclarationOrder:                  // already in declaration order                  break; + +            case RunTests::InLexicographicalOrder: { +                std::vector<TestCase> sorted = unsortedTestCases; +                std::sort( sorted.begin(), sorted.end() ); +                return sorted; +            } + +            case RunTests::InRandomOrder: { +                seedRng( config ); +                TestHasher h{ config.rngSeed() }; + +                using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>; +                std::vector<hashedTest> indexed_tests; +                indexed_tests.reserve( unsortedTestCases.size() ); + +                for (auto const& testCase : unsortedTestCases) { +                    indexed_tests.emplace_back(h(testCase), &testCase); +                } + +                std::sort(indexed_tests.begin(), indexed_tests.end(), +                          [](hashedTest const& lhs, hashedTest const& rhs) { +                          if (lhs.first == rhs.first) { +                              return lhs.second->name < rhs.second->name; +                          } +                          return lhs.first < rhs.first; +                }); + +                std::vector<TestCase> sorted; +                sorted.reserve( indexed_tests.size() ); + +                for (auto const& hashed : indexed_tests) { +                    sorted.emplace_back(*hashed.second); +                } + +                return sorted; +            }          } -        return sorted; +        return unsortedTestCases;      } + +    bool isThrowSafe( TestCase const& testCase, IConfig const& config ) { +        return !testCase.throws() || config.allowThrows(); +    } +      bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { -        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); +        return testSpec.matches( testCase ) && isThrowSafe( testCase, config );      }      void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { @@ -8985,9 +14218,12 @@ namespace Catch {      std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {          std::vector<TestCase> filtered;          filtered.reserve( testCases.size() ); -        for( auto const& testCase : testCases ) -            if( matchTest( testCase, testSpec, config ) ) -                filtered.push_back( testCase ); +        for (auto const& testCase : testCases) { +            if ((!testSpec.hasFilters() && !testCase.isHidden()) || +                (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { +                filtered.push_back(testCase); +            } +        }          return filtered;      }      std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { @@ -8997,9 +14233,9 @@ namespace Catch {      void TestRegistry::registerTest( TestCase const& testCase ) {          std::string name = testCase.getTestCaseInfo().name;          if( name.empty() ) { -            std::ostringstream oss; -            oss << "Anonymous test case " << ++m_unnamedCount; -            return registerTest( testCase.withName( oss.str() ) ); +            ReusableStringStream rss; +            rss << "Anonymous test case " << ++m_unnamedCount; +            return registerTest( testCase.withName( rss.str() ) );          }          m_functions.push_back( testCase );      } @@ -9025,8 +14261,8 @@ namespace Catch {          m_testAsFunction();      } -    std::string extractClassName( std::string const& classOrQualifiedMethodName ) { -        std::string className = classOrQualifiedMethodName; +    std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { +        std::string className(classOrQualifiedMethodName);          if( startsWith( className, '&' ) )          {              std::size_t lastColons = className.rfind( "::" ); @@ -9043,9 +14279,10 @@ namespace Catch {  // start catch_test_case_tracker.cpp  #include <algorithm> -#include <assert.h> +#include <cassert>  #include <stdexcept>  #include <memory> +#include <sstream>  #if defined(__clang__)  #    pragma clang diagnostic push @@ -9062,11 +14299,6 @@ namespace TestCaseTracking {      ITracker::~ITracker() = default; -    TrackerContext& TrackerContext::instance() { -        static TrackerContext s_instance; -        return s_instance; -    } -      ITracker& TrackerContext::startRun() {          m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );          m_currentTracker = nullptr; @@ -9098,22 +14330,12 @@ namespace TestCaseTracking {          m_currentTracker = tracker;      } -    TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} -    bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { -        return -            tracker->nameAndLocation().name == m_nameAndLocation.name && -            tracker->nameAndLocation().location == m_nameAndLocation.location; -    } - -    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) -    :   m_nameAndLocation( nameAndLocation ), +    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): +        ITracker(nameAndLocation),          m_ctx( ctx ),          m_parent( parent )      {} -    NameAndLocation const& TrackerBase::nameAndLocation() const { -        return m_nameAndLocation; -    }      bool TrackerBase::isComplete() const {          return m_runState == CompletedSuccessfully || m_runState == Failed;      } @@ -9132,7 +14354,12 @@ namespace TestCaseTracking {      }      ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { -        auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); +        auto it = std::find_if( m_children.begin(), m_children.end(), +            [&nameAndLocation]( ITrackerPtr const& tracker ){ +                return +                    tracker->nameAndLocation().location == nameAndLocation.location && +                    tracker->nameAndLocation().name == nameAndLocation.name; +            } );          return( it != m_children.end() )              ? *it              : nullptr; @@ -9151,7 +14378,7 @@ namespace TestCaseTracking {      }      bool TrackerBase::isSectionTracker() const { return false; } -    bool TrackerBase::isIndexTracker() const { return false; } +    bool TrackerBase::isGeneratorTracker() const { return false; }      void TrackerBase::open() {          m_runState = Executing; @@ -9174,7 +14401,7 @@ namespace TestCaseTracking {                  m_runState = CompletedSuccessfully;                  break;              case ExecutingChildren: -                if( m_children.empty() || m_children.back()->isComplete() ) +                if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )                      m_runState = CompletedSuccessfully;                  break; @@ -9209,7 +14436,8 @@ namespace TestCaseTracking {      }      SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) -    :   TrackerBase( nameAndLocation, ctx, parent ) +    :   TrackerBase( nameAndLocation, ctx, parent ), +        m_trimmed_name(trim(nameAndLocation.name))      {          if( parent ) {              while( !parent->isSectionTracker() ) @@ -9220,6 +14448,17 @@ namespace TestCaseTracking {          }      } +    bool SectionTracker::isComplete() const { +        bool complete = true; + +        if (m_filters.empty() +            || m_filters[0] == "" +            || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { +            complete = TrackerBase::isComplete(); +        } +        return complete; +    } +      bool SectionTracker::isSectionTracker() const { return true; }      SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { @@ -9241,63 +14480,29 @@ namespace TestCaseTracking {      }      void SectionTracker::tryOpen() { -        if( !isComplete() && (m_filters.empty() || m_filters[0].empty() ||  m_filters[0] == m_nameAndLocation.name ) ) +        if( !isComplete() )              open();      }      void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {          if( !filters.empty() ) { -            m_filters.push_back(""); // Root - should never be consulted -            m_filters.push_back(""); // Test Case - not a section filter +            m_filters.reserve( m_filters.size() + filters.size() + 2 ); +            m_filters.emplace_back(""); // Root - should never be consulted +            m_filters.emplace_back(""); // Test Case - not a section filter              m_filters.insert( m_filters.end(), filters.begin(), filters.end() );          }      }      void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {          if( filters.size() > 1 ) -            m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); -    } - -    IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) -    :   TrackerBase( nameAndLocation, ctx, parent ), -        m_size( size ) -    {} - -    bool IndexTracker::isIndexTracker() const { return true; } - -    IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { -        std::shared_ptr<IndexTracker> tracker; - -        ITracker& currentTracker = ctx.currentTracker(); -        if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { -            assert( childTracker ); -            assert( childTracker->isIndexTracker() ); -            tracker = std::static_pointer_cast<IndexTracker>( childTracker ); -        } -        else { -            tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, ¤tTracker, size ); -            currentTracker.addChild( tracker ); -        } - -        if( !ctx.completedCycle() && !tracker->isComplete() ) { -            if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) -                tracker->moveNext(); -            tracker->open(); -        } - -        return *tracker; +            m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );      } -    int IndexTracker::index() const { return m_index; } - -    void IndexTracker::moveNext() { -        m_index++; -        m_children.clear(); +    std::vector<std::string> const& SectionTracker::getFilters() const { +        return m_filters;      } -    void IndexTracker::close() { -        TrackerBase::close(); -        if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) -            m_runState = Executing; +    std::string const& SectionTracker::trimmedName() const { +        return m_trimmed_name;      }  } // namespace TestCaseTracking @@ -9305,7 +14510,6 @@ namespace TestCaseTracking {  using TestCaseTracking::ITracker;  using TestCaseTracking::TrackerContext;  using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker;  } // namespace Catch @@ -9321,19 +14525,18 @@ namespace Catch {          return new(std::nothrow) TestInvokerAsFunction( testAsFunction );      } -    NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} +    NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} -    AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { -        try { +    AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { +        CATCH_TRY {              getMutableRegistryHub()                      .registerTest(                          makeTestCase(                              invoker,                              extractClassName( classOrMethod ), -                            nameAndTags.name, -                            nameAndTags.tags, +                            nameAndTags,                              lineInfo)); -        } catch (...) { +        } CATCH_CATCH_ALL {              // Do not throw when constructing global objects, instead register the exception to be processed later              getMutableRegistryHub().registerStartupException();          } @@ -9351,47 +14554,81 @@ namespace Catch {  namespace Catch { +    TestSpec::Pattern::Pattern( std::string const& name ) +    : m_name( name ) +    {} +      TestSpec::Pattern::~Pattern() = default; -    TestSpec::NamePattern::~NamePattern() = default; -    TestSpec::TagPattern::~TagPattern() = default; -    TestSpec::ExcludedPattern::~ExcludedPattern() = default; -    TestSpec::NamePattern::NamePattern( std::string const& name ) -    : m_wildcardPattern( toLower( name ), CaseSensitive::No ) +    std::string const& TestSpec::Pattern::name() const { +        return m_name; +    } + +    TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString ) +    : Pattern( filterString ) +    , m_wildcardPattern( toLower( name ), CaseSensitive::No )      {} +      bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { -        return m_wildcardPattern.matches( toLower( testCase.name ) ); +        return m_wildcardPattern.matches( testCase.name );      } -    TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} +    TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString ) +    : Pattern( filterString ) +    , m_tag( toLower( tag ) ) +    {} +      bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {          return std::find(begin(testCase.lcaseTags),                           end(testCase.lcaseTags),                           m_tag) != end(testCase.lcaseTags);      } -    TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} -    bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } +    TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) +    : Pattern( underlyingPattern->name() ) +    , m_underlyingPattern( underlyingPattern ) +    {} + +    bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { +        return !m_underlyingPattern->matches( testCase ); +    }      bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { -        // All patterns in a filter must match for the filter to be a match -        for( auto const& pattern : m_patterns ) { -            if( !pattern->matches( testCase ) ) -                return false; -        } -        return true; +        return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } ); +    } + +    std::string TestSpec::Filter::name() const { +        std::string name; +        for( auto const& p : m_patterns ) +            name += p->name(); +        return name;      }      bool TestSpec::hasFilters() const {          return !m_filters.empty();      } +      bool TestSpec::matches( TestCaseInfo const& testCase ) const { -        // A TestSpec matches if any filter matches -        for( auto const& filter : m_filters ) -            if( filter.matches( testCase ) ) -                return true; -        return false; +        return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );      } + +    TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const +    { +        Matches matches( m_filters.size() ); +        std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ +            std::vector<TestCase const*> currentMatches; +            for( auto const& test : testCases ) +                if( isThrowSafe( test, config ) && filter.matches( test ) ) +                    currentMatches.emplace_back( &test ); +            return FilterMatch{ filter.name(), currentMatches }; +        } ); +        return matches; +    } + +    const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{ +        return  (m_invalidArgs); +    } +  }  // end catch_test_spec.cpp  // start catch_test_spec_parser.cpp @@ -9403,64 +14640,136 @@ namespace Catch {      TestSpecParser& TestSpecParser::parse( std::string const& arg ) {          m_mode = None;          m_exclusion = false; -        m_start = std::string::npos;          m_arg = m_tagAliases->expandAliases( arg );          m_escapeChars.clear(); +        m_substring.reserve(m_arg.size()); +        m_patternName.reserve(m_arg.size()); +        m_realPatternPos = 0; +          for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) -            visitChar( m_arg[m_pos] ); -        if( m_mode == Name ) -            addPattern<TestSpec::NamePattern>(); +          //if visitChar fails +           if( !visitChar( m_arg[m_pos] ) ){ +               m_testSpec.m_invalidArgs.push_back(arg); +               break; +           } +        endMode();          return *this;      }      TestSpec TestSpecParser::testSpec() {          addFilter();          return m_testSpec;      } +    bool TestSpecParser::visitChar( char c ) { +        if( (m_mode != EscapedName) && (c == '\\') ) { +            escape(); +            addCharToPattern(c); +            return true; +        }else if((m_mode != EscapedName) && (c == ',') )  { +            return separate(); +        } -    void TestSpecParser::visitChar( char c ) { -        if( m_mode == None ) { -            switch( c ) { -            case ' ': return; -            case '~': m_exclusion = true; return; -            case '[': return startNewMode( Tag, ++m_pos ); -            case '"': return startNewMode( QuotedName, ++m_pos ); -            case '\\': return escape(); -            default: startNewMode( Name, m_pos ); break; -            } +        switch( m_mode ) { +        case None: +            if( processNoneChar( c ) ) +                return true; +            break; +        case Name: +            processNameChar( c ); +            break; +        case EscapedName: +            endMode(); +            addCharToPattern(c); +            return true; +        default: +        case Tag: +        case QuotedName: +            if( processOtherChar( c ) ) +                return true; +            break;          } -        if( m_mode == Name ) { -            if( c == ',' ) { -                addPattern<TestSpec::NamePattern>(); -                addFilter(); -            } -            else if( c == '[' ) { -                if( subString() == "exclude:" ) -                    m_exclusion = true; -                else -                    addPattern<TestSpec::NamePattern>(); -                startNewMode( Tag, ++m_pos ); -            } -            else if( c == '\\' ) -                escape(); + +        m_substring += c; +        if( !isControlChar( c ) ) { +            m_patternName += c; +            m_realPatternPos++; +        } +        return true; +    } +    // Two of the processing methods return true to signal the caller to return +    // without adding the given character to the current pattern strings +    bool TestSpecParser::processNoneChar( char c ) { +        switch( c ) { +        case ' ': +            return true; +        case '~': +            m_exclusion = true; +            return false; +        case '[': +            startNewMode( Tag ); +            return false; +        case '"': +            startNewMode( QuotedName ); +            return false; +        default: +            startNewMode( Name ); +            return false;          } -        else if( m_mode == EscapedName ) -            m_mode = Name; -        else if( m_mode == QuotedName && c == '"' ) -            addPattern<TestSpec::NamePattern>(); -        else if( m_mode == Tag && c == ']' ) -            addPattern<TestSpec::TagPattern>();      } -    void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { +    void TestSpecParser::processNameChar( char c ) { +        if( c == '[' ) { +            if( m_substring == "exclude:" ) +                m_exclusion = true; +            else +                endMode(); +            startNewMode( Tag ); +        } +    } +    bool TestSpecParser::processOtherChar( char c ) { +        if( !isControlChar( c ) ) +            return false; +        m_substring += c; +        endMode(); +        return true; +    } +    void TestSpecParser::startNewMode( Mode mode ) {          m_mode = mode; -        m_start = start; +    } +    void TestSpecParser::endMode() { +        switch( m_mode ) { +        case Name: +        case QuotedName: +            return addNamePattern(); +        case Tag: +            return addTagPattern(); +        case EscapedName: +            revertBackToLastMode(); +            return; +        case None: +        default: +            return startNewMode( None ); +        }      }      void TestSpecParser::escape() { -        if( m_mode == None ) -            m_start = m_pos; +        saveLastMode();          m_mode = EscapedName; -        m_escapeChars.push_back( m_pos ); +        m_escapeChars.push_back(m_realPatternPos); +    } +    bool TestSpecParser::isControlChar( char c ) const { +        switch( m_mode ) { +            default: +                return false; +            case None: +                return c == '~'; +            case Name: +                return c == '['; +            case EscapedName: +                return true; +            case QuotedName: +                return c == '"'; +            case Tag: +                return c == '[' || c == ']'; +        }      } -    std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }      void TestSpecParser::addFilter() {          if( !m_currentFilter.m_patterns.empty() ) { @@ -9469,6 +14778,86 @@ namespace Catch {          }      } +    void TestSpecParser::saveLastMode() { +      lastMode = m_mode; +    } + +    void TestSpecParser::revertBackToLastMode() { +      m_mode = lastMode; +    } + +    bool TestSpecParser::separate() { +      if( (m_mode==QuotedName) || (m_mode==Tag) ){ +         //invalid argument, signal failure to previous scope. +         m_mode = None; +         m_pos = m_arg.size(); +         m_substring.clear(); +         m_patternName.clear(); +         m_realPatternPos = 0; +         return false; +      } +      endMode(); +      addFilter(); +      return true; //success +    } + +    std::string TestSpecParser::preprocessPattern() { +        std::string token = m_patternName; +        for (std::size_t i = 0; i < m_escapeChars.size(); ++i) +            token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); +        m_escapeChars.clear(); +        if (startsWith(token, "exclude:")) { +            m_exclusion = true; +            token = token.substr(8); +        } + +        m_patternName.clear(); +        m_realPatternPos = 0; + +        return token; +    } + +    void TestSpecParser::addNamePattern() { +        auto token = preprocessPattern(); + +        if (!token.empty()) { +            TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring); +            if (m_exclusion) +                pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); +            m_currentFilter.m_patterns.push_back(pattern); +        } +        m_substring.clear(); +        m_exclusion = false; +        m_mode = None; +    } + +    void TestSpecParser::addTagPattern() { +        auto token = preprocessPattern(); + +        if (!token.empty()) { +            // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) +            // we have to create a separate hide tag and shorten the real one +            if (token.size() > 1 && token[0] == '.') { +                token.erase(token.begin()); +                TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring); +                if (m_exclusion) { +                    pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); +                } +                m_currentFilter.m_patterns.push_back(pattern); +            } + +            TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring); + +            if (m_exclusion) { +                pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); +            } +            m_currentFilter.m_patterns.push_back(pattern); +        } +        m_substring.clear(); +        m_exclusion = false; +        m_mode = None; +    } +      TestSpec parseTestSpec( std::string const& arg ) {          return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();      } @@ -9479,32 +14868,44 @@ namespace Catch {  #include <chrono> +static const uint64_t nanosecondsInSecond = 1000000000; +  namespace Catch {      auto getCurrentNanosecondsSinceEpoch() -> uint64_t {          return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();      } -    auto estimateClockResolution() -> uint64_t { -        uint64_t sum = 0; -        static const uint64_t iterations = 1000000; +    namespace { +        auto estimateClockResolution() -> uint64_t { +            uint64_t sum = 0; +            static const uint64_t iterations = 1000000; + +            auto startTime = getCurrentNanosecondsSinceEpoch(); + +            for( std::size_t i = 0; i < iterations; ++i ) { + +                uint64_t ticks; +                uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); +                do { +                    ticks = getCurrentNanosecondsSinceEpoch(); +                } while( ticks == baseTicks ); -        for( std::size_t i = 0; i < iterations; ++i ) { +                auto delta = ticks - baseTicks; +                sum += delta; -            uint64_t ticks; -            uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); -            do { -                ticks = getCurrentNanosecondsSinceEpoch(); +                // If we have been calibrating for over 3 seconds -- the clock +                // is terrible and we should move on. +                // TBD: How to signal that the measured resolution is probably wrong? +                if (ticks > startTime + 3 * nanosecondsInSecond) { +                    return sum / ( i + 1u ); +                }              } -            while( ticks == baseTicks ); -            auto delta = ticks - baseTicks; -            sum += delta; +            // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers +            // - and potentially do more iterations if there's a high variance. +            return sum/iterations;          } - -        // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers -        // - and potentially do more iterations if there's a high variance. -        return sum/iterations;      }      auto getEstimatedClockResolution() -> uint64_t {          static auto s_resolution = estimateClockResolution(); @@ -9514,11 +14915,11 @@ namespace Catch {      void Timer::start() {         m_nanoseconds = getCurrentNanosecondsSinceEpoch();      } -    auto Timer::getElapsedNanoseconds() const -> unsigned int { -        return static_cast<unsigned int>(getCurrentNanosecondsSinceEpoch() - m_nanoseconds); +    auto Timer::getElapsedNanoseconds() const -> uint64_t { +        return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;      } -    auto Timer::getElapsedMicroseconds() const -> unsigned int { -        return static_cast<unsigned int>(getElapsedNanoseconds()/1000); +    auto Timer::getElapsedMicroseconds() const -> uint64_t { +        return getElapsedNanoseconds()/1000;      }      auto Timer::getElapsedMilliseconds() const -> unsigned int {          return static_cast<unsigned int>(getElapsedMicroseconds()/1000); @@ -9537,6 +14938,12 @@ namespace Catch {  #    pragma clang diagnostic ignored "-Wglobal-constructors"  #endif +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include <cmath>  #include <iomanip>  namespace Catch { @@ -9552,13 +14959,11 @@ namespace Detail {              enum Arch { Big, Little };              static Arch which() { -                union _{ -                    int asInt; -                    char asChar[sizeof (int)]; -                } u; - -                u.asInt = 1; -                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; +                int one = 1; +                // If the lowest byte we read is non-zero, we can assume +                // that little endian format is used. +                auto value = *reinterpret_cast<char*>(&one); +                return value ? Little : Big;              }          };      } @@ -9572,21 +14977,25 @@ namespace Detail {          }          unsigned char const *bytes = static_cast<unsigned char const *>(object); -        std::ostringstream os; -        os << "0x" << std::setfill('0') << std::hex; +        ReusableStringStream rss; +        rss << "0x" << std::setfill('0') << std::hex;          for( ; i != end; i += inc ) -             os << std::setw(2) << static_cast<unsigned>(bytes[i]); -       return os.str(); +             rss << std::setw(2) << static_cast<unsigned>(bytes[i]); +       return rss.str();      }  }  template<typename T>  std::string fpToString( T value, int precision ) { -    std::ostringstream oss; -    oss << std::setprecision( precision ) +    if (Catch::isnan(value)) { +        return "nan"; +    } + +    ReusableStringStream rss; +    rss << std::setprecision( precision )          << std::fixed          << value; -    std::string d = oss.str(); +    std::string d = rss.str();      std::size_t i = d.find_last_not_of( '0' );      if( i != std::string::npos && i != d.size()-1 ) {          if( d[i] == '.' ) @@ -9625,14 +15034,11 @@ std::string StringMaker<std::string>::convert(const std::string& str) {      return s;  } -std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { -    std::string s; -    s.reserve(wstr.size()); -    for (auto c : wstr) { -        s += (c <= 0xff) ? static_cast<char>(c) : '?'; -    } -    return ::Catch::Detail::stringify(s); +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::string_view>::convert(std::string_view str) { +    return ::Catch::Detail::stringify(std::string{ str });  } +#endif  std::string StringMaker<char const*>::convert(char const* str) {      if (str) { @@ -9648,6 +15054,23 @@ std::string StringMaker<char*>::convert(char* str) {          return{ "{null string}" };      }  } + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { +    std::string s; +    s.reserve(wstr.size()); +    for (auto c : wstr) { +        s += (c <= 0xff) ? static_cast<char>(c) : '?'; +    } +    return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) { +    return StringMaker<std::wstring>::convert(std::wstring(str)); +} +# endif +  std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {      if (str) {          return ::Catch::Detail::stringify(std::wstring{ str }); @@ -9662,6 +15085,14 @@ std::string StringMaker<wchar_t *>::convert(wchar_t * str) {          return{ "{null string}" };      }  } +#endif + +#if defined(CATCH_CONFIG_CPP17_BYTE) +#include <cstddef> +std::string StringMaker<std::byte>::convert(std::byte value) { +    return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value)); +} +#endif // defined(CATCH_CONFIG_CPP17_BYTE)  std::string StringMaker<int>::convert(int value) {      return ::Catch::Detail::stringify(static_cast<long long>(value)); @@ -9670,12 +15101,12 @@ std::string StringMaker<long>::convert(long value) {      return ::Catch::Detail::stringify(static_cast<long long>(value));  }  std::string StringMaker<long long>::convert(long long value) { -    std::ostringstream oss; -    oss << value; +    ReusableStringStream rss; +    rss << value;      if (value > Detail::hexThreshold) { -        oss << " (0x" << std::hex << value << ')'; +        rss << " (0x" << std::hex << value << ')';      } -    return oss.str(); +    return rss.str();  }  std::string StringMaker<unsigned int>::convert(unsigned int value) { @@ -9685,19 +15116,19 @@ std::string StringMaker<unsigned long>::convert(unsigned long value) {      return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));  }  std::string StringMaker<unsigned long long>::convert(unsigned long long value) { -    std::ostringstream oss; -    oss << value; +    ReusableStringStream rss; +    rss << value;      if (value > Detail::hexThreshold) { -        oss << " (0x" << std::hex << value << ')'; +        rss << " (0x" << std::hex << value << ')';      } -    return oss.str(); +    return rss.str();  }  std::string StringMaker<bool>::convert(bool b) {      return b ? "true" : "false";  } -std::string StringMaker<char>::convert(char value) { +std::string StringMaker<signed char>::convert(signed char value) {      if (value == '\r') {          return "'\\r'";      } else if (value == '\f') { @@ -9714,8 +15145,8 @@ std::string StringMaker<char>::convert(char value) {          return chstr;      }  } -std::string StringMaker<signed char>::convert(signed char c) { -    return ::Catch::Detail::stringify(static_cast<char>(c)); +std::string StringMaker<char>::convert(char c) { +    return ::Catch::Detail::stringify(static_cast<signed char>(c));  }  std::string StringMaker<unsigned char>::convert(unsigned char c) {      return ::Catch::Detail::stringify(static_cast<char>(c)); @@ -9725,13 +15156,25 @@ std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {      return "nullptr";  } +int StringMaker<float>::precision = 5; +  std::string StringMaker<float>::convert(float value) { -    return fpToString(value, 5) + 'f'; +    return fpToString(value, precision) + 'f';  } + +int StringMaker<double>::precision = 10; +  std::string StringMaker<double>::convert(double value) { -    return fpToString(value, 10); +    return fpToString(value, precision);  } +std::string ratio_string<std::atto>::symbol() { return "a"; } +std::string ratio_string<std::femto>::symbol() { return "f"; } +std::string ratio_string<std::pico>::symbol() { return "p"; } +std::string ratio_string<std::nano>::symbol() { return "n"; } +std::string ratio_string<std::micro>::symbol() { return "u"; } +std::string ratio_string<std::milli>::symbol() { return "m"; } +  } // end namespace Catch  #if defined(__clang__) @@ -9794,6 +15237,57 @@ namespace Catch {  }  // end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +// start catch_config_uncaught_exceptions.hpp + +//              Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +//   (See accompanying file LICENSE_1_0.txt or copy at +//        https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP + +#if defined(_MSC_VER) +#  if _MSC_VER >= 1900 // Visual Studio 2015 or newer +#    define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#  endif +#endif + +#include <exception> + +#if defined(__cpp_lib_uncaught_exceptions) \ +    && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#  define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif // __cpp_lib_uncaught_exceptions + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \ +    && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ +    && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#  define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +// end catch_config_uncaught_exceptions.hpp +#include <exception> + +namespace Catch { +    bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +        return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +        return std::uncaught_exceptions() > 0; +#else +        return std::uncaught_exception(); +#endif +  } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp  // start catch_version.cpp  #include <ostream> @@ -9826,7 +15320,7 @@ namespace Catch {      }      Version const& libraryVersion() { -        static Version version( 2, 0, 1, "", 0 ); +        static Version version( 2, 13, 4, "", 0 );          return version;      } @@ -9839,7 +15333,7 @@ namespace Catch {      WildcardPattern::WildcardPattern( std::string const& pattern,                                        CaseSensitive::Choice caseSensitivity )      :   m_caseSensitivity( caseSensitivity ), -        m_pattern( adjustCase( pattern ) ) +        m_pattern( normaliseString( pattern ) )      {          if( startsWith( m_pattern, '*' ) ) {              m_pattern = m_pattern.substr( 1 ); @@ -9854,125 +15348,89 @@ namespace Catch {      bool WildcardPattern::matches( std::string const& str ) const {          switch( m_wildcard ) {              case NoWildcard: -                return m_pattern == adjustCase( str ); +                return m_pattern == normaliseString( str );              case WildcardAtStart: -                return endsWith( adjustCase( str ), m_pattern ); +                return endsWith( normaliseString( str ), m_pattern );              case WildcardAtEnd: -                return startsWith( adjustCase( str ), m_pattern ); +                return startsWith( normaliseString( str ), m_pattern );              case WildcardAtBothEnds: -                return contains( adjustCase( str ), m_pattern ); +                return contains( normaliseString( str ), m_pattern );              default:                  CATCH_INTERNAL_ERROR( "Unknown enum" );          }      } -    std::string WildcardPattern::adjustCase( std::string const& str ) const { -        return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; +    std::string WildcardPattern::normaliseString( std::string const& str ) const { +        return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );      }  }  // end catch_wildcard_pattern.cpp  // start catch_xmlwriter.cpp -// start catch_xmlwriter.h - -#include <sstream> -#include <vector> +#include <iomanip> +#include <type_traits>  namespace Catch { -    class XmlEncode { -    public: -        enum ForWhat { ForTextNodes, ForAttributes }; - -        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); - -        void encodeTo( std::ostream& os ) const; - -        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); - -    private: -        std::string m_str; -        ForWhat m_forWhat; -    }; - -    class XmlWriter { -    public: - -        class ScopedElement { -        public: -            ScopedElement( XmlWriter* writer ); - -            ScopedElement( ScopedElement&& other ) noexcept; -            ScopedElement& operator=( ScopedElement&& other ) noexcept; - -            ~ScopedElement(); - -            ScopedElement& writeText( std::string const& text, bool indent = true ); - -            template<typename T> -            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { -                m_writer->writeAttribute( name, attribute ); -                return *this; -            } - -        private: -            mutable XmlWriter* m_writer = nullptr; -        }; - -        XmlWriter( std::ostream& os = Catch::cout() ); -        ~XmlWriter(); - -        XmlWriter( XmlWriter const& ) = delete; -        XmlWriter& operator=( XmlWriter const& ) = delete; - -        XmlWriter& startElement( std::string const& name ); - -        ScopedElement scopedElement( std::string const& name ); - -        XmlWriter& endElement(); - -        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); - -        XmlWriter& writeAttribute( std::string const& name, bool attribute ); +namespace { -        template<typename T> -        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { -            m_oss.clear(); -            m_oss.str(std::string()); -            m_oss << attribute; -            return writeAttribute( name, m_oss.str() ); +    size_t trailingBytes(unsigned char c) { +        if ((c & 0xE0) == 0xC0) { +            return 2;          } +        if ((c & 0xF0) == 0xE0) { +            return 3; +        } +        if ((c & 0xF8) == 0xF0) { +            return 4; +        } +        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); +    } -        XmlWriter& writeText( std::string const& text, bool indent = true ); - -        XmlWriter& writeComment( std::string const& text ); - -        void writeStylesheetRef( std::string const& url ); - -        XmlWriter& writeBlankLine(); - -        void ensureTagClosed(); - -    private: +    uint32_t headerValue(unsigned char c) { +        if ((c & 0xE0) == 0xC0) { +            return c & 0x1F; +        } +        if ((c & 0xF0) == 0xE0) { +            return c & 0x0F; +        } +        if ((c & 0xF8) == 0xF0) { +            return c & 0x07; +        } +        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); +    } -        void writeDeclaration(); +    void hexEscapeChar(std::ostream& os, unsigned char c) { +        std::ios_base::fmtflags f(os.flags()); +        os << "\\x" +            << std::uppercase << std::hex << std::setfill('0') << std::setw(2) +            << static_cast<int>(c); +        os.flags(f); +    } -        void newlineIfNecessary(); +    bool shouldNewline(XmlFormatting fmt) { +        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline)); +    } -        bool m_tagIsOpen = false; -        bool m_needsNewline = false; -        std::vector<std::string> m_tags; -        std::string m_indent; -        std::ostream& m_os; -        std::ostringstream m_oss; -    }; +    bool shouldIndent(XmlFormatting fmt) { +        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent)); +    } -} +} // anonymous namespace -// end catch_xmlwriter.h -#include <iomanip> +    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { +        return static_cast<XmlFormatting>( +            static_cast<std::underlying_type<XmlFormatting>::type>(lhs) | +            static_cast<std::underlying_type<XmlFormatting>::type>(rhs) +        ); +    } -namespace Catch { +    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { +        return static_cast<XmlFormatting>( +            static_cast<std::underlying_type<XmlFormatting>::type>(lhs) & +            static_cast<std::underlying_type<XmlFormatting>::type>(rhs) +        ); +    }      XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )      :   m_str( str ), @@ -9980,41 +15438,95 @@ namespace Catch {      {}      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) -        for( std::size_t i = 0; i < m_str.size(); ++ i ) { -            char c = m_str[i]; -            switch( c ) { -                case '<':   os << "<"; break; -                case '&':   os << "&"; break; +        for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { +            unsigned char c = m_str[idx]; +            switch (c) { +            case '<':   os << "<"; break; +            case '&':   os << "&"; break; -                case '>': -                    // See: http://www.w3.org/TR/xml/#syntax -                    if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) -                        os << ">"; -                    else -                        os << c; +            case '>': +                // See: http://www.w3.org/TR/xml/#syntax +                if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') +                    os << ">"; +                else +                    os << c; +                break; + +            case '\"': +                if (m_forWhat == ForAttributes) +                    os << """; +                else +                    os << c; +                break; + +            default: +                // 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 +                if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { +                    hexEscapeChar(os, c);                      break; +                } -                case '\"': -                    if( m_forWhat == ForAttributes ) -                        os << """; -                    else -                        os << c; +                // Plain ASCII: Write it to stream +                if (c < 0x7F) { +                    os << c;                      break; +                } -                default: -                    // Escape control chars - based on contribution by @espenalb in PR #465 and -                    // by @mrpi PR #588 -                    if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { -                        // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 -                        os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) -                           << static_cast<int>( c ); -                    } -                    else -                        os << c; +                // UTF-8 territory +                // Check if the encoding is valid and if it is not, hex escape bytes. +                // Important: We do not check the exact decoded values for validity, only the encoding format +                // First check that this bytes is a valid lead byte: +                // This means that it is not encoded as 1111 1XXX +                // Or as 10XX XXXX +                if (c <  0xC0 || +                    c >= 0xF8) { +                    hexEscapeChar(os, c); +                    break; +                } + +                auto encBytes = trailingBytes(c); +                // Are there enough bytes left to avoid accessing out-of-bounds memory? +                if (idx + encBytes - 1 >= m_str.size()) { +                    hexEscapeChar(os, c); +                    break; +                } +                // The header is valid, check data +                // The next encBytes bytes must together be a valid utf-8 +                // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) +                bool valid = true; +                uint32_t value = headerValue(c); +                for (std::size_t n = 1; n < encBytes; ++n) { +                    unsigned char nc = m_str[idx + n]; +                    valid &= ((nc & 0xC0) == 0x80); +                    value = (value << 6) | (nc & 0x3F); +                } + +                if ( +                    // Wrong bit pattern of following bytes +                    (!valid) || +                    // Overlong encodings +                    (value < 0x80) || +                    (0x80 <= value && value < 0x800   && encBytes > 2) || +                    (0x800 < value && value < 0x10000 && encBytes > 3) || +                    // Encoded value out of range +                    (value >= 0x110000) +                    ) { +                    hexEscapeChar(os, c); +                    break; +                } + +                // If we got here, this is in fact a valid(ish) utf-8 sequence +                for (std::size_t n = 0; n < encBytes; ++n) { +                    os << m_str[idx + n]; +                } +                idx += encBytes - 1; +                break;              }          }      } @@ -10024,13 +15536,17 @@ namespace Catch {          return os;      } -    XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) -    :   m_writer( writer ) +    XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt ) +    :   m_writer( writer ), +        m_fmt(fmt)      {}      XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept -    :   m_writer( other.m_writer ){ +    :   m_writer( other.m_writer ), +        m_fmt(other.m_fmt) +    {          other.m_writer = nullptr; +        other.m_fmt = XmlFormatting::None;      }      XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {          if ( m_writer ) { @@ -10038,16 +15554,19 @@ namespace Catch {          }          m_writer = other.m_writer;          other.m_writer = nullptr; +        m_fmt = other.m_fmt; +        other.m_fmt = XmlFormatting::None;          return *this;      }      XmlWriter::ScopedElement::~ScopedElement() { -        if( m_writer ) -            m_writer->endElement(); +        if (m_writer) { +            m_writer->endElement(m_fmt); +        }      } -    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { -        m_writer->writeText( text, indent ); +    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) { +        m_writer->writeText( text, fmt );          return *this;      } @@ -10057,37 +15576,47 @@ namespace Catch {      }      XmlWriter::~XmlWriter() { -        while( !m_tags.empty() ) +        while (!m_tags.empty()) {              endElement(); +        } +        newlineIfNecessary();      } -    XmlWriter& XmlWriter::startElement( std::string const& name ) { +    XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {          ensureTagClosed();          newlineIfNecessary(); -        m_os << m_indent << '<' << name; +        if (shouldIndent(fmt)) { +            m_os << m_indent; +            m_indent += "  "; +        } +        m_os << '<' << name;          m_tags.push_back( name ); -        m_indent += "  ";          m_tagIsOpen = true; +        applyFormatting(fmt);          return *this;      } -    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { -        ScopedElement scoped( this ); -        startElement( name ); +    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) { +        ScopedElement scoped( this, fmt ); +        startElement( name, fmt );          return scoped;      } -    XmlWriter& XmlWriter::endElement() { -        newlineIfNecessary(); -        m_indent = m_indent.substr( 0, m_indent.size()-2 ); +    XmlWriter& XmlWriter::endElement(XmlFormatting fmt) { +        m_indent = m_indent.substr(0, m_indent.size() - 2); +          if( m_tagIsOpen ) {              m_os << "/>";              m_tagIsOpen = false; +        } else { +            newlineIfNecessary(); +            if (shouldIndent(fmt)) { +                m_os << m_indent; +            } +            m_os << "</" << m_tags.back() << ">";          } -        else { -            m_os << m_indent << "</" << m_tags.back() << ">"; -        } -        m_os << std::endl; +        m_os << std::flush; +        applyFormatting(fmt);          m_tags.pop_back();          return *this;      } @@ -10103,22 +15632,26 @@ namespace Catch {          return *this;      } -    XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { +    XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {          if( !text.empty() ){              bool tagWasOpen = m_tagIsOpen;              ensureTagClosed(); -            if( tagWasOpen && indent ) +            if (tagWasOpen && shouldIndent(fmt)) {                  m_os << m_indent; +            }              m_os << XmlEncode( text ); -            m_needsNewline = true; +            applyFormatting(fmt);          }          return *this;      } -    XmlWriter& XmlWriter::writeComment( std::string const& text ) { +    XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {          ensureTagClosed(); -        m_os << m_indent << "<!--" << text << "-->"; -        m_needsNewline = true; +        if (shouldIndent(fmt)) { +            m_os << m_indent; +        } +        m_os << "<!--" << text << "-->"; +        applyFormatting(fmt);          return *this;      } @@ -10134,11 +15667,16 @@ namespace Catch {      void XmlWriter::ensureTagClosed() {          if( m_tagIsOpen ) { -            m_os << ">" << std::endl; +            m_os << '>' << std::flush; +            newlineIfNecessary();              m_tagIsOpen = false;          }      } +    void XmlWriter::applyFormatting(XmlFormatting fmt) { +        m_needsNewline = shouldNewline(fmt); +    } +      void XmlWriter::writeDeclaration() {          m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";      } @@ -10156,7 +15694,7 @@ namespace Catch {  #include <cstring>  #include <cfloat>  #include <cstdio> -#include <assert.h> +#include <cassert>  #include <memory>  namespace Catch { @@ -10179,14 +15717,44 @@ namespace Catch {  #ifdef _MSC_VER          sprintf_s(buffer, "%.3f", duration);  #else -        sprintf(buffer, "%.3f", duration); +        std::sprintf(buffer, "%.3f", duration);  #endif          return std::string(buffer);      } +    bool shouldShowDuration( IConfig const& config, double duration ) { +        if ( config.showDurations() == ShowDurations::Always ) { +            return true; +        } +        if ( config.showDurations() == ShowDurations::Never ) { +            return false; +        } +        const double min = config.minDuration(); +        return min >= 0 && duration >= min; +    } + +    std::string serializeFilters( std::vector<std::string> const& container ) { +        ReusableStringStream oss; +        bool first = true; +        for (auto&& filter : container) +        { +            if (!first) +                oss << ' '; +            else +                first = false; + +            oss << filter; +        } +        return oss.str(); +    } +      TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)          :StreamingReporterBase(_config) {} +    std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() { +        return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High }; +    } +      void TestEventListenerBase::assertionStarting(AssertionInfo const &) {}      bool TestEventListenerBase::assertionEnded(AssertionStats const &) { @@ -10214,33 +15782,230 @@ namespace {          return count == 1 ? std::string() :                 count == 2 ? "both " : "all " ;      } -} + +} // anon namespace  namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// -   red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// -   red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { +    if (totals.testCases.total() == 0) { +        out << "No tests ran."; +    } else if (totals.testCases.failed == totals.testCases.total()) { +        Colour colour(Colour::ResultError); +        const std::string qualify_assertions_failed = +            totals.assertions.failed == totals.assertions.total() ? +            bothOrAll(totals.assertions.failed) : std::string(); +        out << +            "Failed " << bothOrAll(totals.testCases.failed) +            << pluralise(totals.testCases.failed, "test case") << ", " +            "failed " << qualify_assertions_failed << +            pluralise(totals.assertions.failed, "assertion") << '.'; +    } else if (totals.assertions.total() == 0) { +        out << +            "Passed " << bothOrAll(totals.testCases.total()) +            << pluralise(totals.testCases.total(), "test case") +            << " (no assertions)."; +    } else if (totals.assertions.failed) { +        Colour colour(Colour::ResultError); +        out << +            "Failed " << pluralise(totals.testCases.failed, "test case") << ", " +            "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; +    } else { +        Colour colour(Colour::ResultSuccess); +        out << +            "Passed " << bothOrAll(totals.testCases.passed) +            << pluralise(totals.testCases.passed, "test case") << +            " with " << pluralise(totals.assertions.passed, "assertion") << '.'; +    } +} -    struct CompactReporter : StreamingReporterBase<CompactReporter> { +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: +    AssertionPrinter& operator= (AssertionPrinter const&) = delete; +    AssertionPrinter(AssertionPrinter const&) = delete; +    AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) +        : stream(_stream) +        , result(_stats.assertionResult) +        , messages(_stats.infoMessages) +        , itMessage(_stats.infoMessages.begin()) +        , printInfoMessages(_printInfoMessages) {} + +    void print() { +        printSourceInfo(); + +        itMessage = messages.begin(); + +        switch (result.getResultType()) { +        case ResultWas::Ok: +            printResultType(Colour::ResultSuccess, passedString()); +            printOriginalExpression(); +            printReconstructedExpression(); +            if (!result.hasExpression()) +                printRemainingMessages(Colour::None); +            else +                printRemainingMessages(); +            break; +        case ResultWas::ExpressionFailed: +            if (result.isOk()) +                printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); +            else +                printResultType(Colour::Error, failedString()); +            printOriginalExpression(); +            printReconstructedExpression(); +            printRemainingMessages(); +            break; +        case ResultWas::ThrewException: +            printResultType(Colour::Error, failedString()); +            printIssue("unexpected exception with message:"); +            printMessage(); +            printExpressionWas(); +            printRemainingMessages(); +            break; +        case ResultWas::FatalErrorCondition: +            printResultType(Colour::Error, failedString()); +            printIssue("fatal error condition with message:"); +            printMessage(); +            printExpressionWas(); +            printRemainingMessages(); +            break; +        case ResultWas::DidntThrowException: +            printResultType(Colour::Error, failedString()); +            printIssue("expected exception, got none"); +            printExpressionWas(); +            printRemainingMessages(); +            break; +        case ResultWas::Info: +            printResultType(Colour::None, "info"); +            printMessage(); +            printRemainingMessages(); +            break; +        case ResultWas::Warning: +            printResultType(Colour::None, "warning"); +            printMessage(); +            printRemainingMessages(); +            break; +        case ResultWas::ExplicitFailure: +            printResultType(Colour::Error, failedString()); +            printIssue("explicitly"); +            printRemainingMessages(Colour::None); +            break; +            // These cases are here to prevent compiler warnings +        case ResultWas::Unknown: +        case ResultWas::FailureBit: +        case ResultWas::Exception: +            printResultType(Colour::Error, "** internal error **"); +            break; +        } +    } -        using StreamingReporterBase::StreamingReporterBase; +private: +    void printSourceInfo() const { +        Colour colourGuard(Colour::FileName); +        stream << result.getSourceInfo() << ':'; +    } -        ~CompactReporter() override; +    void printResultType(Colour::Code colour, std::string const& passOrFail) const { +        if (!passOrFail.empty()) { +            { +                Colour colourGuard(colour); +                stream << ' ' << passOrFail; +            } +            stream << ':'; +        } +    } -        static std::string getDescription() { -            return "Reports test results on a single line, suitable for IDEs"; +    void printIssue(std::string const& issue) const { +        stream << ' ' << issue; +    } + +    void printExpressionWas() { +        if (result.hasExpression()) { +            stream << ';'; +            { +                Colour colour(dimColour()); +                stream << " expression was:"; +            } +            printOriginalExpression();          } +    } -        ReporterPreferences getPreferences() const override { -            ReporterPreferences prefs; -            prefs.shouldRedirectStdOut = false; -            return prefs; +    void printOriginalExpression() const { +        if (result.hasExpression()) { +            stream << ' ' << result.getExpression();          } +    } -        void noMatchingTestCases( std::string const& spec ) override { +    void printReconstructedExpression() const { +        if (result.hasExpandedExpression()) { +            { +                Colour colour(dimColour()); +                stream << " for: "; +            } +            stream << result.getExpandedExpression(); +        } +    } + +    void printMessage() { +        if (itMessage != messages.end()) { +            stream << " '" << itMessage->message << '\''; +            ++itMessage; +        } +    } + +    void printRemainingMessages(Colour::Code colour = dimColour()) { +        if (itMessage == messages.end()) +            return; + +        const auto itEnd = messages.cend(); +        const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd)); + +        { +            Colour colourGuard(colour); +            stream << " with " << pluralise(N, "message") << ':'; +        } + +        while (itMessage != itEnd) { +            // If this assertion is a warning ignore any INFO messages +            if (printInfoMessages || itMessage->type != ResultWas::Info) { +                printMessage(); +                if (itMessage != itEnd) { +                    Colour colourGuard(dimColour()); +                    stream << " and"; +                } +                continue; +            } +            ++itMessage; +        } +    } + +private: +    std::ostream& stream; +    AssertionResult const& result; +    std::vector<MessageInfo> messages; +    std::vector<MessageInfo>::const_iterator itMessage; +    bool printInfoMessages; +}; + +} // anon namespace + +        std::string CompactReporter::getDescription() { +            return "Reports test results on a single line, suitable for IDEs"; +        } + +        void CompactReporter::noMatchingTestCases( std::string const& spec ) {              stream << "No test cases matched '" << spec << '\'' << std::endl;          } -        void assertionStarting( AssertionInfo const& ) override {} +        void CompactReporter::assertionStarting( AssertionInfo const& ) {} -        bool assertionEnded( AssertionStats const& _assertionStats ) override { +        bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {              AssertionResult const& result = _assertionStats.assertionResult;              bool printInfoMessages = true; @@ -10259,231 +16024,20 @@ namespace Catch {              return true;          } -        void sectionEnded(SectionStats const& _sectionStats) override { -            if (m_config->showDurations() == ShowDurations::Always) { -                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; +        void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { +            double dur = _sectionStats.durationInSeconds; +            if ( shouldShowDuration( *m_config, dur ) ) { +                stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;              }          } -        void testRunEnded( TestRunStats const& _testRunStats ) override { -            printTotals( _testRunStats.totals ); +        void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { +            printTotals( stream, _testRunStats.totals );              stream << '\n' << std::endl;              StreamingReporterBase::testRunEnded( _testRunStats );          } -    private: -        class AssertionPrinter { -        public: -            AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; -            AssertionPrinter( AssertionPrinter const& ) = delete; -            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) -            : stream( _stream ) -            , result( _stats.assertionResult ) -            , messages( _stats.infoMessages ) -            , itMessage( _stats.infoMessages.begin() ) -            , printInfoMessages( _printInfoMessages ) -            {} - -            void print() { -                printSourceInfo(); - -                itMessage = messages.begin(); - -                switch( result.getResultType() ) { -                    case ResultWas::Ok: -                        printResultType( Colour::ResultSuccess, passedString() ); -                        printOriginalExpression(); -                        printReconstructedExpression(); -                        if ( ! result.hasExpression() ) -                            printRemainingMessages( Colour::None ); -                        else -                            printRemainingMessages(); -                        break; -                    case ResultWas::ExpressionFailed: -                        if( result.isOk() ) -                            printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); -                        else -                            printResultType( Colour::Error, failedString() ); -                        printOriginalExpression(); -                        printReconstructedExpression(); -                        printRemainingMessages(); -                        break; -                    case ResultWas::ThrewException: -                        printResultType( Colour::Error, failedString() ); -                        printIssue( "unexpected exception with message:" ); -                        printMessage(); -                        printExpressionWas(); -                        printRemainingMessages(); -                        break; -                    case ResultWas::FatalErrorCondition: -                        printResultType( Colour::Error, failedString() ); -                        printIssue( "fatal error condition with message:" ); -                        printMessage(); -                        printExpressionWas(); -                        printRemainingMessages(); -                        break; -                    case ResultWas::DidntThrowException: -                        printResultType( Colour::Error, failedString() ); -                        printIssue( "expected exception, got none" ); -                        printExpressionWas(); -                        printRemainingMessages(); -                        break; -                    case ResultWas::Info: -                        printResultType( Colour::None, "info" ); -                        printMessage(); -                        printRemainingMessages(); -                        break; -                    case ResultWas::Warning: -                        printResultType( Colour::None, "warning" ); -                        printMessage(); -                        printRemainingMessages(); -                        break; -                    case ResultWas::ExplicitFailure: -                        printResultType( Colour::Error, failedString() ); -                        printIssue( "explicitly" ); -                        printRemainingMessages( Colour::None ); -                        break; -                    // These cases are here to prevent compiler warnings -                    case ResultWas::Unknown: -                    case ResultWas::FailureBit: -                    case ResultWas::Exception: -                        printResultType( Colour::Error, "** internal error **" ); -                        break; -                } -            } - -        private: -            void printSourceInfo() const { -                Colour colourGuard( Colour::FileName ); -                stream << result.getSourceInfo() << ':'; -            } - -            void printResultType( Colour::Code colour, std::string const& passOrFail ) const { -                if( !passOrFail.empty() ) { -                    { -                        Colour colourGuard( colour ); -                        stream << ' ' << passOrFail; -                    } -                    stream << ':'; -                } -            } - -            void printIssue( std::string const& issue ) const { -                stream << ' ' << issue; -            } - -            void printExpressionWas() { -                if( result.hasExpression() ) { -                    stream << ';'; -                    { -                        Colour colour( dimColour() ); -                        stream << " expression was:"; -                    } -                    printOriginalExpression(); -                } -            } - -            void printOriginalExpression() const { -                if( result.hasExpression() ) { -                    stream << ' ' << result.getExpression(); -                } -            } - -            void printReconstructedExpression() const { -                if( result.hasExpandedExpression() ) { -                    { -                        Colour colour( dimColour() ); -                        stream << " for: "; -                    } -                    stream << result.getExpandedExpression(); -                } -            } - -            void printMessage() { -                if ( itMessage != messages.end() ) { -                    stream << " '" << itMessage->message << '\''; -                    ++itMessage; -                } -            } - -            void printRemainingMessages( Colour::Code colour = dimColour() ) { -                if ( itMessage == messages.end() ) -                    return; - -                // using messages.end() directly yields (or auto) compilation error: -                std::vector<MessageInfo>::const_iterator itEnd = messages.end(); -                const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) ); - -                { -                    Colour colourGuard( colour ); -                    stream << " with " << pluralise( N, "message" ) << ':'; -                } - -                for(; itMessage != itEnd; ) { -                    // If this assertion is a warning ignore any INFO messages -                    if( printInfoMessages || itMessage->type != ResultWas::Info ) { -                        stream << " '" << itMessage->message << '\''; -                        if ( ++itMessage != itEnd ) { -                            Colour colourGuard( dimColour() ); -                            stream << " and"; -                        } -                    } -                } -            } - -        private: -            std::ostream& stream; -            AssertionResult const& result; -            std::vector<MessageInfo> messages; -            std::vector<MessageInfo>::const_iterator itMessage; -            bool printInfoMessages; -        }; - -        // Colour, message variants: -        // - white: No tests ran. -        // -   red: Failed [both/all] N test cases, failed [both/all] M assertions. -        // - white: Passed [both/all] N test cases (no assertions). -        // -   red: Failed N tests cases, failed M assertions. -        // - green: Passed [both/all] N tests cases with M assertions. - -        void printTotals( const Totals& totals ) const { -            if( totals.testCases.total() == 0 ) { -                stream << "No tests ran."; -            } -            else if( totals.testCases.failed == totals.testCases.total() ) { -                Colour colour( Colour::ResultError ); -                const std::string qualify_assertions_failed = -                    totals.assertions.failed == totals.assertions.total() ? -                        bothOrAll( totals.assertions.failed ) : std::string(); -                stream << -                    "Failed " << bothOrAll( totals.testCases.failed ) -                              << pluralise( totals.testCases.failed, "test case"  ) << ", " -                    "failed " << qualify_assertions_failed << -                                 pluralise( totals.assertions.failed, "assertion" ) << '.'; -            } -            else if( totals.assertions.total() == 0 ) { -                stream << -                    "Passed " << bothOrAll( totals.testCases.total() ) -                              << pluralise( totals.testCases.total(), "test case" ) -                              << " (no assertions)."; -            } -            else if( totals.assertions.failed ) { -                Colour colour( Colour::ResultError ); -                stream << -                    "Failed " << pluralise( totals.testCases.failed, "test case"  ) << ", " -                    "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; -            } -            else { -                Colour colour( Colour::ResultSuccess ); -                stream << -                    "Passed " << bothOrAll( totals.testCases.passed ) -                              << pluralise( totals.testCases.passed, "test case"  ) << -                    " with "  << pluralise( totals.assertions.passed, "assertion" ) << '.'; -            } -        } -    }; - -    CompactReporter::~CompactReporter() {} +        CompactReporter::~CompactReporter() {}      CATCH_REGISTER_REPORTER( "compact", CompactReporter ) @@ -10497,640 +16051,686 @@ namespace Catch {  #if defined(_MSC_VER)  #pragma warning(push)  #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch -                              // Note that 4062 (not all labels are handled -                              // and default is missing) is enabled + // Note that 4062 (not all labels are handled and default is missing) is enabled +#endif + +#if defined(__clang__) +#  pragma clang diagnostic push +// For simplicity, benchmarking-only helpers are always enabled +#  pragma clang diagnostic ignored "-Wunused-function"  #endif  namespace Catch { -    namespace { -        std::size_t makeRatio( std::size_t number, std::size_t total ) { -            std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; -            return ( ratio == 0 && number > 0 ) ? 1 : ratio; +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: +    ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; +    ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; +    ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) +        : stream(_stream), +        stats(_stats), +        result(_stats.assertionResult), +        colour(Colour::None), +        message(result.getMessage()), +        messages(_stats.infoMessages), +        printInfoMessages(_printInfoMessages) { +        switch (result.getResultType()) { +        case ResultWas::Ok: +            colour = Colour::Success; +            passOrFail = "PASSED"; +            //if( result.hasMessage() ) +            if (_stats.infoMessages.size() == 1) +                messageLabel = "with message"; +            if (_stats.infoMessages.size() > 1) +                messageLabel = "with messages"; +            break; +        case ResultWas::ExpressionFailed: +            if (result.isOk()) { +                colour = Colour::Success; +                passOrFail = "FAILED - but was ok"; +            } else { +                colour = Colour::Error; +                passOrFail = "FAILED"; +            } +            if (_stats.infoMessages.size() == 1) +                messageLabel = "with message"; +            if (_stats.infoMessages.size() > 1) +                messageLabel = "with messages"; +            break; +        case ResultWas::ThrewException: +            colour = Colour::Error; +            passOrFail = "FAILED"; +            messageLabel = "due to unexpected exception with "; +            if (_stats.infoMessages.size() == 1) +                messageLabel += "message"; +            if (_stats.infoMessages.size() > 1) +                messageLabel += "messages"; +            break; +        case ResultWas::FatalErrorCondition: +            colour = Colour::Error; +            passOrFail = "FAILED"; +            messageLabel = "due to a fatal error condition"; +            break; +        case ResultWas::DidntThrowException: +            colour = Colour::Error; +            passOrFail = "FAILED"; +            messageLabel = "because no exception was thrown where one was expected"; +            break; +        case ResultWas::Info: +            messageLabel = "info"; +            break; +        case ResultWas::Warning: +            messageLabel = "warning"; +            break; +        case ResultWas::ExplicitFailure: +            passOrFail = "FAILED"; +            colour = Colour::Error; +            if (_stats.infoMessages.size() == 1) +                messageLabel = "explicitly with message"; +            if (_stats.infoMessages.size() > 1) +                messageLabel = "explicitly with messages"; +            break; +            // These cases are here to prevent compiler warnings +        case ResultWas::Unknown: +        case ResultWas::FailureBit: +        case ResultWas::Exception: +            passOrFail = "** internal error **"; +            colour = Colour::Error; +            break;          } +    } -        std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { -            if( i > j && i > k ) -                return i; -            else if( j > k ) -                return j; -            else -                return k; +    void print() const { +        printSourceInfo(); +        if (stats.totals.assertions.total() > 0) { +            printResultType(); +            printOriginalExpression(); +            printReconstructedExpression(); +        } else { +            stream << '\n';          } +        printMessage(); +    } -        struct ColumnInfo { -            enum Justification { Left, Right }; -            std::string name; -            int width; -            Justification justification; -        }; -        struct ColumnBreak {}; -        struct RowBreak {}; +private: +    void printResultType() const { +        if (!passOrFail.empty()) { +            Colour colourGuard(colour); +            stream << passOrFail << ":\n"; +        } +    } +    void printOriginalExpression() const { +        if (result.hasExpression()) { +            Colour colourGuard(Colour::OriginalExpression); +            stream << "  "; +            stream << result.getExpressionInMacro(); +            stream << '\n'; +        } +    } +    void printReconstructedExpression() const { +        if (result.hasExpandedExpression()) { +            stream << "with expansion:\n"; +            Colour colourGuard(Colour::ReconstructedExpression); +            stream << Column(result.getExpandedExpression()).indent(2) << '\n'; +        } +    } +    void printMessage() const { +        if (!messageLabel.empty()) +            stream << messageLabel << ':' << '\n'; +        for (auto const& msg : messages) { +            // If this assertion is a warning ignore any INFO messages +            if (printInfoMessages || msg.type != ResultWas::Info) +                stream << Column(msg.message).indent(2) << '\n'; +        } +    } +    void printSourceInfo() const { +        Colour colourGuard(Colour::FileName); +        stream << result.getSourceInfo() << ": "; +    } -        class TablePrinter { -            std::ostream& m_os; -            std::vector<ColumnInfo> m_columnInfos; -            std::ostringstream m_oss; -            int m_currentColumn = -1; -            bool m_isOpen = false; +    std::ostream& stream; +    AssertionStats const& stats; +    AssertionResult const& result; +    Colour::Code colour; +    std::string passOrFail; +    std::string messageLabel; +    std::string message; +    std::vector<MessageInfo> messages; +    bool printInfoMessages; +}; -        public: -            TablePrinter( std::ostream& os, std::vector<ColumnInfo> const& columnInfos ) -            :   m_os( os ), -                m_columnInfos( columnInfos ) -            {} +std::size_t makeRatio(std::size_t number, std::size_t total) { +    std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; +    return (ratio == 0 && number > 0) ? 1 : ratio; +} -            auto columnInfos() const -> std::vector<ColumnInfo> const& { -                return m_columnInfos; -            } +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { +    if (i > j && i > k) +        return i; +    else if (j > k) +        return j; +    else +        return k; +} -            void open() { -                if( !m_isOpen ) { -                    m_isOpen = true; -                    *this << RowBreak(); -                    for( auto const& info : m_columnInfos ) -                        *this << info.name << ColumnBreak(); -                    *this << RowBreak(); -                    m_os << Catch::getLineOfChars<'-'>() << "\n"; -                } -            } -            void close() { -                if( m_isOpen ) { -                    *this << RowBreak(); -                    m_os << std::endl; -                    m_isOpen = false; -                } -            } +struct ColumnInfo { +    enum Justification { Left, Right }; +    std::string name; +    int width; +    Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; -            template<typename T> -            friend TablePrinter& operator << ( TablePrinter& tp, T const& value ) { -                tp.m_oss << value; -                return tp; -            } - -            friend TablePrinter& operator << ( TablePrinter& tp, ColumnBreak ) { -                auto colStr = tp.m_oss.str(); -                // This takes account of utf8 encodings -                auto strSize = Catch::StringRef( colStr ).numberOfCharacters(); -                tp.m_oss.str(""); -                tp.open(); -                if( tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size()-1) ) { -                    tp.m_currentColumn = -1; -                    tp.m_os << "\n"; -                } -                tp.m_currentColumn++; - -                auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; -                auto padding = ( strSize+2 < static_cast<std::size_t>( colInfo.width ) ) -                    ? std::string( colInfo.width-(strSize+2), ' ' ) -                    : std::string(); -                if( colInfo.justification == ColumnInfo::Left ) -                    tp.m_os << colStr << padding << " "; -                else -                    tp.m_os << padding << colStr << " "; -                return tp; -            } +class Duration { +    enum class Unit { +        Auto, +        Nanoseconds, +        Microseconds, +        Milliseconds, +        Seconds, +        Minutes +    }; +    static const uint64_t s_nanosecondsInAMicrosecond = 1000; +    static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; +    static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; +    static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; -            friend TablePrinter& operator << ( TablePrinter& tp, RowBreak ) { -                if( tp.m_currentColumn > 0 ) { -                    tp.m_os << "\n"; -                    tp.m_currentColumn = -1; -                } -                return tp; -            } -        }; +    double m_inNanoseconds; +    Unit m_units; -        class Duration { -            enum class Unit { -                Auto, -                Nanoseconds, -                Microseconds, -                Milliseconds, -                Seconds, -                Minutes -            }; -            static const uint64_t s_nanosecondsInAMicrosecond = 1000; -            static const uint64_t s_nanosecondsInAMillisecond = 1000*s_nanosecondsInAMicrosecond; -            static const uint64_t s_nanosecondsInASecond = 1000*s_nanosecondsInAMillisecond; -            static const uint64_t s_nanosecondsInAMinute = 60*s_nanosecondsInASecond; +public: +    explicit Duration(double inNanoseconds, Unit units = Unit::Auto) +        : m_inNanoseconds(inNanoseconds), +        m_units(units) { +        if (m_units == Unit::Auto) { +            if (m_inNanoseconds < s_nanosecondsInAMicrosecond) +                m_units = Unit::Nanoseconds; +            else if (m_inNanoseconds < s_nanosecondsInAMillisecond) +                m_units = Unit::Microseconds; +            else if (m_inNanoseconds < s_nanosecondsInASecond) +                m_units = Unit::Milliseconds; +            else if (m_inNanoseconds < s_nanosecondsInAMinute) +                m_units = Unit::Seconds; +            else +                m_units = Unit::Minutes; +        } -            uint64_t m_inNanoseconds; -            Unit m_units; +    } -        public: -            Duration( uint64_t inNanoseconds, Unit units = Unit::Auto ) -            :   m_inNanoseconds( inNanoseconds ), -                m_units( units ) -            { -                if( m_units == Unit::Auto ) { -                    if( m_inNanoseconds < s_nanosecondsInAMicrosecond ) -                        m_units = Unit::Nanoseconds; -                    else if( m_inNanoseconds < s_nanosecondsInAMillisecond ) -                        m_units = Unit::Microseconds; -                    else if( m_inNanoseconds < s_nanosecondsInASecond ) -                        m_units = Unit::Milliseconds; -                    else if( m_inNanoseconds < s_nanosecondsInAMinute ) -                        m_units = Unit::Seconds; -                    else -                        m_units = Unit::Minutes; -                } +    auto value() const -> double { +        switch (m_units) { +        case Unit::Microseconds: +            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond); +        case Unit::Milliseconds: +            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond); +        case Unit::Seconds: +            return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond); +        case Unit::Minutes: +            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute); +        default: +            return m_inNanoseconds; +        } +    } +    auto unitsAsString() const -> std::string { +        switch (m_units) { +        case Unit::Nanoseconds: +            return "ns"; +        case Unit::Microseconds: +            return "us"; +        case Unit::Milliseconds: +            return "ms"; +        case Unit::Seconds: +            return "s"; +        case Unit::Minutes: +            return "m"; +        default: +            return "** internal error **"; +        } -            } +    } +    friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { +        return os << duration.value() << ' ' << duration.unitsAsString(); +    } +}; +} // end anon namespace -            auto value() const -> double { -                switch( m_units ) { -                    case Unit::Microseconds: -                        return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMicrosecond ); -                    case Unit::Milliseconds: -                        return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMillisecond ); -                    case Unit::Seconds: -                        return m_inNanoseconds / static_cast<double>( s_nanosecondsInASecond ); -                    case Unit::Minutes: -                        return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMinute ); -                    default: -                        return static_cast<double>( m_inNanoseconds ); -                } -            } -            auto unitsAsString() const -> std::string { -                switch( m_units ) { -                    case Unit::Nanoseconds: -                        return "ns"; -                    case Unit::Microseconds: -                        return "µs"; -                    case Unit::Milliseconds: -                        return "ms"; -                    case Unit::Seconds: -                        return "s"; -                    case Unit::Minutes: -                        return "m"; -                    default: -                        return "** internal error **"; -                } +class TablePrinter { +    std::ostream& m_os; +    std::vector<ColumnInfo> m_columnInfos; +    std::ostringstream m_oss; +    int m_currentColumn = -1; +    bool m_isOpen = false; -            } -            friend auto operator << ( std::ostream& os, Duration const& duration ) -> std::ostream& { -                return os << duration.value() << " " << duration.unitsAsString(); -            } -        }; -    } // end anon namespace +public: +    TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos ) +    :   m_os( os ), +        m_columnInfos( std::move( columnInfos ) ) {} -    struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> { -        TablePrinter m_tablePrinter; - -        ConsoleReporter( ReporterConfig const& config ) -        :   StreamingReporterBase( config ), -            m_tablePrinter( config.stream(), -                            { -                                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH-32, ColumnInfo::Left }, -                                { "iters", 8, ColumnInfo::Right }, -                                { "elapsed ns", 14, ColumnInfo::Right }, -                                { "average", 14, ColumnInfo::Right } -                            } ) -        {} -        ~ConsoleReporter() override; -        static std::string getDescription() { -            return "Reports test results as plain lines of text"; -        } +    auto columnInfos() const -> std::vector<ColumnInfo> const& { +        return m_columnInfos; +    } -        void noMatchingTestCases( std::string const& spec ) override { -            stream << "No test cases matched '" << spec << '\'' << std::endl; -        } +    void open() { +        if (!m_isOpen) { +            m_isOpen = true; +            *this << RowBreak(); -        void assertionStarting( AssertionInfo const& ) override { -        } +			Columns headerCols; +			Spacer spacer(2); +			for (auto const& info : m_columnInfos) { +				headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2)); +				headerCols += spacer; +			} +			m_os << headerCols << '\n'; -        bool assertionEnded( AssertionStats const& _assertionStats ) override { -            AssertionResult const& result = _assertionStats.assertionResult; +            m_os << Catch::getLineOfChars<'-'>() << '\n'; +        } +    } +    void close() { +        if (m_isOpen) { +            *this << RowBreak(); +            m_os << std::endl; +            m_isOpen = false; +        } +    } -            bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); +    template<typename T> +    friend TablePrinter& operator << (TablePrinter& tp, T const& value) { +        tp.m_oss << value; +        return tp; +    } -            // Drop out if result was successful but we're not printing them. -            if( !includeResults && result.getResultType() != ResultWas::Warning ) -                return false; +    friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { +        auto colStr = tp.m_oss.str(); +        const auto strSize = colStr.size(); +        tp.m_oss.str(""); +        tp.open(); +        if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) { +            tp.m_currentColumn = -1; +            tp.m_os << '\n'; +        } +        tp.m_currentColumn++; -            lazyPrint(); +        auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; +        auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width)) +            ? std::string(colInfo.width - (strSize + 1), ' ') +            : std::string(); +        if (colInfo.justification == ColumnInfo::Left) +            tp.m_os << colStr << padding << ' '; +        else +            tp.m_os << padding << colStr << ' '; +        return tp; +    } -            AssertionPrinter printer( stream, _assertionStats, includeResults ); -            printer.print(); -            stream << std::endl; -            return true; +    friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { +        if (tp.m_currentColumn > 0) { +            tp.m_os << '\n'; +            tp.m_currentColumn = -1;          } +        return tp; +    } +}; -        void sectionStarting( SectionInfo const& _sectionInfo ) override { -            m_headerPrinted = false; -            StreamingReporterBase::sectionStarting( _sectionInfo ); +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) +    : StreamingReporterBase(config), +    m_tablePrinter(new TablePrinter(config.stream(), +        [&config]() -> std::vector<ColumnInfo> { +        if (config.fullConfig()->benchmarkNoAnalysis()) +        { +            return{ +                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left }, +                { "     samples", 14, ColumnInfo::Right }, +                { "  iterations", 14, ColumnInfo::Right }, +                { "        mean", 14, ColumnInfo::Right } +            };          } -        void sectionEnded( SectionStats const& _sectionStats ) override { -            m_tablePrinter.close(); -            if( _sectionStats.missingAssertions ) { -                lazyPrint(); -                Colour colour( Colour::ResultError ); -                if( m_sectionStack.size() > 1 ) -                    stream << "\nNo assertions in section"; -                else -                    stream << "\nNo assertions in test case"; -                stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; -            } -            if( m_config->showDurations() == ShowDurations::Always ) { -                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; -            } -            if( m_headerPrinted ) { -                m_headerPrinted = false; -            } -            StreamingReporterBase::sectionEnded( _sectionStats ); +        else +        { +            return{ +                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left }, +                { "samples      mean       std dev", 14, ColumnInfo::Right }, +                { "iterations   low mean   low std dev", 14, ColumnInfo::Right }, +                { "estimated    high mean  high std dev", 14, ColumnInfo::Right } +            };          } +    }())) {} +ConsoleReporter::~ConsoleReporter() = default; -        void benchmarkStarting( BenchmarkInfo const& info ) override { -            lazyPrintWithoutClosingBenchmarkTable(); +std::string ConsoleReporter::getDescription() { +    return "Reports test results as plain lines of text"; +} -            auto nameCol = Column( info.name ).width( m_tablePrinter.columnInfos()[0].width-2 ); +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { +    stream << "No test cases matched '" << spec << '\'' << std::endl; +} -            bool firstLine = true; -            for( auto line : nameCol ) { -                if( !firstLine ) -                    m_tablePrinter << ColumnBreak() << ColumnBreak() << ColumnBreak(); -                else -                    firstLine = false; +void ConsoleReporter::reportInvalidArguments(std::string const&arg){ +    stream << "Invalid Filter: " << arg << std::endl; +} -                m_tablePrinter << line << ColumnBreak(); -            } -        } -        void benchmarkEnded( BenchmarkStats const& stats ) override { -            Duration average( stats.elapsedTimeInNanoseconds/stats.iterations ); -            m_tablePrinter -                    << stats.iterations << ColumnBreak() -                    << stats.elapsedTimeInNanoseconds << ColumnBreak() -                    << average << ColumnBreak(); -        } +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} -        void testCaseEnded( TestCaseStats const& _testCaseStats ) override { -            m_tablePrinter.close(); -            StreamingReporterBase::testCaseEnded( _testCaseStats ); -            m_headerPrinted = false; -        } -        void testGroupEnded( TestGroupStats const& _testGroupStats ) override { -            if( currentGroupInfo.used ) { -                printSummaryDivider(); -                stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; -                printTotals( _testGroupStats.totals ); -                stream << '\n' << std::endl; -            } -            StreamingReporterBase::testGroupEnded( _testGroupStats ); -        } -        void testRunEnded( TestRunStats const& _testRunStats ) override { -            printTotalsDivider( _testRunStats.totals ); -            printTotals( _testRunStats.totals ); -            stream << std::endl; -            StreamingReporterBase::testRunEnded( _testRunStats ); -        } +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { +    AssertionResult const& result = _assertionStats.assertionResult; -    private: +    bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); -        class AssertionPrinter { -        public: -            AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; -            AssertionPrinter( AssertionPrinter const& ) = delete; -            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) -            :   stream( _stream ), -                stats( _stats ), -                result( _stats.assertionResult ), -                colour( Colour::None ), -                message( result.getMessage() ), -                messages( _stats.infoMessages ), -                printInfoMessages( _printInfoMessages ) -            { -                switch( result.getResultType() ) { -                    case ResultWas::Ok: -                        colour = Colour::Success; -                        passOrFail = "PASSED"; -                        //if( result.hasMessage() ) -                        if( _stats.infoMessages.size() == 1 ) -                            messageLabel = "with message"; -                        if( _stats.infoMessages.size() > 1 ) -                            messageLabel = "with messages"; -                        break; -                    case ResultWas::ExpressionFailed: -                        if( result.isOk() ) { -                            colour = Colour::Success; -                            passOrFail = "FAILED - but was ok"; -                        } -                        else { -                            colour = Colour::Error; -                            passOrFail = "FAILED"; -                        } -                        if( _stats.infoMessages.size() == 1 ) -                            messageLabel = "with message"; -                        if( _stats.infoMessages.size() > 1 ) -                            messageLabel = "with messages"; -                        break; -                    case ResultWas::ThrewException: -                        colour = Colour::Error; -                        passOrFail = "FAILED"; -                        messageLabel = "due to unexpected exception with "; -                        if (_stats.infoMessages.size() == 1) -                            messageLabel += "message"; -                        if (_stats.infoMessages.size() > 1) -                            messageLabel += "messages"; -                        break; -                    case ResultWas::FatalErrorCondition: -                        colour = Colour::Error; -                        passOrFail = "FAILED"; -                        messageLabel = "due to a fatal error condition"; -                        break; -                    case ResultWas::DidntThrowException: -                        colour = Colour::Error; -                        passOrFail = "FAILED"; -                        messageLabel = "because no exception was thrown where one was expected"; -                        break; -                    case ResultWas::Info: -                        messageLabel = "info"; -                        break; -                    case ResultWas::Warning: -                        messageLabel = "warning"; -                        break; -                    case ResultWas::ExplicitFailure: -                        passOrFail = "FAILED"; -                        colour = Colour::Error; -                        if( _stats.infoMessages.size() == 1 ) -                            messageLabel = "explicitly with message"; -                        if( _stats.infoMessages.size() > 1 ) -                            messageLabel = "explicitly with messages"; -                        break; -                    // These cases are here to prevent compiler warnings -                    case ResultWas::Unknown: -                    case ResultWas::FailureBit: -                    case ResultWas::Exception: -                        passOrFail = "** internal error **"; -                        colour = Colour::Error; -                        break; -                } -            } +    // Drop out if result was successful but we're not printing them. +    if (!includeResults && result.getResultType() != ResultWas::Warning) +        return false; -            void print() const { -                printSourceInfo(); -                if( stats.totals.assertions.total() > 0 ) { -                    if( result.isOk() ) -                        stream << '\n'; -                    printResultType(); -                    printOriginalExpression(); -                    printReconstructedExpression(); -                } -                else { -                    stream << '\n'; -                } -                printMessage(); -            } +    lazyPrint(); -        private: -            void printResultType() const { -                if( !passOrFail.empty() ) { -                    Colour colourGuard( colour ); -                    stream << passOrFail << ":\n"; -                } -            } -            void printOriginalExpression() const { -                if( result.hasExpression() ) { -                    Colour colourGuard( Colour::OriginalExpression ); -                    stream  << "  "; -                    stream << result.getExpressionInMacro(); -                    stream << '\n'; -                } -            } -            void printReconstructedExpression() const { -                if( result.hasExpandedExpression() ) { -                    stream << "with expansion:\n"; -                    Colour colourGuard( Colour::ReconstructedExpression ); -                    stream << Column( result.getExpandedExpression() ).indent(2) << '\n'; -                } -            } -            void printMessage() const { -                if( !messageLabel.empty() ) -                    stream << messageLabel << ':' << '\n'; -                for( auto const& msg : messages ) { -                    // If this assertion is a warning ignore any INFO messages -                    if( printInfoMessages || msg.type != ResultWas::Info ) -                        stream << Column( msg.message ).indent(2) << '\n'; -                } -            } -            void printSourceInfo() const { -                Colour colourGuard( Colour::FileName ); -                stream << result.getSourceInfo() << ": "; -            } +    ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); +    printer.print(); +    stream << std::endl; +    return true; +} -            std::ostream& stream; -            AssertionStats const& stats; -            AssertionResult const& result; -            Colour::Code colour; -            std::string passOrFail; -            std::string messageLabel; -            std::string message; -            std::vector<MessageInfo> messages; -            bool printInfoMessages; -        }; +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { +    m_tablePrinter->close(); +    m_headerPrinted = false; +    StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { +    m_tablePrinter->close(); +    if (_sectionStats.missingAssertions) { +        lazyPrint(); +        Colour colour(Colour::ResultError); +        if (m_sectionStack.size() > 1) +            stream << "\nNo assertions in section"; +        else +            stream << "\nNo assertions in test case"; +        stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; +    } +    double dur = _sectionStats.durationInSeconds; +    if (shouldShowDuration(*m_config, dur)) { +        stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; +    } +    if (m_headerPrinted) { +        m_headerPrinted = false; +    } +    StreamingReporterBase::sectionEnded(_sectionStats); +} -        void lazyPrint() { +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +void ConsoleReporter::benchmarkPreparing(std::string const& name) { +	lazyPrintWithoutClosingBenchmarkTable(); -            m_tablePrinter.close(); -            lazyPrintWithoutClosingBenchmarkTable(); -        } +	auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2)); -        void lazyPrintWithoutClosingBenchmarkTable() { +	bool firstLine = true; +	for (auto line : nameCol) { +		if (!firstLine) +			(*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); +		else +			firstLine = false; -            if( !currentTestRunInfo.used ) -                lazyPrintRunInfo(); -            if( !currentGroupInfo.used ) -                lazyPrintGroupInfo(); +		(*m_tablePrinter) << line << ColumnBreak(); +	} +} -            if( !m_headerPrinted ) { -                printTestCaseAndSectionHeader(); -                m_headerPrinted = true; -            } -        } -        void lazyPrintRunInfo() { -            stream  << '\n' << getLineOfChars<'~'>() << '\n'; -            Colour colour( Colour::SecondaryText ); -            stream  << currentTestRunInfo->name -                    << " is a Catch v"  << libraryVersion() << " host application.\n" -                    << "Run with -? for options\n\n"; +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { +    (*m_tablePrinter) << info.samples << ColumnBreak() +        << info.iterations << ColumnBreak(); +    if (!m_config->benchmarkNoAnalysis()) +        (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { +    if (m_config->benchmarkNoAnalysis()) +    { +        (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); +    } +    else +    { +        (*m_tablePrinter) << ColumnBreak() +            << Duration(stats.mean.point.count()) << ColumnBreak() +            << Duration(stats.mean.lower_bound.count()) << ColumnBreak() +            << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() +            << Duration(stats.standardDeviation.point.count()) << ColumnBreak() +            << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() +            << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); +    } +} -            if( m_config->rngSeed() != 0 ) -                stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; +void ConsoleReporter::benchmarkFailed(std::string const& error) { +	Colour colour(Colour::Red); +    (*m_tablePrinter) +        << "Benchmark failed (" << error << ')' +        << ColumnBreak() << RowBreak(); +} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -            currentTestRunInfo.used = true; -        } -        void lazyPrintGroupInfo() { -            if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { -                printClosedHeader( "Group: " + currentGroupInfo->name ); -                currentGroupInfo.used = true; -            } -        } -        void printTestCaseAndSectionHeader() { -            assert( !m_sectionStack.empty() ); -            printOpenHeader( currentTestCaseInfo->name ); +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { +    m_tablePrinter->close(); +    StreamingReporterBase::testCaseEnded(_testCaseStats); +    m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { +    if (currentGroupInfo.used) { +        printSummaryDivider(); +        stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; +        printTotals(_testGroupStats.totals); +        stream << '\n' << std::endl; +    } +    StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { +    printTotalsDivider(_testRunStats.totals); +    printTotals(_testRunStats.totals); +    stream << std::endl; +    StreamingReporterBase::testRunEnded(_testRunStats); +} +void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { +    StreamingReporterBase::testRunStarting(_testInfo); +    printTestFilters(); +} -            if( m_sectionStack.size() > 1 ) { -                Colour colourGuard( Colour::Headers ); +void ConsoleReporter::lazyPrint() { -                auto -                    it = m_sectionStack.begin()+1, // Skip first section (test case) -                    itEnd = m_sectionStack.end(); -                for( ; it != itEnd; ++it ) -                    printHeaderString( it->name, 2 ); -            } +    m_tablePrinter->close(); +    lazyPrintWithoutClosingBenchmarkTable(); +} -            SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { -            if( !lineInfo.empty() ){ -                stream << getLineOfChars<'-'>() << '\n'; -                Colour colourGuard( Colour::FileName ); -                stream << lineInfo << '\n'; -            } -            stream << getLineOfChars<'.'>() << '\n' << std::endl; -        } +    if (!currentTestRunInfo.used) +        lazyPrintRunInfo(); +    if (!currentGroupInfo.used) +        lazyPrintGroupInfo(); -        void printClosedHeader( std::string const& _name ) { -            printOpenHeader( _name ); -            stream << getLineOfChars<'.'>() << '\n'; -        } -        void printOpenHeader( std::string const& _name ) { -            stream  << getLineOfChars<'-'>() << '\n'; -            { -                Colour colourGuard( Colour::Headers ); -                printHeaderString( _name ); -            } -        } +    if (!m_headerPrinted) { +        printTestCaseAndSectionHeader(); +        m_headerPrinted = true; +    } +} +void ConsoleReporter::lazyPrintRunInfo() { +    stream << '\n' << getLineOfChars<'~'>() << '\n'; +    Colour colour(Colour::SecondaryText); +    stream << currentTestRunInfo->name +        << " is a Catch v" << libraryVersion() << " host application.\n" +        << "Run with -? for options\n\n"; -        // if string has a : in first line will set indent to follow it on -        // subsequent lines -        void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { -            std::size_t i = _string.find( ": " ); -            if( i != std::string::npos ) -                i+=2; -            else -                i = 0; -            stream << Column( _string ).indent( indent+i ).initialIndent( indent ) << '\n'; -        } +    if (m_config->rngSeed() != 0) +        stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; -        struct SummaryColumn { +    currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { +    if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { +        printClosedHeader("Group: " + currentGroupInfo->name); +        currentGroupInfo.used = true; +    } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { +    assert(!m_sectionStack.empty()); +    printOpenHeader(currentTestCaseInfo->name); -            SummaryColumn( std::string const& _label, Colour::Code _colour ) -            :   label( _label ), -                colour( _colour ) -            {} -            SummaryColumn addRow( std::size_t count ) { -                std::ostringstream oss; -                oss << count; -                std::string row = oss.str(); -                for( auto& oldRow : rows ) { -                    while( oldRow.size() < row.size() ) -                        oldRow = ' ' + oldRow; -                    while( oldRow.size() > row.size() ) -                        row = ' ' + row; -                } -                rows.push_back( row ); -                return *this; -            } +    if (m_sectionStack.size() > 1) { +        Colour colourGuard(Colour::Headers); -            std::string label; -            Colour::Code colour; -            std::vector<std::string> rows; +        auto +            it = m_sectionStack.begin() + 1, // Skip first section (test case) +            itEnd = m_sectionStack.end(); +        for (; it != itEnd; ++it) +            printHeaderString(it->name, 2); +    } -        }; +    SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; -        void printTotals( Totals const& totals ) { -            if( totals.testCases.total() == 0 ) { -                stream << Colour( Colour::Warning ) << "No tests ran\n"; -            } -            else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { -                stream << Colour( Colour::ResultSuccess ) << "All tests passed"; -                stream << " (" -                        << pluralise( totals.assertions.passed, "assertion" ) << " in " -                        << pluralise( totals.testCases.passed, "test case" ) << ')' -                        << '\n'; -            } -            else { +    stream << getLineOfChars<'-'>() << '\n'; +    Colour colourGuard(Colour::FileName); +    stream << lineInfo << '\n'; +    stream << getLineOfChars<'.'>() << '\n' << std::endl; +} -                std::vector<SummaryColumn> columns; -                columns.push_back( SummaryColumn( "", Colour::None ) -                                        .addRow( totals.testCases.total() ) -                                        .addRow( totals.assertions.total() ) ); -                columns.push_back( SummaryColumn( "passed", Colour::Success ) -                                        .addRow( totals.testCases.passed ) -                                        .addRow( totals.assertions.passed ) ); -                columns.push_back( SummaryColumn( "failed", Colour::ResultError ) -                                        .addRow( totals.testCases.failed ) -                                        .addRow( totals.assertions.failed ) ); -                columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) -                                        .addRow( totals.testCases.failedButOk ) -                                        .addRow( totals.assertions.failedButOk ) ); - -                printSummaryRow( "test cases", columns, 0 ); -                printSummaryRow( "assertions", columns, 1 ); -            } -        } -        void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) { -            for( auto col : cols ) { -                std::string value = col.rows[row]; -                if( col.label.empty() ) { -                    stream << label << ": "; -                    if( value != "0" ) -                        stream << value; -                    else -                        stream << Colour( Colour::Warning ) << "- none -"; -                } -                else if( value != "0" ) { -                    stream  << Colour( Colour::LightGrey ) << " | "; -                    stream  << Colour( col.colour ) -                            << value << ' ' << col.label; -                } -            } -            stream << '\n'; -        } +void ConsoleReporter::printClosedHeader(std::string const& _name) { +    printOpenHeader(_name); +    stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { +    stream << getLineOfChars<'-'>() << '\n'; +    { +        Colour colourGuard(Colour::Headers); +        printHeaderString(_name); +    } +} -        void printTotalsDivider( Totals const& totals ) { -            if( totals.testCases.total() > 0 ) { -                std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); -                std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); -                std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); -                while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) -                    findMax( failedRatio, failedButOkRatio, passedRatio )++; -                while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) -                    findMax( failedRatio, failedButOkRatio, passedRatio )--; - -                stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); -                stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); -                if( totals.testCases.allPassed() ) -                    stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); -                else -                    stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); -            } -            else { -                stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); -            } -            stream << '\n'; -        } -        void printSummaryDivider() { -            stream << getLineOfChars<'-'>() << '\n'; +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { +    std::size_t i = _string.find(": "); +    if (i != std::string::npos) +        i += 2; +    else +        i = 0; +    stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + +    SummaryColumn( std::string _label, Colour::Code _colour ) +    :   label( std::move( _label ) ), +        colour( _colour ) {} +    SummaryColumn addRow( std::size_t count ) { +        ReusableStringStream rss; +        rss << count; +        std::string row = rss.str(); +        for (auto& oldRow : rows) { +            while (oldRow.size() < row.size()) +                oldRow = ' ' + oldRow; +            while (oldRow.size() > row.size()) +                row = ' ' + row; +        } +        rows.push_back(row); +        return *this; +    } + +    std::string label; +    Colour::Code colour; +    std::vector<std::string> rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { +    if (totals.testCases.total() == 0) { +        stream << Colour(Colour::Warning) << "No tests ran\n"; +    } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { +        stream << Colour(Colour::ResultSuccess) << "All tests passed"; +        stream << " (" +            << pluralise(totals.assertions.passed, "assertion") << " in " +            << pluralise(totals.testCases.passed, "test case") << ')' +            << '\n'; +    } else { + +        std::vector<SummaryColumn> columns; +        columns.push_back(SummaryColumn("", Colour::None) +                          .addRow(totals.testCases.total()) +                          .addRow(totals.assertions.total())); +        columns.push_back(SummaryColumn("passed", Colour::Success) +                          .addRow(totals.testCases.passed) +                          .addRow(totals.assertions.passed)); +        columns.push_back(SummaryColumn("failed", Colour::ResultError) +                          .addRow(totals.testCases.failed) +                          .addRow(totals.assertions.failed)); +        columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) +                          .addRow(totals.testCases.failedButOk) +                          .addRow(totals.assertions.failedButOk)); + +        printSummaryRow("test cases", columns, 0); +        printSummaryRow("assertions", columns, 1); +    } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) { +    for (auto col : cols) { +        std::string value = col.rows[row]; +        if (col.label.empty()) { +            stream << label << ": "; +            if (value != "0") +                stream << value; +            else +                stream << Colour(Colour::Warning) << "- none -"; +        } else if (value != "0") { +            stream << Colour(Colour::LightGrey) << " | "; +            stream << Colour(col.colour) +                << value << ' ' << col.label;          } +    } +    stream << '\n'; +} -    private: -        bool m_headerPrinted = false; -    }; +void ConsoleReporter::printTotalsDivider(Totals const& totals) { +    if (totals.testCases.total() > 0) { +        std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); +        std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); +        std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); +        while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) +            findMax(failedRatio, failedButOkRatio, passedRatio)++; +        while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) +            findMax(failedRatio, failedButOkRatio, passedRatio)--; + +        stream << Colour(Colour::Error) << std::string(failedRatio, '='); +        stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); +        if (totals.testCases.allPassed()) +            stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); +        else +            stream << Colour(Colour::Success) << std::string(passedRatio, '='); +    } else { +        stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); +    } +    stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { +    stream << getLineOfChars<'-'>() << '\n'; +} -    CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) +void ConsoleReporter::printTestFilters() { +    if (m_config->testSpec().hasFilters()) { +        Colour guard(Colour::BrightYellow); +        stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; +    } +} -    ConsoleReporter::~ConsoleReporter() {} +CATCH_REGISTER_REPORTER("console", ConsoleReporter)  } // end namespace Catch  #if defined(_MSC_VER)  #pragma warning(pop)  #endif + +#if defined(__clang__) +#  pragma clang diagnostic pop +#endif  // end catch_reporter_console.cpp  // start catch_reporter_junit.cpp -#include <assert.h> - +#include <cassert> +#include <sstream>  #include <ctime>  #include <algorithm> @@ -11171,300 +16771,385 @@ namespace Catch {                  return it->substr(1);              return std::string();          } -    } +    } // anonymous namespace -    class JunitReporter : public CumulativeReporterBase<JunitReporter> { -    public: -        JunitReporter( ReporterConfig const& _config ) +    JunitReporter::JunitReporter( ReporterConfig const& _config )          :   CumulativeReporterBase( _config ),              xml( _config.stream() )          {              m_reporterPrefs.shouldRedirectStdOut = true; +            m_reporterPrefs.shouldReportAllAssertions = true;          } -        ~JunitReporter() override; +    JunitReporter::~JunitReporter() {} -        static std::string getDescription() { -            return "Reports test results in an XML format that looks like Ant's junitreport target"; -        } +    std::string JunitReporter::getDescription() { +        return "Reports test results in an XML format that looks like Ant's junitreport target"; +    } -        void noMatchingTestCases( std::string const& /*spec*/ ) override {} +    void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} -        void testRunStarting( TestRunInfo const& runInfo ) override { -            CumulativeReporterBase::testRunStarting( runInfo ); -            xml.startElement( "testsuites" ); -        } +    void JunitReporter::testRunStarting( TestRunInfo const& runInfo )  { +        CumulativeReporterBase::testRunStarting( runInfo ); +        xml.startElement( "testsuites" ); +    } -        void testGroupStarting( GroupInfo const& groupInfo ) override { -            suiteTimer.start(); -            stdOutForSuite.str(""); -            stdErrForSuite.str(""); -            unexpectedExceptions = 0; -            CumulativeReporterBase::testGroupStarting( groupInfo ); -        } +    void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { +        suiteTimer.start(); +        stdOutForSuite.clear(); +        stdErrForSuite.clear(); +        unexpectedExceptions = 0; +        CumulativeReporterBase::testGroupStarting( groupInfo ); +    } -        void testCaseStarting( TestCaseInfo const& testCaseInfo ) override { -            m_okToFail = testCaseInfo.okToFail(); -        } -        bool assertionEnded( AssertionStats const& assertionStats ) override { -            if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) -                unexpectedExceptions++; -            return CumulativeReporterBase::assertionEnded( assertionStats ); -        } +    void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { +        m_okToFail = testCaseInfo.okToFail(); +    } -        void testCaseEnded( TestCaseStats const& testCaseStats ) override { -            stdOutForSuite << testCaseStats.stdOut; -            stdErrForSuite << testCaseStats.stdErr; -            CumulativeReporterBase::testCaseEnded( testCaseStats ); -        } +    bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { +        if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) +            unexpectedExceptions++; +        return CumulativeReporterBase::assertionEnded( assertionStats ); +    } -        void testGroupEnded( TestGroupStats const& testGroupStats ) override { -            double suiteTime = suiteTimer.getElapsedSeconds(); -            CumulativeReporterBase::testGroupEnded( testGroupStats ); -            writeGroup( *m_testGroups.back(), suiteTime ); -        } +    void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { +        stdOutForSuite += testCaseStats.stdOut; +        stdErrForSuite += testCaseStats.stdErr; +        CumulativeReporterBase::testCaseEnded( testCaseStats ); +    } -        void testRunEndedCumulative() override { -            xml.endElement(); -        } +    void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { +        double suiteTime = suiteTimer.getElapsedSeconds(); +        CumulativeReporterBase::testGroupEnded( testGroupStats ); +        writeGroup( *m_testGroups.back(), suiteTime ); +    } -        void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { -            XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); -            TestGroupStats const& stats = groupNode.value; -            xml.writeAttribute( "name", stats.groupInfo.name ); -            xml.writeAttribute( "errors", unexpectedExceptions ); -            xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); -            xml.writeAttribute( "tests", stats.totals.assertions.total() ); -            xml.writeAttribute( "hostname", "tbd" ); // !TBD -            if( m_config->showDurations() == ShowDurations::Never ) -                xml.writeAttribute( "time", "" ); -            else -                xml.writeAttribute( "time", suiteTime ); -            xml.writeAttribute( "timestamp", getCurrentTimestamp() ); +    void JunitReporter::testRunEndedCumulative() { +        xml.endElement(); +    } -            // Write test cases -            for( auto const& child : groupNode.children ) -                writeTestCase( *child ); +    void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { +        XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); -            xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); -            xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); +        TestGroupStats const& stats = groupNode.value; +        xml.writeAttribute( "name", stats.groupInfo.name ); +        xml.writeAttribute( "errors", unexpectedExceptions ); +        xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); +        xml.writeAttribute( "tests", stats.totals.assertions.total() ); +        xml.writeAttribute( "hostname", "tbd" ); // !TBD +        if( m_config->showDurations() == ShowDurations::Never ) +            xml.writeAttribute( "time", "" ); +        else +            xml.writeAttribute( "time", suiteTime ); +        xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + +        // Write properties if there are any +        if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { +            auto properties = xml.scopedElement("properties"); +            if (m_config->hasTestFilters()) { +                xml.scopedElement("property") +                    .writeAttribute("name", "filters") +                    .writeAttribute("value", serializeFilters(m_config->getTestsOrTags())); +            } +            if (m_config->rngSeed() != 0) { +                xml.scopedElement("property") +                    .writeAttribute("name", "random-seed") +                    .writeAttribute("value", m_config->rngSeed()); +            }          } -        void writeTestCase( TestCaseNode const& testCaseNode ) { -            TestCaseStats const& stats = testCaseNode.value; +        // Write test cases +        for( auto const& child : groupNode.children ) +            writeTestCase( *child ); -            // All test cases have exactly one section - which represents the -            // test case itself. That section may have 0-n nested sections -            assert( testCaseNode.children.size() == 1 ); -            SectionNode const& rootSection = *testCaseNode.children.front(); +        xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); +        xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); +    } -            std::string className = stats.testInfo.className; +    void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { +        TestCaseStats const& stats = testCaseNode.value; -            if( className.empty() ) { -                className = fileNameTag(stats.testInfo.tags); -                if ( className.empty() ) -                    className = "global"; -            } +        // All test cases have exactly one section - which represents the +        // test case itself. That section may have 0-n nested sections +        assert( testCaseNode.children.size() == 1 ); +        SectionNode const& rootSection = *testCaseNode.children.front(); -            if ( !m_config->name().empty() ) -                className = m_config->name() + "." + className; +        std::string className = stats.testInfo.className; -            writeSection( className, "", rootSection ); +        if( className.empty() ) { +            className = fileNameTag(stats.testInfo.tags); +            if ( className.empty() ) +                className = "global";          } -        void writeSection(  std::string const& className, -                            std::string const& rootName, -                            SectionNode const& sectionNode ) { -            std::string name = trim( sectionNode.stats.sectionInfo.name ); -            if( !rootName.empty() ) -                name = rootName + '/' + name; +        if ( !m_config->name().empty() ) +            className = m_config->name() + "." + className; -            if( !sectionNode.assertions.empty() || -                !sectionNode.stdOut.empty() || -                !sectionNode.stdErr.empty() ) { -                XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); -                if( className.empty() ) { -                    xml.writeAttribute( "classname", name ); -                    xml.writeAttribute( "name", "root" ); -                } -                else { -                    xml.writeAttribute( "classname", className ); -                    xml.writeAttribute( "name", name ); -                } -                xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); +        writeSection( className, "", rootSection ); +    } -                writeAssertions( sectionNode ); +    void JunitReporter::writeSection(  std::string const& className, +                        std::string const& rootName, +                        SectionNode const& sectionNode ) { +        std::string name = trim( sectionNode.stats.sectionInfo.name ); +        if( !rootName.empty() ) +            name = rootName + '/' + name; -                if( !sectionNode.stdOut.empty() ) -                    xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); -                if( !sectionNode.stdErr.empty() ) -                    xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); +        if( !sectionNode.assertions.empty() || +            !sectionNode.stdOut.empty() || +            !sectionNode.stdErr.empty() ) { +            XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); +            if( className.empty() ) { +                xml.writeAttribute( "classname", name ); +                xml.writeAttribute( "name", "root" );              } -            for( auto const& childNode : sectionNode.childSections ) -                if( className.empty() ) -                    writeSection( name, "", *childNode ); -                else -                    writeSection( className, name, *childNode ); -        } - -        void writeAssertions( SectionNode const& sectionNode ) { -            for( auto const& assertion : sectionNode.assertions ) -                writeAssertion( assertion ); -        } -        void writeAssertion( AssertionStats const& stats ) { -            AssertionResult const& result = stats.assertionResult; -            if( !result.isOk() ) { -                std::string elementName; -                switch( result.getResultType() ) { -                    case ResultWas::ThrewException: -                    case ResultWas::FatalErrorCondition: -                        elementName = "error"; -                        break; -                    case ResultWas::ExplicitFailure: -                        elementName = "failure"; -                        break; -                    case ResultWas::ExpressionFailed: -                        elementName = "failure"; -                        break; -                    case ResultWas::DidntThrowException: -                        elementName = "failure"; -                        break; - -                    // We should never see these here: -                    case ResultWas::Info: -                    case ResultWas::Warning: -                    case ResultWas::Ok: -                    case ResultWas::Unknown: -                    case ResultWas::FailureBit: -                    case ResultWas::Exception: -                        elementName = "internalError"; -                        break; -                } +            else { +                xml.writeAttribute( "classname", className ); +                xml.writeAttribute( "name", name ); +            } +            xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); +            // This is not ideal, but it should be enough to mimic gtest's +            // junit output. +            // Ideally the JUnit reporter would also handle `skipTest` +            // events and write those out appropriately. +            xml.writeAttribute( "status", "run" ); + +            writeAssertions( sectionNode ); + +            if( !sectionNode.stdOut.empty() ) +                xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); +            if( !sectionNode.stdErr.empty() ) +                xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); +        } +        for( auto const& childNode : sectionNode.childSections ) +            if( className.empty() ) +                writeSection( name, "", *childNode ); +            else +                writeSection( className, name, *childNode ); +    } -                XmlWriter::ScopedElement e = xml.scopedElement( elementName ); +    void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { +        for( auto const& assertion : sectionNode.assertions ) +            writeAssertion( assertion ); +    } -                xml.writeAttribute( "message", result.getExpandedExpression() ); -                xml.writeAttribute( "type", result.getTestMacroName() ); +    void JunitReporter::writeAssertion( AssertionStats const& stats ) { +        AssertionResult const& result = stats.assertionResult; +        if( !result.isOk() ) { +            std::string elementName; +            switch( result.getResultType() ) { +                case ResultWas::ThrewException: +                case ResultWas::FatalErrorCondition: +                    elementName = "error"; +                    break; +                case ResultWas::ExplicitFailure: +                case ResultWas::ExpressionFailed: +                case ResultWas::DidntThrowException: +                    elementName = "failure"; +                    break; -                std::ostringstream oss; -                if( !result.getMessage().empty() ) -                    oss << result.getMessage() << '\n'; -                for( auto const& msg : stats.infoMessages ) -                    if( msg.type == ResultWas::Info ) -                        oss << msg.message << '\n'; +                // We should never see these here: +                case ResultWas::Info: +                case ResultWas::Warning: +                case ResultWas::Ok: +                case ResultWas::Unknown: +                case ResultWas::FailureBit: +                case ResultWas::Exception: +                    elementName = "internalError"; +                    break; +            } + +            XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + +            xml.writeAttribute( "message", result.getExpression() ); +            xml.writeAttribute( "type", result.getTestMacroName() ); -                oss << "at " << result.getSourceInfo(); -                xml.writeText( oss.str(), false ); +            ReusableStringStream rss; +            if (stats.totals.assertions.total() > 0) { +                rss << "FAILED" << ":\n"; +                if (result.hasExpression()) { +                    rss << "  "; +                    rss << result.getExpressionInMacro(); +                    rss << '\n'; +                } +                if (result.hasExpandedExpression()) { +                    rss << "with expansion:\n"; +                    rss << Column(result.getExpandedExpression()).indent(2) << '\n'; +                } +            } else { +                rss << '\n';              } -        } -        XmlWriter xml; -        Timer suiteTimer; -        std::ostringstream stdOutForSuite; -        std::ostringstream stdErrForSuite; -        unsigned int unexpectedExceptions = 0; -        bool m_okToFail = false; -    }; +            if( !result.getMessage().empty() ) +                rss << result.getMessage() << '\n'; +            for( auto const& msg : stats.infoMessages ) +                if( msg.type == ResultWas::Info ) +                    rss << msg.message << '\n'; + +            rss << "at " << result.getSourceInfo(); +            xml.writeText( rss.str(), XmlFormatting::Newline ); +        } +    } -    JunitReporter::~JunitReporter() {}      CATCH_REGISTER_REPORTER( "junit", JunitReporter )  } // end namespace Catch  // end catch_reporter_junit.cpp -// start catch_reporter_multi.cpp +// start catch_reporter_listening.cpp + +#include <cassert>  namespace Catch { -    void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { -        m_reporters.push_back( std::move( reporter ) ); +    ListeningReporter::ListeningReporter() { +        // We will assume that listeners will always want all assertions +        m_preferences.shouldReportAllAssertions = true; +    } + +    void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { +        m_listeners.push_back( std::move( listener ) );      } -    ReporterPreferences MultipleReporters::getPreferences() const { -        return m_reporters[0]->getPreferences(); +    void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { +        assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); +        m_reporter = std::move( reporter ); +        m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut;      } -    std::set<Verbosity> MultipleReporters::getSupportedVerbosities() { +    ReporterPreferences ListeningReporter::getPreferences() const { +        return m_preferences; +    } + +    std::set<Verbosity> ListeningReporter::getSupportedVerbosities() {          return std::set<Verbosity>{ };      } -    void MultipleReporters::noMatchingTestCases( std::string const& spec ) { -        for( auto const& reporter : m_reporters ) -            reporter->noMatchingTestCases( spec ); +    void ListeningReporter::noMatchingTestCases( std::string const& spec ) { +        for ( auto const& listener : m_listeners ) { +            listener->noMatchingTestCases( spec ); +        } +        m_reporter->noMatchingTestCases( spec ); +    } + +    void ListeningReporter::reportInvalidArguments(std::string const&arg){ +        for ( auto const& listener : m_listeners ) { +            listener->reportInvalidArguments( arg ); +        } +        m_reporter->reportInvalidArguments( arg );      } -    void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { -        for( auto const& reporter : m_reporters ) -            reporter->benchmarkStarting( benchmarkInfo ); +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +    void ListeningReporter::benchmarkPreparing( std::string const& name ) { +		for (auto const& listener : m_listeners) { +			listener->benchmarkPreparing(name); +		} +		m_reporter->benchmarkPreparing(name); +	} +    void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { +        for ( auto const& listener : m_listeners ) { +            listener->benchmarkStarting( benchmarkInfo ); +        } +        m_reporter->benchmarkStarting( benchmarkInfo );      } -    void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { -        for( auto const& reporter : m_reporters ) -            reporter->benchmarkEnded( benchmarkStats ); +    void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { +        for ( auto const& listener : m_listeners ) { +            listener->benchmarkEnded( benchmarkStats ); +        } +        m_reporter->benchmarkEnded( benchmarkStats );      } -    void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { -        for( auto const& reporter : m_reporters ) -            reporter->testRunStarting( testRunInfo ); +	void ListeningReporter::benchmarkFailed( std::string const& error ) { +		for (auto const& listener : m_listeners) { +			listener->benchmarkFailed(error); +		} +		m_reporter->benchmarkFailed(error); +	} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +    void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { +        for ( auto const& listener : m_listeners ) { +            listener->testRunStarting( testRunInfo ); +        } +        m_reporter->testRunStarting( testRunInfo );      } -    void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { -        for( auto const& reporter : m_reporters ) -            reporter->testGroupStarting( groupInfo ); +    void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { +        for ( auto const& listener : m_listeners ) { +            listener->testGroupStarting( groupInfo ); +        } +        m_reporter->testGroupStarting( groupInfo );      } -    void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { -        for( auto const& reporter : m_reporters ) -            reporter->testCaseStarting( testInfo ); +    void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { +        for ( auto const& listener : m_listeners ) { +            listener->testCaseStarting( testInfo ); +        } +        m_reporter->testCaseStarting( testInfo );      } -    void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { -        for( auto const& reporter : m_reporters ) -            reporter->sectionStarting( sectionInfo ); +    void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { +        for ( auto const& listener : m_listeners ) { +            listener->sectionStarting( sectionInfo ); +        } +        m_reporter->sectionStarting( sectionInfo );      } -    void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { -        for( auto const& reporter : m_reporters ) -            reporter->assertionStarting( assertionInfo ); +    void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { +        for ( auto const& listener : m_listeners ) { +            listener->assertionStarting( assertionInfo ); +        } +        m_reporter->assertionStarting( assertionInfo );      }      // The return value indicates if the messages buffer should be cleared: -    bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { -        bool clearBuffer = false; -        for( auto const& reporter : m_reporters ) -            clearBuffer |= reporter->assertionEnded( assertionStats ); -        return clearBuffer; +    bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { +        for( auto const& listener : m_listeners ) { +            static_cast<void>( listener->assertionEnded( assertionStats ) ); +        } +        return m_reporter->assertionEnded( assertionStats );      } -    void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { -        for( auto const& reporter : m_reporters ) -            reporter->sectionEnded( sectionStats ); +    void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { +        for ( auto const& listener : m_listeners ) { +            listener->sectionEnded( sectionStats ); +        } +        m_reporter->sectionEnded( sectionStats );      } -    void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { -        for( auto const& reporter : m_reporters ) -            reporter->testCaseEnded( testCaseStats ); +    void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { +        for ( auto const& listener : m_listeners ) { +            listener->testCaseEnded( testCaseStats ); +        } +        m_reporter->testCaseEnded( testCaseStats );      } -    void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { -        for( auto const& reporter : m_reporters ) -            reporter->testGroupEnded( testGroupStats ); +    void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { +        for ( auto const& listener : m_listeners ) { +            listener->testGroupEnded( testGroupStats ); +        } +        m_reporter->testGroupEnded( testGroupStats );      } -    void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { -        for( auto const& reporter : m_reporters ) -            reporter->testRunEnded( testRunStats ); +    void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { +        for ( auto const& listener : m_listeners ) { +            listener->testRunEnded( testRunStats ); +        } +        m_reporter->testRunEnded( testRunStats );      } -    void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { -        for( auto const& reporter : m_reporters ) -            reporter->skipTest( testInfo ); +    void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { +        for ( auto const& listener : m_listeners ) { +            listener->skipTest( testInfo ); +        } +        m_reporter->skipTest( testInfo );      } -    bool MultipleReporters::isMulti() const { +    bool ListeningReporter::isMulti() const {          return true;      }  } // end namespace Catch -// end catch_reporter_multi.cpp +// end catch_reporter_listening.cpp  // start catch_reporter_xml.cpp  #if defined(_MSC_VER) @@ -11475,211 +17160,258 @@ namespace Catch {  #endif  namespace Catch { -    class XmlReporter : public StreamingReporterBase<XmlReporter> { -    public: -        XmlReporter( ReporterConfig const& _config ) -        :   StreamingReporterBase( _config ), -            m_xml(_config.stream()) -        { -            m_reporterPrefs.shouldRedirectStdOut = true; -        } +    XmlReporter::XmlReporter( ReporterConfig const& _config ) +    :   StreamingReporterBase( _config ), +        m_xml(_config.stream()) +    { +        m_reporterPrefs.shouldRedirectStdOut = true; +        m_reporterPrefs.shouldReportAllAssertions = true; +    } -        ~XmlReporter() override; +    XmlReporter::~XmlReporter() = default; -        static std::string getDescription() { -            return "Reports test results as an XML document"; -        } +    std::string XmlReporter::getDescription() { +        return "Reports test results as an XML document"; +    } -        virtual std::string getStylesheetRef() const { -            return std::string(); -        } +    std::string XmlReporter::getStylesheetRef() const { +        return std::string(); +    } -        void writeSourceInfo( SourceLineInfo const& sourceInfo ) { -            m_xml -                .writeAttribute( "filename", sourceInfo.file ) -                .writeAttribute( "line", sourceInfo.line ); -        } +    void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { +        m_xml +            .writeAttribute( "filename", sourceInfo.file ) +            .writeAttribute( "line", sourceInfo.line ); +    } -    public: // StreamingReporterBase +    void XmlReporter::noMatchingTestCases( std::string const& s ) { +        StreamingReporterBase::noMatchingTestCases( s ); +    } -        void noMatchingTestCases( std::string const& s ) override { -            StreamingReporterBase::noMatchingTestCases( s ); -        } +    void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { +        StreamingReporterBase::testRunStarting( testInfo ); +        std::string stylesheetRef = getStylesheetRef(); +        if( !stylesheetRef.empty() ) +            m_xml.writeStylesheetRef( stylesheetRef ); +        m_xml.startElement( "Catch" ); +        if( !m_config->name().empty() ) +            m_xml.writeAttribute( "name", m_config->name() ); +        if (m_config->testSpec().hasFilters()) +            m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) ); +        if( m_config->rngSeed() != 0 ) +            m_xml.scopedElement( "Randomness" ) +                .writeAttribute( "seed", m_config->rngSeed() ); +    } -        void testRunStarting( TestRunInfo const& testInfo ) override { -            StreamingReporterBase::testRunStarting( testInfo ); -            std::string stylesheetRef = getStylesheetRef(); -            if( !stylesheetRef.empty() ) -                m_xml.writeStylesheetRef( stylesheetRef ); -            m_xml.startElement( "Catch" ); -            if( !m_config->name().empty() ) -                m_xml.writeAttribute( "name", m_config->name() ); -        } +    void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { +        StreamingReporterBase::testGroupStarting( groupInfo ); +        m_xml.startElement( "Group" ) +            .writeAttribute( "name", groupInfo.name ); +    } -        void testGroupStarting( GroupInfo const& groupInfo ) override { -            StreamingReporterBase::testGroupStarting( groupInfo ); -            m_xml.startElement( "Group" ) -                .writeAttribute( "name", groupInfo.name ); -        } +    void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { +        StreamingReporterBase::testCaseStarting(testInfo); +        m_xml.startElement( "TestCase" ) +            .writeAttribute( "name", trim( testInfo.name ) ) +            .writeAttribute( "description", testInfo.description ) +            .writeAttribute( "tags", testInfo.tagsAsString() ); -        void testCaseStarting( TestCaseInfo const& testInfo ) override { -            StreamingReporterBase::testCaseStarting(testInfo); -            m_xml.startElement( "TestCase" ) -                .writeAttribute( "name", trim( testInfo.name ) ) -                .writeAttribute( "description", testInfo.description ) -                .writeAttribute( "tags", testInfo.tagsAsString() ); +        writeSourceInfo( testInfo.lineInfo ); -            writeSourceInfo( testInfo.lineInfo ); +        if ( m_config->showDurations() == ShowDurations::Always ) +            m_testCaseTimer.start(); +        m_xml.ensureTagClosed(); +    } -            if ( m_config->showDurations() == ShowDurations::Always ) -                m_testCaseTimer.start(); +    void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { +        StreamingReporterBase::sectionStarting( sectionInfo ); +        if( m_sectionDepth++ > 0 ) { +            m_xml.startElement( "Section" ) +                .writeAttribute( "name", trim( sectionInfo.name ) ); +            writeSourceInfo( sectionInfo.lineInfo );              m_xml.ensureTagClosed();          } +    } -        void sectionStarting( SectionInfo const& sectionInfo ) override { -            StreamingReporterBase::sectionStarting( sectionInfo ); -            if( m_sectionDepth++ > 0 ) { -                m_xml.startElement( "Section" ) -                    .writeAttribute( "name", trim( sectionInfo.name ) ) -                    .writeAttribute( "description", sectionInfo.description ); -                writeSourceInfo( sectionInfo.lineInfo ); -                m_xml.ensureTagClosed(); -            } -        } - -        void assertionStarting( AssertionInfo const& ) override { } +    void XmlReporter::assertionStarting( AssertionInfo const& ) { } -        bool assertionEnded( AssertionStats const& assertionStats ) override { +    bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { -            AssertionResult const& result = assertionStats.assertionResult; +        AssertionResult const& result = assertionStats.assertionResult; -            bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); +        bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); -            if( includeResults ) { -                // Print any info messages in <Info> tags. -                for( auto const& msg : assertionStats.infoMessages ) { -                    if( msg.type == ResultWas::Info ) { -                        m_xml.scopedElement( "Info" ) -                                .writeText( msg.message ); -                    } else if ( msg.type == ResultWas::Warning ) { -                        m_xml.scopedElement( "Warning" ) -                                .writeText( msg.message ); -                    } +        if( includeResults || result.getResultType() == ResultWas::Warning ) { +            // Print any info messages in <Info> tags. +            for( auto const& msg : assertionStats.infoMessages ) { +                if( msg.type == ResultWas::Info && includeResults ) { +                    m_xml.scopedElement( "Info" ) +                            .writeText( msg.message ); +                } else if ( msg.type == ResultWas::Warning ) { +                    m_xml.scopedElement( "Warning" ) +                            .writeText( msg.message );                  }              } +        } -            // Drop out if result was successful but we're not printing them. -            if( !includeResults && result.getResultType() != ResultWas::Warning ) -                return true; +        // Drop out if result was successful but we're not printing them. +        if( !includeResults && result.getResultType() != ResultWas::Warning ) +            return true; -            // Print the expression if there is one. -            if( result.hasExpression() ) { -                m_xml.startElement( "Expression" ) -                    .writeAttribute( "success", result.succeeded() ) -                    .writeAttribute( "type", result.getTestMacroName() ); +        // Print the expression if there is one. +        if( result.hasExpression() ) { +            m_xml.startElement( "Expression" ) +                .writeAttribute( "success", result.succeeded() ) +                .writeAttribute( "type", result.getTestMacroName() ); -                writeSourceInfo( result.getSourceInfo() ); +            writeSourceInfo( result.getSourceInfo() ); -                m_xml.scopedElement( "Original" ) -                    .writeText( result.getExpression() ); -                m_xml.scopedElement( "Expanded" ) -                    .writeText( result.getExpandedExpression() ); -            } - -            // And... Print a result applicable to each result type. -            switch( result.getResultType() ) { -                case ResultWas::ThrewException: -                    m_xml.startElement( "Exception" ); -                    writeSourceInfo( result.getSourceInfo() ); -                    m_xml.writeText( result.getMessage() ); -                    m_xml.endElement(); -                    break; -                case ResultWas::FatalErrorCondition: -                    m_xml.startElement( "FatalErrorCondition" ); -                    writeSourceInfo( result.getSourceInfo() ); -                    m_xml.writeText( result.getMessage() ); -                    m_xml.endElement(); -                    break; -                case ResultWas::Info: -                    m_xml.scopedElement( "Info" ) -                        .writeText( result.getMessage() ); -                    break; -                case ResultWas::Warning: -                    // Warning will already have been written -                    break; -                case ResultWas::ExplicitFailure: -                    m_xml.startElement( "Failure" ); -                    writeSourceInfo( result.getSourceInfo() ); -                    m_xml.writeText( result.getMessage() ); -                    m_xml.endElement(); -                    break; -                default: -                    break; -            } - -            if( result.hasExpression() ) -                m_xml.endElement(); - -            return true; +            m_xml.scopedElement( "Original" ) +                .writeText( result.getExpression() ); +            m_xml.scopedElement( "Expanded" ) +                .writeText( result.getExpandedExpression() );          } -        void sectionEnded( SectionStats const& sectionStats ) override { -            StreamingReporterBase::sectionEnded( sectionStats ); -            if( --m_sectionDepth > 0 ) { -                XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); -                e.writeAttribute( "successes", sectionStats.assertions.passed ); -                e.writeAttribute( "failures", sectionStats.assertions.failed ); -                e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); - -                if ( m_config->showDurations() == ShowDurations::Always ) -                    e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); - +        // And... Print a result applicable to each result type. +        switch( result.getResultType() ) { +            case ResultWas::ThrewException: +                m_xml.startElement( "Exception" ); +                writeSourceInfo( result.getSourceInfo() ); +                m_xml.writeText( result.getMessage() );                  m_xml.endElement(); -            } +                break; +            case ResultWas::FatalErrorCondition: +                m_xml.startElement( "FatalErrorCondition" ); +                writeSourceInfo( result.getSourceInfo() ); +                m_xml.writeText( result.getMessage() ); +                m_xml.endElement(); +                break; +            case ResultWas::Info: +                m_xml.scopedElement( "Info" ) +                    .writeText( result.getMessage() ); +                break; +            case ResultWas::Warning: +                // Warning will already have been written +                break; +            case ResultWas::ExplicitFailure: +                m_xml.startElement( "Failure" ); +                writeSourceInfo( result.getSourceInfo() ); +                m_xml.writeText( result.getMessage() ); +                m_xml.endElement(); +                break; +            default: +                break;          } -        void testCaseEnded( TestCaseStats const& testCaseStats ) override { -            StreamingReporterBase::testCaseEnded( testCaseStats ); -            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); -            e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); - -            if ( m_config->showDurations() == ShowDurations::Always ) -                e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); +        if( result.hasExpression() ) +            m_xml.endElement(); -            if( !testCaseStats.stdOut.empty() ) -                m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); -            if( !testCaseStats.stdErr.empty() ) -                m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); +        return true; +    } -            m_xml.endElement(); -        } +    void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { +        StreamingReporterBase::sectionEnded( sectionStats ); +        if( --m_sectionDepth > 0 ) { +            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); +            e.writeAttribute( "successes", sectionStats.assertions.passed ); +            e.writeAttribute( "failures", sectionStats.assertions.failed ); +            e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); -        void testGroupEnded( TestGroupStats const& testGroupStats ) override { -            StreamingReporterBase::testGroupEnded( testGroupStats ); -            // TODO: Check testGroupStats.aborting and act accordingly. -            m_xml.scopedElement( "OverallResults" ) -                .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) -                .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) -                .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); -            m_xml.endElement(); -        } +            if ( m_config->showDurations() == ShowDurations::Always ) +                e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); -        void testRunEnded( TestRunStats const& testRunStats ) override { -            StreamingReporterBase::testRunEnded( testRunStats ); -            m_xml.scopedElement( "OverallResults" ) -                .writeAttribute( "successes", testRunStats.totals.assertions.passed ) -                .writeAttribute( "failures", testRunStats.totals.assertions.failed ) -                .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );              m_xml.endElement();          } +    } -    private: -        Timer m_testCaseTimer; -        XmlWriter m_xml; -        int m_sectionDepth = 0; -    }; +    void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { +        StreamingReporterBase::testCaseEnded( testCaseStats ); +        XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); +        e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + +        if ( m_config->showDurations() == ShowDurations::Always ) +            e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + +        if( !testCaseStats.stdOut.empty() ) +            m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); +        if( !testCaseStats.stdErr.empty() ) +            m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); + +        m_xml.endElement(); +    } + +    void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { +        StreamingReporterBase::testGroupEnded( testGroupStats ); +        // TODO: Check testGroupStats.aborting and act accordingly. +        m_xml.scopedElement( "OverallResults" ) +            .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) +            .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) +            .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); +        m_xml.scopedElement( "OverallResultsCases") +            .writeAttribute( "successes", testGroupStats.totals.testCases.passed ) +            .writeAttribute( "failures", testGroupStats.totals.testCases.failed ) +            .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk ); +        m_xml.endElement(); +    } + +    void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { +        StreamingReporterBase::testRunEnded( testRunStats ); +        m_xml.scopedElement( "OverallResults" ) +            .writeAttribute( "successes", testRunStats.totals.assertions.passed ) +            .writeAttribute( "failures", testRunStats.totals.assertions.failed ) +            .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); +        m_xml.scopedElement( "OverallResultsCases") +            .writeAttribute( "successes", testRunStats.totals.testCases.passed ) +            .writeAttribute( "failures", testRunStats.totals.testCases.failed ) +            .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk ); +        m_xml.endElement(); +    } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +    void XmlReporter::benchmarkPreparing(std::string const& name) { +        m_xml.startElement("BenchmarkResults") +            .writeAttribute("name", name); +    } + +    void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { +        m_xml.writeAttribute("samples", info.samples) +            .writeAttribute("resamples", info.resamples) +            .writeAttribute("iterations", info.iterations) +            .writeAttribute("clockResolution", info.clockResolution) +            .writeAttribute("estimatedDuration", info.estimatedDuration) +            .writeComment("All values in nano seconds"); +    } + +    void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { +        m_xml.startElement("mean") +            .writeAttribute("value", benchmarkStats.mean.point.count()) +            .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count()) +            .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count()) +            .writeAttribute("ci", benchmarkStats.mean.confidence_interval); +        m_xml.endElement(); +        m_xml.startElement("standardDeviation") +            .writeAttribute("value", benchmarkStats.standardDeviation.point.count()) +            .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count()) +            .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count()) +            .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval); +        m_xml.endElement(); +        m_xml.startElement("outliers") +            .writeAttribute("variance", benchmarkStats.outlierVariance) +            .writeAttribute("lowMild", benchmarkStats.outliers.low_mild) +            .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe) +            .writeAttribute("highMild", benchmarkStats.outliers.high_mild) +            .writeAttribute("highSevere", benchmarkStats.outliers.high_severe); +        m_xml.endElement(); +        m_xml.endElement(); +    } + +    void XmlReporter::benchmarkFailed(std::string const &error) { +        m_xml.scopedElement("failed"). +            writeAttribute("message", error); +        m_xml.endElement(); +    } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -    XmlReporter::~XmlReporter() {}      CATCH_REGISTER_REPORTER( "xml", XmlReporter )  } // end namespace Catch @@ -11705,7 +17437,7 @@ namespace Catch {  #ifndef __OBJC__ -#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)  // Standard C/C++ Win32 Unicode wmain entry point  extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {  #else @@ -11739,6 +17471,8 @@ int main (int argc, char * const argv[]) {  // end catch_default_main.hpp  #endif +#if !defined(CATCH_CONFIG_IMPL_ONLY) +  #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED  #  undef CLARA_CONFIG_MAIN  #endif @@ -11751,7 +17485,7 @@ int main (int argc, char * const argv[]) {  #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )  #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )  #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )  #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )  #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) @@ -11765,7 +17499,7 @@ int main (int argc, char * const argv[]) {  #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )  #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) -#define CATCH_CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) +#define CATCH_CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )  #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )  #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )  #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) @@ -11780,28 +17514,66 @@ int main (int argc, char * const argv[]) {  #endif // CATCH_CONFIG_DISABLE_MATCHERS  #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )  #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) +#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )  #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )  #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )  #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )  #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )  #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )  #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )  #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )  #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )  #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__ ,      #__VA_ARGS__ );     CATCH_SUCCEED( #__VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) +#else +#define CATCH_STATIC_REQUIRE( ... )       CATCH_REQUIRE( __VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) +#endif +  // "BDD-style" convenience wrappers  #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )  #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#define CATCH_GIVEN( desc )    CATCH_SECTION( std::string( "Given: ") + desc ) -#define CATCH_WHEN( desc )     CATCH_SECTION( std::string( " When: ") + desc ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc ) -#define CATCH_THEN( desc )     CATCH_SECTION( std::string( " Then: ") + desc ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc ) +#define CATCH_GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc ) +#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define CATCH_WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc ) +#define CATCH_AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define CATCH_THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc ) +#define CATCH_AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc ) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define CATCH_BENCHMARK(...) \ +    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define CATCH_BENCHMARK_ADVANCED(name) \ +    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING  // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required  #else @@ -11838,19 +17610,53 @@ int main (int argc, char * const argv[]) {  #endif // CATCH_CONFIG_DISABLE_MATCHERS  #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )  #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) +#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )  #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )  #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )  #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )  #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )  #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )  #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )  #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )  #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )  #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__,  #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) +#else +#define STATIC_REQUIRE( ... )       REQUIRE( __VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +#endif +  #endif  #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) @@ -11859,15 +17665,24 @@ int main (int argc, char * const argv[]) {  #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )  #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#define GIVEN( desc )    SECTION( std::string("   Given: ") + desc ) -#define WHEN( desc )     SECTION( std::string("    When: ") + desc ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) -#define THEN( desc )     SECTION( std::string("    Then: ") + desc ) -#define AND_THEN( desc ) SECTION( std::string("     And: ") + desc ) +#define GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc ) +#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc ) +#define AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc ) +#define AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc ) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define BENCHMARK(...) \ +    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define BENCHMARK_ADVANCED(name) \ +    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING  using Catch::Detail::Approx; -#else +#else // CATCH_CONFIG_DISABLE +  //////  // If this config identifier is defined then all CATCH macros are prefixed with CATCH_  #ifdef CATCH_CONFIG_PREFIX_ALL @@ -11903,30 +17718,56 @@ using Catch::Detail::Approx;  #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)  #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define CATCH_INFO( msg )    (void)(0) -#define CATCH_WARN( msg )    (void)(0) -#define CATCH_CAPTURE( msg ) (void)(0) +#define CATCH_INFO( msg )          (void)(0) +#define CATCH_UNSCOPED_INFO( msg ) (void)(0) +#define CATCH_WARN( msg )          (void)(0) +#define CATCH_CAPTURE( msg )       (void)(0)  #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))  #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))  #define CATCH_METHOD_AS_TEST_CASE( method, ... )  #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)  #define CATCH_SECTION( ... ) +#define CATCH_DYNAMIC_SECTION( ... )  #define CATCH_FAIL( ... ) (void)(0)  #define CATCH_FAIL_CHECK( ... ) (void)(0)  #define CATCH_SUCCEED( ... ) (void)(0)  #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif +  // "BDD-style" convenience wrappers  #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))  #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )  #define CATCH_GIVEN( desc ) +#define CATCH_AND_GIVEN( desc )  #define CATCH_WHEN( desc )  #define CATCH_AND_WHEN( desc )  #define CATCH_THEN( desc )  #define CATCH_AND_THEN( desc ) +#define CATCH_STATIC_REQUIRE( ... )       (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) +  // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required  #else @@ -11962,6 +17803,7 @@ using Catch::Detail::Approx;  #endif // CATCH_CONFIG_DISABLE_MATCHERS  #define INFO( msg ) (void)(0) +#define UNSCOPED_INFO( msg ) (void)(0)  #define WARN( msg ) (void)(0)  #define CAPTURE( msg ) (void)(0) @@ -11970,11 +17812,35 @@ using Catch::Detail::Approx;  #define METHOD_AS_TEST_CASE( method, ... )  #define REGISTER_TEST_CASE( Function, ... ) (void)(0)  #define SECTION( ... ) +#define DYNAMIC_SECTION( ... )  #define FAIL( ... ) (void)(0)  #define FAIL_CHECK( ... ) (void)(0)  #define SUCCEED( ... ) (void)(0)  #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +#define STATIC_REQUIRE( ... )       (void)(0) +#define STATIC_REQUIRE_FALSE( ... ) (void)(0) +  #endif  #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) @@ -11984,6 +17850,7 @@ using Catch::Detail::Approx;  #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )  #define GIVEN( desc ) +#define AND_GIVEN( desc )  #define WHEN( desc )  #define AND_WHEN( desc )  #define THEN( desc ) @@ -11993,6 +17860,8 @@ using Catch::Detail::Approx;  #endif +#endif // ! CATCH_CONFIG_IMPL_ONLY +  // start catch_reenable_warnings.h  | 
