diff options
author | Étienne Mollier <emollier@debian.org> | 2022-07-03 13:54:42 +0200 |
---|---|---|
committer | Étienne Mollier <emollier@debian.org> | 2022-07-03 13:54:42 +0200 |
commit | 83299c9f8e7b0cc3a745a6a57724695eeebdef71 (patch) | |
tree | a3ad4462f6516bf5261555dd77ba0c30df337387 | |
parent | bcfa0ab9bde3fff55fefecbd74a41171a3f0f642 (diff) |
Delete update-catch2.patch: applied upstream.
-rw-r--r-- | debian/patches/series | 1 | ||||
-rw-r--r-- | debian/patches/update-catch2.patch | 9997 |
2 files changed, 0 insertions, 9998 deletions
diff --git a/debian/patches/series b/debian/patches/series index 2d66ccd..06ebda1 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,4 +1,3 @@ use-packaged-streamvbyte.patch fix-install-location.patch soversion.patch -update-catch2.patch diff --git a/debian/patches/update-catch2.patch b/debian/patches/update-catch2.patch deleted file mode 100644 index 072fc69..0000000 --- a/debian/patches/update-catch2.patch +++ /dev/null @@ -1,9997 +0,0 @@ -Description: import catch2 version 2.13.8 to fix build failure with glibc 2.34 -Author: Steve Langasek <steve.langasek@ubuntu.com> -Last-Update: 2022-04-12 -Forwarded: no -Bug-Debian: https://bugs.debian.org/1009620 - -Index: libvbz-hdf-plugin-1.0.1/third_party/catch2/catch.hpp -=================================================================== ---- libvbz-hdf-plugin-1.0.1.orig/third_party/catch2/catch.hpp -+++ libvbz-hdf-plugin-1.0.1/third_party/catch2/catch.hpp -@@ -1,9 +1,9 @@ - /* -- * Catch v2.5.0 -- * Generated: 2018-11-26 20:46:12.165372 -+ * Catch v2.13.8 -+ * Generated: 2022-01-03 21:20:09.589503 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly -- * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. -+ * Copyright (c) 2022 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) -@@ -14,8 +14,8 @@ - - - #define CATCH_VERSION_MAJOR 2 --#define CATCH_VERSION_MINOR 5 --#define CATCH_VERSION_PATCH 0 -+#define CATCH_VERSION_MINOR 13 -+#define CATCH_VERSION_PATCH 8 - - #ifdef __clang__ - # pragma clang system_header -@@ -36,10 +36,11 @@ - # pragma clang diagnostic ignored "-Wcovered-switch-default" - # endif - #elif defined __GNUC__ -- // GCC likes to warn on REQUIREs, and we cannot suppress them -- // locally because g++'s support for _Pragma is lacking in older, -- // still supported, versions --# 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" -@@ -65,13 +66,16 @@ - #if !defined(CATCH_CONFIG_IMPL_ONLY) - // start catch_platform.h - -+// See e.g.: -+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html - #ifdef __APPLE__ --# include <TargetConditionals.h> --# if TARGET_OS_OSX == 1 --# define CATCH_PLATFORM_MAC --# elif TARGET_OS_IPHONE == 1 --# define CATCH_PLATFORM_IPHONE --# endif -+# include <TargetConditionals.h> -+# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ -+ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -+# define CATCH_PLATFORM_MAC -+# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -+# define CATCH_PLATFORM_IPHONE -+# endif - - #elif defined(linux) || defined(__linux) || defined(__linux__) - # define CATCH_PLATFORM_LINUX -@@ -131,30 +135,51 @@ - - #endif - --#if defined(CATCH_CPP17_OR_GREATER) --# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -+// Only GCC compiler should be used in this block, so other compilers trying to -+// mask themselves as GCC should be ignored. -+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -+# 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 - --#ifdef __clang__ -+#if defined(__clang__) - --# 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" ) -- --# 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_UNUSED_WARNINGS \ -- _Pragma( "clang diagnostic push" ) \ -- _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) --# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ -- _Pragma( "clang diagnostic pop" ) -+# 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_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__ - -@@ -179,6 +204,7 @@ - // 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 - - //////////////////////////////////////////////////////////////////////////////// -@@ -203,20 +229,16 @@ - // 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)) -+ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - --# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - - # endif - #endif // __CYGWIN__ - - //////////////////////////////////////////////////////////////////////////////// - // Visual C++ --#ifdef _MSC_VER -- --# if _MSC_VER >= 1900 // Visual Studio 2015 or newer --# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS --# endif -+#if defined(_MSC_VER) - - // Universal Windows platform does not support SEH - // Or console colours (or console at all...) -@@ -226,13 +248,25 @@ - # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH - # endif - -+# if !defined(__clang__) // Handle Clang masquerading for msvc -+ - // 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(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) --# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR --# endif -+# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -+# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -+# endif // MSVC_TRADITIONAL -+ -+// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -+# 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 - - //////////////////////////////////////////////////////////////////////////////// -@@ -265,30 +299,56 @@ - #endif - - //////////////////////////////////////////////////////////////////////////////// --// Check if string_view is available and usable --// The check is split apart to work around v140 (VS2015) preprocessor issue... --#if defined(__has_include) --#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER) --# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW -+ -+// 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 - --//////////////////////////////////////////////////////////////////////////////// --// Check if variant is available and usable -+// Various stdlib support checks that require __has_include - #if defined(__has_include) --# 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) --# endif // defined(__clang__) && (__clang_major__ < 8) --# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER) --#endif // __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 defined(__cpp_lib_byte) && (__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 -@@ -309,8 +369,8 @@ - # define CATCH_CONFIG_CPP11_TO_STRING - #endif - --#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 -+#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) -@@ -321,6 +381,10 @@ - # 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 -@@ -337,17 +401,53 @@ - # 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 --# define CATCH_INTERNAL_UNSUPPRESS_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) -@@ -407,12 +507,12 @@ - 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; - -@@ -453,9 +553,10 @@ - } // end namespace Catch - - #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_UNSUPPRESS_GLOBALS_WARNINGS -+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - - // end catch_tag_alias_autoregistrar.h - // start catch_test_registry.h -@@ -463,7 +564,6 @@ - // start catch_interfaces_testcase.h - - #include <vector> --#include <memory> - - namespace Catch { - -@@ -474,8 +574,6 @@ - virtual ~ITestInvoker(); - }; - -- using ITestCasePtr = std::shared_ptr<ITestInvoker>; -- - class TestCase; - struct IConfig; - -@@ -485,6 +583,7 @@ - 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 ); -@@ -497,55 +596,30 @@ - #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 { - public: - using size_type = std::size_t; -+ using const_iterator = const char*; - - private: -- friend struct StringRefTestAccess; -- -- char const* m_start; -- size_type m_size; -- -- char* m_data = nullptr; -- -- void takeOwnership(); -- - static constexpr char const* const s_empty = ""; - -- public: // construction/ assignment -- StringRef() noexcept -- : StringRef( s_empty, 0 ) -- {} -+ char const* m_start = s_empty; -+ size_type m_size = 0; - -- StringRef( StringRef const& other ) noexcept -- : m_start( other.m_start ), -- m_size( other.m_size ) -- {} -- -- StringRef( StringRef&& other ) noexcept -- : m_start( other.m_start ), -- m_size( other.m_size ), -- m_data( other.m_data ) -- { -- other.m_data = nullptr; -- } -+ public: // construction -+ constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - -- StringRef( char const* rawChars, size_type size ) noexcept -+ constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} -@@ -555,99 +629,64 @@ - m_size( stdString.size() ) - {} - -- ~StringRef() noexcept { -- delete[] m_data; -- } -- -- auto operator = ( StringRef const &other ) noexcept -> StringRef& { -- delete[] m_data; -- m_data = nullptr; -- m_start = other.m_start; -- m_size = other.m_size; -- return *this; -+ explicit operator std::string() const { -+ return std::string(m_start, m_size); - } - -- operator std::string() const; -- -- void swap( StringRef& other ) noexcept; -- - 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 { -+ constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } -- auto size() const noexcept -> size_type { -+ constexpr auto size() const noexcept -> size_type { - return m_size; - } - -- auto numberOfCharacters() const noexcept -> size_type; -+ // 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; - -- // Returns the current start pointer. -- // Note that the pointer can change when if the StringRef is a substring -- auto currentData() const noexcept -> char const*; -+ // Returns the current start pointer. May not be null-terminated. -+ auto data() const noexcept -> char const*; - -- private: // ownership queries - may not be consistent between calls -- auto isOwned() const noexcept -> bool; -- auto isSubstring() const noexcept -> bool; -- }; -+ constexpr auto isNullTerminated() const noexcept -> bool { -+ return m_start[m_size] == '\0'; -+ } - -- 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; -+ 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&; - -- inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { -+ constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -- - } // namespace Catch - --inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { -+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_type_traits.hpp -- -- --namespace Catch{ -- --#ifdef CATCH_CPP17_OR_GREATER -- template <typename...> -- inline constexpr auto is_unique = std::true_type{}; -- -- template <typename T, typename... Rest> -- inline constexpr auto is_unique<T, Rest...> = std::bool_constant< -- (!std::is_same_v<T, Rest> && ...) && is_unique<Rest...> -- >{}; --#else -- --template <typename...> --struct is_unique : std::true_type{}; -- --template <typename T0, typename T1, typename... Rest> --struct is_unique<T0, T1, Rest...> : std::integral_constant --<bool, -- !std::is_same<T0, T1>::value -- && is_unique<T0, Rest...>::value -- && is_unique<T1, Rest...>::value -->{}; -- --#endif --} -- --// end catch_type_traits.hpp - // start catch_preprocessor.hpp - - -@@ -699,21 +738,224 @@ - #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__) - --#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__) - #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR --#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__ --#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -+#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 --// MSVC is adding extra space and needs more calls to properly remove () --#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__ --#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__) --#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -+#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> -@@ -758,38 +1000,70 @@ - }; \ - } \ - void TestName::test() -- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \ -- template<typename TestType> \ -- static void TestName() -- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ -+ #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{ \ -- template<typename TestType> \ -- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \ -- void test(); \ -- }; \ -+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ -+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ - } \ -- template<typename TestType> \ -- void TestName::test() -+ } \ -+ 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::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ -- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ -+ 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__ ) -+ 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 : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ -@@ -797,88 +1071,273 @@ - }; \ - 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__ ) -+ 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::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ -- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - - /////////////////////////////////////////////////////////////////////////////// -- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\ -+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\ -+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ -- template<typename TestType> \ -- static void TestFunc();\ -+ 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{\ -- template<typename...Ts> \ -- TestName(Ts...names){\ -- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ -+ TestName(){\ -+ int index = 0; \ -+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ - using expander = int[];\ -- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ -+ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ - }\ - };\ -- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \ -+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ -+ TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\ -+ return 0;\ -+ }();\ - }\ -- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ -- template<typename TestType> \ -- static void TestFunc() -+ }\ -+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ -+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature)) - --#if defined(CATCH_CPP17_OR_GREATER) --#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case"); -+#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 CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case"); -+ #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(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, __VA_ARGS__ ) -+ #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(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, __VA_ARGS__ ) ) -+ #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_REGISTRY_INITIATE(TestName, Name, ...)\ -- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ -- TestName<CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)>(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\ -- return 0;\ -- }(); -+ #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 - -- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ -+#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 \ -- namespace{ \ -- template<typename TestType> \ -+ 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();\ - };\ -- template<typename...Types> \ -+ 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{\ -- template<typename...Ts> \ -- TestNameClass(Ts...names){\ -- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ -+ void reg_tests(){\ -+ int index = 0;\ - using expander = int[];\ -- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ -+ 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 */ \ - }\ - };\ -- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\ -+ 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_UNSUPPRESS_GLOBALS_WARNINGS\ -+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - template<typename TestType> \ - void TestName<TestType>::test() - - #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, __VA_ARGS__ ) -+ #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_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, __VA_ARGS__ ) ) -+ #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 - -@@ -978,7 +1437,7 @@ - - auto makeStream( StringRef const &filename ) -> IStream const*; - -- class ReusableStringStream { -+ class ReusableStringStream : NonCopyable { - std::size_t m_index; - std::ostream* m_oss; - public: -@@ -997,6 +1456,42 @@ - } - - // 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> -@@ -1067,9 +1562,9 @@ - - 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; -@@ -1231,6 +1726,12 @@ - } - }; - -+#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); -@@ -1282,10 +1783,13 @@ - 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> -@@ -1321,8 +1825,8 @@ - #endif - - namespace Detail { -- template<typename InputIterator> -- std::string rangeToString(InputIterator first, InputIterator last) { -+ template<typename InputIterator, typename Sentinel = InputIterator> -+ std::string rangeToString(InputIterator first, Sentinel last) { - ReusableStringStream rss; - rss << "{ "; - if (first != last) { -@@ -1370,6 +1874,7 @@ - # 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 -@@ -1391,6 +1896,24 @@ - } - #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> -@@ -1461,20 +1984,27 @@ - #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER - - namespace Catch { -- struct not_this_one {}; // Tag type for detecting which begin/ end are being selected -- -- // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace -+ // Import begin/ end from std here - using std::begin; - using std::end; - -- not_this_one begin( ... ); -- not_this_one 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 { -- static const bool value = -- !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value && -- !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value; -+ struct is_range : detail::is_range_impl<T> { - }; - - #if defined(_MANAGED) // Managed types are never ranges -@@ -1639,6 +2169,18 @@ - } - #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 -@@ -1652,6 +2194,7 @@ - #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 { -@@ -1695,6 +2238,62 @@ - 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> -@@ -1707,7 +2306,7 @@ - - public: - explicit UnaryExpr( LhsT lhs ) -- : ITransientExpression{ false, lhs ? true : false }, -+ : ITransientExpression{ false, static_cast<bool>(lhs) }, - m_lhs( lhs ) - {} - }; -@@ -1773,6 +2372,32 @@ - 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& 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 }; -@@ -1807,6 +2432,7 @@ - // start catch_interfaces_capture.h - - #include <string> -+#include <chrono> - - namespace Catch { - -@@ -1815,15 +2441,20 @@ - struct SectionInfo; - struct SectionEndInfo; - struct MessageInfo; -+ struct MessageBuilder; - struct Counts; -- struct BenchmarkInfo; -- struct BenchmarkStats; - struct AssertionReaction; - struct SourceLineInfo; - - struct ITransientExpression; - struct IGeneratorTracker; - -+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) -+ struct BenchmarkInfo; -+ template <typename Duration = std::chrono::duration<double, std::nano>> -+ struct BenchmarkStats; -+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING -+ - struct IResultCapture { - - virtual ~IResultCapture(); -@@ -1833,14 +2464,20 @@ - virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - -- virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; -+ virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; - -+#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 benchmarkEnded( BenchmarkStats<> const& stats ) = 0; -+ virtual void benchmarkFailed( std::string const& error ) = 0; -+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - - virtual void pushScopedMessage( MessageInfo const& message ) = 0; - virtual void popScopedMessage( MessageInfo const& message ) = 0; - -+ virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; -+ - virtual void handleFatalErrorCondition( StringRef message ) = 0; - - virtual void handleExpr -@@ -2004,9 +2641,12 @@ - class ScopedMessage { - public: - explicit ScopedMessage( MessageBuilder const& builder ); -+ ScopedMessage( ScopedMessage& duplicate ) = delete; -+ ScopedMessage( ScopedMessage&& old ); - ~ScopedMessage(); - - MessageInfo m_info; -+ bool m_moved; - }; - - class Capturer { -@@ -2062,15 +2702,16 @@ - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ - do { \ -+ 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.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ -- CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ -+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ -- } while( (void)0, 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, ... ) \ -@@ -2151,6 +2792,10 @@ - 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 { \ -@@ -2283,62 +2928,18 @@ - } // end namespace Catch - - #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_UNSUPPRESS_UNUSED_WARNINGS -+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - - #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_UNSUPPRESS_UNUSED_WARNINGS -+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - - // end catch_section.h --// start catch_benchmark.h -- --#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 - // start catch_interfaces_exception.h - - // start catch_interfaces_registry_hub.h -@@ -2355,6 +2956,8 @@ - struct IReporterRegistry; - struct IReporterFactory; - struct ITagAliasRegistry; -+ struct IMutableEnumValuesRegistry; -+ - class StartupExceptionRegistry; - - using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; -@@ -2365,7 +2968,6 @@ - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; -- - virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; - - virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; -@@ -2379,6 +2981,7 @@ - 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 const& getRegistryHub(); -@@ -2425,6 +3028,9 @@ - {} - - 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()); -@@ -2434,6 +3040,7 @@ - catch( T& ex ) { - return m_translateFunction( ex ); - } -+#endif - } - - protected: -@@ -2452,9 +3059,10 @@ - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ - static std::string translatorName( signature ); \ -+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ -- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ -+ 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 ) -@@ -2485,7 +3093,7 @@ - Approx operator-() const; - - template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> -- Approx operator()( T const& value ) { -+ Approx operator()( T const& value ) const { - Approx approx( static_cast<double>(value) ); - approx.m_epsilon = m_epsilon; - approx.m_margin = m_margin; -@@ -2585,6 +3193,7 @@ - - #include <string> - #include <iosfwd> -+#include <vector> - - namespace Catch { - -@@ -2595,7 +3204,13 @@ - 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 { -@@ -2648,6 +3263,15 @@ - virtual bool match( ObjectT const& 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 -+ - #ifdef __clang__ - # pragma clang diagnostic pop - #endif -@@ -2685,9 +3309,10 @@ - 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; -@@ -2718,9 +3343,10 @@ - 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; -@@ -2764,10 +3390,34 @@ - } // namespace Catch - - // end catch_matchers.h --// start catch_matchers_floating.h -+// start catch_matchers_exception.hpp - --#include <type_traits> --#include <cmath> -+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 { -@@ -2786,22 +3436,43 @@ - }; - - struct WithinUlpsMatcher : MatcherBase<double> { -- WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); -+ WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); - bool match(double const& matchee) const override; - std::string describe() const override; - private: - double m_target; -- int m_ulps; -+ 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, int maxUlpDiff); -- Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); -+ 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 -@@ -2844,7 +3515,7 @@ - - // The following functions create the actual matcher objects. - // The user has to explicitly specify type to the function, because -- // infering std::function<bool(T const&)> is hard (but possible) and -+ // 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 = "") { -@@ -2932,34 +3603,12 @@ - namespace Matchers { - - namespace Vector { -- namespace Detail { -- template <typename InputIterator, typename T> -- size_t count(InputIterator first, InputIterator last, T const& item) { -- size_t cnt = 0; -- for (; first != last; ++first) { -- if (*first == item) { -- ++cnt; -- } -- } -- return cnt; -- } -- template <typename InputIterator, typename T> -- bool contains(InputIterator first, InputIterator last, T const& item) { -- for (; first != last; ++first) { -- if (*first == item) { -- return true; -- } -- } -- return false; -- } -- } -- -- template<typename T> -- struct ContainsElementMatcher : MatcherBase<std::vector<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; -@@ -2975,12 +3624,12 @@ - T const& m_comparator; - }; - -- template<typename T> -- struct ContainsMatcher : MatcherBase<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; -@@ -3002,18 +3651,18 @@ - 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>> { -+ 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; -@@ -3025,47 +3674,60 @@ - 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> -- struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> { -- UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {} -- bool match(std::vector<T> const& vec) const override { -- // Note: This is a reimplementation of std::is_permutation, -- // because I don't want to include <algorithm> inside the common path -- if (m_target.size() != vec.size()) { -- return false; -- } -- auto lfirst = m_target.begin(), llast = m_target.end(); -- auto rfirst = vec.begin(), rlast = vec.end(); -- // Cut common prefix to optimize checking of permuted parts -- while (lfirst != llast && *lfirst == *rfirst) { -- ++lfirst; ++rfirst; -- } -- if (lfirst == llast) { -- return true; -- } -+ template<typename T, typename AllocComp, typename AllocMatch> -+ struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> { - -- for (auto mid = lfirst; mid != llast; ++mid) { -- // Skip already counted items -- if (Detail::contains(lfirst, mid, *mid)) { -- continue; -- } -- size_t num_vec = Detail::count(rfirst, rlast, *mid); -- if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { -- return false; -- } -- } -+ 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> const& m_target; -+ std::vector<T, AllocComp> const& m_target; - }; - - } // namespace Vector -@@ -3073,24 +3735,29 @@ - // 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> -- Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) { -- return Vector::UnorderedEqualsMatcher<T>(target); -+ 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 -@@ -3175,16 +3842,17 @@ - namespace Catch { - - namespace Generators { -- class GeneratorBase { -- protected: -- size_t m_size = 0; -- -+ class GeneratorUntypedBase { - public: -- GeneratorBase( size_t size ) : m_size( size ) {} -- virtual ~GeneratorBase(); -- auto size() const -> size_t { return m_size; } -+ 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<GeneratorBase>; -+ using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>; - - } // namespace Generators - -@@ -3193,7 +3861,6 @@ - virtual auto hasGenerator() const -> bool = 0; - virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; - virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; -- virtual auto getIndex() const -> std::size_t = 0; - }; - - } // namespace Catch -@@ -3201,7 +3868,7 @@ - // end catch_interfaces_generatortracker.h - // start catch_enforce.h - --#include <stdexcept> -+#include <exception> - - namespace Catch { - #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -@@ -3214,18 +3881,30 @@ - [[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_PREPARE_EXCEPTION( type, msg ) \ -- type( ( Catch::ReusableStringStream() << msg ).str() ) --#define CATCH_INTERNAL_ERROR( msg ) \ -- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg)) --#define CATCH_ERROR( msg ) \ -- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg )) --#define CATCH_RUNTIME_ERROR( msg ) \ -- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg )) --#define CATCH_ENFORCE( condition, msg ) \ -- do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) -+#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> -@@ -3233,8 +3912,21 @@ - #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? -@@ -3246,223 +3938,819 @@ - } - - template<typename T> -- struct IGenerator { -- virtual ~IGenerator() {} -- virtual auto get( size_t index ) const -> T = 0; -+ 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 : public IGenerator<T> { -+ class SingleValueGenerator final : public IGenerator<T> { - T m_value; - public: -- SingleValueGenerator( T const& value ) : m_value( value ) {} -+ SingleValueGenerator(T&& value) : m_value(std::move(value)) {} - -- auto get( size_t ) const -> T override { -+ T const& get() const override { - return m_value; - } -+ bool next() override { -+ return false; -+ } - }; - - template<typename T> -- class FixedValuesGenerator : public IGenerator<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 ) {} - -- auto get( size_t index ) const -> T override { -- return m_values[index]; -+ 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 RangeGenerator : public IGenerator<T> { -- T const m_first; -- T const m_last; -+ 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: -- RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) { -- assert( m_last > m_first ); -+ template <typename... Gs> -+ Generators(Gs &&... moreGenerators) { -+ m_generators.reserve(sizeof...(Gs)); -+ populate(std::forward<Gs>(moreGenerators)...); - } - -- auto get( size_t index ) const -> T override { -- // ToDo:: introduce a safe cast to catch potential overflows -- return static_cast<T>(m_first+index); -+ T const& get() const override { -+ return m_generators[m_current].get(); - } -- }; - -- template<typename T> -- struct NullGenerator : IGenerator<T> { -- auto get( size_t ) const -> T override { -- CATCH_INTERNAL_ERROR("A Null Generator is always empty"); -+ 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> -- class Generator { -- std::unique_ptr<IGenerator<T>> m_generator; -- size_t m_size; -+ 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 )... ); -+ } - -- public: -- Generator( size_t size, std::unique_ptr<IGenerator<T>> generator ) -- : m_generator( std::move( generator ) ), -- m_size( size ) -- {} -+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; - -- auto size() const -> size_t { return m_size; } -- auto operator[]( size_t index ) const -> T { -- assert( index < m_size ); -- return m_generator->get( index ); -+ 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())); - } -- }; - -- std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ); -+ auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); -+ return generator.get(); -+ } -+ -+} // namespace Generators -+} // namespace Catch - -- template<typename T> -- class GeneratorRandomiser : public IGenerator<T> { -- Generator<T> m_baseGenerator; -+#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 { - -- std::vector<size_t> m_indices; -+ template <typename T> -+ class TakeGenerator : public IGenerator<T> { -+ GeneratorWrapper<T> m_generator; -+ size_t m_returned = 0; -+ size_t m_target; - public: -- GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems ) -- : m_baseGenerator( std::move( baseGenerator ) ), -- m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) ) -- {} -+ 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; -+ } - -- auto get( size_t index ) const -> T override { -- return m_baseGenerator[m_indices[index]]; -+ 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> -- struct RequiresASpecialisationFor; -+ 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> -- auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); } -+ 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 = nextImpl(); -+ if (!has_initial_value) { -+ Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); -+ } -+ } -+ } - -- template<> -- auto all<int>() -> Generator<int>; -+ T const& get() const override { -+ return m_generator.get(); -+ } - -- template<typename T> -- auto range( T const& first, T const& last ) -> Generator<T> { -- return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) ); -+ bool next() override { -+ return nextImpl(); -+ } -+ -+ private: -+ bool nextImpl() { -+ 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> -- auto random( T const& first, T const& last ) -> Generator<T> { -- auto gen = range( first, last ); -- auto size = gen.size(); -+ 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; -+ } -+ }; - -- return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) ); -+ 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> -- auto random( size_t size ) -> Generator<T> { -- return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) ); -+ -+ 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> -- auto values( std::initializer_list<T> values ) -> Generator<T> { -- return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) ); -+ 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> -- auto value( T const& val ) -> Generator<T> { -- return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) ); -+ -+ 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)) -+ ); - } - -- template<typename T> -- auto as() -> Generator<T> { -- return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() ); -+} // 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; - } - -- template<typename... Ts> -- auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> { -- return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) ); -+ inline IContext& getCurrentContext() -+ { -+ return getCurrentMutableContext(); - } - -- template<typename T> -- struct Generators : GeneratorBase { -- std::vector<Generator<T>> m_generators; -+ void cleanUpContext(); - -- using type = T; -+ class SimplePcg32; -+ SimplePcg32& rng(); -+} - -- Generators() : GeneratorBase( 0 ) {} -+// end catch_context.h -+// start catch_interfaces_config.h - -- void populate( T&& val ) { -- m_size += 1; -- m_generators.emplace_back( value( std::move( val ) ) ); -+// 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(); - } -- template<typename U> -- void populate( U&& val ) { -- populate( T( std::move( val ) ) ); -+ -+ Option& operator= ( Option const& _other ) { -+ if( &_other != this ) { -+ reset(); -+ if( _other ) -+ nullableValue = new( storage ) T( *_other ); -+ } -+ return *this; - } -- void populate( Generator<T>&& generator ) { -- m_size += generator.size(); -- m_generators.emplace_back( std::move( generator ) ); -+ Option& operator = ( T const& _value ) { -+ reset(); -+ nullableValue = new( storage ) T( _value ); -+ return *this; - } - -- template<typename U, typename... Gs> -- void populate( U&& valueOrGenerator, Gs... moreGenerators ) { -- populate( std::forward<U>( valueOrGenerator ) ); -- populate( std::forward<Gs>( moreGenerators )... ); -+ void reset() { -+ if( nullableValue ) -+ nullableValue->~T(); -+ nullableValue = nullptr; - } - -- auto operator[]( size_t index ) const -> T { -- size_t sizes = 0; -- for( auto const& gen : m_generators ) { -- auto localIndex = index-sizes; -- sizes += gen.size(); -- if( index < sizes ) -- return gen[localIndex]; -- } -- CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')'); -+ 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)]; - }; - -- template<typename T, typename... Gs> -- auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> { -- Generators<T> generators; -- generators.m_generators.reserve( 1+sizeof...(Gs) ); -- generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... ); -- return generators; -+} // 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()); - } -- template<typename T> -- auto makeGenerators( Generator<T>&& generator ) -> Generators<T> { -- Generators<T> generators; -- generators.populate( std::move( generator ) ); -- return generators; -+ -+ Float const& get() const override { -+ return m_current_number; - } -- template<typename T, typename... Gs> -- auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> { -- return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); -+ bool next() override { -+ m_current_number = m_dist(m_rng); -+ return true; - } -- template<typename T, typename U, typename... Gs> -- auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> { -- return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); -+}; -+ -+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; - } -+}; - -- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; -+// 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 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( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) { -- using UnderlyingType = typename decltype(generatorExpression())::type; -+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)); -+} - -- IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); -- if( !tracker.hasGenerator() ) -- tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) ); -+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]; -+ } - -- auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() ); -- return generator[tracker.getIndex()]; -+ 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 - --#define GENERATE( ... ) \ -- Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) -- --// end catch_generators.hpp -+// end catch_generators_specific.hpp - - // These files are included here so the single_include script doesn't put them - // in the conditionally compiled sections -@@ -3662,7 +4950,7 @@ - arcSafeRelease( m_substr ); - } - -- bool match( NSString* arg ) const override { -+ bool match( NSString* str ) const override { - return false; - } - -@@ -3685,7 +4973,7 @@ - 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; - } -@@ -3761,7 +5049,8 @@ - // 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 -@@ -3803,7 +5092,7 @@ - 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; -@@ -3817,36 +5106,40 @@ - - 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; - }; -@@ -3855,15 +5148,25 @@ - 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; - }; - } -@@ -3898,9 +5201,13 @@ - 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; -@@ -3913,32 +5220,32 @@ - 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 ); - -@@ -3949,79 +5256,6 @@ - #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, -- 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 TestSpec const& testSpec() const = 0; -- virtual bool hasTestFilters() 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 - - #include <memory> -@@ -4052,11 +5286,17 @@ - - 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; -@@ -4091,10 +5331,10 @@ - std::string getProcessName() const; - std::string const& getReporterName() const; - -- std::vector<std::string> const& getTestsOrTags() 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; -@@ -4107,14 +5347,19 @@ - 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: - -@@ -4175,76 +5420,61 @@ - } // 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(); -- } -+namespace Catch { -+ namespace Benchmark { -+ template <typename Duration> -+ struct Estimate { -+ Duration point; -+ Duration lower_bound; -+ Duration upper_bound; -+ double confidence_interval; - -- Option& operator= ( Option const& _other ) { -- if( &_other != this ) { -- reset(); -- if( _other ) -- nullableValue = new( storage ) T( *_other ); -+ 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; -- } -+ }; -+ } // namespace Benchmark -+} // namespace Catch - -- T& operator*() { return *nullableValue; } -- T const& operator*() const { return *nullableValue; } -- T* operator->() { return nullableValue; } -- const T* operator->() const { return nullableValue; } -+// end catch_estimate.hpp -+// start catch_outlier_classification.hpp - -- T valueOr( T const& defaultValue ) const { -- return nullableValue ? *nullableValue : defaultValue; -- } -+// Outlier information - -- bool some() const { return nullableValue != nullptr; } -- bool none() const { return nullableValue == nullptr; } -+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 - -- bool operator !() const { return nullableValue == nullptr; } -- explicit operator bool() const { -- return some(); -- } -+ int total() const { -+ return low_severe + low_mild + high_mild + high_severe; -+ } -+ }; -+ } // namespace Benchmark -+} // namespace Catch - -- private: -- T *nullableValue; -- alignas(alignof(T)) char storage[sizeof(T)]; -- }; -+// end catch_outlier_classification.hpp - --} // end namespace Catch -+#include <iterator> -+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - --// end catch_option.hpp - #include <string> - #include <iosfwd> - #include <map> - #include <set> - #include <memory> -+#include <algorithm> - - namespace Catch { - -@@ -4301,8 +5531,8 @@ - - 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; -@@ -4380,14 +5610,43 @@ - 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; -@@ -4400,23 +5659,26 @@ - - 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; -@@ -4465,6 +5727,11 @@ - // 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 { - -@@ -4489,9 +5756,12 @@ - - 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; - } -@@ -4782,11 +6052,11 @@ - - 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(); - } - }; -@@ -4803,10 +6073,10 @@ - - 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(); - } - }; -@@ -4822,14 +6092,16 @@ - #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) -@@ -4851,8 +6123,6 @@ - - static std::string getDescription(); - -- ReporterPreferences getPreferences() const override; -- - void noMatchingTestCases(std::string const& spec) override; - - void assertionStarting(AssertionInfo const&) override; -@@ -4891,6 +6161,8 @@ - - 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; -@@ -4898,13 +6170,17 @@ - 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 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(); -@@ -4926,6 +6202,7 @@ - - void printTotalsDivider(Totals const& totals); - void printSummaryDivider(); -+ void printTestFilters(); - - private: - bool m_headerPrinted = false; -@@ -4945,6 +6222,14 @@ - #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: -@@ -4966,14 +6251,14 @@ - - class ScopedElement { - public: -- ScopedElement( XmlWriter* writer ); -+ ScopedElement( XmlWriter* writer, XmlFormatting fmt ); - - ScopedElement( ScopedElement&& other ) noexcept; - ScopedElement& operator=( ScopedElement&& other ) noexcept; - - ~ScopedElement(); - -- ScopedElement& writeText( std::string const& text, bool indent = true ); -+ ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent ); - - template<typename T> - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { -@@ -4983,6 +6268,7 @@ - - private: - mutable XmlWriter* m_writer = nullptr; -+ XmlFormatting m_fmt; - }; - - XmlWriter( std::ostream& os = Catch::cout() ); -@@ -4991,11 +6277,11 @@ - XmlWriter( XmlWriter const& ) = delete; - XmlWriter& operator=( XmlWriter const& ) = delete; - -- XmlWriter& startElement( std::string const& name ); -+ XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - -- ScopedElement scopedElement( std::string const& name ); -+ ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - -- XmlWriter& endElement(); -+ XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); - -@@ -5008,9 +6294,9 @@ - return writeAttribute( name, rss.str() ); - } - -- XmlWriter& writeText( std::string const& text, bool indent = true ); -+ XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - -- XmlWriter& writeComment( std::string const& text ); -+ XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - - void writeStylesheetRef( std::string const& url ); - -@@ -5020,6 +6306,8 @@ - - private: - -+ void applyFormatting(XmlFormatting fmt); -+ - void writeDeclaration(); - - void newlineIfNecessary(); -@@ -5063,9 +6351,10 @@ - - void writeTestCase(TestCaseNode const& testCaseNode); - -- void writeSection(std::string const& className, -- std::string const& rootName, -- SectionNode const& sectionNode); -+ void writeSection( std::string const& className, -+ std::string const& rootName, -+ SectionNode const& sectionNode, -+ bool testOkToFail ); - - void writeAssertions(SectionNode const& sectionNode); - void writeAssertion(AssertionStats const& stats); -@@ -5120,6 +6409,13 @@ - - 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; -@@ -5133,6 +6429,1032 @@ - // 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; -+ } -+ Catch::throw_exception(optimized_away_error{}); -+ } -+ } // namespace Detail -+ } // namespace Benchmark -+} // namespace Catch -+ -+// end catch_run_for_at_least.hpp -+#include <algorithm> -+#include <iterator> -+ -+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 -@@ -5158,23 +7480,37 @@ - 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; - -@@ -5189,7 +7525,7 @@ - - // Debug/ checking - virtual bool isSectionTracker() const = 0; -- virtual bool isIndexTracker() const = 0; -+ virtual bool isGeneratorTracker() const = 0; - }; - - class TrackerContext { -@@ -5206,8 +7542,6 @@ - - public: - -- static TrackerContext& instance(); -- - ITracker& startRun(); - void endRun(); - -@@ -5231,7 +7565,6 @@ - }; - - using Children = std::vector<ITrackerPtr>; -- NameAndLocation m_nameAndLocation; - TrackerContext& m_ctx; - ITracker* m_parent; - Children m_children; -@@ -5240,11 +7573,13 @@ - 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; - -@@ -5254,7 +7589,7 @@ - void openChild() override; - - bool isSectionTracker() const override; -- bool isIndexTracker() const override; -+ bool isGeneratorTracker() const override; - - void open(); - -@@ -5269,33 +7604,24 @@ - - 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 -@@ -5303,7 +7629,6 @@ - using TestCaseTracking::ITracker; - using TestCaseTracking::TrackerContext; - using TestCaseTracking::SectionTracker; --using TestCaseTracking::IndexTracker; - - } // namespace Catch - -@@ -5321,6 +7646,217 @@ - } - // 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> -@@ -5365,21 +7901,22 @@ - 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 margin) { -- CATCH_ENFORCE(margin >= 0, -- "Invalid Approx::margin: " << margin << '.' -+ void Approx::setMargin(double newMargin) { -+ CATCH_ENFORCE(newMargin >= 0, -+ "Invalid Approx::margin: " << newMargin << '.' - << " Approx::Margin has to be non-negative."); -- m_margin = margin; -+ m_margin = newMargin; - } - -- void Approx::setEpsilon(double epsilon) { -- CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0, -- "Invalid Approx::epsilon: " << epsilon << '.' -+ 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 = epsilon; -+ m_epsilon = newEpsilon; - } - - } // end namespace Detail -@@ -5401,58 +7938,6 @@ - // end catch_approx.cpp - // start catch_assertionhandler.cpp - --// 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(); -- return *IMutableContext::currentContext; -- } -- -- inline IContext& getCurrentContext() -- { -- return getCurrentMutableContext(); -- } -- -- void cleanUpContext(); --} -- --// end catch_context.h - // start catch_debugger.h - - namespace Catch { -@@ -5461,7 +7946,24 @@ - - #ifdef CATCH_PLATFORM_MAC - -- #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ -+ #if defined(__i386__) || defined(__x86_64__) -+ #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ -+ #elif defined(__aarch64__) -+ #define CATCH_TRAP() __asm__(".inst 0xd4200000") -+ #endif -+ -+#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 - - #elif defined(CATCH_PLATFORM_LINUX) - // If we can use inline assembler, do it because this allows us to break -@@ -5481,13 +7983,12 @@ - #define CATCH_TRAP() DebugBreak() - #endif - --#ifdef CATCH_TRAP -- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } --#else -- namespace Catch { -- inline void doNothing() {} -- } -- #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing() -+#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 -@@ -5495,86 +7996,58 @@ - - // 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 ) -+#include <cassert> - - namespace Catch { - -- struct FatalConditionHandler { -- -- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); -+ // Wrapper for platform-specific fatal error (signals/SEH) handlers -+ // -+ // Tries to be cooperative with other handlers, and not step over -+ // other handlers. This means that unknown structured exceptions -+ // are passed on, previous signal handlers are called, and so on. -+ // -+ // Can only be instantiated once, and assumes that once a signal -+ // is caught, the binary will end up terminating. Thus, there -+ class FatalConditionHandler { -+ bool m_started = false; -+ -+ // Install/disengage implementation for specific platform. -+ // Should be if-defed to work on current platform, can assume -+ // engage-disengage 1:1 pairing. -+ void engage_platform(); -+ void disengage_platform(); -+ public: -+ // Should also have platform-specific implementations as needed - FatalConditionHandler(); -- static void reset(); - ~FatalConditionHandler(); - -- private: -- static bool isSet; -- static ULONG guaranteeSize; -- static PVOID exceptionHandlerHandle; -- }; -- --} // 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 ); -+ void engage() { -+ assert(!m_started && "Handler cannot be installed twice."); -+ m_started = true; -+ engage_platform(); -+ } - -- FatalConditionHandler(); -- ~FatalConditionHandler(); -- static void reset(); -+ void disengage() { -+ assert(m_started && "Handler cannot be uninstalled without being installed first"); -+ m_started = false; -+ disengage_platform(); -+ } - }; - --} // namespace Catch -- --#else -- --namespace Catch { -- struct FatalConditionHandler { -- void reset(); -+ //! Simple RAII guard for (dis)engaging the FatalConditionHandler -+ class FatalConditionHandlerGuard { -+ FatalConditionHandler* m_handler; -+ public: -+ FatalConditionHandlerGuard(FatalConditionHandler* handler): -+ m_handler(handler) { -+ m_handler->engage(); -+ } -+ ~FatalConditionHandlerGuard() { -+ m_handler->disengage(); -+ } - }; --} - --#endif -+} // end namespace Catch - - // end catch_fatal_condition.h - #include <string> -@@ -5634,14 +8107,20 @@ - void sectionEnded( SectionEndInfo const& endInfo ) override; - void sectionEndedEarly( SectionEndInfo const& endInfo ) override; - -- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& 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 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; -@@ -5682,22 +8161,26 @@ - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase = nullptr; -- ITracker* m_testCaseTracker; -+ 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; -+ FatalConditionHandler m_fatalConditionhandler; - 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 -@@ -5844,7 +8327,7 @@ - } - - bool AssertionResult::hasExpression() const { -- return m_info.capturedExpression[0] != 0; -+ return !m_info.capturedExpression.empty(); - } - - bool AssertionResult::hasMessage() const { -@@ -5852,16 +8335,22 @@ - } - - std::string AssertionResult::getExpression() const { -- if( isFalseTest( m_info.resultDisposition ) ) -- return "!(" + 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; -@@ -5896,32 +8385,6 @@ - - } // 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 { -@@ -6067,6 +8530,9 @@ - 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; - -@@ -6325,6 +8791,7 @@ - // ----------- end of #include from clara_textflow.hpp ----------- - // ........... back in clara.hpp - -+#include <cctype> - #include <string> - #include <memory> - #include <set> -@@ -6617,7 +9084,7 @@ - } - 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") -@@ -7265,9 +9732,14 @@ - 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 ) { -@@ -7302,14 +9774,16 @@ - }; - 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 ) { -@@ -7379,6 +9853,9 @@ - | 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" ) -@@ -7409,13 +9886,24 @@ - | 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" ); - -@@ -7431,9 +9919,6 @@ - - namespace Catch { - -- 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); - } -@@ -7469,14 +9954,23 @@ - : m_data( data ), - m_stream( openStream() ) - { -- TestSpecParser parser(ITagAliasRegistry::get()); -- if (data.testsOrTags.empty()) { -- parser.parse("~[.]"); // All not hidden tests -+ // 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); - } -- else { -+ 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 : data.testsOrTags ) -- parser.parse( testOrTags ); -+ for (auto const& testOrTags : m_data.testsOrTags) { -+ parser.parse(testOrTags); -+ } - } - m_testSpec = parser.testSpec(); - } -@@ -7509,15 +10003,21 @@ - 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() { - return Catch::makeStream(m_data.outputFilename); - } -@@ -7546,6 +10046,36 @@ - } - - // 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) -+ -+// end catch_windows_h_proxy.h - #include <sstream> - - namespace Catch { -@@ -7557,7 +10087,7 @@ - }; - - struct NoColourImpl : IColourImpl { -- void use( Colour::Code ) {} -+ void use( Colour::Code ) override {} - - static IColourImpl* instance() { - static NoColourImpl s_instance; -@@ -7591,7 +10121,7 @@ - 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 ); -@@ -7654,7 +10184,7 @@ - // 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" ); -@@ -7682,13 +10212,14 @@ - - 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 - #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) -@@ -7729,13 +10260,13 @@ - namespace Catch { - - Colour::Colour( Code _colourCode ) { use( _colourCode ); } -- Colour::Colour( Colour&& rhs ) noexcept { -- m_moved = rhs.m_moved; -- rhs.m_moved = true; -- } -- Colour& Colour::operator=( 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&& other ) noexcept { -+ m_moved = other.m_moved; -+ other.m_moved = true; - return *this; - } - -@@ -7743,7 +10274,13 @@ - - 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& ) { -@@ -7764,27 +10301,27 @@ - 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 const& 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; - } - -@@ -7810,6 +10347,12 @@ - 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 -@@ -7823,7 +10366,16 @@ - } - - // 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 ) { -@@ -7844,25 +10396,28 @@ - // 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 <cassert> - # include <sys/types.h> - # include <unistd.h> --# include <sys/sysctl.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; -@@ -7892,6 +10447,12 @@ - - 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) -@@ -7962,6 +10523,8 @@ - // 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]] -@@ -7971,8 +10534,116 @@ - 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 occurrence 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> -@@ -7996,7 +10667,7 @@ - public: - ~ExceptionTranslatorRegistry(); - virtual void registerTranslator( const IExceptionTranslator* translator ); -- virtual std::string translateActiveException() const override; -+ std::string translateActiveException() const override; - std::string tryTranslators() const; - - private: -@@ -8061,54 +10732,82 @@ - } - } - -+ std::string ExceptionTranslatorRegistry::tryTranslators() const { -+ if (m_translators.empty()) { -+ std::rethrow_exception(std::current_exception()); -+ } else { -+ return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); -+ } -+ } -+ - #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!"); - } --#endif - - std::string ExceptionTranslatorRegistry::tryTranslators() const { -- if( m_translators.empty() ) -- std::rethrow_exception(std::current_exception()); -- else -- return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); -+ CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); - } -+#endif -+ - } - // end catch_exception_translator_registry.cpp - // start catch_fatal_condition.cpp - --#if defined(__GNUC__) --# pragma GCC diagnostic push --# pragma GCC diagnostic ignored "-Wmissing-field-initializers" --#endif -+#include <algorithm> -+ -+#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) -+ -+namespace Catch { -+ -+ // If neither SEH nor signal handling is required, the handler impls -+ // do not have to do anything, and can be empty. -+ void FatalConditionHandler::engage_platform() {} -+ void FatalConditionHandler::disengage_platform() {} -+ FatalConditionHandler::FatalConditionHandler() = default; -+ FatalConditionHandler::~FatalConditionHandler() = default; -+ -+} // end namespace Catch -+ -+#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS -+ -+#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) -+#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" -+#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS - - #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) - - namespace { -- // Report the error condition -+ //! Signals fatal error message to the run context - void reportFatal( char const * const message ) { - Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); - } --} - --#endif // signals/SEH handling -+ //! Minimal size Catch2 needs for its own fatal error handling. -+ //! Picked anecdotally, so it might not be sufficient on all -+ //! platforms, and for all configurations. -+ constexpr std::size_t minStackSizeForErrors = 32 * 1024; -+} // end unnamed namespace -+ -+#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS - - #if defined( CATCH_CONFIG_WINDOWS_SEH ) - - namespace Catch { -+ - struct SignalDefs { DWORD id; const char* name; }; - - // There is no 1-1 mapping between signals and windows exceptions. - // 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) { -+ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { - for (auto const& def : signalDefs) { - if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { - reportFatal(def.name); -@@ -8119,38 +10818,50 @@ - return EXCEPTION_CONTINUE_SEARCH; - } - -+ // Since we do not support multiple instantiations, we put these -+ // into global variables and rely on cleaning them up in outlined -+ // constructors/destructors -+ static PVOID exceptionHandlerHandle = nullptr; -+ -+ // For MSVC, we reserve part of the stack memory for handling -+ // memory overflow structured exception. - FatalConditionHandler::FatalConditionHandler() { -- isSet = true; -- // 32k seems enough for Catch to handle stack overflow, -- // but the value was found experimentally, so there is no strong guarantee -- guaranteeSize = 32 * 1024; -- exceptionHandlerHandle = nullptr; -+ ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors); -+ if (!SetThreadStackGuarantee(&guaranteeSize)) { -+ // We do not want to fully error out, because needing -+ // the stack reserve should be rare enough anyway. -+ Catch::cerr() -+ << "Failed to reserve piece of stack." -+ << " Stack overflows will not be reported successfully."; -+ } -+ } -+ -+ // We do not attempt to unset the stack guarantee, because -+ // Windows does not support lowering the stack size guarantee. -+ FatalConditionHandler::~FatalConditionHandler() = default; -+ -+ void FatalConditionHandler::engage_platform() { - // Register as first handler in current chain - exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); -- // Pass in guarantee size to be filled -- SetThreadStackGuarantee(&guaranteeSize); -+ if (!exceptionHandlerHandle) { -+ CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); -+ } - } - -- void FatalConditionHandler::reset() { -- if (isSet) { -- RemoveVectoredExceptionHandler(exceptionHandlerHandle); -- SetThreadStackGuarantee(&guaranteeSize); -- exceptionHandlerHandle = nullptr; -- isSet = false; -+ void FatalConditionHandler::disengage_platform() { -+ if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { -+ CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); - } -+ exceptionHandlerHandle = nullptr; - } - -- FatalConditionHandler::~FatalConditionHandler() { -- reset(); -- } -+} // end namespace Catch - --bool FatalConditionHandler::isSet = false; --ULONG FatalConditionHandler::guaranteeSize = 0; --PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; -+#endif // CATCH_CONFIG_WINDOWS_SEH - --} // namespace Catch -+#if defined( CATCH_CONFIG_POSIX_SIGNALS ) - --#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) -+#include <signal.h> - - namespace Catch { - -@@ -8159,10 +10870,6 @@ - const char* name; - }; - -- // 32kb for the alternate stack seems to be sufficient. However, this value -- // is experimentally determined, so that's not guaranteed. -- constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; -- - static SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, -@@ -8172,7 +10879,32 @@ - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; - -- void FatalConditionHandler::handleSignal( int sig ) { -+// Older GCCs trigger -Wmissing-field-initializers for T foo = {} -+// which is zero initialization, but not explicit. We want to avoid -+// that. -+#if defined(__GNUC__) -+# pragma GCC diagnostic push -+# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -+#endif -+ -+ static char* altStackMem = nullptr; -+ static std::size_t altStackSize = 0; -+ static stack_t oldSigStack{}; -+ static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; -+ -+ static void restorePreviousSignalHandlers() { -+ // We set signal handlers back to the previous ones. Hopefully -+ // nobody overwrote them in the meantime, and doesn't expect -+ // their signal handlers to live past ours given that they -+ // installed them after ours.. -+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { -+ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); -+ } -+ // Return the old stack -+ sigaltstack(&oldSigStack, nullptr); -+ } -+ -+ static void handleSignal( int sig ) { - char const * name = "<unknown signal>"; - for (auto const& def : signalDefs) { - if (sig == def.id) { -@@ -8180,16 +10912,33 @@ - break; - } - } -- reset(); -- reportFatal(name); -+ // We need to restore previous signal handlers and let them do -+ // their thing, so that the users can have the debugger break -+ // when a signal is raised, and so on. -+ restorePreviousSignalHandlers(); -+ reportFatal( name ); - raise( sig ); - } - - FatalConditionHandler::FatalConditionHandler() { -- isSet = true; -+ assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); -+ if (altStackSize == 0) { -+ altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors); -+ } -+ altStackMem = new char[altStackSize](); -+ } -+ -+ FatalConditionHandler::~FatalConditionHandler() { -+ delete[] altStackMem; -+ // We signal that another instance can be constructed by zeroing -+ // out the pointer. -+ altStackMem = nullptr; -+ } -+ -+ void FatalConditionHandler::engage_platform() { - stack_t sigStack; - sigStack.ss_sp = altStackMem; -- sigStack.ss_size = sigStackSize; -+ sigStack.ss_size = altStackSize; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = { }; -@@ -8201,59 +10950,20 @@ - } - } - -- FatalConditionHandler::~FatalConditionHandler() { -- reset(); -- } -- -- void FatalConditionHandler::reset() { -- if( isSet ) { -- // Set signals back to previous values -- hopefully nobody overwrote them in the meantime -- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { -- sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); -- } -- // Return the old stack -- sigaltstack(&oldSigStack, nullptr); -- isSet = false; -- } -- } -- -- bool FatalConditionHandler::isSet = false; -- struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; -- stack_t FatalConditionHandler::oldSigStack = {}; -- char FatalConditionHandler::altStackMem[sigStackSize] = {}; -- --} // namespace Catch -- --#else -- --namespace Catch { -- void FatalConditionHandler::reset() {} --} -- --#endif // signals/SEH handling -- - #if defined(__GNUC__) - # pragma GCC diagnostic pop - #endif --// end catch_fatal_condition.cpp --// start catch_generators.cpp -- --// start catch_random_number_generator.h -- --#include <algorithm> --#include <random> -- --namespace Catch { - -- struct IConfig; -+ void FatalConditionHandler::disengage_platform() { -+ restorePreviousSignalHandlers(); -+ } - -- std::mt19937& rng(); -- void seedRng( IConfig const& config ); -- unsigned int rngSeed(); -+} // end namespace Catch - --} -+#endif // CATCH_CONFIG_POSIX_SIGNALS -+// end catch_fatal_condition.cpp -+// start catch_generators.cpp - --// end catch_random_number_generator.h - #include <limits> - #include <set> - -@@ -8261,34 +10971,16 @@ - - IGeneratorTracker::~IGeneratorTracker() {} - --namespace Generators { -- -- GeneratorBase::~GeneratorBase() {} -- -- std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ) { -- -- assert( selectionSize <= sourceSize ); -- std::vector<size_t> indices; -- indices.reserve( selectionSize ); -- std::uniform_int_distribution<size_t> uid( 0, sourceSize-1 ); -+const char* GeneratorException::what() const noexcept { -+ return m_msg; -+} - -- std::set<size_t> seen; -- // !TBD: improve this algorithm -- while( indices.size() < selectionSize ) { -- auto index = uid( rng() ); -- if( seen.insert( index ).second ) -- indices.push_back( index ); -- } -- return indices; -- } -+namespace Generators { - -- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { -- return getResultCapture().acquireGeneratorTracker( lineInfo ); -- } -+ GeneratorUntypedBase::~GeneratorUntypedBase() {} - -- template<> -- auto all<int>() -> Generator<int> { -- return range( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() ); -+ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { -+ return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); - } - - } // namespace Generators -@@ -8344,10 +11036,16 @@ - - 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; -@@ -8538,7 +11236,7 @@ - - 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 - -@@ -8557,7 +11255,7 @@ - namespace Catch { - - std::size_t listTests( Config const& config ) { -- TestSpec testSpec = config.testSpec(); -+ TestSpec const& testSpec = config.testSpec(); - if( config.hasTestFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { -@@ -8591,7 +11289,7 @@ - } - - std::size_t listTestsNamesOnly( Config const& config ) { -- TestSpec testSpec = config.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 ) { -@@ -8613,14 +11311,23 @@ - } - - 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(); -+ TestSpec const& testSpec = config.testSpec(); - if( config.hasTestFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { -@@ -8676,15 +11383,16 @@ - 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() ) -+ 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; - } -@@ -8713,6 +11421,29 @@ - - } // 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 -@@ -8741,74 +11472,101 @@ - } // 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 Matchers { --namespace Floating { --enum class FloatingPointKind : uint8_t { -- Float, -- Double --}; --} --} --} -- - namespace { - --template <typename T> --struct Converter; -- --template <> --struct Converter<float> { -- static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); -- Converter(float f) { -+ 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; - } -- int32_t i; --}; - --template <> --struct Converter<double> { -- static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); -- Converter(double d) { -+ 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; - } -- int64_t i; --}; - --template <typename T> --auto convert(T t) -> Converter<T> { -- return Converter<T>(t); --} -+ 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; -+ } - --template <typename FP> --bool almostEqualUlps(FP lhs, FP rhs, int 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; -+ } -+ -+ // static cast as a workaround for IBM XLC -+ auto ulpDiff = std::abs(static_cast<FP>(lc - rc)); -+ return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff; - } - -- auto lc = convert(lhs); -- auto rc = convert(rhs); -+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -+ -+ float nextafter(float x, float y) { -+ return ::nextafterf(x, y); -+ } - -- if ((lc.i < 0) != (rc.i < 0)) { -- // Potentially we can have +0 and -0 -- return lhs == rhs; -+ double nextafter(double x, double y) { -+ return ::nextafter(x, y); - } - -- auto ulpDiff = std::abs(lc.i - rc.i); -- return ulpDiff <= maxUlpDiff; -+#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; - } - --namespace Catch { -+} // 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 << '.' -@@ -8825,10 +11583,11 @@ - return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); - } - -- WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) -+ WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType) - :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { -- CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' -- << " ULPs have to be non-negative."); -+ 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__) -@@ -8853,16 +11612,59 @@ - #endif - - std::string WithinUlpsMatcher::describe() const { -- return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); -+ 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, int maxUlpDiff) { -+Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { - return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); - } - --Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { -+Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { - return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); - } - -@@ -8870,9 +11672,24 @@ - 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 - -@@ -9030,28 +11847,43 @@ - //////////////////////////////////////////////////////////////////////////// - - 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 ( !uncaught_exceptions() ){ -+ 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(names[start])) { -+ while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) { - ++start; - } -- while (names[end] == ',' || isspace(names[end])) { -+ 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; -@@ -9072,18 +11904,22 @@ - // case '>': - openings.pop(); - break; -+ case '"': -+ case '\'': -+ pos = skipq(pos, c); -+ break; - case ',': -- if (start != pos && openings.size() == 0) { -+ if (start != pos && openings.empty()) { - m_messages.emplace_back(macroName, lineInfo, resultType); -- m_messages.back().message = trimmed(start, pos); -+ m_messages.back().message = static_cast<std::string>(trimmed(start, pos)); - m_messages.back().message += " := "; - start = pos; - } - } - } -- assert(openings.size() == 0 && "Mismatched openings"); -+ assert(openings.empty() && "Mismatched openings"); - m_messages.emplace_back(macroName, lineInfo, resultType); -- m_messages.back().message = trimmed(start, names.size() - 1); -+ m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1)); - m_messages.back().message += " := "; - } - Capturer::~Capturer() { -@@ -9145,6 +11981,22 @@ - auto str() const -> std::string; - }; - -+ class RedirectedStreams { -+ public: -+ RedirectedStreams(RedirectedStreams const&) = delete; -+ RedirectedStreams& operator=(RedirectedStreams const&) = delete; -+ RedirectedStreams(RedirectedStreams&&) = delete; -+ RedirectedStreams& operator=(RedirectedStreams&&) = delete; -+ -+ RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); -+ ~RedirectedStreams(); -+ private: -+ std::string& m_redirectedCout; -+ std::string& m_redirectedCerr; -+ RedirectedStdOut m_redirectedStdOut; -+ RedirectedStdErr m_redirectedStdErr; -+ }; -+ - #if defined(CATCH_CONFIG_NEW_CAPTURE) - - // Windows's implementation of std::tmpfile is terrible (it tries -@@ -9236,6 +12088,16 @@ - {} - 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) -@@ -9243,12 +12105,12 @@ - if (tmpnam_s(m_buffer)) { - CATCH_RUNTIME_ERROR("Could not get a temp filename"); - } -- if (fopen_s(&m_file, m_buffer, "w")) { -+ 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("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer); -+ CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); - } - } - #else -@@ -9351,20 +12213,61 @@ - - namespace Catch { - -- std::mt19937& rng() { -- static std::mt19937 s_rng; -- return s_rng; -+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 seedRng( IConfig const& config ) { -- if( config.rngSeed() != 0 ) { -- std::srand( config.rngSeed() ); -- rng().seed( config.rngSeed() ); -+ 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)()); - } - } - -- unsigned int rngSeed() { -- return getCurrentContext().getConfig()->rngSeed(); -+ 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; -+ } -+ -+ 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 -@@ -9383,6 +12286,8 @@ - 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 ); -@@ -9495,11 +12400,13 @@ - 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 -@@ -9582,7 +12489,14 @@ - 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: -@@ -9591,6 +12505,7 @@ - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - TagAliasRegistry m_tagAliasRegistry; - StartupExceptionRegistry m_exceptionRegistry; -+ Detail::EnumValuesRegistry m_enumValuesRegistry; - }; - } - -@@ -9671,7 +12586,6 @@ - - namespace Generators { - struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { -- size_t m_index = static_cast<size_t>( -1 ); - GeneratorBasePtr m_generator; - - GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) -@@ -9683,39 +12597,110 @@ - std::shared_ptr<GeneratorTracker> tracker; - - ITracker& currentTracker = ctx.currentTracker(); -- if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { -+ // 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->isIndexTracker() ); -+ assert( childTracker->isGeneratorTracker() ); - tracker = std::static_pointer_cast<GeneratorTracker>( childTracker ); -- } -- else { -+ } else { - tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, ¤tTracker ); - currentTracker.addChild( tracker ); - } - -- if( !ctx.completedCycle() && !tracker->isComplete() ) { -- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) -- tracker->moveNext(); -+ if( !tracker->isComplete() ) { - tracker->open(); - } - - return *tracker; - } - -- void moveNext() { -- m_index++; -- m_children.clear(); -- } -- - // TrackerBase interface -- bool isIndexTracker() const override { return true; } -+ bool isGeneratorTracker() const override { return true; } - auto hasGenerator() const -> bool override { - return !!m_generator; - } - void close() override { - TrackerBase::close(); -- if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 ) -+ // 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; -+ } -+ -+ // 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; -+ } -+ -+ 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; -+ } - } - - // IGeneratorTracker interface -@@ -9725,9 +12710,6 @@ - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = std::move( generator ); - } -- auto getIndex() const -> size_t override { -- return m_index; -- } - }; - GeneratorTracker::~GeneratorTracker() {} - } -@@ -9825,6 +12807,9 @@ - // 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 - resetAssertionInfo(); - m_lastResult = result; -@@ -9848,10 +12833,10 @@ - - return true; - } -- auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { -+ auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { - using namespace Generators; -- GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); -- assert( tracker.isOpen() ); -+ GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, -+ TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) ); - m_lastAssertionInfo.lineInfo = lineInfo; - return tracker; - } -@@ -9879,6 +12864,7 @@ - - m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); - m_messages.clear(); -+ m_messageScopes.clear(); - } - - void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { -@@ -9890,12 +12876,21 @@ - - 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); -@@ -9905,6 +12900,10 @@ - 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 -@@ -9926,7 +12925,7 @@ - // 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); - - assertionEnded(result); -@@ -9965,6 +12964,7 @@ - m_lastAssertionPassed = true; - ++m_totals.assertions.passed; - resetAssertionInfo(); -+ m_messageScopes.clear(); - } - - bool RunContext::aborting() const { -@@ -9986,13 +12986,10 @@ - CATCH_TRY { - if (m_reporter->getPreferences().shouldRedirectStdOut) { - #if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -- RedirectedStdOut redirectedStdOut; -- RedirectedStdErr redirectedStdErr; -+ RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); - - timer.start(); - invokeActiveTestCase(); -- redirectedCout += redirectedStdOut.str(); -- redirectedCerr += redirectedStdErr.str(); - #else - OutputRedirect r(redirectedCout, redirectedCerr); - timer.start(); -@@ -10019,15 +13016,15 @@ - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); -+ m_messageScopes.clear(); - - SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); - m_reporter->sectionEnded(testCaseSectionStats); - } - - void RunContext::invokeActiveTestCase() { -- FatalConditionHandler fatalConditionHandler; // Handle signals -+ FatalConditionHandlerGuard _(&m_fatalConditionhandler); - m_activeTestCase->invoke(); -- fatalConditionHandler.reset(); - } - - void RunContext::handleUnfinishedSections() { -@@ -10090,7 +13087,7 @@ - m_lastAssertionInfo = info; - - AssertionResultData data( resultType, LazyExpression( false ) ); -- data.message = message; -+ data.message = static_cast<std::string>(message); - AssertionResult assertionResult{ m_lastAssertionInfo, data }; - assertionEnded( assertionResult ); - if( !assertionResult.isOk() ) -@@ -10153,6 +13150,18 @@ - 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 -@@ -10214,7 +13223,7 @@ - void libIdentify(); - - int applyCommandLine( int argc, char const * const * argv ); -- #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) -+ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) - int applyCommandLine( int argc, wchar_t const * const * argv ); - #endif - -@@ -10281,6 +13290,8 @@ - // end catch_version.h - #include <cstdlib> - #include <iomanip> -+#include <set> -+#include <iterator> - - namespace Catch { - -@@ -10299,52 +13310,76 @@ - return createReporter(config->getReporterName(), config); - } - -- auto multi = std::unique_ptr<ListeningReporter>(new ListeningReporter); -- -+ // 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.addListener(listener->create(Catch::ReporterConfig(config))); - } -- multi->addReporter(createReporter(config->getReporterName(), config)); -- return std::move(multi); -+ multi.addReporter(createReporter(config->getReporterName(), config)); -+ return ret; - } - -- Catch::Totals runTests(std::shared_ptr<Config> const& config) { -- auto reporter = makeReporter(config); -- -- RunContext context(config, std::move(reporter)); -- -- Totals totals; -- -- context.testGroupStarting(config->name(), 1, 1); -- -- TestSpec testSpec = config->testSpec(); -- -- 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); -+ 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()); -+ } - } - -- if (config->warnAboutNoTests() && totals.testCases.total() == 0) { -- ReusableStringStream testConfig; -+ 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); -+ } - -- bool first = true; -- for (const auto& input : config->getTestsOrTags()) { -- if (!first) { testConfig << ' '; } -- first = false; -- testConfig << input; -+ for (auto const& match : m_matches) { -+ if (match.tests.empty()) { -+ m_context.reporter().noMatchingTestCases(match.name); -+ totals.error = -1; -+ } - } - -- context.reporter().noMatchingTestCases(testConfig.str()); -- totals.error = -1; -+ if (!invalidArgs.empty()) { -+ for (auto const& invalidArg: invalidArgs) -+ m_context.reporter().reportInvalidArguments(invalidArg); -+ } -+ -+ m_context.testGroupEnded(m_config->name(), totals, 1, 1); -+ return totals; - } - -- context.testGroupEnded(config->name(), totals, 1, 1); -- return totals; -- } -+ private: -+ using Tests = std::set<TestCase const*>; -+ -+ std::shared_ptr<Config> m_config; -+ RunContext m_context; -+ Tests m_tests; -+ TestSpec::Matches m_matches; -+ }; - - void applyFilenamesAsTags(Catch::IConfig const& config) { - auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); -@@ -10381,6 +13416,9 @@ - #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 occurred during startup!" << '\n'; -@@ -10410,7 +13448,7 @@ - } - 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; -@@ -10422,6 +13460,8 @@ - - 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" -@@ -10439,17 +13479,17 @@ - return 0; - } - --#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) -+#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 = applyCommandLine( argc, utf8Argv ); -@@ -10513,10 +13553,15 @@ - 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 ); - -- auto totals = runTests( m_config ); -+ 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 -@@ -10564,6 +13609,7 @@ - // 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 { - CATCH_TRY { -@@ -10579,6 +13625,7 @@ - } - - } // end namespace Catch -+#endif - // end catch_startup_exception_registry.cpp - // start catch_stream.cpp - -@@ -10593,7 +13640,7 @@ - - Catch::IStream::~IStream() = default; - -- namespace detail { namespace { -+ namespace Detail { namespace { - template<typename WriterF, std::size_t bufferSize=256> - class StreamBufImpl : public std::streambuf { - char data[bufferSize]; -@@ -10692,15 +13739,15 @@ - - auto makeStream( StringRef const &filename ) -> IStream const* { - if( filename.empty() ) -- return new detail::CoutStream(); -+ return new Detail::CoutStream(); - else if( filename[0] == '%' ) { - if( filename == "%debug" ) -- return new detail::DebugOutStream(); -+ return new Detail::DebugOutStream(); - else - CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); - } - else -- return new detail::FileStream( filename ); -+ return new Detail::FileStream( filename ); - } - - // This class encapsulates the idea of a pool of ostringstreams that can be reused. -@@ -10757,12 +13804,13 @@ - #include <ostream> - #include <cstring> - #include <cctype> -+#include <vector> - - namespace Catch { - - namespace { - char toLowerCh(char c) { -- return static_cast<char>( std::tolower( c ) ); -+ return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) ); - } - } - -@@ -10797,6 +13845,18 @@ - 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 ); -@@ -10811,6 +13871,21 @@ - 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 ) -@@ -10827,123 +13902,46 @@ - // 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 <cstring> - #include <cstdint> - --namespace { -- const uint32_t byte_2_lead = 0xC0; -- const uint32_t byte_3_lead = 0xE0; -- const uint32_t byte_4_lead = 0xF0; --} -- - namespace Catch { - StringRef::StringRef( char const* rawChars ) noexcept - : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) ) - {} - -- 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::currentData() const noexcept -> char const* { -+ 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::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 & byte_2_lead ) == byte_2_lead ) { -- noChars--; -- if (( c & byte_3_lead ) == byte_3_lead ) -- noChars--; -- if( ( c & byte_4_lead ) == byte_4_lead ) -- noChars--; -- } - } -- return noChars; - } -- -- 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 StringRef::operator == ( StringRef const& other ) const noexcept -> bool { -+ return m_size == other.m_size -+ && (std::memcmp( m_start, other.m_start, m_size ) == 0); - } - - auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { -- return os.write(str.currentData(), str.size()); -+ return os.write(str.data(), str.size()); - } - - auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { -- lhs.append(rhs.currentData(), rhs.size()); -+ 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 - -@@ -11046,7 +14044,7 @@ - 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" -+ << "Tag names starting with non alphanumeric characters are reserved\n" - << _lineInfo ); - } - } -@@ -11062,8 +14060,7 @@ - std::vector<std::string> tags; - std::string desc, tag; - bool inTag = false; -- std::string _descOrTags = nameAndTags.tags; -- for (char c : _descOrTags) { -+ for (char c : nameAndTags.tags) { - if( !inTag ) { - if( c == '[' ) - inTag = true; -@@ -11078,6 +14075,12 @@ - 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; -@@ -11087,10 +14090,11 @@ - } - } - if( isHidden ) { -- tags.push_back( "." ); -+ // Add all "hidden" tags to make them behave identically -+ tags.insert( tags.end(), { ".", "!hide" } ); - } - -- TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); -+ TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo ); - return TestCase( _testCase, std::move(info) ); - } - -@@ -11182,30 +14186,89 @@ - // 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; - -- std::vector<TestCase> sorted = unsortedTestCases; -+ 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; -+ } -+ -+ 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 ); -- std::shuffle( sorted.begin(), sorted.end(), rng() ); -- 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 ) { -@@ -11222,9 +14285,12 @@ - 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 ) { -@@ -11263,7 +14329,7 @@ - } - - std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { -- std::string className = classOrQualifiedMethodName; -+ std::string className(classOrQualifiedMethodName); - if( startsWith( className, '&' ) ) - { - std::size_t lastColons = className.rfind( "::" ); -@@ -11300,11 +14366,6 @@ - - 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; -@@ -11336,15 +14397,12 @@ - m_currentTracker = tracker; - } - -- 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; - } -@@ -11387,7 +14445,7 @@ - } - - bool TrackerBase::isSectionTracker() const { return false; } -- bool TrackerBase::isIndexTracker() const { return false; } -+ bool TrackerBase::isGeneratorTracker() const { return false; } - - void TrackerBase::open() { - m_runState = Executing; -@@ -11410,7 +14468,7 @@ - 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; - -@@ -11445,7 +14503,8 @@ - } - - 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() ) -@@ -11456,6 +14515,17 @@ - } - } - -+ 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 ) { -@@ -11477,63 +14547,29 @@ - } - - 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 -@@ -11541,7 +14577,6 @@ - using TestCaseTracking::ITracker; - using TestCaseTracking::TrackerContext; - using TestCaseTracking::SectionTracker; --using TestCaseTracking::IndexTracker; - - } // namespace Catch - -@@ -11586,47 +14621,81 @@ - - 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 -@@ -11638,64 +14707,136 @@ - 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; -- } -- } -- 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(); -+ 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; -+ } -+ -+ 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; -+ } -+ } -+ void TestSpecParser::processNameChar( char c ) { -+ if( c == '[' ) { -+ if( m_substring == "exclude:" ) -+ m_exclusion = true; -+ else -+ endMode(); -+ startNewMode( Tag ); - } -- 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 ) { -+ 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() ) { -@@ -11704,6 +14845,86 @@ - } - } - -+ 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(); - } -@@ -11744,7 +14965,7 @@ - // 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; -+ return sum / ( i + 1u ); - } - } - -@@ -11805,13 +15026,11 @@ - 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; - } - }; - } -@@ -11935,6 +15154,13 @@ - } - #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)); - } -@@ -11997,11 +15223,16 @@ - 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"; } -@@ -12075,11 +15306,48 @@ - // 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_CPP17_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(); -@@ -12119,7 +15387,7 @@ - } - - Version const& libraryVersion() { -- static Version version( 2, 5, 0, "", 0 ); -+ static Version version( 2, 13, 8, "", 0 ); - return version; - } - -@@ -12127,14 +15395,12 @@ - // end catch_version.cpp - // start catch_wildcard_pattern.cpp - --#include <sstream> -- - 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 ); -@@ -12149,28 +15415,27 @@ - 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 - - #include <iomanip> -- --using uchar = unsigned char; -+#include <type_traits> - - namespace Catch { - -@@ -12203,13 +15468,37 @@ - } - - 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); -+ } -+ -+ bool shouldNewline(XmlFormatting fmt) { -+ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline)); -+ } -+ -+ bool shouldIndent(XmlFormatting fmt) { -+ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent)); - } - - } // anonymous namespace - -+ 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) -+ ); -+ } -+ -+ 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 ), - m_forWhat( forWhat ) -@@ -12220,7 +15509,7 @@ - // (see: http://www.w3.org/TR/xml/#syntax) - - for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { -- uchar c = m_str[idx]; -+ unsigned char c = m_str[idx]; - switch (c) { - case '<': os << "<"; break; - case '&': os << "&"; break; -@@ -12280,7 +15569,7 @@ - bool valid = true; - uint32_t value = headerValue(c); - for (std::size_t n = 1; n < encBytes; ++n) { -- uchar nc = m_str[idx + n]; -+ unsigned char nc = m_str[idx + n]; - valid &= ((nc & 0xC0) == 0x80); - value = (value << 6) | (nc & 0x3F); - } -@@ -12314,13 +15603,17 @@ - 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 ) { -@@ -12328,16 +15621,19 @@ - } - 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; - } - -@@ -12347,37 +15643,47 @@ - } - - 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; - } -@@ -12393,22 +15699,26 @@ - 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; - } - -@@ -12424,11 +15734,16 @@ - - 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"; - } -@@ -12469,11 +15784,37 @@ - #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) {} - -@@ -12689,24 +16030,25 @@ - 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)); -+ 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") << ':'; - } - -- for (; itMessage != itEnd; ) { -+ while (itMessage != itEnd) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || itMessage->type != ResultWas::Info) { -- stream << " '" << itMessage->message << '\''; -- if (++itMessage != itEnd) { -+ printMessage(); -+ if (itMessage != itEnd) { - Colour colourGuard(dimColour()); - stream << " and"; - } -+ continue; - } -+ ++itMessage; - } - } - -@@ -12724,10 +16066,6 @@ - return "Reports test results on a single line, suitable for IDEs"; - } - -- ReporterPreferences CompactReporter::getPreferences() const { -- return m_reporterPrefs; -- } -- - void CompactReporter::noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << '\'' << std::endl; - } -@@ -12754,8 +16092,9 @@ - } - - void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { -- if (m_config->showDurations() == ShowDurations::Always) { -- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; -+ double dur = _sectionStats.durationInSeconds; -+ if ( shouldShowDuration( *m_config, dur ) ) { -+ stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl; - } - } - -@@ -12779,8 +16118,13 @@ - #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 { -@@ -12962,11 +16306,11 @@ - static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; - static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; - -- uint64_t m_inNanoseconds; -+ double m_inNanoseconds; - Unit m_units; - - public: -- explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) -+ explicit Duration(double inNanoseconds, Unit units = Unit::Auto) - : m_inNanoseconds(inNanoseconds), - m_units(units) { - if (m_units == Unit::Auto) { -@@ -12995,7 +16339,7 @@ - case Unit::Minutes: - return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute); - default: -- return static_cast<double>(m_inNanoseconds); -+ return m_inNanoseconds; - } - } - auto unitsAsString() const -> std::string { -@@ -13003,7 +16347,7 @@ - case Unit::Nanoseconds: - return "ns"; - case Unit::Microseconds: -- return "µs"; -+ return "us"; - case Unit::Milliseconds: - return "ms"; - case Unit::Seconds: -@@ -13016,7 +16360,7 @@ - - } - friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { -- return os << duration.value() << " " << duration.unitsAsString(); -+ return os << duration.value() << ' ' << duration.unitsAsString(); - } - }; - } // end anon namespace -@@ -13041,10 +16385,16 @@ - 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"; -+ -+ 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'; -+ -+ m_os << Catch::getLineOfChars<'-'>() << '\n'; - } - } - void close() { -@@ -13063,30 +16413,29 @@ - - friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { - auto colStr = tp.m_oss.str(); -- // This takes account of utf8 encodings -- auto strSize = Catch::StringRef(colStr).numberOfCharacters(); -+ 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_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), ' ') -+ 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 << " "; -+ tp.m_os << colStr << padding << ' '; - else -- tp.m_os << padding << colStr << " "; -+ tp.m_os << padding << colStr << ' '; - return tp; - } - - friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { - if (tp.m_currentColumn > 0) { -- tp.m_os << "\n"; -+ tp.m_os << '\n'; - tp.m_currentColumn = -1; - } - return tp; -@@ -13096,12 +16445,26 @@ - ConsoleReporter::ConsoleReporter(ReporterConfig const& config) - : StreamingReporterBase(config), - m_tablePrinter(new 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 } -- })) {} -+ [&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 } -+ }; -+ } -+ 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; - - std::string ConsoleReporter::getDescription() { -@@ -13112,6 +16475,10 @@ - stream << "No test cases matched '" << spec << '\'' << std::endl; - } - -+void ConsoleReporter::reportInvalidArguments(std::string const&arg){ -+ stream << "Invalid Filter: " << arg << std::endl; -+} -+ - void ConsoleReporter::assertionStarting(AssertionInfo const&) {} - - bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { -@@ -13132,6 +16499,7 @@ - } - - void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { -+ m_tablePrinter->close(); - m_headerPrinted = false; - StreamingReporterBase::sectionStarting(_sectionInfo); - } -@@ -13146,8 +16514,9 @@ - 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; -+ double dur = _sectionStats.durationInSeconds; -+ if (shouldShowDuration(*m_config, dur)) { -+ stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; - } - if (m_headerPrinted) { - m_headerPrinted = false; -@@ -13155,28 +16524,53 @@ - StreamingReporterBase::sectionEnded(_sectionStats); - } - --void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { -- lazyPrintWithoutClosingBenchmarkTable(); -- -- auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) ); -+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) -+void ConsoleReporter::benchmarkPreparing(std::string const& name) { -+ lazyPrintWithoutClosingBenchmarkTable(); -+ -+ auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2)); -+ -+ bool firstLine = true; -+ for (auto line : nameCol) { -+ if (!firstLine) -+ (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); -+ else -+ firstLine = false; - -- bool firstLine = true; -- for (auto line : nameCol) { -- if (!firstLine) -- (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); -- else -- firstLine = false; -+ (*m_tablePrinter) << line << ColumnBreak(); -+ } -+} - -- (*m_tablePrinter) << line << ColumnBreak(); -+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(); - } - } --void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { -- Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); -+ -+void ConsoleReporter::benchmarkFailed(std::string const& error) { -+ Colour colour(Colour::Red); - (*m_tablePrinter) -- << stats.iterations << ColumnBreak() -- << stats.elapsedTimeInNanoseconds << ColumnBreak() -- << average << ColumnBreak(); -+ << "Benchmark failed (" << error << ')' -+ << ColumnBreak() << RowBreak(); - } -+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - - void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { - m_tablePrinter->close(); -@@ -13198,6 +16592,10 @@ - stream << std::endl; - StreamingReporterBase::testRunEnded(_testRunStats); - } -+void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { -+ StreamingReporterBase::testRunStarting(_testInfo); -+ printTestFilters(); -+} - - void ConsoleReporter::lazyPrint() { - -@@ -13251,11 +16649,9 @@ - - SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; - -- if (!lineInfo.empty()) { -- stream << getLineOfChars<'-'>() << '\n'; -- Colour colourGuard(Colour::FileName); -- stream << lineInfo << '\n'; -- } -+ stream << getLineOfChars<'-'>() << '\n'; -+ Colour colourGuard(Colour::FileName); -+ stream << lineInfo << '\n'; - stream << getLineOfChars<'.'>() << '\n' << std::endl; - } - -@@ -13379,6 +16775,13 @@ - stream << getLineOfChars<'-'>() << '\n'; - } - -+void ConsoleReporter::printTestFilters() { -+ if (m_config->testSpec().hasFilters()) { -+ Colour guard(Colour::BrightYellow); -+ stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; -+ } -+} -+ - CATCH_REGISTER_REPORTER("console", ConsoleReporter) - - } // end namespace Catch -@@ -13386,6 +16789,10 @@ - #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 - -@@ -13393,6 +16800,7 @@ - #include <sstream> - #include <ctime> - #include <algorithm> -+#include <iomanip> - - namespace Catch { - -@@ -13420,7 +16828,7 @@ - #else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); - #endif -- return std::string(timeStamp); -+ return std::string(timeStamp, timeStampSize-1); - } - - std::string fileNameTag(const std::vector<std::string> &tags) { -@@ -13431,6 +16839,17 @@ - return it->substr(1); - return std::string(); - } -+ -+ // Formats the duration in seconds to 3 decimal places. -+ // This is done because some genius defined Maven Surefire schema -+ // in a way that only accepts 3 decimal places, and tools like -+ // Jenkins use that schema for validation JUnit reporter output. -+ std::string formatDuration( double seconds ) { -+ ReusableStringStream rss; -+ rss << std::fixed << std::setprecision( 3 ) << seconds; -+ return rss.str(); -+ } -+ - } // anonymous namespace - - JunitReporter::JunitReporter( ReporterConfig const& _config ) -@@ -13490,6 +16909,7 @@ - - void JunitReporter::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 ); -@@ -13499,15 +16919,30 @@ - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else -- xml.writeAttribute( "time", suiteTime ); -+ xml.writeAttribute( "time", formatDuration( 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()); -+ } -+ } -+ - // Write test cases - for( auto const& child : groupNode.children ) - writeTestCase( *child ); - -- xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); -- xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); -+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); -+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); - } - - void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { -@@ -13529,12 +16964,13 @@ - if ( !m_config->name().empty() ) - className = m_config->name() + "." + className; - -- writeSection( className, "", rootSection ); -+ writeSection( className, "", rootSection, stats.testInfo.okToFail() ); - } - -- void JunitReporter::writeSection( std::string const& className, -- std::string const& rootName, -- SectionNode const& sectionNode ) { -+ void JunitReporter::writeSection( std::string const& className, -+ std::string const& rootName, -+ SectionNode const& sectionNode, -+ bool testOkToFail) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + '/' + name; -@@ -13551,20 +16987,30 @@ - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } -- xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); -+ xml.writeAttribute( "time", formatDuration( 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" ); -+ -+ if (sectionNode.stats.assertions.failedButOk) { -+ xml.scopedElement("skipped") -+ .writeAttribute("message", "TEST_CASE tagged with !mayfail"); -+ } - - writeAssertions( sectionNode ); - - if( !sectionNode.stdOut.empty() ) -- xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); -+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); - if( !sectionNode.stdErr.empty() ) -- xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); -+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); - } - for( auto const& childNode : sectionNode.childSections ) - if( className.empty() ) -- writeSection( name, "", *childNode ); -+ writeSection( name, "", *childNode, testOkToFail ); - else -- writeSection( className, name, *childNode ); -+ writeSection( className, name, *childNode, testOkToFail ); - } - - void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { -@@ -13582,11 +17028,7 @@ - elementName = "error"; - break; - case ResultWas::ExplicitFailure: -- elementName = "failure"; -- break; - case ResultWas::ExpressionFailed: -- elementName = "failure"; -- break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; -@@ -13604,10 +17046,25 @@ - - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - -- xml.writeAttribute( "message", result.getExpandedExpression() ); -+ xml.writeAttribute( "message", result.getExpression() ); - xml.writeAttribute( "type", result.getTestMacroName() ); - - 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'; -+ } -+ - if( !result.getMessage().empty() ) - rss << result.getMessage() << '\n'; - for( auto const& msg : stats.infoMessages ) -@@ -13615,7 +17072,7 @@ - rss << msg.message << '\n'; - - rss << "at " << result.getSourceInfo(); -- xml.writeText( rss.str(), false ); -+ xml.writeText( rss.str(), XmlFormatting::Newline ); - } - } - -@@ -13659,19 +17116,41 @@ - m_reporter->noMatchingTestCases( spec ); - } - -+ void ListeningReporter::reportInvalidArguments(std::string const&arg){ -+ for ( auto const& listener : m_listeners ) { -+ listener->reportInvalidArguments( arg ); -+ } -+ m_reporter->reportInvalidArguments( arg ); -+ } -+ -+#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 ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { -+ void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { - for ( auto const& listener : m_listeners ) { - listener->benchmarkEnded( benchmarkStats ); - } - m_reporter->benchmarkEnded( benchmarkStats ); - } - -+ 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 ); -@@ -13802,6 +17281,8 @@ - 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() ); -@@ -13937,9 +17418,9 @@ - e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); - - if( !testCaseStats.stdOut.empty() ) -- m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); -+ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); - if( !testCaseStats.stdErr.empty() ) -- m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); -+ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); - - m_xml.endElement(); - } -@@ -13951,6 +17432,10 @@ - .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(); - } - -@@ -13960,9 +17445,58 @@ - .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 -+ - CATCH_REGISTER_REPORTER( "xml", XmlReporter ) - - } // end namespace Catch -@@ -13988,7 +17522,7 @@ - - #ifndef __OBJC__ - --#if defined(CATCH_CONFIG_WCHAR) && 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 -@@ -14036,7 +17570,7 @@ - #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) -@@ -14050,7 +17584,7 @@ - #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) -@@ -14065,6 +17599,7 @@ - #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( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ ) - -@@ -14082,10 +17617,22 @@ - - #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) -@@ -14106,6 +17653,13 @@ - #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 - -@@ -14141,6 +17695,7 @@ - #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( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ ) - -@@ -14157,10 +17712,26 @@ - - #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) -@@ -14186,6 +17757,13 @@ - #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 // CATCH_CONFIG_DISABLE -@@ -14225,12 +17803,13 @@ - #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_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( ... ) -@@ -14239,19 +17818,31 @@ - #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____ )) -+#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(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) --#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) -+#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(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) --#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) -+#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_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 ) -@@ -14297,11 +17888,12 @@ - #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) - --#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) --#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) -+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) -+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) - #define METHOD_AS_TEST_CASE( method, ... ) - #define REGISTER_TEST_CASE( Function, ... ) (void)(0) - #define SECTION( ... ) -@@ -14309,14 +17901,26 @@ - #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____ )) -+#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(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) --#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) -+#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(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) --#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) -+#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) -@@ -14327,8 +17931,8 @@ - #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) - - // "BDD-style" convenience wrappers --#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) --#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 SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ) ) -+#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 ) |