[neon/3rdparty/zug] debian: New packaging needed by krita
Scarlett Moore
null at kde.org
Tue Sep 26 17:44:44 BST 2023
Git commit 2b7e6632a68e685148aec8b8557741df3590bb2f by Scarlett Moore.
Committed on 26/09/2023 at 18:44.
Pushed by scarlettmoore into branch 'master'.
New packaging needed by krita
A +1 -0 debian/.gitattributes
A +13 -0 debian/changelog
A +28 -0 debian/control
A +42 -0 debian/copyright
A +1 -0 debian/patches/series
A +5905 -0 debian/patches/update-catch2.diff
A +14 -0 debian/rules
A +6 -0 debian/salsa-ci.yml
A +1 -0 debian/source/format
A +5 -0 debian/upstream/metadata
A +5 -0 debian/watch
https://invent.kde.org/neon/3rdparty/zug/-/commit/2b7e6632a68e685148aec8b8557741df3590bb2f
diff --git a/debian/.gitattributes b/debian/.gitattributes
new file mode 100644
index 0000000..6a03163
--- /dev/null
+++ b/debian/.gitattributes
@@ -0,0 +1 @@
+changelog merge=dpkg-mergechangelogs
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..c9cb368
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,13 @@
+zug (0.1.0-2) unstable; urgency=medium
+
+ * Upload to unstable.
+ * update-catch2.diff: mention https://github.com/arximboldi/zug/pull/36 that
+ will fix the issue in the long term.
+
+ -- Pino Toscano <pino at debian.org> Sun, 06 Aug 2023 22:04:48 +0200
+
+zug (0.1.0-1) experimental; urgency=medium
+
+ * Initial packaging.
+
+ -- Pino Toscano <pino at debian.org> Fri, 28 Jul 2023 13:47:27 +0200
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..44e2d3a
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,28 @@
+Source: zug
+Section: libs
+Priority: optional
+Maintainer: Debian Qt/KDE Maintainers <debian-qt-kde at lists.debian.org>
+Uploaders: Pino Toscano <pino at debian.org>,
+Build-Depends: cmake (>= 3.8~),
+ debhelper-compat (= 13),
+ libboost-dev,
+Standards-Version: 4.6.2
+Rules-Requires-Root: no
+Homepage: https://github.com/arximboldi/zug
+Vcs-Browser: https://salsa.debian.org/qt-kde-team/3rdparty/zug
+Vcs-Git: https://salsa.debian.org/qt-kde-team/3rdparty/zug.git
+
+Package: libzug-dev
+Architecture: any
+Multi-Arch: same
+Section: libdevel
+Depends: ${misc:Depends},
+ libboost-dev,
+Description: C++ library providing transducers -- development files
+ zug is a C++ library providing transducers. Transducers are composable
+ sequential transformations independent of the source. They are extremely
+ lightweight, and can be used to express algorithms over pull-based sequences
+ (iterators, files) but also push based sequences (signals, events,
+ asynchronous streams) in a generic way.
+ .
+ This package contains the development files.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..1a89bd8
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,42 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: zug
+Source: https://github.com/arximboldi/zug
+
+Files: *
+Copyright: 2016-2020, Juan Pedro Bolivar Puente
+ 2019, Juan Pedro Bolivar Puente (and Carl Bussey, maybe?)
+ 2019, Juan Pedro Bolivar Puente and Carl Bussey
+License: BSL-1.0
+
+Files: tools/include/catch2/catch.hpp
+Copyright: 2019, Two Blue Cubes Ltd. All rights reserved.
+License: BSL-1.0
+
+Files: debian/*
+Copyright: 2023, Pino Toscano <pino at debian.org>
+License: BSL-1.0
+
+License: BSL-1.0
+ Boost Software License - Version 1.0 - August 17th, 2003
+ .
+ Permission is hereby granted, free of charge, to any person or organization
+ obtaining a copy of the software and accompanying documentation covered by
+ this license (the "Software") to use, reproduce, display, distribute,
+ execute, and transmit the Software, and to prepare derivative works of the
+ Software, and to permit third-parties to whom the Software is furnished to
+ do so, all subject to the following:
+ .
+ The copyright notices in the Software and this entire statement, including
+ the above license grant, this restriction and the following disclaimer,
+ must be included in all copies of the Software, in whole or in part, and
+ all derivative works of the Software, unless such copies or derivative
+ works are solely in the form of machine-executable object code generated by
+ a source language processor.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..cf51af9
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+update-catch2.diff
diff --git a/debian/patches/update-catch2.diff b/debian/patches/update-catch2.diff
new file mode 100644
index 0000000..fd3af07
--- /dev/null
+++ b/debian/patches/update-catch2.diff
@@ -0,0 +1,5905 @@
+Author: Pino Toscano <pino at debian.org>
+Description: Update catch2
+ Update the catch2 copy from the version shipped in immer:
+ https://github.com/arximboldi/immer/blob/master/tools/include/catch.hpp
+ .
+ This is needed until the next upstream release, as this embedded copy of
+ catch2 is replaced by the usage of a system-installed version:
+ https://github.com/arximboldi/zug/pull/36
+Last-Update: 2023-07-28
+Forwarded: not-needed
+
+--- a/tools/include/catch2/catch.hpp
++++ b/tools/include/catch2/catch.hpp
+@@ -1,9 +1,9 @@
+ /*
+- * Catch v2.9.1
+- * Generated: 2019-06-17 11:59:24.363643
++ * Catch v2.13.7
++ * Generated: 2021-07-28 20:29:27.753164
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+- * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved.
++ * Copyright (c) 2021 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 9
+-#define CATCH_VERSION_PATCH 1
++#define CATCH_VERSION_MINOR 13
++#define CATCH_VERSION_PATCH 7
+
+ #ifdef __clang__
+ # pragma clang system_header
+@@ -66,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
+@@ -132,36 +135,51 @@ namespace Catch {
+
+ #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_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_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_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+- _Pragma( "clang diagnostic push" ) \
+- _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
+-# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
+- _Pragma( "clang diagnostic pop" )
++# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
++
++# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
+
+ #endif // __clang__
+
+@@ -186,6 +204,7 @@ namespace Catch {
+ // 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
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -219,11 +238,10 @@ namespace Catch {
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Visual C++
+-#ifdef _MSC_VER
++#if defined(_MSC_VER)
+
+-# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+-# endif
++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
+
+ // Universal Windows platform does not support SEH
+ // Or console colours (or console at all...)
+@@ -236,9 +254,12 @@ namespace Catch {
+ // 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(__clang__) // Handle Clang masquerading for msvc
++# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
++# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++# endif // MSVC_TRADITIONAL
++# endif // __clang__
++
+ #endif // _MSC_VER
+
+ #if defined(_REENTRANT) || defined(_MSC_VER)
+@@ -276,40 +297,56 @@ namespace Catch {
+ #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
+-#endif
++
++// RTX is a special version of Windows that is real time.
++// This means that it is detected as Windows, but does not provide
++// the same set of capabilities as real Windows does.
++#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
++ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
++ #define CATCH_INTERNAL_CONFIG_NO_ASYNC
++ #define CATCH_CONFIG_COLOUR_NONE
+ #endif
+
+-////////////////////////////////////////////////////////////////////////////////
+-// Check if optional is available and usable
+-#if defined(__has_include)
+-# if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+-# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
+-# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+-#endif // __has_include
++#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)
+-# else
+-# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+-# 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
+@@ -334,10 +371,6 @@ namespace Catch {
+ # define CATCH_CONFIG_CPP17_OPTIONAL
+ #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
+-#endif
+-
+ #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+ # define CATCH_CONFIG_CPP17_STRING_VIEW
+ #endif
+@@ -346,6 +379,10 @@ namespace Catch {
+ # 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
+@@ -362,25 +399,53 @@ namespace Catch {
+ # define CATCH_CONFIG_POLYFILL_ISNAN
+ #endif
+
+-#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
++#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
+-# define CATCH_INTERNAL_UNSUPPRESS_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)
+@@ -445,7 +510,7 @@ namespace Catch {
+ 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;
+
+@@ -486,9 +551,10 @@ namespace Catch {
+ } // 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
+@@ -515,6 +581,7 @@ namespace Catch {
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
++ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+@@ -527,53 +594,30 @@ namespace Catch {
+ #include <cstddef>
+ #include <string>
+ #include <iosfwd>
++#include <cassert>
+
+ namespace Catch {
+
+ /// 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 )
+ {}
+@@ -583,101 +627,64 @@ namespace Catch {
+ m_size( stdString.size() )
+ {}
+
+- ~StringRef() noexcept {
+- delete[] m_data;
++ explicit operator std::string() const {
++ return std::string(m_start, m_size);
+ }
+
+- 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;
+- }
+-
+- 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
+-
+-
+-#include <type_traits>
+-
+-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
+
+
+@@ -762,7 +769,7 @@ struct is_unique<T0, T1, Rest...> : std:
+ #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, _4, _5, _6)
++#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)
+@@ -774,35 +781,49 @@ struct is_unique<T0, T1, Rest...> : std:
+ 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<template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2> \
+- constexpr auto append(L1<E1...>, L2<E2...>) noexcept -> L1<E1...,E2...> { return {}; }\
++ template<typename T> \
++ struct append<T> { using type = T; };\
+ template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
+- constexpr auto append(L1<E1...>, L2<E2...>, Rest...) noexcept -> decltype(append(L1<E1...,E2...>{}, Rest{}...)) { return {}; }\
++ 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>\
+- constexpr auto append(L1<E1...>, TypeList<mpl_::na>, Rest...) noexcept -> L1<E1...> { return {}; }\
++ struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
+ \
+ template< template<typename...> class Container, template<typename...> class List, typename...elems>\
+- constexpr auto rewrap(List<elems...>) noexcept -> TypeList<Container<elems...>> { return {}; }\
++ struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
+ template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
+- constexpr auto rewrap(List<Elems...>,Elements...) noexcept -> decltype(append(TypeList<Container<Elems...>>{}, rewrap<Container>(Elements{}...))) { return {}; }\
++ 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>\
+- constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }\
++ 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>\
+- constexpr auto convert(List<Ts...>) noexcept -> decltype(append(Final<>{},TypeList<Ts>{}...)) { return {}; }
++ 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)>\
+- constexpr auto rewrap(List<__VA_ARGS__>) noexcept -> TypeList<Container<__VA_ARGS__>> { return {}; }\
++ 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>\
+- constexpr auto rewrap(List<__VA_ARGS__>,Elements...elems) noexcept -> decltype(append(TypeList<Container<__VA_ARGS__>>{}, rewrap<Container>(elems...))) { return {}; }\
++ 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>\
+- constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }
++ 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)\
+@@ -898,22 +919,33 @@ struct is_unique<T0, T1, Rest...> : std:
+ #include <type_traits>
+
+ namespace Catch {
+-template<typename T>
+-struct always_false : std::false_type {};
++ 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> 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 T>
++ struct is_callable;
++
++ template <typename Fun, typename... Args>
++ struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
+
+-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
+
+@@ -1012,21 +1044,24 @@ struct AutoReg : NonCopyable {
+ ///////////////////////////////////////////////////////////////////////////////
+ #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__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #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) { \
+@@ -1034,21 +1069,24 @@ struct AutoReg : NonCopyable {
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::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, Signature, ... )\
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+@@ -1061,7 +1099,7 @@ struct AutoReg : NonCopyable {
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+- (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \
++ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+@@ -1070,8 +1108,7 @@ struct AutoReg : NonCopyable {
+ }();\
+ }\
+ }\
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+- CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+@@ -1091,8 +1128,10 @@ struct AutoReg : NonCopyable {
+ #endif
+
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> static void TestFuncName(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+@@ -1106,19 +1145,18 @@ struct AutoReg : NonCopyable {
+ 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++, 0)... };/* NOLINT */\
++ (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 = decltype(create<TestName, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{})); \
++ 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_UNSUPPRESS_GLOBALS_WARNINGS \
+- CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ static void TestFuncName()
+
+@@ -1139,7 +1177,9 @@ struct AutoReg : NonCopyable {
+ #endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template<typename TestType> static void TestFunc(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+@@ -1149,17 +1189,17 @@ struct AutoReg : NonCopyable {
+ 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++, 0)... };/* NOLINT */\
++ (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 = decltype(convert<TestName>(TmplList {})); \
++ using TestInit = typename convert<TestName, TmplList>::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+- }(); \
++ }(); \
+ }}\
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ static void TestFunc()
+
+@@ -1167,8 +1207,10 @@ struct AutoReg : NonCopyable {
+ 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\
+@@ -1181,7 +1223,7 @@ struct AutoReg : NonCopyable {
+ 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++, 0)... };/* NOLINT */ \
++ (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+@@ -1190,8 +1232,7 @@ struct AutoReg : NonCopyable {
+ }();\
+ }\
+ }\
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
+- CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS\
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+@@ -1211,8 +1252,10 @@ struct AutoReg : NonCopyable {
+ #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();\
+@@ -1229,19 +1272,18 @@ struct AutoReg : NonCopyable {
+ 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++, 0)... };/* NOLINT */ \
++ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+- using TestInit = decltype(create<TestNameClass, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{}));\
++ 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_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ void TestName<TestType>::test()
+
+@@ -1262,7 +1304,9 @@ struct AutoReg : NonCopyable {
+ #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();\
+@@ -1275,17 +1319,17 @@ struct AutoReg : NonCopyable {
+ 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++, 0)... };/* NOLINT */ \
++ (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 = decltype(convert<TestNameClass>(TmplList {}));\
++ using TestInit = typename convert<TestNameClass, TmplList>::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()
+
+@@ -1391,7 +1435,7 @@ namespace Catch {
+
+ auto makeStream( StringRef const &filename ) -> IStream const*;
+
+- class ReusableStringStream {
++ class ReusableStringStream : NonCopyable {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+@@ -1419,7 +1463,7 @@ namespace Catch {
+ namespace Detail {
+ struct EnumInfo {
+ StringRef m_name;
+- std::vector<std::pair<int, std::string>> m_values;
++ std::vector<std::pair<int, StringRef>> m_values;
+
+ ~EnumInfo();
+
+@@ -1434,6 +1478,7 @@ namespace Catch {
+
+ 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 )
+@@ -1515,9 +1560,9 @@ namespace Catch {
+
+ template<typename T>
+ class IsStreamInsertable {
+- template<typename SS, typename TT>
++ template<typename Stream, typename U>
+ static auto test(int)
+- -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
++ -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
+
+ template<typename, typename>
+ static auto test(...)->std::false_type;
+@@ -1679,6 +1724,12 @@ namespace Catch {
+ }
+ };
+
++#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);
+@@ -1772,8 +1823,8 @@ namespace Catch {
+ #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) {
+@@ -1931,20 +1982,27 @@ namespace Catch {
+ #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
+@@ -2114,7 +2172,7 @@ 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 enumInfo.lookup( static_cast<int>( value ) ); \
++ return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
+ } \
+ }; \
+ }
+@@ -2312,6 +2370,18 @@ namespace Catch {
+ 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 {
+@@ -2392,7 +2462,7 @@ namespace Catch {
+ 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;
+@@ -2630,15 +2700,16 @@ namespace Catch {
+ ///////////////////////////////////////////////////////////////////////////////
+ #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, ... ) \
+@@ -2855,14 +2926,16 @@ namespace Catch {
+ } // 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_interfaces_exception.h
+@@ -2953,6 +3026,9 @@ namespace Catch {
+ {}
+
+ std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
++ return "";
++#else
+ try {
+ if( it == itEnd )
+ std::rethrow_exception(std::current_exception());
+@@ -2962,6 +3038,7 @@ namespace Catch {
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
++#endif
+ }
+
+ protected:
+@@ -2980,9 +3057,10 @@ namespace Catch {
+ ///////////////////////////////////////////////////////////////////////////////
+ #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 )
+@@ -3124,7 +3202,10 @@ namespace Catch {
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
++ //! Returns a new string without whitespace at the start/end
+ std::string trim( std::string const& str );
++ //! Returns a substring of the original ref without whitespace. Beware lifetimes!
++ StringRef trim(StringRef ref);
+
+ // !!! Be aware, returns refs into original string - make sure original string outlives them
+ std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
+@@ -3180,6 +3261,15 @@ namespace Matchers {
+ 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
+@@ -3217,9 +3307,10 @@ namespace Matchers {
+ return description;
+ }
+
+- MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+- m_matchers.push_back( &other );
+- return *this;
++ MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
++ auto copy(*this);
++ copy.m_matchers.push_back( &other );
++ return copy;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+@@ -3250,9 +3341,10 @@ namespace Matchers {
+ return description;
+ }
+
+- MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+- m_matchers.push_back( &other );
+- return *this;
++ MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
++ auto copy(*this);
++ copy.m_matchers.push_back( &other );
++ return copy;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+@@ -3296,10 +3388,34 @@ using Matchers::Impl::MatcherBase;
+ } // 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 {
+@@ -3318,22 +3434,43 @@ namespace Matchers {
+ };
+
+ 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
+@@ -3464,12 +3601,12 @@ namespace Catch {
+ namespace Matchers {
+
+ namespace Vector {
+- 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;
+@@ -3485,12 +3622,12 @@ namespace Matchers {
+ 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;
+@@ -3512,18 +3649,18 @@ namespace Matchers {
+ return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+ }
+
+- std::vector<T> const& m_comparator;
++ std::vector<T, AllocComp> const& m_comparator;
+ };
+
+- template<typename T>
+- struct EqualsMatcher : MatcherBase<std::vector<T>> {
++ 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;
+@@ -3535,15 +3672,15 @@ namespace Matchers {
+ std::string describe() const override {
+ return "Equals: " + ::Catch::Detail::stringify( m_comparator );
+ }
+- std::vector<T> const& m_comparator;
++ std::vector<T, AllocComp> const& m_comparator;
+ };
+
+- template<typename T>
+- struct ApproxMatcher : MatcherBase<std::vector<T>> {
++ template<typename T, typename AllocComp, typename AllocMatch>
++ struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+- ApproxMatcher(std::vector<T> const& comparator) : m_comparator( comparator ) {}
++ ApproxMatcher(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 {
+ if (m_comparator.size() != v.size())
+ return false;
+ for (std::size_t i = 0; i < v.size(); ++i)
+@@ -3570,16 +3707,14 @@ namespace Matchers {
+ return *this;
+ }
+
+- std::vector<T> const& m_comparator;
++ std::vector<T, AllocComp> const& m_comparator;
+ mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom();
+ };
+
+- 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
++ 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;
+ }
+@@ -3590,7 +3725,7 @@ namespace Matchers {
+ return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
+ }
+ private:
+- std::vector<T> const& m_target;
++ std::vector<T, AllocComp> const& m_target;
+ };
+
+ } // namespace Vector
+@@ -3598,29 +3733,29 @@ namespace Matchers {
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+- template<typename T>
+- Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+- return Vector::ContainsMatcher<T>( comparator );
++ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++ Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
++ return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+- template<typename T>
+- Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+- return Vector::ContainsElementMatcher<T>( comparator );
++ template<typename T, typename Alloc = std::allocator<T>>
++ Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
++ return Vector::ContainsElementMatcher<T, Alloc>( comparator );
+ }
+
+- template<typename T>
+- Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+- return Vector::EqualsMatcher<T>( comparator );
++ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++ Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
++ return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+- template<typename T>
+- Vector::ApproxMatcher<T> Approx( std::vector<T> const& comparator ) {
+- return Vector::ApproxMatcher<T>( comparator );
++ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++ Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
++ return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+- template<typename T>
+- 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::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
++ return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target );
+ }
+
+ } // namespace Matchers
+@@ -3758,13 +3893,13 @@ namespace Catch {
+ (Catch::ReusableStringStream() << __VA_ARGS__).str()
+
+ #define CATCH_INTERNAL_ERROR(...) \
+- Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__));
++ 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__ ));
++ Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+ #define CATCH_RUNTIME_ERROR(...) \
+- Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ));
++ Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+ #define CATCH_ENFORCE( condition, ... ) \
+ do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+@@ -3816,7 +3951,6 @@ namespace Generators {
+ 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)) {}
+
+ T const& get() const override {
+@@ -3829,6 +3963,9 @@ namespace Generators {
+
+ template<typename T>
+ class FixedValuesGenerator final : public IGenerator<T> {
++ static_assert(!std::is_same<T, bool>::value,
++ "FixedValuesGenerator does not support bools because of std::vector<bool>"
++ "specialization, use SingleValue Generator instead.");
+ std::vector<T> m_values;
+ size_t m_idx = 0;
+ public:
+@@ -3876,21 +4013,21 @@ namespace Generators {
+ m_generators.emplace_back(std::move(generator));
+ }
+ void populate(T&& val) {
+- m_generators.emplace_back(value(std::move(val)));
++ m_generators.emplace_back(value(std::forward<T>(val)));
+ }
+ template<typename U>
+ void populate(U&& val) {
+- populate(T(std::move(val)));
++ populate(T(std::forward<U>(val)));
+ }
+ template<typename U, typename... Gs>
+- void populate(U&& valueOrGenerator, Gs... moreGenerators) {
++ void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
+ populate(std::forward<U>(valueOrGenerator));
+ populate(std::forward<Gs>(moreGenerators)...);
+ }
+
+ public:
+ template <typename... Gs>
+- Generators(Gs... moreGenerators) {
++ Generators(Gs &&... moreGenerators) {
+ m_generators.reserve(sizeof...(Gs));
+ populate(std::forward<Gs>(moreGenerators)...);
+ }
+@@ -3921,7 +4058,7 @@ namespace Generators {
+ struct as {};
+
+ template<typename T, typename... Gs>
+- auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
++ auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
+ return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
+ }
+ template<typename T>
+@@ -3929,24 +4066,24 @@ namespace Generators {
+ return Generators<T>(std::move(generator));
+ }
+ template<typename T, typename... Gs>
+- auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
++ 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> {
++ auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
+ return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
+ }
+
+- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
++ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+
+ template<typename L>
+ // Note: The type after -> is weird, because VS2015 cannot parse
+ // the expression used in the typedef inside, when it is in
+ // return type. Yeah.
+- auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
++ 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( lineInfo );
++ IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
+ if (!tracker.hasGenerator()) {
+ tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+ }
+@@ -3959,11 +4096,17 @@ namespace Generators {
+ } // namespace Catch
+
+ #define GENERATE( ... ) \
+- Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
++ 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( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
++ 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( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
++ 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
+@@ -4048,6 +4191,9 @@ namespace Generators {
+
+ 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;
+@@ -4126,18 +4272,7 @@ namespace Generators {
+ }
+ };
+
+-#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. Also *_t format is preferred over
+- // typename *::type format.
+- template <typename Func, typename U>
+- using MapFunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U>>>;
+-#else
+- template <typename Func, typename U>
+- using MapFunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U)>::type>::type>::type;
+-#endif
+-
+- template <typename Func, typename U, typename T = MapFunctionReturnType<Func, U>>
++ 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))
+@@ -4162,12 +4297,14 @@ namespace Generators {
+ m_chunk_size(size), m_generator(std::move(generator))
+ {
+ m_chunk.reserve(m_chunk_size);
+- 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"));
+- }
++ 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 {
+@@ -4238,6 +4375,7 @@ namespace Catch {
+ {
+ if( !IMutableContext::currentContext )
+ IMutableContext::createContext();
++ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+ return *IMutableContext::currentContext;
+ }
+
+@@ -4247,6 +4385,9 @@ namespace Catch {
+ }
+
+ void cleanUpContext();
++
++ class SimplePcg32;
++ SimplePcg32& rng();
+ }
+
+ // end catch_context.h
+@@ -4317,6 +4458,7 @@ namespace Catch {
+ } // end namespace Catch
+
+ // end catch_option.hpp
++#include <chrono>
+ #include <iosfwd>
+ #include <string>
+ #include <vector>
+@@ -4374,6 +4516,7 @@ namespace Catch {
+ 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;
+@@ -4387,12 +4530,63 @@ namespace Catch {
+ 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 {
+@@ -4400,14 +4594,13 @@ namespace Generators {
+
+ template <typename Float>
+ class RandomFloatingGenerator final : public IGenerator<Float> {
+- // FIXME: What is the right seed?
+- std::minstd_rand m_rand;
++ Catch::SimplePcg32& m_rng;
+ std::uniform_real_distribution<Float> m_dist;
+ Float m_current_number;
+ public:
+
+ RandomFloatingGenerator(Float a, Float b):
+- m_rand(getCurrentContext().getConfig()->rngSeed()),
++ m_rng(rng()),
+ m_dist(a, b) {
+ static_cast<void>(next());
+ }
+@@ -4416,20 +4609,20 @@ public:
+ return m_current_number;
+ }
+ bool next() override {
+- m_current_number = m_dist(m_rand);
++ m_current_number = m_dist(m_rng);
+ return true;
+ }
+ };
+
+ template <typename Integer>
+ class RandomIntegerGenerator final : public IGenerator<Integer> {
+- std::minstd_rand m_rand;
++ Catch::SimplePcg32& m_rng;
+ std::uniform_int_distribution<Integer> m_dist;
+ Integer m_current_number;
+ public:
+
+ RandomIntegerGenerator(Integer a, Integer b):
+- m_rand(getCurrentContext().getConfig()->rngSeed()),
++ m_rng(rng()),
+ m_dist(a, b) {
+ static_cast<void>(next());
+ }
+@@ -4438,7 +4631,7 @@ public:
+ return m_current_number;
+ }
+ bool next() override {
+- m_current_number = m_dist(m_rand);
++ m_current_number = m_dist(m_rng);
+ return true;
+ }
+ };
+@@ -4498,7 +4691,7 @@ public:
+
+ template <typename T>
+ GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
+- static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
++ 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));
+ }
+
+@@ -4508,6 +4701,45 @@ GeneratorWrapper<T> range(T const& start
+ return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
+ }
+
++template <typename T>
++class IteratorGenerator final : public IGenerator<T> {
++ static_assert(!std::is_same<T, bool>::value,
++ "IteratorGenerator currently does not support bools"
++ "because of std::vector<bool> specialization");
++
++ std::vector<T> m_elems;
++ size_t m_current = 0;
++public:
++ template <typename InputIterator, typename InputSentinel>
++ IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
++ if (m_elems.empty()) {
++ Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
++ }
++ }
++
++ T const& get() const override {
++ return m_elems[m_current];
++ }
++
++ bool next() override {
++ ++m_current;
++ return m_current != m_elems.size();
++ }
++};
++
++template <typename InputIterator,
++ typename InputSentinel,
++ typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
++GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
++ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
++}
++
++template <typename Container,
++ typename ResultType = typename Container::value_type>
++GeneratorWrapper<ResultType> from_range(Container const& cnt) {
++ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
++}
++
+ } // namespace Generators
+ } // namespace Catch
+
+@@ -4711,7 +4943,7 @@ namespace Catch {
+ arcSafeRelease( m_substr );
+ }
+
+- bool match( NSString* const& str ) const override {
++ bool match( NSString* str ) const override {
+ return false;
+ }
+
+@@ -4721,7 +4953,7 @@ namespace Catch {
+ struct Equals : StringHolder {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+- bool match( NSString* const& str ) const override {
++ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+@@ -4734,7 +4966,7 @@ namespace Catch {
+ struct Contains : StringHolder {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+- bool match( NSString* const& str ) const override {
++ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+@@ -4747,7 +4979,7 @@ namespace Catch {
+ struct StartsWith : StringHolder {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+- bool match( NSString* const& str ) const override {
++ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+@@ -4759,7 +4991,7 @@ namespace Catch {
+ struct EndsWith : StringHolder {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+- bool match( NSString* const& str ) const override {
++ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+@@ -4853,7 +5085,7 @@ namespace Catch
+ virtual bool matches( std::string const& str ) const;
+
+ private:
+- std::string adjustCase( std::string const& str ) const;
++ std::string normaliseString( std::string const& str ) const;
+ CaseSensitive::Choice m_caseSensitivity;
+ WildcardPosition m_wildcard = NoWildcard;
+ std::string m_pattern;
+@@ -4867,17 +5099,23 @@ namespace Catch
+
+ namespace Catch {
+
++ struct IConfig;
++
+ class TestSpec {
+- struct Pattern {
++ class Pattern {
++ public:
++ explicit Pattern( std::string const& name );
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
++ std::string const& name() const;
++ private:
++ std::string const m_name;
+ };
+ using PatternPtr = std::shared_ptr<Pattern>;
+
+ class NamePattern : public Pattern {
+ public:
+- NamePattern( std::string const& name );
+- virtual ~NamePattern();
++ explicit NamePattern( std::string const& name, std::string const& filterString );
+ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ WildcardPattern m_wildcardPattern;
+@@ -4885,8 +5123,7 @@ namespace Catch {
+
+ class TagPattern : public Pattern {
+ public:
+- TagPattern( std::string const& tag );
+- virtual ~TagPattern();
++ explicit TagPattern( std::string const& tag, std::string const& filterString );
+ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ std::string m_tag;
+@@ -4894,8 +5131,7 @@ namespace Catch {
+
+ class ExcludedPattern : public Pattern {
+ public:
+- ExcludedPattern( PatternPtr const& underlyingPattern );
+- virtual ~ExcludedPattern();
++ explicit ExcludedPattern( PatternPtr const& underlyingPattern );
+ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ PatternPtr m_underlyingPattern;
+@@ -4905,15 +5141,25 @@ namespace Catch {
+ std::vector<PatternPtr> m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const;
++ std::string name() const;
+ };
+
+ public:
++ struct FilterMatch {
++ std::string name;
++ std::vector<TestCase const*> tests;
++ };
++ using Matches = std::vector<FilterMatch>;
++ using vectorStrings = std::vector<std::string>;
++
+ bool hasFilters() const;
+ bool matches( TestCaseInfo const& testCase ) const;
++ Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
++ const vectorStrings & getInvalidArgs() const;
+
+ private:
+ std::vector<Filter> m_filters;
+-
++ std::vector<std::string> m_invalidArgs;
+ friend class TestSpecParser;
+ };
+ }
+@@ -4948,9 +5194,13 @@ namespace Catch {
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+ Mode m_mode = None;
++ Mode lastMode = None;
+ bool m_exclusion = false;
+- std::size_t m_start = std::string::npos, m_pos = 0;
++ std::size_t m_pos = 0;
++ std::size_t m_realPatternPos = 0;
+ std::string m_arg;
++ std::string m_substring;
++ std::string m_patternName;
+ std::vector<std::size_t> m_escapeChars;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+@@ -4963,32 +5213,32 @@ namespace Catch {
+ TestSpec testSpec();
+
+ private:
+- void visitChar( char c );
+- void startNewMode( Mode mode, std::size_t start );
++ bool visitChar( char c );
++ void startNewMode( Mode mode );
++ bool processNoneChar( char c );
++ void processNameChar( char c );
++ bool processOtherChar( char c );
++ void endMode();
+ void escape();
+- std::string subString() const;
++ bool isControlChar( char c ) const;
++ void saveLastMode();
++ void revertBackToLastMode();
++ void addFilter();
++ bool separate();
+
+- template<typename T>
+- void addPattern() {
+- std::string token = subString();
+- for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
+- token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+- m_escapeChars.clear();
+- if( startsWith( token, "exclude:" ) ) {
+- m_exclusion = true;
+- token = token.substr( 8 );
+- }
+- if( !token.empty() ) {
+- TestSpec::PatternPtr pattern = std::make_shared<T>( token );
+- if( m_exclusion )
+- pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
+- m_currentFilter.m_patterns.push_back( pattern );
+- }
+- m_exclusion = false;
+- m_mode = None;
++ // Handles common preprocessing of the pattern for name/tag patterns
++ std::string preprocessPattern();
++ // Adds the current pattern as a test name
++ void addNamePattern();
++ // Adds the current pattern as a tag
++ void addTagPattern();
++
++ inline void addCharToPattern(char c) {
++ m_substring += c;
++ m_patternName += c;
++ m_realPatternPos++;
+ }
+
+- void addFilter();
+ };
+ TestSpec parseTestSpec( std::string const& arg );
+
+@@ -5034,10 +5284,12 @@ namespace Catch {
+ 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;
+@@ -5088,6 +5340,7 @@ namespace Catch {
+ 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;
+ UseColour::YesOrNo useColour() const override;
+@@ -5099,6 +5352,7 @@ namespace Catch {
+ int benchmarkSamples() const override;
+ double benchmarkConfidenceInterval() const override;
+ unsigned int benchmarkResamples() const override;
++ std::chrono::milliseconds benchmarkWarmupTime() const override;
+
+ private:
+
+@@ -5204,6 +5458,8 @@ namespace Catch {
+ } // namespace Catch
+
+ // end catch_outlier_classification.hpp
++
++#include <iterator>
+ #endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ #include <string>
+@@ -5396,6 +5652,8 @@ namespace Catch {
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
++ virtual void reportInvalidArguments(std::string const&) {}
++
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+@@ -5462,6 +5720,9 @@ namespace Catch {
+ // Returns double formatted as %.3f (format expected on output)
+ std::string getFormattedDuration( double duration );
+
++ //! Should the reporter show
++ bool shouldShowDuration( IConfig const& config, double duration );
++
+ std::string serializeFilters( std::vector<std::string> const& container );
+
+ template<typename DerivedT>
+@@ -5488,6 +5749,8 @@ namespace Catch {
+
+ void noMatchingTestCases(std::string const&) override {}
+
++ void reportInvalidArguments(std::string const&) override {}
++
+ void testRunStarting(TestRunInfo const& _testRunInfo) override {
+ currentTestRunInfo = _testRunInfo;
+ }
+@@ -5822,14 +6085,16 @@ namespace Catch {
+ #if !defined(CATCH_CONFIG_DISABLE)
+
+ #define CATCH_REGISTER_REPORTER( name, reporterType ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ #define CATCH_REGISTER_LISTENER( listenerType ) \
+- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+- namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
+- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ #else // CATCH_CONFIG_DISABLE
+
+ #define CATCH_REGISTER_REPORTER(name, reporterType)
+@@ -5851,8 +6116,6 @@ namespace Catch {
+
+ static std::string getDescription();
+
+- ReporterPreferences getPreferences() const override;
+-
+ void noMatchingTestCases(std::string const& spec) override;
+
+ void assertionStarting(AssertionInfo const&) override;
+@@ -5891,6 +6154,8 @@ namespace Catch {
+
+ 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;
+@@ -5950,6 +6215,14 @@ namespace Catch {
+ #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:
+@@ -5971,14 +6244,14 @@ namespace Catch {
+
+ 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 ) {
+@@ -5988,6 +6261,7 @@ namespace Catch {
+
+ private:
+ mutable XmlWriter* m_writer = nullptr;
++ XmlFormatting m_fmt;
+ };
+
+ XmlWriter( std::ostream& os = Catch::cout() );
+@@ -5996,11 +6270,11 @@ namespace Catch {
+ 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 );
+
+@@ -6013,9 +6287,9 @@ namespace Catch {
+ 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 );
+
+@@ -6025,6 +6299,8 @@ namespace Catch {
+
+ private:
+
++ void applyFormatting(XmlFormatting fmt);
++
+ void writeDeclaration();
+
+ void newlineIfNecessary();
+@@ -6068,9 +6344,10 @@ namespace Catch {
+
+ 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);
+@@ -6126,6 +6403,7 @@ namespace Catch {
+ 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;
+@@ -6145,6 +6423,12 @@ namespace Catch {
+ #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
+@@ -6280,20 +6564,18 @@ namespace Catch {
+ return {};
+ }
+ };
+- template <typename Sig>
+- using ResultOf_t = typename std::result_of<Sig>::type;
+
+ // invoke and not return void :(
+ template <typename Fun, typename... Args>
+- CompleteType_t<ResultOf_t<Fun(Args...)>> complete_invoke(Fun&& fun, Args&&... args) {
+- return CompleteInvoker<ResultOf_t<Fun(Args...)>>::invoke(std::forward<Fun>(fun), std::forward<Args>(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<Detail::ResultOf_t<Fun()>> user_code(Fun&& fun) {
++ Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
+ CATCH_TRY{
+ return Detail::complete_invoke(std::forward<Fun>(fun));
+ } CATCH_CATCH_ALL{
+@@ -6538,8 +6820,8 @@ namespace Catch {
+ Result result;
+ int iterations;
+ };
+- template <typename Clock, typename Sig>
+- using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<Detail::ResultOf_t<Sig>>>;
++ template <typename Clock, typename Func, typename... Args>
++ using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
+ } // namespace Benchmark
+ } // namespace Catch
+
+@@ -6550,7 +6832,7 @@ namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock, typename Fun, typename... Args>
+- TimingOf<Clock, Fun(Args...)> measure(Fun&& fun, Args&&... 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();
+@@ -6569,11 +6851,11 @@ namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ template <typename Clock, typename Fun>
+- TimingOf<Clock, Fun(int)> measure_one(Fun&& fun, int iters, std::false_type) {
++ 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) {
++ 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));
+
+@@ -6590,7 +6872,7 @@ namespace Catch {
+ };
+
+ 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) {
++ 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)>());
+@@ -6600,7 +6882,7 @@ namespace Catch {
+ }
+ iters *= 2;
+ }
+- throw optimized_away_error{};
++ Catch::throw_exception(optimized_away_error{});
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+@@ -6608,6 +6890,7 @@ namespace Catch {
+
+ // end catch_run_for_at_least.hpp
+ #include <algorithm>
++#include <iterator>
+
+ namespace Catch {
+ namespace Benchmark {
+@@ -6658,11 +6941,13 @@ namespace Catch {
+ #include <algorithm>
+ #include <functional>
+ #include <vector>
++#include <iterator>
+ #include <numeric>
+ #include <tuple>
+ #include <cmath>
+ #include <utility>
+ #include <cstddef>
++#include <random>
+
+ namespace Catch {
+ namespace Benchmark {
+@@ -6776,8 +7061,8 @@ namespace Catch {
+ 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);
++ auto lo = (std::max)(cumn(a1), 0);
++ auto hi = (std::min)(cumn(a2), n - 1);
+
+ return { point, resample[lo], resample[hi], confidence_level };
+ }
+@@ -6846,7 +7131,9 @@ namespace Catch {
+ }
+ 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_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) {
+@@ -7012,10 +7299,10 @@ namespace Catch {
+ 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)>(Detail::warmup_time));
++ 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>>(Detail::warmup_time), Detail::warmup_iterations };
++ 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>
+@@ -7047,7 +7334,7 @@ namespace Catch {
+ });
+
+ auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
+- BenchmarkStats<std::chrono::duration<double, std::nano>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
++ BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
+ getResultCapture().benchmarkEnded(stats);
+
+ } CATCH_CATCH_ALL{
+@@ -7088,6 +7375,77 @@ namespace Catch {
+ 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
+@@ -7115,23 +7473,37 @@ namespace TestCaseTracking {
+ SourceLineInfo location;
+
+ NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
++ friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
++ return lhs.name == rhs.name
++ && lhs.location == rhs.location;
++ }
+ };
+
+- struct ITracker;
++ class ITracker;
+
+ using ITrackerPtr = std::shared_ptr<ITracker>;
+
+- struct ITracker {
+- virtual ~ITracker();
++ class ITracker {
++ NameAndLocation m_nameAndLocation;
++
++ public:
++ ITracker(NameAndLocation const& nameAndLoc) :
++ m_nameAndLocation(nameAndLoc)
++ {}
+
+ // static queries
+- virtual NameAndLocation const& nameAndLocation() const = 0;
++ NameAndLocation const& nameAndLocation() const {
++ return m_nameAndLocation;
++ }
++
++ virtual ~ITracker();
+
+ // dynamic queries
+ virtual bool isComplete() const = 0; // Successfully completed or failed
+ virtual bool isSuccessfullyCompleted() const = 0;
+ virtual bool isOpen() const = 0; // Started but not complete
+ virtual bool hasChildren() const = 0;
++ virtual bool hasStarted() const = 0;
+
+ virtual ITracker& parent() = 0;
+
+@@ -7186,7 +7558,6 @@ namespace TestCaseTracking {
+ };
+
+ using Children = std::vector<ITrackerPtr>;
+- NameAndLocation m_nameAndLocation;
+ TrackerContext& m_ctx;
+ ITracker* m_parent;
+ Children m_children;
+@@ -7195,11 +7566,13 @@ namespace TestCaseTracking {
+ public:
+ TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+- NameAndLocation const& nameAndLocation() const override;
+ bool isComplete() const override;
+ bool isSuccessfullyCompleted() const override;
+ bool isOpen() const override;
+ bool hasChildren() const override;
++ bool hasStarted() const override {
++ return m_runState != NotStarted;
++ }
+
+ void addChild( ITrackerPtr const& child ) override;
+
+@@ -7224,6 +7597,7 @@ namespace TestCaseTracking {
+
+ class SectionTracker : public TrackerBase {
+ std::vector<std::string> m_filters;
++ std::string m_trimmed_name;
+ public:
+ SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+@@ -7237,6 +7611,10 @@ namespace TestCaseTracking {
+
+ void addInitialFilters( std::vector<std::string> const& filters );
+ void addNextFilters( std::vector<std::string> const& filters );
++ //! 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
+@@ -7402,7 +7780,7 @@ namespace Catch {
+ 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 sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
+ double sg2 = sg * sg;
+ double sb2 = sb * sb;
+
+@@ -7421,13 +7799,14 @@ namespace Catch {
+ return (nc / n) * (sb2 - nc * sg2);
+ };
+
+- return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2;
++ 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_UNSUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
+
+@@ -7515,7 +7894,8 @@ namespace Detail {
+ bool Approx::equalityComparisonImpl(const double other) const {
+ // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
+ // Thanks to Richard Harris for his help refining the scaled margin value
+- return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
++ return marginComparison(m_value, other, m_margin)
++ || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
+ }
+
+ void Approx::setMargin(double newMargin) {
+@@ -7559,7 +7939,24 @@ namespace Catch {
+
+ #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
+@@ -7579,10 +7976,12 @@ namespace Catch {
+ #define CATCH_TRAP() DebugBreak()
+ #endif
+
+-#ifdef CATCH_TRAP
+- #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
+-#else
+- #define CATCH_BREAK_INTO_DEBUGGER() []{}()
++#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
+@@ -7590,86 +7989,58 @@ namespace Catch {
+
+ // 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>
+@@ -7729,7 +8100,7 @@ namespace Catch {
+ 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;
+@@ -7795,11 +8166,14 @@ namespace Catch {
+ 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
+@@ -7946,7 +8320,7 @@ namespace Catch {
+ }
+
+ bool AssertionResult::hasExpression() const {
+- return m_info.capturedExpression[0] != 0;
++ return !m_info.capturedExpression.empty();
+ }
+
+ bool AssertionResult::hasMessage() const {
+@@ -7954,16 +8328,22 @@ namespace Catch {
+ }
+
+ 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;
+@@ -8697,7 +9077,7 @@ namespace detail {
+ }
+ inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
+ std::string srcLC = source;
+- std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( std::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")
+@@ -9345,9 +9725,14 @@ namespace Catch {
+ if( !line.empty() && !startsWith( line, '#' ) ) {
+ if( !startsWith( line, '"' ) )
+ line = '"' + line + '"';
+- config.testsOrTags.push_back( line + ',' );
++ config.testsOrTags.push_back( line );
++ config.testsOrTags.emplace_back( "," );
+ }
+ }
++ //Remove comma in the end
++ if(!config.testsOrTags.empty())
++ config.testsOrTags.erase( config.testsOrTags.end()-1 );
++
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setTestOrder = [&]( std::string const& order ) {
+@@ -9382,14 +9767,16 @@ namespace Catch {
+ };
+ auto const setWaitForKeypress = [&]( std::string const& keypress ) {
+ auto keypressLc = toLower( keypress );
+- if( keypressLc == "start" )
++ if (keypressLc == "never")
++ config.waitForKeypress = WaitForKeypress::Never;
++ else if( keypressLc == "start" )
+ config.waitForKeypress = WaitForKeypress::BeforeStart;
+ else if( keypressLc == "exit" )
+ config.waitForKeypress = WaitForKeypress::BeforeExit;
+ else if( keypressLc == "both" )
+ config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
+ else
+- return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" );
++ return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setVerbosity = [&]( std::string const& verbosity ) {
+@@ -9459,6 +9846,9 @@ namespace Catch {
+ | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
+ ["-d"]["--durations"]
+ ( "show test durations" )
++ | Opt( config.minDuration, "seconds" )
++ ["-D"]["--min-duration"]
++ ( "show test durations for tests taking at least the given number of seconds" )
+ | Opt( loadTestNamesFromFile, "filename" )
+ ["-f"]["--input-file"]
+ ( "load test names to run from a file" )
+@@ -9489,7 +9879,7 @@ namespace Catch {
+ | Opt( config.libIdentify )
+ ["--libidentify"]
+ ( "report name and version according to libidentify standard" )
+- | Opt( setWaitForKeypress, "start|exit|both" )
++ | Opt( setWaitForKeypress, "never|start|exit|both" )
+ ["--wait-for-keypress"]
+ ( "waits for a keypress before exiting" )
+ | Opt( config.benchmarkSamples, "samples" )
+@@ -9504,7 +9894,10 @@ namespace Catch {
+ | Opt( config.benchmarkNoAnalysis )
+ ["--benchmark-no-analysis"]
+ ( "perform only measurements; do not perform any analysis" )
+- | Arg( config.testsOrTags, "test name|pattern|tags" )
++ | 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" );
+
+ return cli;
+@@ -9519,9 +9912,6 @@ namespace Catch {
+
+ 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);
+ }
+@@ -9557,11 +9947,23 @@ namespace Catch {
+ : m_data( data ),
+ m_stream( openStream() )
+ {
++ // We need to trim filter specs to avoid trouble with superfluous
++ // whitespace (esp. important for bdd macros, as those are manually
++ // aligned with whitespace).
++
++ for (auto& elem : m_data.testsOrTags) {
++ elem = trim(elem);
++ }
++ for (auto& elem : m_data.sectionsToRun) {
++ elem = trim(elem);
++ }
++
+ TestSpecParser parser(ITagAliasRegistry::get());
+- if (!data.testsOrTags.empty()) {
++ 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();
+ }
+@@ -9594,6 +9996,7 @@ namespace Catch {
+ 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; }
+ UseColour::YesOrNo Config::useColour() const { return m_data.useColour; }
+@@ -9602,10 +10005,11 @@ namespace Catch {
+ 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; }
++ 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);
+@@ -9635,6 +10039,36 @@ namespace Catch {
+ }
+
+ // end catch_errno_guard.h
++// start catch_windows_h_proxy.h
++
++
++#if defined(CATCH_PLATFORM_WINDOWS)
++
++#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
++# define CATCH_DEFINED_NOMINMAX
++# define NOMINMAX
++#endif
++#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
++# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
++# define WIN32_LEAN_AND_MEAN
++#endif
++
++#ifdef __AFXDLL
++#include <AfxWin.h>
++#else
++#include <windows.h>
++#endif
++
++#ifdef CATCH_DEFINED_NOMINMAX
++# undef NOMINMAX
++#endif
++#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
++# undef WIN32_LEAN_AND_MEAN
++#endif
++
++#endif // defined(CATCH_PLATFORM_WINDOWS)
++
++// end catch_windows_h_proxy.h
+ #include <sstream>
+
+ namespace Catch {
+@@ -9646,7 +10080,7 @@ namespace Catch {
+ };
+
+ struct NoColourImpl : IColourImpl {
+- void use( Colour::Code ) {}
++ void use( Colour::Code ) override {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+@@ -9778,7 +10212,7 @@ namespace {
+
+ bool useColourOnPlatform() {
+ return
+-#ifdef CATCH_PLATFORM_MAC
++#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+ !isDebuggerActive() &&
+ #endif
+ #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
+@@ -9819,13 +10253,13 @@ namespace Catch {
+ namespace Catch {
+
+ Colour::Colour( Code _colourCode ) { use( _colourCode ); }
+- Colour::Colour( Colour&& rhs ) noexcept {
+- m_moved = rhs.m_moved;
+- rhs.m_moved = true;
+- }
+- Colour& Colour::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;
+ }
+
+@@ -9837,7 +10271,7 @@ namespace Catch {
+ // 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 != NULL) {
++ if (impl != nullptr) {
+ impl->use( _colourCode );
+ }
+ }
+@@ -9906,6 +10340,12 @@ namespace Catch {
+ 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
+@@ -9919,7 +10359,16 @@ namespace Catch {
+ }
+
+ // end catch_debug_console.h
+-#ifdef CATCH_PLATFORM_WINDOWS
++#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
++#include <android/log.h>
++
++ namespace Catch {
++ void writeToDebugConsole( std::string const& text ) {
++ __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
++ }
++ }
++
++#elif defined(CATCH_PLATFORM_WINDOWS)
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+@@ -9940,10 +10389,9 @@ namespace Catch {
+ // 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 <cstddef>
+@@ -10116,7 +10564,7 @@ namespace Catch {
+ EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
+ };
+
+- std::vector<std::string> parseEnums( StringRef enums );
++ std::vector<StringRef> parseEnums( StringRef enums );
+
+ } // Detail
+
+@@ -10133,13 +10581,25 @@ namespace Catch {
+
+ namespace Detail {
+
+- std::vector<std::string> parseEnums( StringRef enums ) {
++ 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<std::string> parsed;
++ std::vector<StringRef> parsed;
+ parsed.reserve( enumValues.size() );
+ for( auto const& enumValue : enumValues ) {
+- auto identifiers = splitStringRef( enumValue, ':' );
+- parsed.push_back( Catch::trim( identifiers.back() ) );
++ parsed.push_back(trim(extractInstanceName(enumValue)));
+ }
+ return parsed;
+ }
+@@ -10151,7 +10611,7 @@ namespace Catch {
+ if( valueToName.first == value )
+ return valueToName.second;
+ }
+- return "{** unexpected enum value **}";
++ return "{** unexpected enum value **}"_sr;
+ }
+
+ std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+@@ -10163,16 +10623,14 @@ namespace Catch {
+ assert( valueNames.size() == values.size() );
+ std::size_t i = 0;
+ for( auto value : values )
+- enumInfo->m_values.push_back({ value, valueNames[i++] });
++ enumInfo->m_values.emplace_back(value, valueNames[i++]);
+
+ return enumInfo;
+ }
+
+ EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+- auto enumInfo = makeEnumInfo( enumName, allValueNames, values );
+- EnumInfo* raw = enumInfo.get();
+- m_enumInfos.push_back( std::move( enumInfo ) );
+- return *raw;
++ m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
++ return *m_enumInfos.back();
+ }
+
+ } // Detail
+@@ -10289,25 +10747,47 @@ namespace Catch {
+ // 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.
+@@ -10320,7 +10800,7 @@ namespace Catch {
+ { 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);
+@@ -10331,38 +10811,50 @@ namespace Catch {
+ 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 {
+
+@@ -10371,10 +10863,6 @@ namespace Catch {
+ 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" },
+@@ -10384,7 +10872,32 @@ namespace Catch {
+ { 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) {
+@@ -10392,16 +10905,33 @@ namespace Catch {
+ 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 = { };
+@@ -10413,59 +10943,20 @@ namespace Catch {
+ }
+ }
+
+- 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>
+
+@@ -10481,8 +10972,8 @@ namespace Generators {
+
+ GeneratorUntypedBase::~GeneratorUntypedBase() {}
+
+- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+- return getResultCapture().acquireGeneratorTracker( lineInfo );
++ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
++ return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+ }
+
+ } // namespace Generators
+@@ -10538,6 +11029,8 @@ namespace Catch {
+
+ void noMatchingTestCases( std::string const& spec ) override;
+
++ void reportInvalidArguments(std::string const&arg) override;
++
+ static std::set<Verbosity> getSupportedVerbosities();
+
+ #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+@@ -10755,7 +11248,7 @@ namespace Catch {
+ 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 {
+@@ -10789,7 +11282,7 @@ namespace Catch {
+ }
+
+ 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 ) {
+@@ -10811,14 +11304,23 @@ namespace Catch {
+ }
+
+ std::string TagInfo::all() const {
+- std::string out;
+- for( auto const& spelling : spellings )
+- out += "[" + spelling + "]";
++ size_t size = 0;
++ for (auto const& spelling : spellings) {
++ // Add 2 for the brackes
++ size += spelling.size() + 2;
++ }
++
++ std::string out; out.reserve(size);
++ for (auto const& spelling : spellings) {
++ out += '[';
++ out += spelling;
++ out += ']';
++ }
+ return out;
+ }
+
+ std::size_t listTags( Config const& config ) {
+- TestSpec testSpec = config.testSpec();
++ TestSpec const& testSpec = config.testSpec();
+ if( config.hasTestFilters() )
+ Catch::cout() << "Tags for matching test cases:\n";
+ else {
+@@ -10912,6 +11414,29 @@ using Matchers::Impl::MatcherBase;
+
+ } // namespace Catch
+ // end catch_matchers.cpp
++// start catch_matchers_exception.cpp
++
++namespace Catch {
++namespace Matchers {
++namespace Exception {
++
++bool ExceptionMessageMatcher::match(std::exception const& ex) const {
++ return ex.what() == m_message;
++}
++
++std::string ExceptionMessageMatcher::describe() const {
++ return "exception message matches \"" + m_message + "\"";
++}
++
++}
++Exception::ExceptionMessageMatcher Message(std::string const& message) {
++ return Exception::ExceptionMessageMatcher(message);
++}
++
++// namespace Exception
++} // namespace Matchers
++} // namespace Catch
++// end catch_matchers_exception.cpp
+ // start catch_matchers_floating.cpp
+
+ // start catch_polyfills.hpp
+@@ -10940,85 +11465,101 @@ namespace Catch {
+ } // 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)
+
+- if ((lc.i < 0) != (rc.i < 0)) {
+- // Potentially we can have +0 and -0
+- return lhs == rhs;
++ float nextafter(float x, float y) {
++ return ::nextafterf(x, y);
+ }
+
+- auto ulpDiff = std::abs(lc.i - rc.i);
+- return ulpDiff <= maxUlpDiff;
+-}
++ double nextafter(double x, double y) {
++ return ::nextafter(x, y);
++ }
++
++#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
+
+ template <typename FP>
+-FP step(FP start, FP direction, int steps) {
+- for (int i = 0; i < steps; ++i) {
++FP step(FP start, FP direction, uint64_t steps) {
++ for (uint64_t i = 0; i < steps; ++i) {
++#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
++ start = Catch::nextafter(start, direction);
++#else
+ start = std::nextafter(start, direction);
++#endif
+ }
+ return start;
+ }
+
++// Performs equivalent check of std::fabs(lhs - rhs) <= margin
++// But without the subtraction to allow for INFINITY in comparison
++bool marginComparison(double lhs, double rhs, double margin) {
++ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
++}
++
++template <typename FloatingPoint>
++void write(std::ostream& out, FloatingPoint num) {
++ out << std::scientific
++ << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
++ << num;
++}
++
+ } // end anonymous namespace
+
+-namespace Catch {
+ 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 << '.'
+@@ -11035,10 +11576,11 @@ namespace Floating {
+ 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__)
+@@ -11065,36 +11607,57 @@ namespace Floating {
+ std::string WithinUlpsMatcher::describe() const {
+ std::stringstream ret;
+
+- ret << "is within " << m_ulps << " ULPs of " << ::Catch::Detail::stringify(m_target);
++ 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 << " ([";
+- ret << std::fixed << std::setprecision(std::numeric_limits<double>::max_digits10);
+ if (m_type == FloatingPointKind::Double) {
+- ret << step(m_target, static_cast<double>(-INFINITY), m_ulps)
+- << ", "
+- << step(m_target, static_cast<double>(INFINITY), m_ulps);
++ write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
++ ret << ", ";
++ write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
+ } else {
+- ret << step<float>(static_cast<float>(m_target), -INFINITY, m_ulps)
+- << ", "
+- << step<float>(static_cast<float>(m_target), INFINITY, m_ulps);
++ // 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();
+- //return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
++ }
++
++ 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);
+ }
+
+@@ -11102,9 +11665,24 @@ Floating::WithinAbsMatcher WithinAbs(dou
+ 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
+
+@@ -11282,10 +11860,10 @@ namespace Catch {
+
+ 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);
+@@ -11324,17 +11902,17 @@ namespace Catch {
+ 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() {
+@@ -11520,7 +12098,7 @@ namespace Catch {
+ 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");
+@@ -11628,20 +12206,61 @@ namespace Catch {
+
+ 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
+@@ -11660,6 +12279,8 @@ namespace Catch {
+ struct IConfig;
+
+ std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
++
++ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
+@@ -11772,11 +12393,13 @@ namespace Catch {
+ namespace Catch {
+
+ class StartupExceptionRegistry {
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ public:
+ void add(std::exception_ptr const& exception) noexcept;
+ std::vector<std::exception_ptr> const& getExceptions() const noexcept;
+ private:
+ std::vector<std::exception_ptr> m_exceptions;
++#endif
+ };
+
+ } // end namespace Catch
+@@ -11859,7 +12482,11 @@ namespace Catch {
+ m_tagAliasRegistry.add( alias, tag, lineInfo );
+ }
+ void registerStartupException() noexcept override {
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ m_exceptionRegistry.add(std::current_exception());
++#else
++ CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
++#endif
+ }
+ IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
+ return m_enumValuesRegistry;
+@@ -11963,17 +12590,32 @@ namespace Catch {
+ 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->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->isComplete() ) {
+ tracker->open();
+ }
+
+@@ -11987,8 +12629,68 @@ namespace Catch {
+ }
+ void close() override {
+ TrackerBase::close();
+- // Generator interface only finds out if it has another item on atual move
+- if (m_runState == CompletedSuccessfully && m_generator->next()) {
++ // 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;
+ }
+@@ -12124,10 +12826,10 @@ namespace Catch {
+
+ 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;
+ }
+@@ -12170,17 +12872,17 @@ namespace Catch {
+
+ #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void RunContext::benchmarkPreparing(std::string const& name) {
+- m_reporter->benchmarkPreparing(name);
+- }
++ m_reporter->benchmarkPreparing(name);
++ }
+ void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
+ m_reporter->benchmarkStarting( info );
+ }
+ void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
+ m_reporter->benchmarkEnded( stats );
+ }
+- void RunContext::benchmarkFailed(std::string const & error) {
+- m_reporter->benchmarkFailed(error);
+- }
++ void RunContext::benchmarkFailed(std::string const & error) {
++ m_reporter->benchmarkFailed(error);
++ }
+ #endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void RunContext::pushScopedMessage(MessageInfo const & message) {
+@@ -12216,7 +12918,7 @@ namespace Catch {
+ // Don't rebuild the result -- the stringification itself can cause more fatal errors
+ // Instead, fake a result data.
+ AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
+- tempResult.message = message;
++ tempResult.message = static_cast<std::string>(message);
+ AssertionResult result(m_lastAssertionInfo, tempResult);
+
+ assertionEnded(result);
+@@ -12314,9 +13016,8 @@ namespace Catch {
+ }
+
+ void RunContext::invokeActiveTestCase() {
+- FatalConditionHandler fatalConditionHandler; // Handle signals
++ FatalConditionHandlerGuard _(&m_fatalConditionhandler);
+ m_activeTestCase->invoke();
+- fatalConditionHandler.reset();
+ }
+
+ void RunContext::handleUnfinishedSections() {
+@@ -12379,7 +13080,7 @@ namespace Catch {
+ 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() )
+@@ -12442,6 +13143,18 @@ namespace Catch {
+ 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
+@@ -12503,7 +13216,7 @@ namespace Catch {
+ 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
+
+@@ -12570,6 +13283,8 @@ namespace Catch {
+ // end catch_version.h
+ #include <cstdlib>
+ #include <iomanip>
++#include <set>
++#include <iterator>
+
+ namespace Catch {
+
+@@ -12603,45 +13318,61 @@ namespace Catch {
+ 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) {
+- bool matching = (!testSpec.hasFilters() && !testCase.isHidden()) ||
+- (testSpec.hasFilters() && matchTest(testCase, testSpec, *config));
+-
+- if (!context.aborting() && matching)
+- 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;
++ }
++ }
++
++ if (!invalidArgs.empty()) {
++ for (auto const& invalidArg: invalidArgs)
++ m_context.reporter().reportInvalidArguments(invalidArg);
+ }
+
+- context.reporter().noMatchingTestCases(testConfig.str());
+- totals.error = -1;
++ 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));
+@@ -12710,7 +13441,7 @@ namespace Catch {
+ }
+ void Session::libIdentify() {
+ Catch::cout()
+- << std::left << std::setw(16) << "description: " << "A Catch test executable\n"
++ << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
+ << std::left << std::setw(16) << "category: " << "testframework\n"
+ << std::left << std::setw(16) << "framework: " << "Catch Test\n"
+ << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
+@@ -12741,17 +13472,17 @@ namespace Catch {
+ 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 );
+@@ -12818,7 +13549,12 @@ namespace Catch {
+ 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
+@@ -12866,6 +13602,7 @@ namespace Catch {
+ // end catch_singletons.cpp
+ // start catch_startup_exception_registry.cpp
+
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ namespace Catch {
+ void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
+ CATCH_TRY {
+@@ -12881,6 +13618,7 @@ void StartupExceptionRegistry::add( std:
+ }
+
+ } // end namespace Catch
++#endif
+ // end catch_startup_exception_registry.cpp
+ // start catch_stream.cpp
+
+@@ -13065,7 +13803,7 @@ 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) ) );
+ }
+ }
+
+@@ -13100,6 +13838,18 @@ namespace Catch {
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+ }
+
++ StringRef trim(StringRef ref) {
++ const auto is_ws = [](char c) {
++ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
++ };
++ size_t real_begin = 0;
++ while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; }
++ size_t real_end = ref.size();
++ while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; }
++
++ return ref.substr(real_begin, real_end - real_begin);
++ }
++
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+ bool replaced = false;
+ std::size_t i = str.find( replaceThis );
+@@ -13145,124 +13895,46 @@ namespace Catch {
+ // end catch_string_manip.cpp
+ // start catch_stringref.cpp
+
+-#if defined(__clang__)
+-# pragma clang diagnostic push
+-# pragma clang diagnostic ignored "-Wexit-time-destructors"
+-#endif
+-
++#include <algorithm>
+ #include <ostream>
+ #include <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() )
+- return m_start;
+-
+- const_cast<StringRef *>( this )->takeOwnership();
+- return m_data;
+- }
+- auto StringRef::currentData() const noexcept -> char const* {
++ CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
+ 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';
++ auto StringRef::data() const noexcept -> char const* {
++ return m_start;
+ }
+
+- void StringRef::takeOwnership() {
+- if( !isOwned() ) {
+- m_data = new char[m_size+1];
+- memcpy( m_data, m_start, m_size );
+- m_data[m_size] = '\0';
+- }
+- }
+ 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
+
+@@ -13381,8 +14053,7 @@ namespace Catch {
+ 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;
+@@ -13412,10 +14083,11 @@ namespace Catch {
+ }
+ }
+ 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) );
+ }
+
+@@ -13507,30 +14179,89 @@ namespace Catch {
+ // end catch_test_case_info.cpp
+ // start catch_test_case_registry_impl.cpp
+
++#include <algorithm>
+ #include <sstream>
+
+ namespace Catch {
+
+- std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
++ namespace {
++ struct TestHasher {
++ using hash_t = uint64_t;
+
+- 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 ) {
+@@ -13591,7 +14322,7 @@ namespace Catch {
+ }
+
+ std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
+- std::string className = classOrQualifiedMethodName;
++ std::string className(classOrQualifiedMethodName);
+ if( startsWith( className, '&' ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+@@ -13659,15 +14390,12 @@ namespace TestCaseTracking {
+ 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;
+ }
+@@ -13733,7 +14461,7 @@ namespace TestCaseTracking {
+ m_runState = CompletedSuccessfully;
+ break;
+ case ExecutingChildren:
+- if( m_children.empty() || m_children.back()->isComplete() )
++ if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
+ m_runState = CompletedSuccessfully;
+ break;
+
+@@ -13768,7 +14496,8 @@ namespace TestCaseTracking {
+ }
+
+ SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+- : TrackerBase( nameAndLocation, ctx, parent )
++ : TrackerBase( nameAndLocation, ctx, parent ),
++ m_trimmed_name(trim(nameAndLocation.name))
+ {
+ if( parent ) {
+ while( !parent->isSectionTracker() )
+@@ -13782,12 +14511,12 @@ namespace TestCaseTracking {
+ bool SectionTracker::isComplete() const {
+ bool complete = true;
+
+- if ((m_filters.empty() || m_filters[0] == "") ||
+- std::find(m_filters.begin(), m_filters.end(),
+- m_nameAndLocation.name) != m_filters.end())
++ 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; }
+@@ -13811,20 +14540,29 @@ namespace TestCaseTracking {
+ }
+
+ void SectionTracker::tryOpen() {
+- if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
++ if( !isComplete() )
+ open();
+ }
+
+ void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
+ if( !filters.empty() ) {
+- m_filters.push_back(""); // Root - should never be consulted
+- m_filters.push_back(""); // Test Case - not a section filter
++ m_filters.reserve( m_filters.size() + filters.size() + 2 );
++ m_filters.emplace_back(""); // Root - should never be consulted
++ m_filters.emplace_back(""); // Test Case - not a section filter
+ m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+ }
+ }
+ void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+ if( filters.size() > 1 )
+- m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
++ m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
++ }
++
++ std::vector<std::string> const& SectionTracker::getFilters() const {
++ return m_filters;
++ }
++
++ std::string const& SectionTracker::trimmedName() const {
++ return m_trimmed_name;
+ }
+
+ } // namespace TestCaseTracking
+@@ -13876,47 +14614,81 @@ namespace Catch {
+
+ namespace Catch {
+
++ TestSpec::Pattern::Pattern( std::string const& name )
++ : m_name( name )
++ {}
++
+ TestSpec::Pattern::~Pattern() = default;
+- TestSpec::NamePattern::~NamePattern() = default;
+- TestSpec::TagPattern::~TagPattern() = default;
+- TestSpec::ExcludedPattern::~ExcludedPattern() = default;
+
+- TestSpec::NamePattern::NamePattern( std::string const& name )
+- : m_wildcardPattern( toLower( name ), CaseSensitive::No )
++ std::string const& TestSpec::Pattern::name() const {
++ return m_name;
++ }
++
++ TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
++ : Pattern( filterString )
++ , m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ {}
++
+ bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
+- return m_wildcardPattern.matches( toLower( testCase.name ) );
++ return m_wildcardPattern.matches( testCase.name );
+ }
+
+- TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
++ TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
++ : Pattern( filterString )
++ , m_tag( toLower( tag ) )
++ {}
++
+ bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
+ return std::find(begin(testCase.lcaseTags),
+ end(testCase.lcaseTags),
+ m_tag) != end(testCase.lcaseTags);
+ }
+
+- TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+- bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
++ TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
++ : Pattern( underlyingPattern->name() )
++ , m_underlyingPattern( underlyingPattern )
++ {}
++
++ bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
++ return !m_underlyingPattern->matches( testCase );
++ }
+
+ bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
+- // All patterns in a filter must match for the filter to be a match
+- for( auto const& pattern : m_patterns ) {
+- if( !pattern->matches( testCase ) )
+- return false;
+- }
+- return true;
++ return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
++ }
++
++ std::string TestSpec::Filter::name() const {
++ std::string name;
++ for( auto const& p : m_patterns )
++ name += p->name();
++ return name;
+ }
+
+ bool TestSpec::hasFilters() const {
+ return !m_filters.empty();
+ }
++
+ bool TestSpec::matches( TestCaseInfo const& testCase ) const {
+- // A TestSpec matches if any filter matches
+- for( auto const& filter : m_filters )
+- if( filter.matches( testCase ) )
+- return true;
+- return false;
++ return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
++ }
++
++ TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const
++ {
++ Matches matches( m_filters.size() );
++ std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
++ std::vector<TestCase const*> currentMatches;
++ for( auto const& test : testCases )
++ if( isThrowSafe( test, config ) && filter.matches( test ) )
++ currentMatches.emplace_back( &test );
++ return FilterMatch{ filter.name(), currentMatches };
++ } );
++ return matches;
+ }
++
++ const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
++ return (m_invalidArgs);
++ }
++
+ }
+ // end catch_test_spec.cpp
+ // start catch_test_spec_parser.cpp
+@@ -13928,64 +14700,136 @@ namespace Catch {
+ TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+- m_start = std::string::npos;
+ m_arg = m_tagAliases->expandAliases( arg );
+ m_escapeChars.clear();
++ m_substring.reserve(m_arg.size());
++ m_patternName.reserve(m_arg.size());
++ m_realPatternPos = 0;
++
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+- visitChar( m_arg[m_pos] );
+- if( m_mode == Name )
+- addPattern<TestSpec::NamePattern>();
++ //if visitChar fails
++ if( !visitChar( m_arg[m_pos] ) ){
++ m_testSpec.m_invalidArgs.push_back(arg);
++ break;
++ }
++ endMode();
+ return *this;
+ }
+ TestSpec TestSpecParser::testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
++ bool TestSpecParser::visitChar( char c ) {
++ if( (m_mode != EscapedName) && (c == '\\') ) {
++ escape();
++ addCharToPattern(c);
++ return true;
++ }else if((m_mode != EscapedName) && (c == ',') ) {
++ return separate();
++ }
+
+- void TestSpecParser::visitChar( char c ) {
+- if( m_mode == None ) {
+- switch( c ) {
+- case ' ': return;
+- case '~': m_exclusion = true; return;
+- case '[': return startNewMode( Tag, ++m_pos );
+- case '"': return startNewMode( QuotedName, ++m_pos );
+- case '\\': return escape();
+- default: startNewMode( Name, m_pos ); break;
+- }
+- }
+- 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;
+ }
+- 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>();
++
++ m_substring += c;
++ if( !isControlChar( c ) ) {
++ m_patternName += c;
++ m_realPatternPos++;
++ }
++ return true;
+ }
+- void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
++ // 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 );
++ }
++ }
++ 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() ) {
+@@ -13994,6 +14838,86 @@ namespace Catch {
+ }
+ }
+
++ void TestSpecParser::saveLastMode() {
++ lastMode = m_mode;
++ }
++
++ void TestSpecParser::revertBackToLastMode() {
++ m_mode = lastMode;
++ }
++
++ bool TestSpecParser::separate() {
++ if( (m_mode==QuotedName) || (m_mode==Tag) ){
++ //invalid argument, signal failure to previous scope.
++ m_mode = None;
++ m_pos = m_arg.size();
++ m_substring.clear();
++ m_patternName.clear();
++ m_realPatternPos = 0;
++ return false;
++ }
++ endMode();
++ addFilter();
++ return true; //success
++ }
++
++ std::string TestSpecParser::preprocessPattern() {
++ std::string token = m_patternName;
++ for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
++ token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
++ m_escapeChars.clear();
++ if (startsWith(token, "exclude:")) {
++ m_exclusion = true;
++ token = token.substr(8);
++ }
++
++ m_patternName.clear();
++ m_realPatternPos = 0;
++
++ return token;
++ }
++
++ void TestSpecParser::addNamePattern() {
++ auto token = preprocessPattern();
++
++ if (!token.empty()) {
++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
++ if (m_exclusion)
++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++ m_currentFilter.m_patterns.push_back(pattern);
++ }
++ m_substring.clear();
++ m_exclusion = false;
++ m_mode = None;
++ }
++
++ void TestSpecParser::addTagPattern() {
++ auto token = preprocessPattern();
++
++ if (!token.empty()) {
++ // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
++ // we have to create a separate hide tag and shorten the real one
++ if (token.size() > 1 && token[0] == '.') {
++ token.erase(token.begin());
++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
++ if (m_exclusion) {
++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++ }
++ m_currentFilter.m_patterns.push_back(pattern);
++ }
++
++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
++
++ if (m_exclusion) {
++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++ }
++ m_currentFilter.m_patterns.push_back(pattern);
++ }
++ m_substring.clear();
++ m_exclusion = false;
++ m_mode = None;
++ }
++
+ TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+@@ -14095,13 +15019,11 @@ namespace Detail {
+ enum Arch { Big, Little };
+
+ static Arch which() {
+- union _{
+- int asInt;
+- char asChar[sizeof (int)];
+- } u;
+-
+- u.asInt = 1;
+- return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
++ int one = 1;
++ // If the lowest byte we read is non-zero, we can assume
++ // that little endian format is used.
++ auto value = *reinterpret_cast<char*>(&one);
++ return value ? Little : Big;
+ }
+ };
+ }
+@@ -14225,6 +15147,13 @@ std::string StringMaker<wchar_t *>::conv
+ }
+ #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));
+ }
+@@ -14370,11 +15299,48 @@ namespace Catch {
+ // end catch_totals.cpp
+ // start catch_uncaught_exceptions.cpp
+
++// start catch_config_uncaught_exceptions.hpp
++
++// Copyright Catch2 Authors
++// Distributed under the Boost Software License, Version 1.0.
++// (See accompanying file LICENSE_1_0.txt or copy at
++// https://www.boost.org/LICENSE_1_0.txt)
++
++// SPDX-License-Identifier: BSL-1.0
++
++#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++
++#if defined(_MSC_VER)
++# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++# endif
++#endif
++
++#include <exception>
++
++#if defined(__cpp_lib_uncaught_exceptions) \
++ && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++
++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#endif // __cpp_lib_uncaught_exceptions
++
++#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \
++ && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \
++ && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++
++# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#endif
++
++#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++// end catch_config_uncaught_exceptions.hpp
+ #include <exception>
+
+ namespace Catch {
+ bool uncaught_exceptions() {
+-#if defined(CATCH_CONFIG_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();
+@@ -14414,7 +15380,7 @@ namespace Catch {
+ }
+
+ Version const& libraryVersion() {
+- static Version version( 2, 9, 1, "", 0 );
++ static Version version( 2, 13, 7, "", 0 );
+ return version;
+ }
+
+@@ -14422,14 +15388,12 @@ namespace Catch {
+ // 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 );
+@@ -14444,28 +15408,27 @@ namespace Catch {
+ bool WildcardPattern::matches( std::string const& str ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+- return m_pattern == adjustCase( str );
++ return m_pattern == normaliseString( str );
+ case WildcardAtStart:
+- return endsWith( adjustCase( str ), m_pattern );
++ return endsWith( normaliseString( str ), m_pattern );
+ case WildcardAtEnd:
+- return startsWith( adjustCase( str ), m_pattern );
++ return startsWith( normaliseString( str ), m_pattern );
+ case WildcardAtBothEnds:
+- return contains( adjustCase( str ), m_pattern );
++ return contains( normaliseString( str ), m_pattern );
+ default:
+ CATCH_INTERNAL_ERROR( "Unknown enum" );
+ }
+ }
+
+- std::string WildcardPattern::adjustCase( std::string const& str ) const {
+- return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
++ std::string WildcardPattern::normaliseString( std::string const& str ) const {
++ return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
+ }
+ }
+ // end catch_wildcard_pattern.cpp
+ // start catch_xmlwriter.cpp
+
+ #include <iomanip>
+-
+-using uchar = unsigned char;
++#include <type_traits>
+
+ namespace Catch {
+
+@@ -14505,8 +15468,30 @@ namespace {
+ 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 )
+@@ -14517,7 +15502,7 @@ namespace {
+ // (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;
+@@ -14577,7 +15562,7 @@ namespace {
+ 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);
+ }
+@@ -14611,13 +15596,17 @@ namespace {
+ 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 ) {
+@@ -14625,16 +15614,19 @@ namespace {
+ }
+ 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;
+ }
+
+@@ -14644,37 +15636,47 @@ namespace {
+ }
+
+ 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;
+ }
+@@ -14690,22 +15692,26 @@ namespace {
+ 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;
+ }
+
+@@ -14721,11 +15727,16 @@ namespace {
+
+ 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";
+ }
+@@ -14771,6 +15782,17 @@ namespace Catch {
+ 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;
+@@ -15001,24 +16023,25 @@ private:
+ 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;
+ }
+ }
+
+@@ -15036,10 +16059,6 @@ private:
+ 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;
+ }
+@@ -15066,8 +16085,9 @@ private:
+ }
+
+ 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;
+ }
+ }
+
+@@ -15279,15 +16299,11 @@ class Duration {
+ 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(double inNanoseconds, Unit units = Unit::Auto)
+- : Duration(static_cast<uint64_t>(inNanoseconds), units) {
+- }
+-
+- 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) {
+@@ -15316,7 +16332,7 @@ public:
+ 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 {
+@@ -15337,7 +16353,7 @@ public:
+
+ }
+ 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
+@@ -15369,9 +16385,9 @@ public:
+ headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
+ headerCols += spacer;
+ }
+- m_os << headerCols << "\n";
++ m_os << headerCols << '\n';
+
+- m_os << Catch::getLineOfChars<'-'>() << "\n";
++ m_os << Catch::getLineOfChars<'-'>() << '\n';
+ }
+ }
+ void close() {
+@@ -15390,30 +16406,29 @@ public:
+
+ 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;
+@@ -15423,12 +16438,26 @@ public:
+ ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
+ : StreamingReporterBase(config),
+ m_tablePrinter(new TablePrinter(config.stream(),
+- {
+- { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, 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 }
+- })) {}
++ [&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() {
+@@ -15439,6 +16468,10 @@ void ConsoleReporter::noMatchingTestCase
+ 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) {
+@@ -15474,8 +16507,9 @@ void ConsoleReporter::sectionEnded(Secti
+ 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;
+@@ -15501,24 +16535,32 @@ void ConsoleReporter::benchmarkPreparing
+ }
+
+ void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
+- (*m_tablePrinter) << info.samples << ColumnBreak()
+- << info.iterations << ColumnBreak()
+- << Duration(info.estimatedDuration) << ColumnBreak();
++ (*m_tablePrinter) << info.samples << ColumnBreak()
++ << info.iterations << ColumnBreak();
++ if (!m_config->benchmarkNoAnalysis())
++ (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
+ }
+ void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
+- (*m_tablePrinter) << ColumnBreak()
+- << Duration(stats.mean.point.count()) << ColumnBreak()
+- << Duration(stats.mean.lower_bound.count()) << ColumnBreak()
+- << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
+- << Duration(stats.standardDeviation.point.count()) << ColumnBreak()
+- << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
+- << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
++ if (m_config->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::benchmarkFailed(std::string const& error) {
+ Colour colour(Colour::Red);
+ (*m_tablePrinter)
+- << "Benchmark failed (" << error << ")"
++ << "Benchmark failed (" << error << ')'
+ << ColumnBreak() << RowBreak();
+ }
+ #endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+@@ -15600,11 +16642,9 @@ void ConsoleReporter::printTestCaseAndSe
+
+ 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;
+ }
+
+@@ -15729,8 +16769,10 @@ void ConsoleReporter::printSummaryDivide
+ }
+
+ void ConsoleReporter::printTestFilters() {
+- if (m_config->testSpec().hasFilters())
+- stream << Colour(Colour::BrightYellow) << "Filters: " << serializeFilters( m_config->getTestsOrTags() ) << '\n';
++ if (m_config->testSpec().hasFilters()) {
++ Colour guard(Colour::BrightYellow);
++ stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
++ }
+ }
+
+ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
+@@ -15751,6 +16793,7 @@ CATCH_REGISTER_REPORTER("console", Conso
+ #include <sstream>
+ #include <ctime>
+ #include <algorithm>
++#include <iomanip>
+
+ namespace Catch {
+
+@@ -15778,7 +16821,7 @@ namespace Catch {
+ #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) {
+@@ -15789,6 +16832,17 @@ namespace Catch {
+ 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 )
+@@ -15858,7 +16912,7 @@ namespace Catch {
+ 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
+@@ -15880,8 +16934,8 @@ namespace Catch {
+ 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 ) {
+@@ -15903,12 +16957,13 @@ namespace Catch {
+ 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;
+@@ -15925,20 +16980,30 @@ namespace Catch {
+ 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 ) {
+@@ -15956,11 +17021,7 @@ namespace Catch {
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+- elementName = "failure";
+- break;
+ case ResultWas::ExpressionFailed:
+- elementName = "failure";
+- break;
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+@@ -15978,10 +17039,25 @@ namespace Catch {
+
+ 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 )
+@@ -15989,7 +17065,7 @@ namespace Catch {
+ rss << msg.message << '\n';
+
+ rss << "at " << result.getSourceInfo();
+- xml.writeText( rss.str(), false );
++ xml.writeText( rss.str(), XmlFormatting::Newline );
+ }
+ }
+
+@@ -16033,6 +17109,13 @@ namespace Catch {
+ 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) {
+@@ -16328,9 +17411,9 @@ namespace Catch {
+ 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();
+ }
+@@ -16342,6 +17425,10 @@ namespace Catch {
+ .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();
+ }
+
+@@ -16351,26 +17438,33 @@ namespace Catch {
+ .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::benchmarkStarting(BenchmarkInfo const &info) {
++ void XmlReporter::benchmarkPreparing(std::string const& name) {
+ m_xml.startElement("BenchmarkResults")
+- .writeAttribute("name", info.name)
+- .writeAttribute("samples", info.samples)
++ .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", static_cast<uint64_t>(info.clockResolution))
+- .writeAttribute("estimatedDuration", static_cast<uint64_t>(info.estimatedDuration))
++ .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", static_cast<uint64_t>(benchmarkStats.mean.point.count()))
+- .writeAttribute("lowerBound", static_cast<uint64_t>(benchmarkStats.mean.lower_bound.count()))
+- .writeAttribute("upperBound", static_cast<uint64_t>(benchmarkStats.mean.upper_bound.count()))
++ .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")
+@@ -16421,7 +17515,7 @@ namespace Catch {
+
+ #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
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..29aa4b2
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,14 @@
+#!/usr/bin/make -f
+
+%:
+ dh $@ --buildsystem=cmake
+
+override_dh_auto_configure:
+ dh_auto_configure --buildsystem=cmake -- \
+ -Dzug_BUILD_EXAMPLES=OFF \
+ -Dzug_BUILD_DOCS=OFF
+
+ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
+execute_after_dh_auto_build:
+ dh_auto_build -- tests
+endif
diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml
new file mode 100644
index 0000000..31e31c9
--- /dev/null
+++ b/debian/salsa-ci.yml
@@ -0,0 +1,6 @@
+include:
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+variables:
+ SALSA_CI_DISABLE_MISSING_BREAKS: 'no'
+ SALSA_CI_DISABLE_RC_BUGS: 'no'
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/upstream/metadata b/debian/upstream/metadata
new file mode 100644
index 0000000..464c681
--- /dev/null
+++ b/debian/upstream/metadata
@@ -0,0 +1,5 @@
+---
+Bug-Database: https://github.com/arximboldi/zug/issues
+Bug-Submit: https://github.com/arximboldi/zug/issues/new
+Repository: https://github.com/arximboldi/zug.git
+Repository-Browse: https://github.com/arximboldi/zug
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..6354648
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,5 @@
+version=4
+
+opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%@PACKAGE at -$1.tar.gz%" \
+https://github.com/arximboldi/zug/tags \
+(?:.*?/)?v?(\d[\d.]*)\.tar\.gz
More information about the Neon-commits
mailing list