summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNilesh Patra <nilesh@debian.org>2022-04-14 20:28:47 +0200
committerNilesh Patra <nilesh@debian.org>2022-04-14 20:28:47 +0200
commit9ab78b5e3fc5ebc4c8e658ae05ecde18966d1a10 (patch)
tree2589a1710df571064a3a654e3aaaf7d937831a7b
Import libvbz-hdf-plugin_1.0.1-3.debian.tar.xz
[dgit import tarball libvbz-hdf-plugin 1.0.1-3 libvbz-hdf-plugin_1.0.1-3.debian.tar.xz]
-rw-r--r--changelog26
-rw-r--r--control55
-rw-r--r--copyright254
-rw-r--r--libvbz-hdf-plugin-dev.install4
-rw-r--r--libvbz-hdf-plugin0.install1
-rw-r--r--patches/fix-install-location.patch15
-rw-r--r--patches/series4
-rw-r--r--patches/soversion.patch13
-rw-r--r--patches/update-catch2.patch9997
-rw-r--r--patches/use-packaged-streamvbyte.patch79
-rwxr-xr-xrules24
-rw-r--r--salsa-ci.yml7
-rw-r--r--source/format1
-rw-r--r--upstream/metadata7
-rw-r--r--watch4
15 files changed, 10491 insertions, 0 deletions
diff --git a/changelog b/changelog
new file mode 100644
index 0000000..c810268
--- /dev/null
+++ b/changelog
@@ -0,0 +1,26 @@
+libvbz-hdf-plugin (1.0.1-3) unstable; urgency=medium
+
+ [ Steve Langasek ]
+ * Import catch2 version 2.13.8 to fix build failure with glibc 2.34
+ Closes: #1009620
+
+ [ Andreas Tille ]
+ * Fix watchfile to detect new versions on github
+ * Standards-Version: 4.6.0 (routine-update)
+
+ -- Nilesh Patra <nilesh@debian.org> Thu, 14 Apr 2022 23:58:47 +0530
+
+libvbz-hdf-plugin (1.0.1-2) unstable; urgency=medium
+
+ * Use DEB_HOST_GNU type for every arch since
+ $(DEB_HOST_MULTIARCH) does not match the default
+ build directory used by debhelper
+ * d/salsa-ci.yml: disable reprotest for now
+
+ -- Nilesh Patra <nilesh@debian.org> Fri, 20 Aug 2021 00:23:50 +0530
+
+libvbz-hdf-plugin (1.0.1-1) unstable; urgency=medium
+
+ * Initial release (Closes: #988993)
+
+ -- Andreas Tille <tille@debian.org> Sun, 23 May 2021 10:28:06 +0200
diff --git a/control b/control
new file mode 100644
index 0000000..bdb016d
--- /dev/null
+++ b/control
@@ -0,0 +1,55 @@
+Source: libvbz-hdf-plugin
+Maintainer: Debian Med Packaging Team <debian-med-packaging@lists.alioth.debian.org>
+Uploaders: Andreas Tille <tille@debian.org>, Nilesh Patra <nilesh@debian.org>
+Section: science
+Priority: optional
+Build-Depends: debhelper-compat (= 13),
+# d-shlibs, # not possible since no static lib
+ cmake,
+ libzstd-dev,
+ libhdf5-dev,
+ libstreamvbyte-dev,
+ dh-exec
+Standards-Version: 4.6.0
+Vcs-Browser: https://salsa.debian.org/med-team/libvbz-hdf-plugin
+Vcs-Git: https://salsa.debian.org/med-team/libvbz-hdf-plugin.git
+Homepage: https://github.com/nanoporetech/vbz_compression/
+Rules-Requires-Root: no
+
+Package: libvbz-hdf-plugin0
+Architecture: any
+Section: libs
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Multi-Arch: same
+Description: VBZ compression plugin for nanopore signal data
+ VBZ Compression uses variable byte integer encoding to compress nanopore
+ signal data.
+ .
+ The performance of VBZ is achieved by taking advantage of the properties
+ of the raw signal and therefore is most effective when applied to the
+ signal dataset. Other datasets you may have in your Fast5 files will not
+ be able to take advantage of the default VBZ settings for compression.
+ VBZ will be used as the default compression scheme in a future release
+ of MinKNOW.
+ .
+ This package contains the shared object library.
+
+Package: libvbz-hdf-plugin-dev
+Architecture: any
+Section: libdevel
+Depends: libvbz-hdf-plugin0 (= ${binary:Version}),
+ ${shlibs:Depends},
+ ${misc:Depends}
+Description: VBZ compression plugin for nanopore signal data (devel)
+ VBZ Compression uses variable byte integer encoding to compress nanopore
+ signal data.
+ .
+ The performance of VBZ is achieved by taking advantage of the properties
+ of the raw signal and therefore is most effective when applied to the
+ signal dataset. Other datasets you may have in your Fast5 files will not
+ be able to take advantage of the default VBZ settings for compression.
+ VBZ will be used as the default compression scheme in a future release
+ of MinKNOW.
+ .
+ This package contains the header files.
diff --git a/copyright b/copyright
new file mode 100644
index 0000000..5ffeaba
--- /dev/null
+++ b/copyright
@@ -0,0 +1,254 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: vbz-compression
+Source: https://github.com/nanoporetech/vbz_compression/releases
+
+Files: *
+Copyright: Oxford Nanopore Technologies
+License: MPL-2.0
+
+Files: third_party/gsl/*
+ third_party/gsl.h
+Copyright: 2015, Martin Moene
+ 2015, Microsoft Corporation.
+License: Expat
+
+Files: third_party/catch2/catch.hpp
+Copyright: 2018, Two Blue Cubes Ltd.
+License: Boost-1.0
+
+Files: third_party/catch2/fakeit.hpp
+Copyright: Eran Pe'er 2013
+License: Expat
+
+Files: third_party/hdf5/*
+Copyright: HDF Group <help@hdfgroup.org>
+License: BSD-3-clause
+Comment: Same copyright is also mentioned in src:hdf5
+
+Files: cmake/asan-wrapper
+ cmake/FindASan.cmake
+ cmake/FindMSan.cmake
+ cmake/FindSanitizers.cmake
+ cmake/FindTSan.cmake
+ cmake/FindUBSan.cmake
+ cmake/sanitize-helpers.cmake
+Copyright: 2013, Matthew Arsenault
+ 2015-2016 RWTH Aachen University, Federal Republic of Germany
+License: Expat
+
+Files: cmake/conan.cmake
+Copyright: 2018 JFrog
+License: Expat
+
+Files: debian/*
+Copyright: 2020 Andreas Tille <tille@debian.org>
+ 2021 Nilesh Patra <nilesh@debian.org>
+License: MPL-2.0
+
+License: MPL-2.0
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ .
+ On Debian systems, the complete text of the MPL-2.0 license can be found
+ in "/usr/share/common-licenses/MPL-2.0".
+
+License: Expat
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+ .
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ .
+ 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 AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+License: Boost-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.
+
+License: BSD-3-clause
+ Copyright Notice and License Terms for
+ HDF5 (Hierarchical Data Format 5) Software Library and Utilities
+ -----------------------------------------------------------------------------
+ .
+ HDF5 (Hierarchical Data Format 5) Software Library and Utilities
+ Copyright (c) 2006-2018, The HDF Group.
+ .
+ NCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities
+ Copyright (c) 1998-2006, The Board of Trustees of the University of Illinois.
+ .
+ All rights reserved.
+ .
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted for any purpose (including commercial purposes)
+ provided that the following conditions are met:
+ .
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions, and the following disclaimer.
+ .
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions, and the following disclaimer in the documentation
+ and/or materials provided with the distribution.
+ .
+ 3. Neither the name of The HDF Group, the name of the University, nor the
+ name of any Contributor may be used to endorse or promote products derived
+ from this software without specific prior written permission from
+ The HDF Group, the University, or the Contributor, respectively.
+ .
+ DISCLAIMER:
+ THIS SOFTWARE IS PROVIDED BY THE HDF GROUP AND THE CONTRIBUTORS
+ "AS IS" WITH NO WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. IN NO EVENT
+ SHALL THE HDF GROUP OR THE CONTRIBUTORS BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ THE USERS ARISING OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ .
+ You are under no obligation whatsoever to provide any bug fixes, patches, or
+ upgrades to the features, functionality or performance of the source code
+ ("Enhancements") to anyone; however, if you choose to make your Enhancements
+ available either publicly, or directly to The HDF Group, without imposing a
+ separate written license agreement for such Enhancements, then you hereby grant
+ the following license: a non-exclusive, royalty-free perpetual license to
+ install, use, modify, prepare derivative works, incorporate into other computer
+ software, distribute, and sublicense such enhancements or derivative works
+ thereof, in binary and source code form.
+ .
+ -----------------------------------------------------------------------------
+ -----------------------------------------------------------------------------
+ .
+ Limited portions of HDF5 were developed by Lawrence Berkeley National
+ Laboratory (LBNL). LBNL's Copyright Notice and Licensing Terms can be
+ found here: COPYING_LBNL_HDF5 file in this directory or at
+ http://support.hdfgroup.org/ftp/HDF5/releases/COPYING_LBNL_HDF5.
+ .
+ -----------------------------------------------------------------------------
+ -----------------------------------------------------------------------------
+ .
+ Contributors: National Center for Supercomputing Applications (NCSA) at
+ the University of Illinois, Fortner Software, Unidata Program Center (netCDF),
+ The Independent JPEG Group (JPEG), Jean-loup Gailly and Mark Adler (gzip), and
+ Digital Equipment Corporation (DEC).
+ .
+ -----------------------------------------------------------------------------
+ .
+ Portions of HDF5 were developed with support from the Lawrence Berkeley
+ National Laboratory (LBNL) and the United States Department of Energy
+ under Prime Contract No. DE-AC02-05CH11231.
+ .
+ -----------------------------------------------------------------------------
+ .
+ Portions of HDF5 were developed with support from the University of
+ California, Lawrence Livermore National Laboratory (UC LLNL).
+ The following statement applies to those portions of the product and must
+ be retained in any redistribution of source code, binaries, documentation,
+ and/or accompanying materials:
+ .
+ This work was partially produced at the University of California,
+ Lawrence Livermore National Laboratory (UC LLNL) under contract
+ no. W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy
+ (DOE) and The Regents of the University of California (University)
+ for the operation of UC LLNL.
+ .
+ DISCLAIMER:
+ This work was prepared as an account of work sponsored by an agency of
+ the United States Government. Neither the United States Government nor
+ the University of California nor any of their employees, makes any
+ warranty, express or implied, or assumes any liability or responsibility
+ for the accuracy, completeness, or usefulness of any information,
+ apparatus, product, or process disclosed, or represents that its use
+ would not infringe privately- owned rights. Reference herein to any
+ specific commercial products, process, or service by trade name,
+ trademark, manufacturer, or otherwise, does not necessarily constitute
+ or imply its endorsement, recommendation, or favoring by the United
+ States Government or the University of California. The views and
+ opinions of authors expressed herein do not necessarily state or reflect
+ those of the United States Government or the University of California,
+ and shall not be used for advertising or product endorsement purposes.
+ .
+ -----------------------------------------------------------------------------
+ .
+ HDF5 is available with the SZIP compression library but SZIP is not part
+ of HDF5 and has separate copyright and license terms. See SZIP Compression
+ in HDF Products (www.hdfgroup.org/doc_resource/SZIP/) for further details.
+ .
+ -----------------------------------------------------------------------------
+ .
+ Copyright Notice and License Terms for
+ HDF5 (Hierarchical Data Format 5) Software Library and Utilities
+ -----------------------------------------------------------------------------
+ .
+ HDF5 (Hierarchical Data Format 5)
+ Copyright (c) 2016, The Regents of the University of California, through
+ Lawrence Berkeley National Laboratory (subject to receipt of any required
+ approvals from the U.S. Dept. of Energy).
+ .
+ All rights reserved.
+ .
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ .
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions, and the following disclaimer.
+ .
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions, and the following disclaimer in the documentation
+ and/or materials provided with the distribution.
+ .
+ 3. Neither the name of the University of California, Lawrence Berkeley
+ National Laboratory, U.S. Dept. of Energy nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ THE POSSIBILITY OF SUCH DAMAGE.
+ .
+ You are under no obligation whatsoever to provide any bug fixes, patches,
+ or upgrades to the features, functionality or performance of the source
+ code ("Enhancements") to anyone; however, if you choose to make your
+ Enhancements available either publicly, or directly to Lawrence Berkeley
+ National Laboratory, without imposing a separate written license agreement
+ for such Enhancements, then you hereby grant the following license:
+ a non-exclusive, royalty-free perpetual license to install, use, modify,
+ prepare derivative works, incorporate into other computer software,
+ distribute, and sublicense such enhancements or derivative works thereof,
+ in binary and source code form.
diff --git a/libvbz-hdf-plugin-dev.install b/libvbz-hdf-plugin-dev.install
new file mode 100644
index 0000000..6b92c5b
--- /dev/null
+++ b/libvbz-hdf-plugin-dev.install
@@ -0,0 +1,4 @@
+#!/usr/bin/dh-exec
+
+usr/lib/*/lib*.so
+vbz_plugin/*.h vbz/*.h vbz/v0/*.h obj-${DEB_HOST_GNU_TYPE}/*/*/*.h usr/include/vbz-compression
diff --git a/libvbz-hdf-plugin0.install b/libvbz-hdf-plugin0.install
new file mode 100644
index 0000000..3ddde58
--- /dev/null
+++ b/libvbz-hdf-plugin0.install
@@ -0,0 +1 @@
+usr/lib/*/lib*.so.*
diff --git a/patches/fix-install-location.patch b/patches/fix-install-location.patch
new file mode 100644
index 0000000..e6a3177
--- /dev/null
+++ b/patches/fix-install-location.patch
@@ -0,0 +1,15 @@
+Description: Fix install location for .so
+Author: Nilesh Patra <nilesh@debian.org>
+Forwarded: not-needed
+Last-Update: 2021-05-22
+--- a/vbz_plugin/CMakeLists.txt
++++ b/vbz_plugin/CMakeLists.txt
+@@ -62,7 +62,7 @@
+ set(VBZ_PLUGIN_COMPONENT COMPONENT hdf_plugin)
+ endif()
+
+-set(INSTALL_DESTINATION "hdf5/lib/plugin")
++set(INSTALL_DESTINATION "lib/$ENV{DEB_HOST_MULTIARCH}")
+ if (STANDARD_LIB_INSTALL)
+ set(VBZ_PLUGIN_COMPONENT COMPONENT vbz_hdf_plugin)
+ set_target_properties(vbz_hdf_plugin
diff --git a/patches/series b/patches/series
new file mode 100644
index 0000000..2d66ccd
--- /dev/null
+++ b/patches/series
@@ -0,0 +1,4 @@
+use-packaged-streamvbyte.patch
+fix-install-location.patch
+soversion.patch
+update-catch2.patch
diff --git a/patches/soversion.patch b/patches/soversion.patch
new file mode 100644
index 0000000..97f9721
--- /dev/null
+++ b/patches/soversion.patch
@@ -0,0 +1,13 @@
+Description: Add a SOVERSION
+Author: Nilesh Patra <nilesh@debian.org>
+Last-Update: 2021-05-22
+--- a/vbz_plugin/CMakeLists.txt
++++ b/vbz_plugin/CMakeLists.txt
+@@ -4,6 +4,7 @@
+ vbz_plugin.cpp
+ vbz_plugin.h
+ )
++set_target_properties(vbz_hdf_plugin PROPERTIES SOVERSION 0)
+ add_sanitizers(vbz_hdf_plugin)
+
+ generate_export_header(vbz_hdf_plugin EXPORT_FILE_NAME vbz_plugin/vbz_hdf_plugin_export.h)
diff --git a/patches/update-catch2.patch b/patches/update-catch2.patch
new file mode 100644
index 0000000..072fc69
--- /dev/null
+++ b/patches/update-catch2.patch
@@ -0,0 +1,9997 @@
+Description: import catch2 version 2.13.8 to fix build failure with glibc 2.34
+Author: Steve Langasek <steve.langasek@ubuntu.com>
+Last-Update: 2022-04-12
+Forwarded: no
+Bug-Debian: https://bugs.debian.org/1009620
+
+Index: libvbz-hdf-plugin-1.0.1/third_party/catch2/catch.hpp
+===================================================================
+--- libvbz-hdf-plugin-1.0.1.orig/third_party/catch2/catch.hpp
++++ libvbz-hdf-plugin-1.0.1/third_party/catch2/catch.hpp
+@@ -1,9 +1,9 @@
+ /*
+- * Catch v2.5.0
+- * Generated: 2018-11-26 20:46:12.165372
++ * Catch v2.13.8
++ * Generated: 2022-01-03 21:20:09.589503
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+- * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
++ * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+@@ -14,8 +14,8 @@
+
+
+ #define CATCH_VERSION_MAJOR 2
+-#define CATCH_VERSION_MINOR 5
+-#define CATCH_VERSION_PATCH 0
++#define CATCH_VERSION_MINOR 13
++#define CATCH_VERSION_PATCH 8
+
+ #ifdef __clang__
+ # pragma clang system_header
+@@ -36,10 +36,11 @@
+ # pragma clang diagnostic ignored "-Wcovered-switch-default"
+ # endif
+ #elif defined __GNUC__
+- // GCC likes to warn on REQUIREs, and we cannot suppress them
+- // locally because g++'s support for _Pragma is lacking in older,
+- // still supported, versions
+-# pragma GCC diagnostic ignored "-Wparentheses"
++ // Because REQUIREs trigger GCC's -Wparentheses, and because still
++ // supported version of g++ have only buggy support for _Pragmas,
++ // Wparentheses have to be suppressed globally.
++# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
++
+ # pragma GCC diagnostic push
+ # pragma GCC diagnostic ignored "-Wunused-variable"
+ # pragma GCC diagnostic ignored "-Wpadded"
+@@ -65,13 +66,16 @@
+ #if !defined(CATCH_CONFIG_IMPL_ONLY)
+ // start catch_platform.h
+
++// See e.g.:
++// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
+ #ifdef __APPLE__
+-# include <TargetConditionals.h>
+-# if TARGET_OS_OSX == 1
+-# define CATCH_PLATFORM_MAC
+-# elif TARGET_OS_IPHONE == 1
+-# define CATCH_PLATFORM_IPHONE
+-# endif
++# include <TargetConditionals.h>
++# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
++ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
++# define CATCH_PLATFORM_MAC
++# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
++# define CATCH_PLATFORM_IPHONE
++# endif
+
+ #elif defined(linux) || defined(__linux) || defined(__linux__)
+ # define CATCH_PLATFORM_LINUX
+@@ -131,30 +135,51 @@
+
+ #endif
+
+-#if defined(CATCH_CPP17_OR_GREATER)
+-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++// Only GCC compiler should be used in this block, so other compilers trying to
++// mask themselves as GCC should be ignored.
++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
++
++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
++
+ #endif
+
+-#ifdef __clang__
++#if defined(__clang__)
+
+-# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+- _Pragma( "clang diagnostic push" ) \
+- _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+- _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+-# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+- _Pragma( "clang diagnostic pop" )
+-
+-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+- _Pragma( "clang diagnostic push" ) \
+- _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+-# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+- _Pragma( "clang diagnostic pop" )
+-
+-# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+- _Pragma( "clang diagnostic push" ) \
+- _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+-# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
+- _Pragma( "clang diagnostic pop" )
++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
++
++// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
++// which results in calls to destructors being emitted for each temporary,
++// without a matching initialization. In practice, this can result in something
++// like `std::string::~string` being called on an uninitialized value.
++//
++// For example, this code will likely segfault under IBM XL:
++// ```
++// REQUIRE(std::string("12") + "34" == "1234")
++// ```
++//
++// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
++# if !defined(__ibmxl__) && !defined(__CUDACC__)
++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
++# endif
++
++# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
++ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
++
++# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
++ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
++
++# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
++ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
++
++# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
++
++# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
+
+ #endif // __clang__
+
+@@ -179,6 +204,7 @@
+ // Android somehow still does not support std::to_string
+ #if defined(__ANDROID__)
+ # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
++# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+ #endif
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -203,20 +229,16 @@
+ // some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+ // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+ # if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+- && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
++ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+
+-# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
++# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+
+ # endif
+ #endif // __CYGWIN__
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Visual C++
+-#ifdef _MSC_VER
+-
+-# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+-# endif
++#if defined(_MSC_VER)
+
+ // Universal Windows platform does not support SEH
+ // Or console colours (or console at all...)
+@@ -226,13 +248,25 @@
+ # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+ # endif
+
++# if !defined(__clang__) // Handle Clang masquerading for msvc
++
+ // MSVC traditional preprocessor needs some workaround for __VA_ARGS__
+ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor
+ // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
+-# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+-# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-# endif
++# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
++# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++# endif // MSVC_TRADITIONAL
++
++// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`
++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
++# endif // __clang__
++
++#endif // _MSC_VER
+
++#if defined(_REENTRANT) || defined(_MSC_VER)
++// Enable async processing, as -pthread is specified or no additional linking is required
++# define CATCH_INTERNAL_CONFIG_USE_ASYNC
+ #endif // _MSC_VER
+
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -265,30 +299,56 @@
+ #endif
+
+ ////////////////////////////////////////////////////////////////////////////////
+-// Check if string_view is available and usable
+-// The check is split apart to work around v140 (VS2015) preprocessor issue...
+-#if defined(__has_include)
+-#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
+-# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
++
++// RTX is a special version of Windows that is real time.
++// This means that it is detected as Windows, but does not provide
++// the same set of capabilities as real Windows does.
++#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
++ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
++ #define CATCH_INTERNAL_CONFIG_NO_ASYNC
++ #define CATCH_CONFIG_COLOUR_NONE
+ #endif
++
++#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
++#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+ #endif
+
+-////////////////////////////////////////////////////////////////////////////////
+-// Check if variant is available and usable
++// Various stdlib support checks that require __has_include
+ #if defined(__has_include)
+-# if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+-# if defined(__clang__) && (__clang_major__ < 8)
+- // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+- // fix should be in clang 8, workaround in libstdc++ 8.2
+-# include <ciso646>
+-# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+-# define CATCH_CONFIG_NO_CPP17_VARIANT
+-# else
+-# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+-# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+-# endif // defined(__clang__) && (__clang_major__ < 8)
+-# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+-#endif // __has_include
++ // Check if string_view is available and usable
++ #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
++ # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
++ #endif
++
++ // Check if optional is available and usable
++ # if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
++ # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
++ # endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
++
++ // Check if byte is available and usable
++ # if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
++ # include <cstddef>
++ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
++ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE
++ # endif
++ # endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
++
++ // Check if variant is available and usable
++ # if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
++ # if defined(__clang__) && (__clang_major__ < 8)
++ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
++ // fix should be in clang 8, workaround in libstdc++ 8.2
++ # include <ciso646>
++ # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
++ # define CATCH_CONFIG_NO_CPP17_VARIANT
++ # else
++ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
++ # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
++ # else
++ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
++ # endif // defined(__clang__) && (__clang_major__ < 8)
++ # endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
++#endif // defined(__has_include)
+
+ #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+ # define CATCH_CONFIG_COUNTER
+@@ -309,8 +369,8 @@
+ # define CATCH_CONFIG_CPP11_TO_STRING
+ #endif
+
+-#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+-# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
++# define CATCH_CONFIG_CPP17_OPTIONAL
+ #endif
+
+ #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+@@ -321,6 +381,10 @@
+ # define CATCH_CONFIG_CPP17_VARIANT
+ #endif
+
++#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
++# define CATCH_CONFIG_CPP17_BYTE
++#endif
++
+ #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+ # define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+ #endif
+@@ -337,17 +401,53 @@
+ # define CATCH_CONFIG_POLYFILL_ISNAN
+ #endif
+
++#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
++# define CATCH_CONFIG_USE_ASYNC
++#endif
++
++#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
++# define CATCH_CONFIG_ANDROID_LOGWRITE
++#endif
++
++#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
++# define CATCH_CONFIG_GLOBAL_NEXTAFTER
++#endif
++
++// Even if we do not think the compiler has that warning, we still have
++// to provide a macro that can be used by the code.
++#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
++# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
++#endif
++#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
++# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
++#endif
+ #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+ # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+-# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+ #endif
+ #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+ # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+-# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ #endif
+ #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+ # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
+-# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
++#endif
++#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
++# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
++#endif
++
++// The goal of this macro is to avoid evaluation of the arguments, but
++// still have the compiler warn on problems inside...
++#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
++# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
++#endif
++
++#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
++# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
++#elif defined(__clang__) && (__clang_major__ < 5)
++# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
++#endif
++
++#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
++# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+ #endif
+
+ #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+@@ -407,12 +507,12 @@
+ line( _line )
+ {}
+
+- SourceLineInfo( SourceLineInfo const& other ) = default;
+- SourceLineInfo( SourceLineInfo && ) = default;
+- SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+- SourceLineInfo& operator = ( SourceLineInfo && ) = default;
++ SourceLineInfo( SourceLineInfo const& other ) = default;
++ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
++ SourceLineInfo( SourceLineInfo&& ) noexcept = default;
++ SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
+
+- bool empty() const noexcept;
++ bool empty() const noexcept { return file[0] == '\0'; }
+ bool operator == ( SourceLineInfo const& other ) const noexcept;
+ bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+@@ -453,9 +553,10 @@
+ } // end namespace Catch
+
+ #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ // end catch_tag_alias_autoregistrar.h
+ // start catch_test_registry.h
+@@ -463,7 +564,6 @@
+ // start catch_interfaces_testcase.h
+
+ #include <vector>
+-#include <memory>
+
+ namespace Catch {
+
+@@ -474,8 +574,6 @@
+ virtual ~ITestInvoker();
+ };
+
+- using ITestCasePtr = std::shared_ptr<ITestInvoker>;
+-
+ class TestCase;
+ struct IConfig;
+
+@@ -485,6 +583,7 @@
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
++ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+@@ -497,55 +596,30 @@
+ #include <cstddef>
+ #include <string>
+ #include <iosfwd>
++#include <cassert>
+
+ namespace Catch {
+
+- class StringData;
+-
+ /// A non-owning string class (similar to the forthcoming std::string_view)
+ /// Note that, because a StringRef may be a substring of another string,
+- /// it may not be null terminated. c_str() must return a null terminated
+- /// string, however, and so the StringRef will internally take ownership
+- /// (taking a copy), if necessary. In theory this ownership is not externally
+- /// visible - but it does mean (substring) StringRefs should not be shared between
+- /// threads.
++ /// it may not be null terminated.
+ class StringRef {
+ public:
+ using size_type = std::size_t;
++ using const_iterator = const char*;
+
+ private:
+- friend struct StringRefTestAccess;
+-
+- char const* m_start;
+- size_type m_size;
+-
+- char* m_data = nullptr;
+-
+- void takeOwnership();
+-
+ static constexpr char const* const s_empty = "";
+
+- public: // construction/ assignment
+- StringRef() noexcept
+- : StringRef( s_empty, 0 )
+- {}
++ char const* m_start = s_empty;
++ size_type m_size = 0;
+
+- StringRef( StringRef const& other ) noexcept
+- : m_start( other.m_start ),
+- m_size( other.m_size )
+- {}
+-
+- StringRef( StringRef&& other ) noexcept
+- : m_start( other.m_start ),
+- m_size( other.m_size ),
+- m_data( other.m_data )
+- {
+- other.m_data = nullptr;
+- }
++ public: // construction
++ constexpr StringRef() noexcept = default;
+
+ StringRef( char const* rawChars ) noexcept;
+
+- StringRef( char const* rawChars, size_type size ) noexcept
++ constexpr StringRef( char const* rawChars, size_type size ) noexcept
+ : m_start( rawChars ),
+ m_size( size )
+ {}
+@@ -555,99 +629,64 @@
+ m_size( stdString.size() )
+ {}
+
+- ~StringRef() noexcept {
+- delete[] m_data;
+- }
+-
+- auto operator = ( StringRef const &other ) noexcept -> StringRef& {
+- delete[] m_data;
+- m_data = nullptr;
+- m_start = other.m_start;
+- m_size = other.m_size;
+- return *this;
++ explicit operator std::string() const {
++ return std::string(m_start, m_size);
+ }
+
+- operator std::string() const;
+-
+- void swap( StringRef& other ) noexcept;
+-
+ public: // operators
+ auto operator == ( StringRef const& other ) const noexcept -> bool;
+- auto operator != ( StringRef const& other ) const noexcept -> bool;
++ auto operator != (StringRef const& other) const noexcept -> bool {
++ return !(*this == other);
++ }
+
+- auto operator[] ( size_type index ) const noexcept -> char;
++ auto operator[] ( size_type index ) const noexcept -> char {
++ assert(index < m_size);
++ return m_start[index];
++ }
+
+ public: // named queries
+- auto empty() const noexcept -> bool {
++ constexpr auto empty() const noexcept -> bool {
+ return m_size == 0;
+ }
+- auto size() const noexcept -> size_type {
++ constexpr auto size() const noexcept -> size_type {
+ return m_size;
+ }
+
+- auto numberOfCharacters() const noexcept -> size_type;
++ // Returns the current start pointer. If the StringRef is not
++ // null-terminated, throws std::domain_exception
+ auto c_str() const -> char const*;
+
+ public: // substrings and searches
+- auto substr( size_type start, size_type size ) const noexcept -> StringRef;
++ // Returns a substring of [start, start + length).
++ // If start + length > size(), then the substring is [start, size()).
++ // If start > size(), then the substring is empty.
++ auto substr( size_type start, size_type length ) const noexcept -> StringRef;
+
+- // Returns the current start pointer.
+- // Note that the pointer can change when if the StringRef is a substring
+- auto currentData() const noexcept -> char const*;
++ // Returns the current start pointer. May not be null-terminated.
++ auto data() const noexcept -> char const*;
+
+- private: // ownership queries - may not be consistent between calls
+- auto isOwned() const noexcept -> bool;
+- auto isSubstring() const noexcept -> bool;
+- };
++ constexpr auto isNullTerminated() const noexcept -> bool {
++ return m_start[m_size] == '\0';
++ }
+
+- auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
+- auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
+- auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
++ public: // iterators
++ constexpr const_iterator begin() const { return m_start; }
++ constexpr const_iterator end() const { return m_start + m_size; }
++ };
+
+ auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+ auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+
+- inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
++ constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ return StringRef( rawChars, size );
+ }
+-
+ } // namespace Catch
+
+-inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
++constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+ return Catch::StringRef( rawChars, size );
+ }
+
+ // end catch_stringref.h
+-// start catch_type_traits.hpp
+-
+-
+-namespace Catch{
+-
+-#ifdef CATCH_CPP17_OR_GREATER
+- template <typename...>
+- inline constexpr auto is_unique = std::true_type{};
+-
+- template <typename T, typename... Rest>
+- inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
+- (!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
+- >{};
+-#else
+-
+-template <typename...>
+-struct is_unique : std::true_type{};
+-
+-template <typename T0, typename T1, typename... Rest>
+-struct is_unique<T0, T1, Rest...> : std::integral_constant
+-<bool,
+- !std::is_same<T0, T1>::value
+- && is_unique<T0, Rest...>::value
+- && is_unique<T1, Rest...>::value
+->{};
+-
+-#endif
+-}
+-
+-// end catch_type_traits.hpp
+ // start catch_preprocessor.hpp
+
+
+@@ -699,21 +738,224 @@
+ #define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
+ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
+ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
++#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
++#else
++// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
++#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
++#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
++#endif
++
++#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
++#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
+
+ #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
+
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__)
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
++#else
++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
++#endif
++
++#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
++ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
++
++#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
++#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
++#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
++#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
++#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
++#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
++#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
++#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
++#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
++#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
++#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
++
++#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
++
++#define INTERNAL_CATCH_TYPE_GEN\
++ template<typename...> struct TypeList {};\
++ template<typename...Ts>\
++ constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
++ template<template<typename...> class...> struct TemplateTypeList{};\
++ template<template<typename...> class...Cs>\
++ constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
++ template<typename...>\
++ struct append;\
++ template<typename...>\
++ struct rewrap;\
++ template<template<typename...> class, typename...>\
++ struct create;\
++ template<template<typename...> class, typename>\
++ struct convert;\
++ \
++ template<typename T> \
++ struct append<T> { using type = T; };\
++ template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
++ struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
++ template< template<typename...> class L1, typename...E1, typename...Rest>\
++ struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
++ \
++ template< template<typename...> class Container, template<typename...> class List, typename...elems>\
++ struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
++ template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
++ struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
++ \
++ template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
++ struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
++ template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
++ struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
++
++#define INTERNAL_CATCH_NTTP_1(signature, ...)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
++ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
++ template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
++ constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
++ \
++ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
++ template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
++ struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
++ template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
++ struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
++
++#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ static void TestName()
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ static void TestName()
++
++#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ static void TestName()
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ static void TestName()
++
++#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
++ template<typename Type>\
++ void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
++ {\
++ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
++ }
++
++#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
++ {\
++ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
++ }
++
++#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
++ template<typename Type>\
++ void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
++ {\
++ Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
++ }
++
++#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++ void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
++ {\
++ Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
++ }
++
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
++ template<typename TestType> \
++ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
++ void test();\
++ }
++
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
++ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
++ void test();\
++ }
++
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
++ template<typename TestType> \
++ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
++ template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
++ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
++
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++#define INTERNAL_CATCH_NTTP_0
++#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
++#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+ #else
+-// MSVC is adding extra space and needs more calls to properly remove ()
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__)
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
++#define INTERNAL_CATCH_NTTP_0(signature)
++#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
++#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
++#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
+ #endif
+
+ // end catch_preprocessor.hpp
++// start catch_meta.hpp
++
++
++#include <type_traits>
++
++namespace Catch {
++ template<typename T>
++ struct always_false : std::false_type {};
++
++ template <typename> struct true_given : std::true_type {};
++ struct is_callable_tester {
++ template <typename Fun, typename... Args>
++ true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
++ template <typename...>
++ std::false_type static test(...);
++ };
++
++ template <typename T>
++ struct is_callable;
++
++ template <typename Fun, typename... Args>
++ struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
++
++#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
++ // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
++ // replaced with std::invoke_result here.
++ template <typename Func, typename... U>
++ using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>;
++#else
++ // Keep ::type here because we still support C++11
++ template <typename Func, typename... U>
++ using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U...)>::type>::type>::type;
++#endif
++
++} // namespace Catch
++
++namespace mpl_{
++ struct na;
++}
++
++// end catch_meta.hpp
+ namespace Catch {
+
+ template<typename C>
+@@ -758,38 +1000,70 @@
+ }; \
+ } \
+ void TestName::test()
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \
+- template<typename TestType> \
+- static void TestName()
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
++ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+ namespace{ \
+- template<typename TestType> \
+- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+- void test(); \
+- }; \
++ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
++ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ } \
+- template<typename TestType> \
+- void TestName::test()
++ } \
++ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
++
++ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
++ #else
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
++ #endif
++
++ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
++ #else
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
++ #endif
++
++ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
++ #else
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
++ #endif
++
++ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
++ #else
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
++ #endif
+ #endif
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+- INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
++ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+@@ -797,88 +1071,273 @@
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+- INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
++ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ ///////////////////////////////////////////////////////////////////////////////
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+- template<typename TestType> \
+- static void TestFunc();\
++ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ namespace {\
++ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
++ INTERNAL_CATCH_TYPE_GEN\
++ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++ INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template<typename...Types> \
+ struct TestName{\
+- template<typename...Ts> \
+- TestName(Ts...names){\
+- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
++ TestName(){\
++ int index = 0; \
++ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
++ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \
++ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++ TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
++ return 0;\
++ }();\
+ }\
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+- template<typename TestType> \
+- static void TestFunc()
++ }\
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+-#if defined(CATCH_CPP17_OR_GREATER)
+-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case");
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
+ #else
+-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case");
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ )
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
+ #else
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) )
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
+
+- #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\
+- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+- TestName<CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)>(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\
+- return 0;\
+- }();
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ template<typename TestType> static void TestFuncName(); \
++ namespace {\
++ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
++ INTERNAL_CATCH_TYPE_GEN \
++ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
++ template<typename... Types> \
++ struct TestName { \
++ void reg_tests() { \
++ int index = 0; \
++ using expander = int[]; \
++ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
++ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
++ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
++ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
++ } \
++ }; \
++ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
++ using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
++ TestInit t; \
++ t.reg_tests(); \
++ return 0; \
++ }(); \
++ } \
++ } \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++ template<typename TestType> \
++ static void TestFuncName()
++
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
++ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__)
++#else
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) )
++#endif
+
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
++ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__)
++#else
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
++#endif
++
++ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+- namespace{ \
+- template<typename TestType> \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ template<typename TestType> static void TestFunc(); \
++ namespace {\
++ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
++ INTERNAL_CATCH_TYPE_GEN\
++ template<typename... Types> \
++ struct TestName { \
++ void reg_tests() { \
++ int index = 0; \
++ using expander = int[]; \
++ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
++ } \
++ };\
++ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
++ using TestInit = typename convert<TestName, TmplList>::type; \
++ TestInit t; \
++ t.reg_tests(); \
++ return 0; \
++ }(); \
++ }}\
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++ template<typename TestType> \
++ static void TestFunc()
++
++ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
++ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList )
++
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ namespace {\
++ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
++ INTERNAL_CATCH_TYPE_GEN\
++ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
++ INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++ template<typename...Types> \
++ struct TestNameClass{\
++ TestNameClass(){\
++ int index = 0; \
++ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
++ using expander = int[];\
++ (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
++ }\
++ };\
++ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++ TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
++ return 0;\
++ }();\
++ }\
++ }\
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
++
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
++#else
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
++#endif
++
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
++ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
++#else
++ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
++#endif
++
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ template<typename TestType> \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+ void test();\
+ };\
+- template<typename...Types> \
++ namespace {\
++ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
++ INTERNAL_CATCH_TYPE_GEN \
++ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++ template<typename...Types>\
+ struct TestNameClass{\
+- template<typename...Ts> \
+- TestNameClass(Ts...names){\
+- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
++ void reg_tests(){\
++ int index = 0;\
+ using expander = int[];\
+- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
++ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
++ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
++ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
++ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\
++ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++ using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
++ TestInit t;\
++ t.reg_tests();\
++ return 0;\
++ }(); \
++ }\
+ }\
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template<typename TestType> \
+ void TestName<TestType>::test()
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ )
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
++ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ #else
+- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) )
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+ #endif
+
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
++ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
++#else
++ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
++ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
++#endif
++
++ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++ template<typename TestType> \
++ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
++ void test();\
++ };\
++ namespace {\
++ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
++ INTERNAL_CATCH_TYPE_GEN\
++ template<typename...Types>\
++ struct TestNameClass{\
++ void reg_tests(){\
++ int index = 0;\
++ using expander = int[];\
++ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
++ }\
++ };\
++ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++ using TestInit = typename convert<TestNameClass, TmplList>::type;\
++ TestInit t;\
++ t.reg_tests();\
++ return 0;\
++ }(); \
++ }}\
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++ template<typename TestType> \
++ void TestName<TestType>::test()
++
++#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
++ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList )
++
+ // end catch_test_registry.h
+ // start catch_capture.hpp
+
+@@ -978,7 +1437,7 @@
+
+ auto makeStream( StringRef const &filename ) -> IStream const*;
+
+- class ReusableStringStream {
++ class ReusableStringStream : NonCopyable {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+@@ -997,6 +1456,42 @@
+ }
+
+ // end catch_stream.h
++// start catch_interfaces_enum_values_registry.h
++
++#include <vector>
++
++namespace Catch {
++
++ namespace Detail {
++ struct EnumInfo {
++ StringRef m_name;
++ std::vector<std::pair<int, StringRef>> m_values;
++
++ ~EnumInfo();
++
++ StringRef lookup( int value ) const;
++ };
++ } // namespace Detail
++
++ struct IMutableEnumValuesRegistry {
++ virtual ~IMutableEnumValuesRegistry();
++
++ virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
++
++ template<typename E>
++ Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
++ static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
++ std::vector<int> intValues;
++ intValues.reserve( values.size() );
++ for( auto enumValue : values )
++ intValues.push_back( static_cast<int>( enumValue ) );
++ return registerEnum( enumName, allEnums, intValues );
++ }
++ };
++
++} // Catch
++
++// end catch_interfaces_enum_values_registry.h
+
+ #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ #include <string_view>
+@@ -1067,9 +1562,9 @@
+
+ template<typename T>
+ class IsStreamInsertable {
+- template<typename SS, typename TT>
++ template<typename Stream, typename U>
+ static auto test(int)
+- -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
++ -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
+
+ template<typename, typename>
+ static auto test(...)->std::false_type;
+@@ -1231,6 +1726,12 @@
+ }
+ };
+
++#if defined(CATCH_CONFIG_CPP17_BYTE)
++ template<>
++ struct StringMaker<std::byte> {
++ static std::string convert(std::byte value);
++ };
++#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+ template<>
+ struct StringMaker<int> {
+ static std::string convert(int value);
+@@ -1282,10 +1783,13 @@
+ template<>
+ struct StringMaker<float> {
+ static std::string convert(float value);
++ static int precision;
+ };
++
+ template<>
+ struct StringMaker<double> {
+ static std::string convert(double value);
++ static int precision;
+ };
+
+ template <typename T>
+@@ -1321,8 +1825,8 @@
+ #endif
+
+ namespace Detail {
+- template<typename InputIterator>
+- std::string rangeToString(InputIterator first, InputIterator last) {
++ template<typename InputIterator, typename Sentinel = InputIterator>
++ std::string rangeToString(InputIterator first, Sentinel last) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ if (first != last) {
+@@ -1370,6 +1874,7 @@
+ # define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+ # define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+ # define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
++# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+ #endif
+
+ // Separate std::pair specialization
+@@ -1391,6 +1896,24 @@
+ }
+ #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+
++#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
++#include <optional>
++namespace Catch {
++ template<typename T>
++ struct StringMaker<std::optional<T> > {
++ static std::string convert(const std::optional<T>& optional) {
++ ReusableStringStream rss;
++ if (optional.has_value()) {
++ rss << ::Catch::Detail::stringify(*optional);
++ } else {
++ rss << "{ }";
++ }
++ return rss.str();
++ }
++ };
++}
++#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
++
+ // Separate std::tuple specialization
+ #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+ #include <tuple>
+@@ -1461,20 +1984,27 @@
+ #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+
+ namespace Catch {
+- struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
+-
+- // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
++ // Import begin/ end from std here
+ using std::begin;
+ using std::end;
+
+- not_this_one begin( ... );
+- not_this_one end( ... );
++ namespace detail {
++ template <typename...>
++ struct void_type {
++ using type = void;
++ };
++
++ template <typename T, typename = void>
++ struct is_range_impl : std::false_type {
++ };
++
++ template <typename T>
++ struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type {
++ };
++ } // namespace detail
+
+ template <typename T>
+- struct is_range {
+- static const bool value =
+- !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value &&
+- !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
++ struct is_range : detail::is_range_impl<T> {
+ };
+
+ #if defined(_MANAGED) // Managed types are never ranges
+@@ -1639,6 +2169,18 @@
+ }
+ #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+
++#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
++namespace Catch { \
++ template<> struct StringMaker<enumName> { \
++ static std::string convert( enumName value ) { \
++ static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
++ return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
++ } \
++ }; \
++}
++
++#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
++
+ #ifdef _MSC_VER
+ #pragma warning(pop)
+ #endif
+@@ -1652,6 +2194,7 @@
+ #pragma warning(disable:4018) // more "signed/unsigned mismatch"
+ #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+ #pragma warning(disable:4180) // qualifier applied to function type has no meaning
++#pragma warning(disable:4800) // Forcing result to true or false
+ #endif
+
+ namespace Catch {
+@@ -1695,6 +2238,62 @@
+ m_op( op ),
+ m_rhs( rhs )
+ {}
++
++ template<typename T>
++ auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename T>
++ auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename T>
++ auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename T>
++ auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename T>
++ auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename T>
++ auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename T>
++ auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename T>
++ auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<T>::value,
++ "chained comparisons are not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
+ };
+
+ template<typename LhsT>
+@@ -1707,7 +2306,7 @@
+
+ public:
+ explicit UnaryExpr( LhsT lhs )
+- : ITransientExpression{ false, lhs ? true : false },
++ : ITransientExpression{ false, static_cast<bool>(lhs) },
+ m_lhs( lhs )
+ {}
+ };
+@@ -1773,6 +2372,32 @@
+ auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+ return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs };
+ }
++ template <typename RhsT>
++ auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
++ return { static_cast<bool>(m_lhs | rhs), m_lhs, "|", rhs };
++ }
++ template <typename RhsT>
++ auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
++ return { static_cast<bool>(m_lhs & rhs), m_lhs, "&", rhs };
++ }
++ template <typename RhsT>
++ auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
++ return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^", rhs };
++ }
++
++ template<typename RhsT>
++ auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<RhsT>::value,
++ "operator&& is not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
++
++ template<typename RhsT>
++ auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
++ static_assert(always_false<RhsT>::value,
++ "operator|| is not supported inside assertions, "
++ "wrap the expression inside parentheses, or decompose it");
++ }
+
+ auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
+ return UnaryExpr<LhsT>{ m_lhs };
+@@ -1807,6 +2432,7 @@
+ // start catch_interfaces_capture.h
+
+ #include <string>
++#include <chrono>
+
+ namespace Catch {
+
+@@ -1815,15 +2441,20 @@
+ struct SectionInfo;
+ struct SectionEndInfo;
+ struct MessageInfo;
++ struct MessageBuilder;
+ struct Counts;
+- struct BenchmarkInfo;
+- struct BenchmarkStats;
+ struct AssertionReaction;
+ struct SourceLineInfo;
+
+ struct ITransientExpression;
+ struct IGeneratorTracker;
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ struct BenchmarkInfo;
++ template <typename Duration = std::chrono::duration<double, std::nano>>
++ struct BenchmarkStats;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+@@ -1833,14 +2464,20 @@
+ virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+
+- virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
++ virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ virtual void benchmarkPreparing( std::string const& name ) = 0;
+ virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
+- virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0;
++ virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
++ virtual void benchmarkFailed( std::string const& error ) = 0;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
++ virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
++
+ virtual void handleFatalErrorCondition( StringRef message ) = 0;
+
+ virtual void handleExpr
+@@ -2004,9 +2641,12 @@
+ class ScopedMessage {
+ public:
+ explicit ScopedMessage( MessageBuilder const& builder );
++ ScopedMessage( ScopedMessage& duplicate ) = delete;
++ ScopedMessage( ScopedMessage&& old );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
++ bool m_moved;
+ };
+
+ class Capturer {
+@@ -2062,15 +2702,16 @@
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
+ do { \
++ CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \
+ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
+ INTERNAL_CATCH_TRY { \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
+- CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+ INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+- } while( (void)0, false && static_cast<bool>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
+- // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
++ } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
+@@ -2151,6 +2792,10 @@
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
+
+ ///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
++ Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
++
++///////////////////////////////////////////////////////////////////////////////
+ // Although this is matcher-based, it can be used with just a string
+ #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
+ do { \
+@@ -2283,62 +2928,18 @@
+ } // end namespace Catch
+
+ #define INTERNAL_CATCH_SECTION( ... ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
+- CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ #define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
+- CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ // end catch_section.h
+-// start catch_benchmark.h
+-
+-#include <cstdint>
+-#include <string>
+-
+-namespace Catch {
+-
+- class BenchmarkLooper {
+-
+- std::string m_name;
+- std::size_t m_count = 0;
+- std::size_t m_iterationsToRun = 1;
+- uint64_t m_resolution;
+- Timer m_timer;
+-
+- static auto getResolution() -> uint64_t;
+- public:
+- // Keep most of this inline as it's on the code path that is being timed
+- BenchmarkLooper( StringRef name )
+- : m_name( name ),
+- m_resolution( getResolution() )
+- {
+- reportStart();
+- m_timer.start();
+- }
+-
+- explicit operator bool() {
+- if( m_count < m_iterationsToRun )
+- return true;
+- return needsMoreIterations();
+- }
+-
+- void increment() {
+- ++m_count;
+- }
+-
+- void reportStart();
+- auto needsMoreIterations() -> bool;
+- };
+-
+-} // end namespace Catch
+-
+-#define BENCHMARK( name ) \
+- for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() )
+-
+-// end catch_benchmark.h
+ // start catch_interfaces_exception.h
+
+ // start catch_interfaces_registry_hub.h
+@@ -2355,6 +2956,8 @@
+ struct IReporterRegistry;
+ struct IReporterFactory;
+ struct ITagAliasRegistry;
++ struct IMutableEnumValuesRegistry;
++
+ class StartupExceptionRegistry;
+
+ using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
+@@ -2365,7 +2968,6 @@
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+-
+ virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
+
+ virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
+@@ -2379,6 +2981,7 @@
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
+ virtual void registerStartupException() noexcept = 0;
++ virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
+ };
+
+ IRegistryHub const& getRegistryHub();
+@@ -2425,6 +3028,9 @@
+ {}
+
+ std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
++ return "";
++#else
+ try {
+ if( it == itEnd )
+ std::rethrow_exception(std::current_exception());
+@@ -2434,6 +3040,7 @@
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
++#endif
+ }
+
+ protected:
+@@ -2452,9 +3059,10 @@
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+ static std::string translatorName( signature ); \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ static std::string translatorName( signature )
+
+ #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+@@ -2485,7 +3093,7 @@
+ Approx operator-() const;
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+- Approx operator()( T const& value ) {
++ Approx operator()( T const& value ) const {
+ Approx approx( static_cast<double>(value) );
+ approx.m_epsilon = m_epsilon;
+ approx.m_margin = m_margin;
+@@ -2585,6 +3193,7 @@
+
+ #include <string>
+ #include <iosfwd>
++#include <vector>
+
+ namespace Catch {
+
+@@ -2595,7 +3204,13 @@
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
++ //! Returns a new string without whitespace at the start/end
+ std::string trim( std::string const& str );
++ //! Returns a substring of the original ref without whitespace. Beware lifetimes!
++ StringRef trim(StringRef ref);
++
++ // !!! Be aware, returns refs into original string - make sure original string outlives them
++ std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+ struct pluralise {
+@@ -2648,6 +3263,15 @@
+ virtual bool match( ObjectT const& arg ) const = 0;
+ };
+
++#if defined(__OBJC__)
++ // Hack to fix Catch GH issue #1661. Could use id for generic Object support.
++ // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
++ template<>
++ struct MatcherMethod<NSString*> {
++ virtual bool match( NSString* arg ) const = 0;
++ };
++#endif
++
+ #ifdef __clang__
+ # pragma clang diagnostic pop
+ #endif
+@@ -2685,9 +3309,10 @@
+ return description;
+ }
+
+- MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+- m_matchers.push_back( &other );
+- return *this;
++ MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
++ auto copy(*this);
++ copy.m_matchers.push_back( &other );
++ return copy;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+@@ -2718,9 +3343,10 @@
+ return description;
+ }
+
+- MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+- m_matchers.push_back( &other );
+- return *this;
++ MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
++ auto copy(*this);
++ copy.m_matchers.push_back( &other );
++ return copy;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+@@ -2764,10 +3390,34 @@
+ } // namespace Catch
+
+ // end catch_matchers.h
+-// start catch_matchers_floating.h
++// start catch_matchers_exception.hpp
+
+-#include <type_traits>
+-#include <cmath>
++namespace Catch {
++namespace Matchers {
++namespace Exception {
++
++class ExceptionMessageMatcher : public MatcherBase<std::exception> {
++ std::string m_message;
++public:
++
++ ExceptionMessageMatcher(std::string const& message):
++ m_message(message)
++ {}
++
++ bool match(std::exception const& ex) const override;
++
++ std::string describe() const override;
++};
++
++} // namespace Exception
++
++Exception::ExceptionMessageMatcher Message(std::string const& message);
++
++} // namespace Matchers
++} // namespace Catch
++
++// end catch_matchers_exception.hpp
++// start catch_matchers_floating.h
+
+ namespace Catch {
+ namespace Matchers {
+@@ -2786,22 +3436,43 @@
+ };
+
+ struct WithinUlpsMatcher : MatcherBase<double> {
+- WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
++ WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
+ bool match(double const& matchee) const override;
+ std::string describe() const override;
+ private:
+ double m_target;
+- int m_ulps;
++ uint64_t m_ulps;
+ FloatingPointKind m_type;
+ };
+
++ // Given IEEE-754 format for floats and doubles, we can assume
++ // that float -> double promotion is lossless. Given this, we can
++ // assume that if we do the standard relative comparison of
++ // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
++ // the same result if we do this for floats, as if we do this for
++ // doubles that were promoted from floats.
++ struct WithinRelMatcher : MatcherBase<double> {
++ WithinRelMatcher(double target, double epsilon);
++ bool match(double const& matchee) const override;
++ std::string describe() const override;
++ private:
++ double m_target;
++ double m_epsilon;
++ };
++
+ } // namespace Floating
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+- Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
+- Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
++ Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
++ Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
+ Floating::WithinAbsMatcher WithinAbs(double target, double margin);
++ Floating::WithinRelMatcher WithinRel(double target, double eps);
++ // defaults epsilon to 100*numeric_limits<double>::epsilon()
++ Floating::WithinRelMatcher WithinRel(double target);
++ Floating::WithinRelMatcher WithinRel(float target, float eps);
++ // defaults epsilon to 100*numeric_limits<float>::epsilon()
++ Floating::WithinRelMatcher WithinRel(float target);
+
+ } // namespace Matchers
+ } // namespace Catch
+@@ -2844,7 +3515,7 @@
+
+ // The following functions create the actual matcher objects.
+ // The user has to explicitly specify type to the function, because
+- // infering std::function<bool(T const&)> is hard (but possible) and
++ // inferring std::function<bool(T const&)> is hard (but possible) and
+ // requires a lot of TMP.
+ template<typename T>
+ Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
+@@ -2932,34 +3603,12 @@
+ namespace Matchers {
+
+ namespace Vector {
+- namespace Detail {
+- template <typename InputIterator, typename T>
+- size_t count(InputIterator first, InputIterator last, T const& item) {
+- size_t cnt = 0;
+- for (; first != last; ++first) {
+- if (*first == item) {
+- ++cnt;
+- }
+- }
+- return cnt;
+- }
+- template <typename InputIterator, typename T>
+- bool contains(InputIterator first, InputIterator last, T const& item) {
+- for (; first != last; ++first) {
+- if (*first == item) {
+- return true;
+- }
+- }
+- return false;
+- }
+- }
+-
+- template<typename T>
+- struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
++ template<typename T, typename Alloc>
++ struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> {
+
+ ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+- bool match(std::vector<T> const &v) const override {
++ bool match(std::vector<T, Alloc> const &v) const override {
+ for (auto const& el : v) {
+ if (el == m_comparator) {
+ return true;
+@@ -2975,12 +3624,12 @@
+ T const& m_comparator;
+ };
+
+- template<typename T>
+- struct ContainsMatcher : MatcherBase<std::vector<T>> {
++ template<typename T, typename AllocComp, typename AllocMatch>
++ struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+- ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
++ ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+
+- bool match(std::vector<T> const &v) const override {
++ bool match(std::vector<T, AllocMatch> const &v) const override {
+ // !TBD: see note in EqualsMatcher
+ if (m_comparator.size() > v.size())
+ return false;
+@@ -3002,18 +3651,18 @@
+ return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+ }
+
+- std::vector<T> const& m_comparator;
++ std::vector<T, AllocComp> const& m_comparator;
+ };
+
+- template<typename T>
+- struct EqualsMatcher : MatcherBase<std::vector<T>> {
++ template<typename T, typename AllocComp, typename AllocMatch>
++ struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+- EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
++ EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+
+- bool match(std::vector<T> const &v) const override {
++ bool match(std::vector<T, AllocMatch> const &v) const override {
+ // !TBD: This currently works if all elements can be compared using !=
+ // - a more general approach would be via a compare template that defaults
+- // to using !=. but could be specialised for, e.g. std::vector<T> etc
++ // to using !=. but could be specialised for, e.g. std::vector<T, Alloc> etc
+ // - then just call that directly
+ if (m_comparator.size() != v.size())
+ return false;
+@@ -3025,47 +3674,60 @@
+ std::string describe() const override {
+ return "Equals: " + ::Catch::Detail::stringify( m_comparator );
+ }
+- std::vector<T> const& m_comparator;
++ std::vector<T, AllocComp> const& m_comparator;
+ };
+
+- template<typename T>
+- struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
+- UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
+- bool match(std::vector<T> const& vec) const override {
+- // Note: This is a reimplementation of std::is_permutation,
+- // because I don't want to include <algorithm> inside the common path
+- if (m_target.size() != vec.size()) {
+- return false;
+- }
+- auto lfirst = m_target.begin(), llast = m_target.end();
+- auto rfirst = vec.begin(), rlast = vec.end();
+- // Cut common prefix to optimize checking of permuted parts
+- while (lfirst != llast && *lfirst == *rfirst) {
+- ++lfirst; ++rfirst;
+- }
+- if (lfirst == llast) {
+- return true;
+- }
++ template<typename T, typename AllocComp, typename AllocMatch>
++ struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+- for (auto mid = lfirst; mid != llast; ++mid) {
+- // Skip already counted items
+- if (Detail::contains(lfirst, mid, *mid)) {
+- continue;
+- }
+- size_t num_vec = Detail::count(rfirst, rlast, *mid);
+- if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
+- return false;
+- }
+- }
++ ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {}
+
++ bool match(std::vector<T, AllocMatch> const &v) const override {
++ if (m_comparator.size() != v.size())
++ return false;
++ for (std::size_t i = 0; i < v.size(); ++i)
++ if (m_comparator[i] != approx(v[i]))
++ return false;
+ return true;
+ }
++ std::string describe() const override {
++ return "is approx: " + ::Catch::Detail::stringify( m_comparator );
++ }
++ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
++ ApproxMatcher& epsilon( T const& newEpsilon ) {
++ approx.epsilon(newEpsilon);
++ return *this;
++ }
++ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
++ ApproxMatcher& margin( T const& newMargin ) {
++ approx.margin(newMargin);
++ return *this;
++ }
++ template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
++ ApproxMatcher& scale( T const& newScale ) {
++ approx.scale(newScale);
++ return *this;
++ }
++
++ std::vector<T, AllocComp> const& m_comparator;
++ mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom();
++ };
++
++ template<typename T, typename AllocComp, typename AllocMatch>
++ struct UnorderedEqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
++ UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target) : m_target(target) {}
++ bool match(std::vector<T, AllocMatch> const& vec) const override {
++ if (m_target.size() != vec.size()) {
++ return false;
++ }
++ return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
++ }
+
+ std::string describe() const override {
+ return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
+ }
+ private:
+- std::vector<T> const& m_target;
++ std::vector<T, AllocComp> const& m_target;
+ };
+
+ } // namespace Vector
+@@ -3073,24 +3735,29 @@
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+- template<typename T>
+- Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+- return Vector::ContainsMatcher<T>( comparator );
++ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++ Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
++ return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+- template<typename T>
+- Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+- return Vector::ContainsElementMatcher<T>( comparator );
++ template<typename T, typename Alloc = std::allocator<T>>
++ Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
++ return Vector::ContainsElementMatcher<T, Alloc>( comparator );
+ }
+
+- template<typename T>
+- Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+- return Vector::EqualsMatcher<T>( comparator );
++ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++ Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
++ return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator );
+ }
+
+- template<typename T>
+- Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
+- return Vector::UnorderedEqualsMatcher<T>(target);
++ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++ Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
++ return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator );
++ }
++
++ template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++ Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
++ return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target );
+ }
+
+ } // namespace Matchers
+@@ -3175,16 +3842,17 @@
+ namespace Catch {
+
+ namespace Generators {
+- class GeneratorBase {
+- protected:
+- size_t m_size = 0;
+-
++ class GeneratorUntypedBase {
+ public:
+- GeneratorBase( size_t size ) : m_size( size ) {}
+- virtual ~GeneratorBase();
+- auto size() const -> size_t { return m_size; }
++ GeneratorUntypedBase() = default;
++ virtual ~GeneratorUntypedBase();
++ // Attempts to move the generator to the next element
++ //
++ // Returns true iff the move succeeded (and a valid element
++ // can be retrieved).
++ virtual bool next() = 0;
+ };
+- using GeneratorBasePtr = std::unique_ptr<GeneratorBase>;
++ using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>;
+
+ } // namespace Generators
+
+@@ -3193,7 +3861,6 @@
+ virtual auto hasGenerator() const -> bool = 0;
+ virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
+ virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
+- virtual auto getIndex() const -> std::size_t = 0;
+ };
+
+ } // namespace Catch
+@@ -3201,7 +3868,7 @@
+ // end catch_interfaces_generatortracker.h
+ // start catch_enforce.h
+
+-#include <stdexcept>
++#include <exception>
+
+ namespace Catch {
+ #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+@@ -3214,18 +3881,30 @@
+ [[noreturn]]
+ void throw_exception(std::exception const& e);
+ #endif
++
++ [[noreturn]]
++ void throw_logic_error(std::string const& msg);
++ [[noreturn]]
++ void throw_domain_error(std::string const& msg);
++ [[noreturn]]
++ void throw_runtime_error(std::string const& msg);
++
+ } // namespace Catch;
+
+-#define CATCH_PREPARE_EXCEPTION( type, msg ) \
+- type( ( Catch::ReusableStringStream() << msg ).str() )
+-#define CATCH_INTERNAL_ERROR( msg ) \
+- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg))
+-#define CATCH_ERROR( msg ) \
+- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg ))
+-#define CATCH_RUNTIME_ERROR( msg ) \
+- Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg ))
+-#define CATCH_ENFORCE( condition, msg ) \
+- do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
++#define CATCH_MAKE_MSG(...) \
++ (Catch::ReusableStringStream() << __VA_ARGS__).str()
++
++#define CATCH_INTERNAL_ERROR(...) \
++ Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
++
++#define CATCH_ERROR(...) \
++ Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
++
++#define CATCH_RUNTIME_ERROR(...) \
++ Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
++
++#define CATCH_ENFORCE( condition, ... ) \
++ do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+
+ // end catch_enforce.h
+ #include <memory>
+@@ -3233,8 +3912,21 @@
+ #include <cassert>
+
+ #include <utility>
++#include <exception>
+
+ namespace Catch {
++
++class GeneratorException : public std::exception {
++ const char* const m_msg = "";
++
++public:
++ GeneratorException(const char* msg):
++ m_msg(msg)
++ {}
++
++ const char* what() const noexcept override final;
++};
++
+ namespace Generators {
+
+ // !TBD move this into its own location?
+@@ -3246,223 +3938,819 @@
+ }
+
+ template<typename T>
+- struct IGenerator {
+- virtual ~IGenerator() {}
+- virtual auto get( size_t index ) const -> T = 0;
++ struct IGenerator : GeneratorUntypedBase {
++ virtual ~IGenerator() = default;
++
++ // Returns the current element of the generator
++ //
++ // \Precondition The generator is either freshly constructed,
++ // or the last call to `next()` returned true
++ virtual T const& get() const = 0;
++ using type = T;
+ };
+
+ template<typename T>
+- class SingleValueGenerator : public IGenerator<T> {
++ class SingleValueGenerator final : public IGenerator<T> {
+ T m_value;
+ public:
+- SingleValueGenerator( T const& value ) : m_value( value ) {}
++ SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
+
+- auto get( size_t ) const -> T override {
++ T const& get() const override {
+ return m_value;
+ }
++ bool next() override {
++ return false;
++ }
+ };
+
+ template<typename T>
+- class FixedValuesGenerator : public IGenerator<T> {
++ class FixedValuesGenerator final : public IGenerator<T> {
++ static_assert(!std::is_same<T, bool>::value,
++ "FixedValuesGenerator does not support bools because of std::vector<bool>"
++ "specialization, use SingleValue Generator instead.");
+ std::vector<T> m_values;
+-
++ size_t m_idx = 0;
+ public:
+ FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
+
+- auto get( size_t index ) const -> T override {
+- return m_values[index];
++ T const& get() const override {
++ return m_values[m_idx];
++ }
++ bool next() override {
++ ++m_idx;
++ return m_idx < m_values.size();
++ }
++ };
++
++ template <typename T>
++ class GeneratorWrapper final {
++ std::unique_ptr<IGenerator<T>> m_generator;
++ public:
++ GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
++ m_generator(std::move(generator))
++ {}
++ T const& get() const {
++ return m_generator->get();
++ }
++ bool next() {
++ return m_generator->next();
+ }
+ };
+
++ template <typename T>
++ GeneratorWrapper<T> value(T&& value) {
++ return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
++ }
++ template <typename T>
++ GeneratorWrapper<T> values(std::initializer_list<T> values) {
++ return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
++ }
++
+ template<typename T>
+- class RangeGenerator : public IGenerator<T> {
+- T const m_first;
+- T const m_last;
++ class Generators : public IGenerator<T> {
++ std::vector<GeneratorWrapper<T>> m_generators;
++ size_t m_current = 0;
++
++ void populate(GeneratorWrapper<T>&& generator) {
++ m_generators.emplace_back(std::move(generator));
++ }
++ void populate(T&& val) {
++ m_generators.emplace_back(value(std::forward<T>(val)));
++ }
++ template<typename U>
++ void populate(U&& val) {
++ populate(T(std::forward<U>(val)));
++ }
++ template<typename U, typename... Gs>
++ void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
++ populate(std::forward<U>(valueOrGenerator));
++ populate(std::forward<Gs>(moreGenerators)...);
++ }
+
+ public:
+- RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) {
+- assert( m_last > m_first );
++ template <typename... Gs>
++ Generators(Gs &&... moreGenerators) {
++ m_generators.reserve(sizeof...(Gs));
++ populate(std::forward<Gs>(moreGenerators)...);
+ }
+
+- auto get( size_t index ) const -> T override {
+- // ToDo:: introduce a safe cast to catch potential overflows
+- return static_cast<T>(m_first+index);
++ T const& get() const override {
++ return m_generators[m_current].get();
+ }
+- };
+
+- template<typename T>
+- struct NullGenerator : IGenerator<T> {
+- auto get( size_t ) const -> T override {
+- CATCH_INTERNAL_ERROR("A Null Generator is always empty");
++ bool next() override {
++ if (m_current >= m_generators.size()) {
++ return false;
++ }
++ const bool current_status = m_generators[m_current].next();
++ if (!current_status) {
++ ++m_current;
++ }
++ return m_current < m_generators.size();
+ }
+ };
+
++ template<typename... Ts>
++ GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
++ return values<std::tuple<Ts...>>( tuples );
++ }
++
++ // Tag type to signal that a generator sequence should convert arguments to a specific type
++ template <typename T>
++ struct as {};
++
++ template<typename T, typename... Gs>
++ auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
++ return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
++ }
+ template<typename T>
+- class Generator {
+- std::unique_ptr<IGenerator<T>> m_generator;
+- size_t m_size;
++ auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
++ return Generators<T>(std::move(generator));
++ }
++ template<typename T, typename... Gs>
++ auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
++ return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
++ }
++ template<typename T, typename U, typename... Gs>
++ auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
++ return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
++ }
+
+- public:
+- Generator( size_t size, std::unique_ptr<IGenerator<T>> generator )
+- : m_generator( std::move( generator ) ),
+- m_size( size )
+- {}
++ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+
+- auto size() const -> size_t { return m_size; }
+- auto operator[]( size_t index ) const -> T {
+- assert( index < m_size );
+- return m_generator->get( index );
++ template<typename L>
++ // Note: The type after -> is weird, because VS2015 cannot parse
++ // the expression used in the typedef inside, when it is in
++ // return type. Yeah.
++ auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
++ using UnderlyingType = typename decltype(generatorExpression())::type;
++
++ IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
++ if (!tracker.hasGenerator()) {
++ tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+ }
+- };
+
+- std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize );
++ auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
++ return generator.get();
++ }
++
++} // namespace Generators
++} // namespace Catch
+
+- template<typename T>
+- class GeneratorRandomiser : public IGenerator<T> {
+- Generator<T> m_baseGenerator;
++#define GENERATE( ... ) \
++ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
++ CATCH_INTERNAL_LINEINFO, \
++ [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
++#define GENERATE_COPY( ... ) \
++ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
++ CATCH_INTERNAL_LINEINFO, \
++ [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
++#define GENERATE_REF( ... ) \
++ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
++ CATCH_INTERNAL_LINEINFO, \
++ [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
++
++// end catch_generators.hpp
++// start catch_generators_generic.hpp
++
++namespace Catch {
++namespace Generators {
+
+- std::vector<size_t> m_indices;
++ template <typename T>
++ class TakeGenerator : public IGenerator<T> {
++ GeneratorWrapper<T> m_generator;
++ size_t m_returned = 0;
++ size_t m_target;
+ public:
+- GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems )
+- : m_baseGenerator( std::move( baseGenerator ) ),
+- m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) )
+- {}
++ TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
++ m_generator(std::move(generator)),
++ m_target(target)
++ {
++ assert(target != 0 && "Empty generators are not allowed");
++ }
++ T const& get() const override {
++ return m_generator.get();
++ }
++ bool next() override {
++ ++m_returned;
++ if (m_returned >= m_target) {
++ return false;
++ }
+
+- auto get( size_t index ) const -> T override {
+- return m_baseGenerator[m_indices[index]];
++ const auto success = m_generator.next();
++ // If the underlying generator does not contain enough values
++ // then we cut short as well
++ if (!success) {
++ m_returned = m_target;
++ }
++ return success;
+ }
+ };
+
+- template<typename T>
+- struct RequiresASpecialisationFor;
++ template <typename T>
++ GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
++ return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
++ }
+
+- template<typename T>
+- auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); }
++ template <typename T, typename Predicate>
++ class FilterGenerator : public IGenerator<T> {
++ GeneratorWrapper<T> m_generator;
++ Predicate m_predicate;
++ public:
++ template <typename P = Predicate>
++ FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
++ m_generator(std::move(generator)),
++ m_predicate(std::forward<P>(pred))
++ {
++ if (!m_predicate(m_generator.get())) {
++ // It might happen that there are no values that pass the
++ // filter. In that case we throw an exception.
++ auto has_initial_value = nextImpl();
++ if (!has_initial_value) {
++ Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
++ }
++ }
++ }
+
+- template<>
+- auto all<int>() -> Generator<int>;
++ T const& get() const override {
++ return m_generator.get();
++ }
+
+- template<typename T>
+- auto range( T const& first, T const& last ) -> Generator<T> {
+- return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) );
++ bool next() override {
++ return nextImpl();
++ }
++
++ private:
++ bool nextImpl() {
++ bool success = m_generator.next();
++ if (!success) {
++ return false;
++ }
++ while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
++ return success;
++ }
++ };
++
++ template <typename T, typename Predicate>
++ GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
++ return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
+ }
+
+- template<typename T>
+- auto random( T const& first, T const& last ) -> Generator<T> {
+- auto gen = range( first, last );
+- auto size = gen.size();
++ template <typename T>
++ class RepeatGenerator : public IGenerator<T> {
++ static_assert(!std::is_same<T, bool>::value,
++ "RepeatGenerator currently does not support bools"
++ "because of std::vector<bool> specialization");
++ GeneratorWrapper<T> m_generator;
++ mutable std::vector<T> m_returned;
++ size_t m_target_repeats;
++ size_t m_current_repeat = 0;
++ size_t m_repeat_index = 0;
++ public:
++ RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
++ m_generator(std::move(generator)),
++ m_target_repeats(repeats)
++ {
++ assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
++ }
++
++ T const& get() const override {
++ if (m_current_repeat == 0) {
++ m_returned.push_back(m_generator.get());
++ return m_returned.back();
++ }
++ return m_returned[m_repeat_index];
++ }
++
++ bool next() override {
++ // There are 2 basic cases:
++ // 1) We are still reading the generator
++ // 2) We are reading our own cache
++
++ // In the first case, we need to poke the underlying generator.
++ // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
++ if (m_current_repeat == 0) {
++ const auto success = m_generator.next();
++ if (!success) {
++ ++m_current_repeat;
++ }
++ return m_current_repeat < m_target_repeats;
++ }
++
++ // In the second case, we need to move indices forward and check that we haven't run up against the end
++ ++m_repeat_index;
++ if (m_repeat_index == m_returned.size()) {
++ m_repeat_index = 0;
++ ++m_current_repeat;
++ }
++ return m_current_repeat < m_target_repeats;
++ }
++ };
+
+- return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) );
++ template <typename T>
++ GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
++ return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
+ }
+- template<typename T>
+- auto random( size_t size ) -> Generator<T> {
+- return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) );
++
++ template <typename T, typename U, typename Func>
++ class MapGenerator : public IGenerator<T> {
++ // TBD: provide static assert for mapping function, for friendly error message
++ GeneratorWrapper<U> m_generator;
++ Func m_function;
++ // To avoid returning dangling reference, we have to save the values
++ T m_cache;
++ public:
++ template <typename F2 = Func>
++ MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
++ m_generator(std::move(generator)),
++ m_function(std::forward<F2>(function)),
++ m_cache(m_function(m_generator.get()))
++ {}
++
++ T const& get() const override {
++ return m_cache;
++ }
++ bool next() override {
++ const auto success = m_generator.next();
++ if (success) {
++ m_cache = m_function(m_generator.get());
++ }
++ return success;
++ }
++ };
++
++ template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
++ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
++ return GeneratorWrapper<T>(
++ pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
++ );
+ }
+
+- template<typename T>
+- auto values( std::initializer_list<T> values ) -> Generator<T> {
+- return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) );
++ template <typename T, typename U, typename Func>
++ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
++ return GeneratorWrapper<T>(
++ pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
++ );
+ }
+- template<typename T>
+- auto value( T const& val ) -> Generator<T> {
+- return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) );
++
++ template <typename T>
++ class ChunkGenerator final : public IGenerator<std::vector<T>> {
++ std::vector<T> m_chunk;
++ size_t m_chunk_size;
++ GeneratorWrapper<T> m_generator;
++ bool m_used_up = false;
++ public:
++ ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
++ m_chunk_size(size), m_generator(std::move(generator))
++ {
++ m_chunk.reserve(m_chunk_size);
++ if (m_chunk_size != 0) {
++ m_chunk.push_back(m_generator.get());
++ for (size_t i = 1; i < m_chunk_size; ++i) {
++ if (!m_generator.next()) {
++ Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
++ }
++ m_chunk.push_back(m_generator.get());
++ }
++ }
++ }
++ std::vector<T> const& get() const override {
++ return m_chunk;
++ }
++ bool next() override {
++ m_chunk.clear();
++ for (size_t idx = 0; idx < m_chunk_size; ++idx) {
++ if (!m_generator.next()) {
++ return false;
++ }
++ m_chunk.push_back(m_generator.get());
++ }
++ return true;
++ }
++ };
++
++ template <typename T>
++ GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
++ return GeneratorWrapper<std::vector<T>>(
++ pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
++ );
+ }
+
+- template<typename T>
+- auto as() -> Generator<T> {
+- return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() );
++} // namespace Generators
++} // namespace Catch
++
++// end catch_generators_generic.hpp
++// start catch_generators_specific.hpp
++
++// start catch_context.h
++
++#include <memory>
++
++namespace Catch {
++
++ struct IResultCapture;
++ struct IRunner;
++ struct IConfig;
++ struct IMutableContext;
++
++ using IConfigPtr = std::shared_ptr<IConfig const>;
++
++ struct IContext
++ {
++ virtual ~IContext();
++
++ virtual IResultCapture* getResultCapture() = 0;
++ virtual IRunner* getRunner() = 0;
++ virtual IConfigPtr const& getConfig() const = 0;
++ };
++
++ struct IMutableContext : IContext
++ {
++ virtual ~IMutableContext();
++ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
++ virtual void setRunner( IRunner* runner ) = 0;
++ virtual void setConfig( IConfigPtr const& config ) = 0;
++
++ private:
++ static IMutableContext *currentContext;
++ friend IMutableContext& getCurrentMutableContext();
++ friend void cleanUpContext();
++ static void createContext();
++ };
++
++ inline IMutableContext& getCurrentMutableContext()
++ {
++ if( !IMutableContext::currentContext )
++ IMutableContext::createContext();
++ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
++ return *IMutableContext::currentContext;
+ }
+
+- template<typename... Ts>
+- auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> {
+- return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) );
++ inline IContext& getCurrentContext()
++ {
++ return getCurrentMutableContext();
+ }
+
+- template<typename T>
+- struct Generators : GeneratorBase {
+- std::vector<Generator<T>> m_generators;
++ void cleanUpContext();
+
+- using type = T;
++ class SimplePcg32;
++ SimplePcg32& rng();
++}
+
+- Generators() : GeneratorBase( 0 ) {}
++// end catch_context.h
++// start catch_interfaces_config.h
+
+- void populate( T&& val ) {
+- m_size += 1;
+- m_generators.emplace_back( value( std::move( val ) ) );
++// start catch_option.hpp
++
++namespace Catch {
++
++ // An optional type
++ template<typename T>
++ class Option {
++ public:
++ Option() : nullableValue( nullptr ) {}
++ Option( T const& _value )
++ : nullableValue( new( storage ) T( _value ) )
++ {}
++ Option( Option const& _other )
++ : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
++ {}
++
++ ~Option() {
++ reset();
+ }
+- template<typename U>
+- void populate( U&& val ) {
+- populate( T( std::move( val ) ) );
++
++ Option& operator= ( Option const& _other ) {
++ if( &_other != this ) {
++ reset();
++ if( _other )
++ nullableValue = new( storage ) T( *_other );
++ }
++ return *this;
+ }
+- void populate( Generator<T>&& generator ) {
+- m_size += generator.size();
+- m_generators.emplace_back( std::move( generator ) );
++ Option& operator = ( T const& _value ) {
++ reset();
++ nullableValue = new( storage ) T( _value );
++ return *this;
+ }
+
+- template<typename U, typename... Gs>
+- void populate( U&& valueOrGenerator, Gs... moreGenerators ) {
+- populate( std::forward<U>( valueOrGenerator ) );
+- populate( std::forward<Gs>( moreGenerators )... );
++ void reset() {
++ if( nullableValue )
++ nullableValue->~T();
++ nullableValue = nullptr;
+ }
+
+- auto operator[]( size_t index ) const -> T {
+- size_t sizes = 0;
+- for( auto const& gen : m_generators ) {
+- auto localIndex = index-sizes;
+- sizes += gen.size();
+- if( index < sizes )
+- return gen[localIndex];
+- }
+- CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')');
++ T& operator*() { return *nullableValue; }
++ T const& operator*() const { return *nullableValue; }
++ T* operator->() { return nullableValue; }
++ const T* operator->() const { return nullableValue; }
++
++ T valueOr( T const& defaultValue ) const {
++ return nullableValue ? *nullableValue : defaultValue;
++ }
++
++ bool some() const { return nullableValue != nullptr; }
++ bool none() const { return nullableValue == nullptr; }
++
++ bool operator !() const { return nullableValue == nullptr; }
++ explicit operator bool() const {
++ return some();
+ }
++
++ private:
++ T *nullableValue;
++ alignas(alignof(T)) char storage[sizeof(T)];
+ };
+
+- template<typename T, typename... Gs>
+- auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
+- Generators<T> generators;
+- generators.m_generators.reserve( 1+sizeof...(Gs) );
+- generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... );
+- return generators;
++} // end namespace Catch
++
++// end catch_option.hpp
++#include <chrono>
++#include <iosfwd>
++#include <string>
++#include <vector>
++#include <memory>
++
++namespace Catch {
++
++ enum class Verbosity {
++ Quiet = 0,
++ Normal,
++ High
++ };
++
++ struct WarnAbout { enum What {
++ Nothing = 0x00,
++ NoAssertions = 0x01,
++ NoTests = 0x02
++ }; };
++
++ struct ShowDurations { enum OrNot {
++ DefaultForReporter,
++ Always,
++ Never
++ }; };
++ struct RunTests { enum InWhatOrder {
++ InDeclarationOrder,
++ InLexicographicalOrder,
++ InRandomOrder
++ }; };
++ struct UseColour { enum YesOrNo {
++ Auto,
++ Yes,
++ No
++ }; };
++ struct WaitForKeypress { enum When {
++ Never,
++ BeforeStart = 1,
++ BeforeExit = 2,
++ BeforeStartAndExit = BeforeStart | BeforeExit
++ }; };
++
++ class TestSpec;
++
++ struct IConfig : NonCopyable {
++
++ virtual ~IConfig();
++
++ virtual bool allowThrows() const = 0;
++ virtual std::ostream& stream() const = 0;
++ virtual std::string name() const = 0;
++ virtual bool includeSuccessfulResults() const = 0;
++ virtual bool shouldDebugBreak() const = 0;
++ virtual bool warnAboutMissingAssertions() const = 0;
++ virtual bool warnAboutNoTests() const = 0;
++ virtual int abortAfter() const = 0;
++ virtual bool showInvisibles() const = 0;
++ virtual ShowDurations::OrNot showDurations() const = 0;
++ virtual double minDuration() const = 0;
++ virtual TestSpec const& testSpec() const = 0;
++ virtual bool hasTestFilters() const = 0;
++ virtual std::vector<std::string> const& getTestsOrTags() const = 0;
++ virtual RunTests::InWhatOrder runOrder() const = 0;
++ virtual unsigned int rngSeed() const = 0;
++ virtual UseColour::YesOrNo useColour() const = 0;
++ virtual std::vector<std::string> const& getSectionsToRun() const = 0;
++ virtual Verbosity verbosity() const = 0;
++
++ virtual bool benchmarkNoAnalysis() const = 0;
++ virtual int benchmarkSamples() const = 0;
++ virtual double benchmarkConfidenceInterval() const = 0;
++ virtual unsigned int benchmarkResamples() const = 0;
++ virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
++ };
++
++ using IConfigPtr = std::shared_ptr<IConfig const>;
++}
++
++// end catch_interfaces_config.h
++// start catch_random_number_generator.h
++
++#include <cstdint>
++
++namespace Catch {
++
++ // This is a simple implementation of C++11 Uniform Random Number
++ // Generator. It does not provide all operators, because Catch2
++ // does not use it, but it should behave as expected inside stdlib's
++ // distributions.
++ // The implementation is based on the PCG family (http://pcg-random.org)
++ class SimplePcg32 {
++ using state_type = std::uint64_t;
++ public:
++ using result_type = std::uint32_t;
++ static constexpr result_type (min)() {
++ return 0;
++ }
++ static constexpr result_type (max)() {
++ return static_cast<result_type>(-1);
++ }
++
++ // Provide some default initial state for the default constructor
++ SimplePcg32():SimplePcg32(0xed743cc4U) {}
++
++ explicit SimplePcg32(result_type seed_);
++
++ void seed(result_type seed_);
++ void discard(uint64_t skip);
++
++ result_type operator()();
++
++ private:
++ friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
++ friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
++
++ // In theory we also need operator<< and operator>>
++ // In practice we do not use them, so we will skip them for now
++
++ std::uint64_t m_state;
++ // This part of the state determines which "stream" of the numbers
++ // is chosen -- we take it as a constant for Catch2, so we only
++ // need to deal with seeding the main state.
++ // Picked by reading 8 bytes from `/dev/random` :-)
++ static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL;
++ };
++
++} // end namespace Catch
++
++// end catch_random_number_generator.h
++#include <random>
++
++namespace Catch {
++namespace Generators {
++
++template <typename Float>
++class RandomFloatingGenerator final : public IGenerator<Float> {
++ Catch::SimplePcg32& m_rng;
++ std::uniform_real_distribution<Float> m_dist;
++ Float m_current_number;
++public:
++
++ RandomFloatingGenerator(Float a, Float b):
++ m_rng(rng()),
++ m_dist(a, b) {
++ static_cast<void>(next());
+ }
+- template<typename T>
+- auto makeGenerators( Generator<T>&& generator ) -> Generators<T> {
+- Generators<T> generators;
+- generators.populate( std::move( generator ) );
+- return generators;
++
++ Float const& get() const override {
++ return m_current_number;
+ }
+- template<typename T, typename... Gs>
+- auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
+- return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
++ bool next() override {
++ m_current_number = m_dist(m_rng);
++ return true;
+ }
+- template<typename T, typename U, typename... Gs>
+- auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> {
+- return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
++};
++
++template <typename Integer>
++class RandomIntegerGenerator final : public IGenerator<Integer> {
++ Catch::SimplePcg32& m_rng;
++ std::uniform_int_distribution<Integer> m_dist;
++ Integer m_current_number;
++public:
++
++ RandomIntegerGenerator(Integer a, Integer b):
++ m_rng(rng()),
++ m_dist(a, b) {
++ static_cast<void>(next());
++ }
++
++ Integer const& get() const override {
++ return m_current_number;
++ }
++ bool next() override {
++ m_current_number = m_dist(m_rng);
++ return true;
+ }
++};
+
+- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
++// TODO: Ideally this would be also constrained against the various char types,
++// but I don't expect users to run into that in practice.
++template <typename T>
++typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
++GeneratorWrapper<T>>::type
++random(T a, T b) {
++ return GeneratorWrapper<T>(
++ pf::make_unique<RandomIntegerGenerator<T>>(a, b)
++ );
++}
+
+- template<typename L>
+- // Note: The type after -> is weird, because VS2015 cannot parse
+- // the expression used in the typedef inside, when it is in
+- // return type. Yeah, ¯\_(ツ)_/¯
+- auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) {
+- using UnderlyingType = typename decltype(generatorExpression())::type;
++template <typename T>
++typename std::enable_if<std::is_floating_point<T>::value,
++GeneratorWrapper<T>>::type
++random(T a, T b) {
++ return GeneratorWrapper<T>(
++ pf::make_unique<RandomFloatingGenerator<T>>(a, b)
++ );
++}
++
++template <typename T>
++class RangeGenerator final : public IGenerator<T> {
++ T m_current;
++ T m_end;
++ T m_step;
++ bool m_positive;
++
++public:
++ RangeGenerator(T const& start, T const& end, T const& step):
++ m_current(start),
++ m_end(end),
++ m_step(step),
++ m_positive(m_step > T(0))
++ {
++ assert(m_current != m_end && "Range start and end cannot be equal");
++ assert(m_step != T(0) && "Step size cannot be zero");
++ assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end");
++ }
++
++ RangeGenerator(T const& start, T const& end):
++ RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
++ {}
++
++ T const& get() const override {
++ return m_current;
++ }
++
++ bool next() override {
++ m_current += m_step;
++ return (m_positive) ? (m_current < m_end) : (m_current > m_end);
++ }
++};
++
++template <typename T>
++GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
++ static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
++ return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
++}
++
++template <typename T>
++GeneratorWrapper<T> range(T const& start, T const& end) {
++ static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
++ return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
++}
+
+- IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
+- if( !tracker.hasGenerator() )
+- tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) );
++template <typename T>
++class IteratorGenerator final : public IGenerator<T> {
++ static_assert(!std::is_same<T, bool>::value,
++ "IteratorGenerator currently does not support bools"
++ "because of std::vector<bool> specialization");
++
++ std::vector<T> m_elems;
++ size_t m_current = 0;
++public:
++ template <typename InputIterator, typename InputSentinel>
++ IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
++ if (m_elems.empty()) {
++ Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
++ }
++ }
++
++ T const& get() const override {
++ return m_elems[m_current];
++ }
+
+- auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() );
+- return generator[tracker.getIndex()];
++ bool next() override {
++ ++m_current;
++ return m_current != m_elems.size();
+ }
++};
++
++template <typename InputIterator,
++ typename InputSentinel,
++ typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
++GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
++ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
++}
++
++template <typename Container,
++ typename ResultType = typename Container::value_type>
++GeneratorWrapper<ResultType> from_range(Container const& cnt) {
++ return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
++}
+
+ } // namespace Generators
+ } // namespace Catch
+
+-#define GENERATE( ... ) \
+- Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
+-
+-// end catch_generators.hpp
++// end catch_generators_specific.hpp
+
+ // These files are included here so the single_include script doesn't put them
+ // in the conditionally compiled sections
+@@ -3662,7 +4950,7 @@
+ arcSafeRelease( m_substr );
+ }
+
+- bool match( NSString* arg ) const override {
++ bool match( NSString* str ) const override {
+ return false;
+ }
+
+@@ -3685,7 +4973,7 @@
+ struct Contains : StringHolder {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+- bool match( NSString* str ) const {
++ bool match( NSString* str ) const override {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+@@ -3761,7 +5049,8 @@
+ // end catch_objc.hpp
+ #endif
+
+-#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES
++// Benchmarking needs the externally-facing parts of reporters to work
++#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ // start catch_external_interfaces.h
+
+ // start catch_reporter_bases.hpp
+@@ -3803,7 +5092,7 @@
+ virtual bool matches( std::string const& str ) const;
+
+ private:
+- std::string adjustCase( std::string const& str ) const;
++ std::string normaliseString( std::string const& str ) const;
+ CaseSensitive::Choice m_caseSensitivity;
+ WildcardPosition m_wildcard = NoWildcard;
+ std::string m_pattern;
+@@ -3817,36 +5106,40 @@
+
+ namespace Catch {
+
++ struct IConfig;
++
+ class TestSpec {
+- struct Pattern {
++ class Pattern {
++ public:
++ explicit Pattern( std::string const& name );
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
++ std::string const& name() const;
++ private:
++ std::string const m_name;
+ };
+ using PatternPtr = std::shared_ptr<Pattern>;
+
+ class NamePattern : public Pattern {
+ public:
+- NamePattern( std::string const& name );
+- virtual ~NamePattern();
+- virtual bool matches( TestCaseInfo const& testCase ) const override;
++ explicit NamePattern( std::string const& name, std::string const& filterString );
++ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ WildcardPattern m_wildcardPattern;
+ };
+
+ class TagPattern : public Pattern {
+ public:
+- TagPattern( std::string const& tag );
+- virtual ~TagPattern();
+- virtual bool matches( TestCaseInfo const& testCase ) const override;
++ explicit TagPattern( std::string const& tag, std::string const& filterString );
++ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ std::string m_tag;
+ };
+
+ class ExcludedPattern : public Pattern {
+ public:
+- ExcludedPattern( PatternPtr const& underlyingPattern );
+- virtual ~ExcludedPattern();
+- virtual bool matches( TestCaseInfo const& testCase ) const override;
++ explicit ExcludedPattern( PatternPtr const& underlyingPattern );
++ bool matches( TestCaseInfo const& testCase ) const override;
+ private:
+ PatternPtr m_underlyingPattern;
+ };
+@@ -3855,15 +5148,25 @@
+ std::vector<PatternPtr> m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const;
++ std::string name() const;
+ };
+
+ public:
++ struct FilterMatch {
++ std::string name;
++ std::vector<TestCase const*> tests;
++ };
++ using Matches = std::vector<FilterMatch>;
++ using vectorStrings = std::vector<std::string>;
++
+ bool hasFilters() const;
+ bool matches( TestCaseInfo const& testCase ) const;
++ Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
++ const vectorStrings & getInvalidArgs() const;
+
+ private:
+ std::vector<Filter> m_filters;
+-
++ std::vector<std::string> m_invalidArgs;
+ friend class TestSpecParser;
+ };
+ }
+@@ -3898,9 +5201,13 @@
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+ Mode m_mode = None;
++ Mode lastMode = None;
+ bool m_exclusion = false;
+- std::size_t m_start = std::string::npos, m_pos = 0;
++ std::size_t m_pos = 0;
++ std::size_t m_realPatternPos = 0;
+ std::string m_arg;
++ std::string m_substring;
++ std::string m_patternName;
+ std::vector<std::size_t> m_escapeChars;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+@@ -3913,32 +5220,32 @@
+ TestSpec testSpec();
+
+ private:
+- void visitChar( char c );
+- void startNewMode( Mode mode, std::size_t start );
++ bool visitChar( char c );
++ void startNewMode( Mode mode );
++ bool processNoneChar( char c );
++ void processNameChar( char c );
++ bool processOtherChar( char c );
++ void endMode();
+ void escape();
+- std::string subString() const;
++ bool isControlChar( char c ) const;
++ void saveLastMode();
++ void revertBackToLastMode();
++ void addFilter();
++ bool separate();
+
+- template<typename T>
+- void addPattern() {
+- std::string token = subString();
+- for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
+- token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+- m_escapeChars.clear();
+- if( startsWith( token, "exclude:" ) ) {
+- m_exclusion = true;
+- token = token.substr( 8 );
+- }
+- if( !token.empty() ) {
+- TestSpec::PatternPtr pattern = std::make_shared<T>( token );
+- if( m_exclusion )
+- pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
+- m_currentFilter.m_patterns.push_back( pattern );
+- }
+- m_exclusion = false;
+- m_mode = None;
++ // Handles common preprocessing of the pattern for name/tag patterns
++ std::string preprocessPattern();
++ // Adds the current pattern as a test name
++ void addNamePattern();
++ // Adds the current pattern as a tag
++ void addTagPattern();
++
++ inline void addCharToPattern(char c) {
++ m_substring += c;
++ m_patternName += c;
++ m_realPatternPos++;
+ }
+
+- void addFilter();
+ };
+ TestSpec parseTestSpec( std::string const& arg );
+
+@@ -3949,79 +5256,6 @@
+ #endif
+
+ // end catch_test_spec_parser.h
+-// start catch_interfaces_config.h
+-
+-#include <iosfwd>
+-#include <string>
+-#include <vector>
+-#include <memory>
+-
+-namespace Catch {
+-
+- enum class Verbosity {
+- Quiet = 0,
+- Normal,
+- High
+- };
+-
+- struct WarnAbout { enum What {
+- Nothing = 0x00,
+- NoAssertions = 0x01,
+- NoTests = 0x02
+- }; };
+-
+- struct ShowDurations { enum OrNot {
+- DefaultForReporter,
+- Always,
+- Never
+- }; };
+- struct RunTests { enum InWhatOrder {
+- InDeclarationOrder,
+- InLexicographicalOrder,
+- InRandomOrder
+- }; };
+- struct UseColour { enum YesOrNo {
+- Auto,
+- Yes,
+- No
+- }; };
+- struct WaitForKeypress { enum When {
+- Never,
+- BeforeStart = 1,
+- BeforeExit = 2,
+- BeforeStartAndExit = BeforeStart | BeforeExit
+- }; };
+-
+- class TestSpec;
+-
+- struct IConfig : NonCopyable {
+-
+- virtual ~IConfig();
+-
+- virtual bool allowThrows() const = 0;
+- virtual std::ostream& stream() const = 0;
+- virtual std::string name() const = 0;
+- virtual bool includeSuccessfulResults() const = 0;
+- virtual bool shouldDebugBreak() const = 0;
+- virtual bool warnAboutMissingAssertions() const = 0;
+- virtual bool warnAboutNoTests() const = 0;
+- virtual int abortAfter() const = 0;
+- virtual bool showInvisibles() const = 0;
+- virtual ShowDurations::OrNot showDurations() const = 0;
+- virtual TestSpec const& testSpec() const = 0;
+- virtual bool hasTestFilters() const = 0;
+- virtual RunTests::InWhatOrder runOrder() const = 0;
+- virtual unsigned int rngSeed() const = 0;
+- virtual int benchmarkResolutionMultiple() const = 0;
+- virtual UseColour::YesOrNo useColour() const = 0;
+- virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+- virtual Verbosity verbosity() const = 0;
+- };
+-
+- using IConfigPtr = std::shared_ptr<IConfig const>;
+-}
+-
+-// end catch_interfaces_config.h
+ // Libstdc++ doesn't like incomplete classes for unique_ptr
+
+ #include <memory>
+@@ -4052,11 +5286,17 @@
+
+ int abortAfter = -1;
+ unsigned int rngSeed = 0;
+- int benchmarkResolutionMultiple = 100;
++
++ bool benchmarkNoAnalysis = false;
++ unsigned int benchmarkSamples = 100;
++ double benchmarkConfidenceInterval = 0.95;
++ unsigned int benchmarkResamples = 100000;
++ std::chrono::milliseconds::rep benchmarkWarmupTime = 100;
+
+ Verbosity verbosity = Verbosity::Normal;
+ WarnAbout::What warnings = WarnAbout::Nothing;
+ ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter;
++ double minDuration = -1;
+ RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder;
+ UseColour::YesOrNo useColour = UseColour::Auto;
+ WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
+@@ -4091,10 +5331,10 @@
+ std::string getProcessName() const;
+ std::string const& getReporterName() const;
+
+- std::vector<std::string> const& getTestsOrTags() const;
++ std::vector<std::string> const& getTestsOrTags() const override;
+ std::vector<std::string> const& getSectionsToRun() const override;
+
+- virtual TestSpec const& testSpec() const override;
++ TestSpec const& testSpec() const override;
+ bool hasTestFilters() const override;
+
+ bool showHelp() const;
+@@ -4107,14 +5347,19 @@
+ bool warnAboutMissingAssertions() const override;
+ bool warnAboutNoTests() const override;
+ ShowDurations::OrNot showDurations() const override;
++ double minDuration() const override;
+ RunTests::InWhatOrder runOrder() const override;
+ unsigned int rngSeed() const override;
+- int benchmarkResolutionMultiple() const override;
+ UseColour::YesOrNo useColour() const override;
+ bool shouldDebugBreak() const override;
+ int abortAfter() const override;
+ bool showInvisibles() const override;
+ Verbosity verbosity() const override;
++ bool benchmarkNoAnalysis() const override;
++ int benchmarkSamples() const override;
++ double benchmarkConfidenceInterval() const override;
++ unsigned int benchmarkResamples() const override;
++ std::chrono::milliseconds benchmarkWarmupTime() const override;
+
+ private:
+
+@@ -4175,76 +5420,61 @@
+ } // end namespace Catch
+
+ // end catch_assertionresult.h
+-// start catch_option.hpp
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++// start catch_estimate.hpp
+
+-namespace Catch {
++ // Statistics estimates
+
+- // An optional type
+- template<typename T>
+- class Option {
+- public:
+- Option() : nullableValue( nullptr ) {}
+- Option( T const& _value )
+- : nullableValue( new( storage ) T( _value ) )
+- {}
+- Option( Option const& _other )
+- : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
+- {}
+
+- ~Option() {
+- reset();
+- }
++namespace Catch {
++ namespace Benchmark {
++ template <typename Duration>
++ struct Estimate {
++ Duration point;
++ Duration lower_bound;
++ Duration upper_bound;
++ double confidence_interval;
+
+- Option& operator= ( Option const& _other ) {
+- if( &_other != this ) {
+- reset();
+- if( _other )
+- nullableValue = new( storage ) T( *_other );
++ template <typename Duration2>
++ operator Estimate<Duration2>() const {
++ return { point, lower_bound, upper_bound, confidence_interval };
+ }
+- return *this;
+- }
+- Option& operator = ( T const& _value ) {
+- reset();
+- nullableValue = new( storage ) T( _value );
+- return *this;
+- }
+-
+- void reset() {
+- if( nullableValue )
+- nullableValue->~T();
+- nullableValue = nullptr;
+- }
++ };
++ } // namespace Benchmark
++} // namespace Catch
+
+- T& operator*() { return *nullableValue; }
+- T const& operator*() const { return *nullableValue; }
+- T* operator->() { return nullableValue; }
+- const T* operator->() const { return nullableValue; }
++// end catch_estimate.hpp
++// start catch_outlier_classification.hpp
+
+- T valueOr( T const& defaultValue ) const {
+- return nullableValue ? *nullableValue : defaultValue;
+- }
++// Outlier information
+
+- bool some() const { return nullableValue != nullptr; }
+- bool none() const { return nullableValue == nullptr; }
++namespace Catch {
++ namespace Benchmark {
++ struct OutlierClassification {
++ int samples_seen = 0;
++ int low_severe = 0; // more than 3 times IQR below Q1
++ int low_mild = 0; // 1.5 to 3 times IQR below Q1
++ int high_mild = 0; // 1.5 to 3 times IQR above Q3
++ int high_severe = 0; // more than 3 times IQR above Q3
+
+- bool operator !() const { return nullableValue == nullptr; }
+- explicit operator bool() const {
+- return some();
+- }
++ int total() const {
++ return low_severe + low_mild + high_mild + high_severe;
++ }
++ };
++ } // namespace Benchmark
++} // namespace Catch
+
+- private:
+- T *nullableValue;
+- alignas(alignof(T)) char storage[sizeof(T)];
+- };
++// end catch_outlier_classification.hpp
+
+-} // end namespace Catch
++#include <iterator>
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+-// end catch_option.hpp
+ #include <string>
+ #include <iosfwd>
+ #include <map>
+ #include <set>
+ #include <memory>
++#include <algorithm>
+
+ namespace Catch {
+
+@@ -4301,8 +5531,8 @@
+
+ AssertionStats( AssertionStats const& ) = default;
+ AssertionStats( AssertionStats && ) = default;
+- AssertionStats& operator = ( AssertionStats const& ) = default;
+- AssertionStats& operator = ( AssertionStats && ) = default;
++ AssertionStats& operator = ( AssertionStats const& ) = delete;
++ AssertionStats& operator = ( AssertionStats && ) = delete;
+ virtual ~AssertionStats();
+
+ AssertionResult assertionResult;
+@@ -4380,14 +5610,43 @@
+ bool aborting;
+ };
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ struct BenchmarkInfo {
+ std::string name;
++ double estimatedDuration;
++ int iterations;
++ int samples;
++ unsigned int resamples;
++ double clockResolution;
++ double clockCost;
+ };
++
++ template <class Duration>
+ struct BenchmarkStats {
+ BenchmarkInfo info;
+- std::size_t iterations;
+- uint64_t elapsedTimeInNanoseconds;
++
++ std::vector<Duration> samples;
++ Benchmark::Estimate<Duration> mean;
++ Benchmark::Estimate<Duration> standardDeviation;
++ Benchmark::OutlierClassification outliers;
++ double outlierVariance;
++
++ template <typename Duration2>
++ operator BenchmarkStats<Duration2>() const {
++ std::vector<Duration2> samples2;
++ samples2.reserve(samples.size());
++ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
++ return {
++ info,
++ std::move(samples2),
++ mean,
++ standardDeviation,
++ outliers,
++ outlierVariance,
++ };
++ }
+ };
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ struct IStreamingReporter {
+ virtual ~IStreamingReporter() = default;
+@@ -4400,23 +5659,26 @@
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
++ virtual void reportInvalidArguments(std::string const&) {}
++
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+- // *** experimental ***
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ virtual void benchmarkPreparing( std::string const& ) {}
+ virtual void benchmarkStarting( BenchmarkInfo const& ) {}
++ virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
++ virtual void benchmarkFailed( std::string const& ) {}
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+- // *** experimental ***
+- virtual void benchmarkEnded( BenchmarkStats const& ) {}
+-
+ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+@@ -4465,6 +5727,11 @@
+ // Returns double formatted as %.3f (format expected on output)
+ std::string getFormattedDuration( double duration );
+
++ //! Should the reporter show
++ bool shouldShowDuration( IConfig const& config, double duration );
++
++ std::string serializeFilters( std::vector<std::string> const& container );
++
+ template<typename DerivedT>
+ struct StreamingReporterBase : IStreamingReporter {
+
+@@ -4489,9 +5756,12 @@
+
+ void noMatchingTestCases(std::string const&) override {}
+
++ void reportInvalidArguments(std::string const&) override {}
++
+ void testRunStarting(TestRunInfo const& _testRunInfo) override {
+ currentTestRunInfo = _testRunInfo;
+ }
++
+ void testGroupStarting(GroupInfo const& _groupInfo) override {
+ currentGroupInfo = _groupInfo;
+ }
+@@ -4782,11 +6052,11 @@
+
+ class ReporterFactory : public IReporterFactory {
+
+- virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
++ IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+ return std::unique_ptr<T>( new T( config ) );
+ }
+
+- virtual std::string getDescription() const override {
++ std::string getDescription() const override {
+ return T::getDescription();
+ }
+ };
+@@ -4803,10 +6073,10 @@
+
+ class ListenerFactory : public IReporterFactory {
+
+- virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
++ IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+ return std::unique_ptr<T>( new T( config ) );
+ }
+- virtual std::string getDescription() const override {
++ std::string getDescription() const override {
+ return std::string();
+ }
+ };
+@@ -4822,14 +6092,16 @@
+ #if !defined(CATCH_CONFIG_DISABLE)
+
+ #define CATCH_REGISTER_REPORTER( name, reporterType ) \
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
+- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ #define CATCH_REGISTER_LISTENER( listenerType ) \
+- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+- namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
+- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ #else // CATCH_CONFIG_DISABLE
+
+ #define CATCH_REGISTER_REPORTER(name, reporterType)
+@@ -4851,8 +6123,6 @@
+
+ static std::string getDescription();
+
+- ReporterPreferences getPreferences() const override;
+-
+ void noMatchingTestCases(std::string const& spec) override;
+
+ void assertionStarting(AssertionInfo const&) override;
+@@ -4891,6 +6161,8 @@
+
+ void noMatchingTestCases(std::string const& spec) override;
+
++ void reportInvalidArguments(std::string const&arg) override;
++
+ void assertionStarting(AssertionInfo const&) override;
+
+ bool assertionEnded(AssertionStats const& _assertionStats) override;
+@@ -4898,13 +6170,17 @@
+ void sectionStarting(SectionInfo const& _sectionInfo) override;
+ void sectionEnded(SectionStats const& _sectionStats) override;
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ void benchmarkPreparing(std::string const& name) override;
+ void benchmarkStarting(BenchmarkInfo const& info) override;
+- void benchmarkEnded(BenchmarkStats const& stats) override;
++ void benchmarkEnded(BenchmarkStats<> const& stats) override;
++ void benchmarkFailed(std::string const& error) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void testCaseEnded(TestCaseStats const& _testCaseStats) override;
+ void testGroupEnded(TestGroupStats const& _testGroupStats) override;
+ void testRunEnded(TestRunStats const& _testRunStats) override;
+-
++ void testRunStarting(TestRunInfo const& _testRunInfo) override;
+ private:
+
+ void lazyPrint();
+@@ -4926,6 +6202,7 @@
+
+ void printTotalsDivider(Totals const& totals);
+ void printSummaryDivider();
++ void printTestFilters();
+
+ private:
+ bool m_headerPrinted = false;
+@@ -4945,6 +6222,14 @@
+ #include <vector>
+
+ namespace Catch {
++ enum class XmlFormatting {
++ None = 0x00,
++ Indent = 0x01,
++ Newline = 0x02,
++ };
++
++ XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
++ XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
+
+ class XmlEncode {
+ public:
+@@ -4966,14 +6251,14 @@
+
+ class ScopedElement {
+ public:
+- ScopedElement( XmlWriter* writer );
++ ScopedElement( XmlWriter* writer, XmlFormatting fmt );
+
+ ScopedElement( ScopedElement&& other ) noexcept;
+ ScopedElement& operator=( ScopedElement&& other ) noexcept;
+
+ ~ScopedElement();
+
+- ScopedElement& writeText( std::string const& text, bool indent = true );
++ ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+@@ -4983,6 +6268,7 @@
+
+ private:
+ mutable XmlWriter* m_writer = nullptr;
++ XmlFormatting m_fmt;
+ };
+
+ XmlWriter( std::ostream& os = Catch::cout() );
+@@ -4991,11 +6277,11 @@
+ XmlWriter( XmlWriter const& ) = delete;
+ XmlWriter& operator=( XmlWriter const& ) = delete;
+
+- XmlWriter& startElement( std::string const& name );
++ XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+- ScopedElement scopedElement( std::string const& name );
++ ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+- XmlWriter& endElement();
++ XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
+
+@@ -5008,9 +6294,9 @@
+ return writeAttribute( name, rss.str() );
+ }
+
+- XmlWriter& writeText( std::string const& text, bool indent = true );
++ XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+- XmlWriter& writeComment( std::string const& text );
++ XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+ void writeStylesheetRef( std::string const& url );
+
+@@ -5020,6 +6306,8 @@
+
+ private:
+
++ void applyFormatting(XmlFormatting fmt);
++
+ void writeDeclaration();
+
+ void newlineIfNecessary();
+@@ -5063,9 +6351,10 @@
+
+ void writeTestCase(TestCaseNode const& testCaseNode);
+
+- void writeSection(std::string const& className,
+- std::string const& rootName,
+- SectionNode const& sectionNode);
++ void writeSection( std::string const& className,
++ std::string const& rootName,
++ SectionNode const& sectionNode,
++ bool testOkToFail );
+
+ void writeAssertions(SectionNode const& sectionNode);
+ void writeAssertion(AssertionStats const& stats);
+@@ -5120,6 +6409,13 @@
+
+ void testRunEnded(TestRunStats const& testRunStats) override;
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ void benchmarkPreparing(std::string const& name) override;
++ void benchmarkStarting(BenchmarkInfo const&) override;
++ void benchmarkEnded(BenchmarkStats<> const&) override;
++ void benchmarkFailed(std::string const&) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ private:
+ Timer m_testCaseTimer;
+ XmlWriter m_xml;
+@@ -5133,6 +6429,1032 @@
+ // end catch_external_interfaces.h
+ #endif
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++// start catch_benchmarking_all.hpp
++
++// A proxy header that includes all of the benchmarking headers to allow
++// concise include of the benchmarking features. You should prefer the
++// individual includes in standard use.
++
++// start catch_benchmark.hpp
++
++ // Benchmark
++
++// start catch_chronometer.hpp
++
++// User-facing chronometer
++
++
++// start catch_clock.hpp
++
++// Clocks
++
++
++#include <chrono>
++#include <ratio>
++
++namespace Catch {
++ namespace Benchmark {
++ template <typename Clock>
++ using ClockDuration = typename Clock::duration;
++ template <typename Clock>
++ using FloatDuration = std::chrono::duration<double, typename Clock::period>;
++
++ template <typename Clock>
++ using TimePoint = typename Clock::time_point;
++
++ using default_clock = std::chrono::steady_clock;
++
++ template <typename Clock>
++ struct now {
++ TimePoint<Clock> operator()() const {
++ return Clock::now();
++ }
++ };
++
++ using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_clock.hpp
++// start catch_optimizer.hpp
++
++ // Hinting the optimizer
++
++
++#if defined(_MSC_VER)
++# include <atomic> // atomic_thread_fence
++#endif
++
++namespace Catch {
++ namespace Benchmark {
++#if defined(__GNUC__) || defined(__clang__)
++ template <typename T>
++ inline void keep_memory(T* p) {
++ asm volatile("" : : "g"(p) : "memory");
++ }
++ inline void keep_memory() {
++ asm volatile("" : : : "memory");
++ }
++
++ namespace Detail {
++ inline void optimizer_barrier() { keep_memory(); }
++ } // namespace Detail
++#elif defined(_MSC_VER)
++
++#pragma optimize("", off)
++ template <typename T>
++ inline void keep_memory(T* p) {
++ // thanks @milleniumbug
++ *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
++ }
++ // TODO equivalent keep_memory()
++#pragma optimize("", on)
++
++ namespace Detail {
++ inline void optimizer_barrier() {
++ std::atomic_thread_fence(std::memory_order_seq_cst);
++ }
++ } // namespace Detail
++
++#endif
++
++ template <typename T>
++ inline void deoptimize_value(T&& x) {
++ keep_memory(&x);
++ }
++
++ template <typename Fn, typename... Args>
++ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
++ deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
++ }
++
++ template <typename Fn, typename... Args>
++ inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type {
++ std::forward<Fn>(fn) (std::forward<Args...>(args...));
++ }
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_optimizer.hpp
++// start catch_complete_invoke.hpp
++
++// Invoke with a special case for void
++
++
++#include <type_traits>
++#include <utility>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename T>
++ struct CompleteType { using type = T; };
++ template <>
++ struct CompleteType<void> { struct type {}; };
++
++ template <typename T>
++ using CompleteType_t = typename CompleteType<T>::type;
++
++ template <typename Result>
++ struct CompleteInvoker {
++ template <typename Fun, typename... Args>
++ static Result invoke(Fun&& fun, Args&&... args) {
++ return std::forward<Fun>(fun)(std::forward<Args>(args)...);
++ }
++ };
++ template <>
++ struct CompleteInvoker<void> {
++ template <typename Fun, typename... Args>
++ static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
++ std::forward<Fun>(fun)(std::forward<Args>(args)...);
++ return {};
++ }
++ };
++
++ // invoke and not return void :(
++ template <typename Fun, typename... Args>
++ CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
++ return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
++ }
++
++ const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
++ } // namespace Detail
++
++ template <typename Fun>
++ Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
++ CATCH_TRY{
++ return Detail::complete_invoke(std::forward<Fun>(fun));
++ } CATCH_CATCH_ALL{
++ getResultCapture().benchmarkFailed(translateActiveException());
++ CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg);
++ }
++ }
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_complete_invoke.hpp
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ struct ChronometerConcept {
++ virtual void start() = 0;
++ virtual void finish() = 0;
++ virtual ~ChronometerConcept() = default;
++ };
++ template <typename Clock>
++ struct ChronometerModel final : public ChronometerConcept {
++ void start() override { started = Clock::now(); }
++ void finish() override { finished = Clock::now(); }
++
++ ClockDuration<Clock> elapsed() const { return finished - started; }
++
++ TimePoint<Clock> started;
++ TimePoint<Clock> finished;
++ };
++ } // namespace Detail
++
++ struct Chronometer {
++ public:
++ template <typename Fun>
++ void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
++
++ int runs() const { return k; }
++
++ Chronometer(Detail::ChronometerConcept& meter, int k)
++ : impl(&meter)
++ , k(k) {}
++
++ private:
++ template <typename Fun>
++ void measure(Fun&& fun, std::false_type) {
++ measure([&fun](int) { return fun(); }, std::true_type());
++ }
++
++ template <typename Fun>
++ void measure(Fun&& fun, std::true_type) {
++ Detail::optimizer_barrier();
++ impl->start();
++ for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i);
++ impl->finish();
++ Detail::optimizer_barrier();
++ }
++
++ Detail::ChronometerConcept* impl;
++ int k;
++ };
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_chronometer.hpp
++// start catch_environment.hpp
++
++// Environment information
++
++
++namespace Catch {
++ namespace Benchmark {
++ template <typename Duration>
++ struct EnvironmentEstimate {
++ Duration mean;
++ OutlierClassification outliers;
++
++ template <typename Duration2>
++ operator EnvironmentEstimate<Duration2>() const {
++ return { mean, outliers };
++ }
++ };
++ template <typename Clock>
++ struct Environment {
++ using clock_type = Clock;
++ EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
++ EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
++ };
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_environment.hpp
++// start catch_execution_plan.hpp
++
++ // Execution plan
++
++
++// start catch_benchmark_function.hpp
++
++ // Dumb std::function implementation for consistent call overhead
++
++
++#include <cassert>
++#include <type_traits>
++#include <utility>
++#include <memory>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename T>
++ using Decay = typename std::decay<T>::type;
++ template <typename T, typename U>
++ struct is_related
++ : std::is_same<Decay<T>, Decay<U>> {};
++
++ /// We need to reinvent std::function because every piece of code that might add overhead
++ /// in a measurement context needs to have consistent performance characteristics so that we
++ /// can account for it in the measurement.
++ /// Implementations of std::function with optimizations that aren't always applicable, like
++ /// small buffer optimizations, are not uncommon.
++ /// This is effectively an implementation of std::function without any such optimizations;
++ /// it may be slow, but it is consistently slow.
++ struct BenchmarkFunction {
++ private:
++ struct callable {
++ virtual void call(Chronometer meter) const = 0;
++ virtual callable* clone() const = 0;
++ virtual ~callable() = default;
++ };
++ template <typename Fun>
++ struct model : public callable {
++ model(Fun&& fun) : fun(std::move(fun)) {}
++ model(Fun const& fun) : fun(fun) {}
++
++ model<Fun>* clone() const override { return new model<Fun>(*this); }
++
++ void call(Chronometer meter) const override {
++ call(meter, is_callable<Fun(Chronometer)>());
++ }
++ void call(Chronometer meter, std::true_type) const {
++ fun(meter);
++ }
++ void call(Chronometer meter, std::false_type) const {
++ meter.measure(fun);
++ }
++
++ Fun fun;
++ };
++
++ struct do_nothing { void operator()() const {} };
++
++ template <typename T>
++ BenchmarkFunction(model<T>* c) : f(c) {}
++
++ public:
++ BenchmarkFunction()
++ : f(new model<do_nothing>{ {} }) {}
++
++ template <typename Fun,
++ typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
++ BenchmarkFunction(Fun&& fun)
++ : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
++
++ BenchmarkFunction(BenchmarkFunction&& that)
++ : f(std::move(that.f)) {}
++
++ BenchmarkFunction(BenchmarkFunction const& that)
++ : f(that.f->clone()) {}
++
++ BenchmarkFunction& operator=(BenchmarkFunction&& that) {
++ f = std::move(that.f);
++ return *this;
++ }
++
++ BenchmarkFunction& operator=(BenchmarkFunction const& that) {
++ f.reset(that.f->clone());
++ return *this;
++ }
++
++ void operator()(Chronometer meter) const { f->call(meter); }
++
++ private:
++ std::unique_ptr<callable> f;
++ };
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_benchmark_function.hpp
++// start catch_repeat.hpp
++
++// repeat algorithm
++
++
++#include <type_traits>
++#include <utility>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename Fun>
++ struct repeater {
++ void operator()(int k) const {
++ for (int i = 0; i < k; ++i) {
++ fun();
++ }
++ }
++ Fun fun;
++ };
++ template <typename Fun>
++ repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
++ return { std::forward<Fun>(fun) };
++ }
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_repeat.hpp
++// start catch_run_for_at_least.hpp
++
++// Run a function for a minimum amount of time
++
++
++// start catch_measure.hpp
++
++// Measure
++
++
++// start catch_timing.hpp
++
++// Timing
++
++
++#include <tuple>
++#include <type_traits>
++
++namespace Catch {
++ namespace Benchmark {
++ template <typename Duration, typename Result>
++ struct Timing {
++ Duration elapsed;
++ Result result;
++ int iterations;
++ };
++ template <typename Clock, typename Func, typename... Args>
++ using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_timing.hpp
++#include <utility>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename Clock, typename Fun, typename... Args>
++ TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
++ auto start = Clock::now();
++ auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...);
++ auto end = Clock::now();
++ auto delta = end - start;
++ return { delta, std::forward<decltype(r)>(r), 1 };
++ }
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_measure.hpp
++#include <utility>
++#include <type_traits>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename Clock, typename Fun>
++ TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
++ return Detail::measure<Clock>(fun, iters);
++ }
++ template <typename Clock, typename Fun>
++ TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
++ Detail::ChronometerModel<Clock> meter;
++ auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
++
++ return { meter.elapsed(), std::move(result), iters };
++ }
++
++ template <typename Clock, typename Fun>
++ using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
++
++ struct optimized_away_error : std::exception {
++ const char* what() const noexcept override {
++ return "could not measure benchmark, maybe it was optimized away";
++ }
++ };
++
++ template <typename Clock, typename Fun>
++ TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) {
++ auto iters = seed;
++ while (iters < (1 << 30)) {
++ auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
++
++ if (Timing.elapsed >= how_long) {
++ return { Timing.elapsed, std::move(Timing.result), iters };
++ }
++ iters *= 2;
++ }
++ Catch::throw_exception(optimized_away_error{});
++ }
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_run_for_at_least.hpp
++#include <algorithm>
++#include <iterator>
++
++namespace Catch {
++ namespace Benchmark {
++ template <typename Duration>
++ struct ExecutionPlan {
++ int iterations_per_sample;
++ Duration estimated_duration;
++ Detail::BenchmarkFunction benchmark;
++ Duration warmup_time;
++ int warmup_iterations;
++
++ template <typename Duration2>
++ operator ExecutionPlan<Duration2>() const {
++ return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
++ }
++
++ template <typename Clock>
++ std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
++ // warmup a bit
++ Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
++
++ std::vector<FloatDuration<Clock>> times;
++ times.reserve(cfg.benchmarkSamples());
++ std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
++ Detail::ChronometerModel<Clock> model;
++ this->benchmark(Chronometer(model, iterations_per_sample));
++ auto sample_time = model.elapsed() - env.clock_cost.mean;
++ if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
++ return sample_time / iterations_per_sample;
++ });
++ return times;
++ }
++ };
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_execution_plan.hpp
++// start catch_estimate_clock.hpp
++
++ // Environment measurement
++
++
++// start catch_stats.hpp
++
++// Statistical analysis tools
++
++
++#include <algorithm>
++#include <functional>
++#include <vector>
++#include <iterator>
++#include <numeric>
++#include <tuple>
++#include <cmath>
++#include <utility>
++#include <cstddef>
++#include <random>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ using sample = std::vector<double>;
++
++ double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
++
++ template <typename Iterator>
++ OutlierClassification classify_outliers(Iterator first, Iterator last) {
++ std::vector<double> copy(first, last);
++
++ auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
++ auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
++ auto iqr = q3 - q1;
++ auto los = q1 - (iqr * 3.);
++ auto lom = q1 - (iqr * 1.5);
++ auto him = q3 + (iqr * 1.5);
++ auto his = q3 + (iqr * 3.);
++
++ OutlierClassification o;
++ for (; first != last; ++first) {
++ auto&& t = *first;
++ if (t < los) ++o.low_severe;
++ else if (t < lom) ++o.low_mild;
++ else if (t > his) ++o.high_severe;
++ else if (t > him) ++o.high_mild;
++ ++o.samples_seen;
++ }
++ return o;
++ }
++
++ template <typename Iterator>
++ double mean(Iterator first, Iterator last) {
++ auto count = last - first;
++ double sum = std::accumulate(first, last, 0.);
++ return sum / count;
++ }
++
++ template <typename URng, typename Iterator, typename Estimator>
++ sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) {
++ auto n = last - first;
++ std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
++
++ sample out;
++ out.reserve(resamples);
++ std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
++ std::vector<double> resampled;
++ resampled.reserve(n);
++ std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
++ return estimator(resampled.begin(), resampled.end());
++ });
++ std::sort(out.begin(), out.end());
++ return out;
++ }
++
++ template <typename Estimator, typename Iterator>
++ sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
++ auto n = last - first;
++ auto second = std::next(first);
++ sample results;
++ results.reserve(n);
++
++ for (auto it = first; it != last; ++it) {
++ std::iter_swap(it, first);
++ results.push_back(estimator(second, last));
++ }
++
++ return results;
++ }
++
++ inline double normal_cdf(double x) {
++ return std::erfc(-x / std::sqrt(2.0)) / 2.0;
++ }
++
++ double erfc_inv(double x);
++
++ double normal_quantile(double p);
++
++ template <typename Iterator, typename Estimator>
++ Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
++ auto n_samples = last - first;
++
++ double point = estimator(first, last);
++ // Degenerate case with a single sample
++ if (n_samples == 1) return { point, point, point, confidence_level };
++
++ sample jack = jackknife(estimator, first, last);
++ double jack_mean = mean(jack.begin(), jack.end());
++ double sum_squares, sum_cubes;
++ std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
++ auto d = jack_mean - x;
++ auto d2 = d * d;
++ auto d3 = d2 * d;
++ return { sqcb.first + d2, sqcb.second + d3 };
++ });
++
++ double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
++ int n = static_cast<int>(resample.size());
++ double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n;
++ // degenerate case with uniform samples
++ if (prob_n == 0) return { point, point, point, confidence_level };
++
++ double bias = normal_quantile(prob_n);
++ double z1 = normal_quantile((1. - confidence_level) / 2.);
++
++ auto cumn = [n](double x) -> int {
++ return std::lround(normal_cdf(x) * n); };
++ auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
++ double b1 = bias + z1;
++ double b2 = bias - z1;
++ double a1 = a(b1);
++ double a2 = a(b2);
++ auto lo = (std::max)(cumn(a1), 0);
++ auto hi = (std::min)(cumn(a2), n - 1);
++
++ return { point, resample[lo], resample[hi], confidence_level };
++ }
++
++ double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
++
++ struct bootstrap_analysis {
++ Estimate<double> mean;
++ Estimate<double> standard_deviation;
++ double outlier_variance;
++ };
++
++ bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_stats.hpp
++#include <algorithm>
++#include <iterator>
++#include <tuple>
++#include <vector>
++#include <cmath>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename Clock>
++ std::vector<double> resolution(int k) {
++ std::vector<TimePoint<Clock>> times;
++ times.reserve(k + 1);
++ std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
++
++ std::vector<double> deltas;
++ deltas.reserve(k);
++ std::transform(std::next(times.begin()), times.end(), times.begin(),
++ std::back_inserter(deltas),
++ [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
++
++ return deltas;
++ }
++
++ const auto warmup_iterations = 10000;
++ const auto warmup_time = std::chrono::milliseconds(100);
++ const auto minimum_ticks = 1000;
++ const auto warmup_seed = 10000;
++ const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
++ const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
++ const auto clock_cost_estimation_tick_limit = 100000;
++ const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
++ const auto clock_cost_estimation_iterations = 10000;
++
++ template <typename Clock>
++ int warmup() {
++ return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
++ .iterations;
++ }
++ template <typename Clock>
++ EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
++ auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
++ .result;
++ return {
++ FloatDuration<Clock>(mean(r.begin(), r.end())),
++ classify_outliers(r.begin(), r.end()),
++ };
++ }
++ template <typename Clock>
++ EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
++ auto time_limit = (std::min)(
++ resolution * clock_cost_estimation_tick_limit,
++ FloatDuration<Clock>(clock_cost_estimation_time_limit));
++ auto time_clock = [](int k) {
++ return Detail::measure<Clock>([k] {
++ for (int i = 0; i < k; ++i) {
++ volatile auto ignored = Clock::now();
++ (void)ignored;
++ }
++ }).elapsed;
++ };
++ time_clock(1);
++ int iters = clock_cost_estimation_iterations;
++ auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
++ std::vector<double> times;
++ int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
++ times.reserve(nsamples);
++ std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
++ return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
++ });
++ return {
++ FloatDuration<Clock>(mean(times.begin(), times.end())),
++ classify_outliers(times.begin(), times.end()),
++ };
++ }
++
++ template <typename Clock>
++ Environment<FloatDuration<Clock>> measure_environment() {
++ static Environment<FloatDuration<Clock>>* env = nullptr;
++ if (env) {
++ return *env;
++ }
++
++ auto iters = Detail::warmup<Clock>();
++ auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
++ auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
++
++ env = new Environment<FloatDuration<Clock>>{ resolution, cost };
++ return *env;
++ }
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_estimate_clock.hpp
++// start catch_analyse.hpp
++
++ // Run and analyse one benchmark
++
++
++// start catch_sample_analysis.hpp
++
++// Benchmark results
++
++
++#include <algorithm>
++#include <vector>
++#include <string>
++#include <iterator>
++
++namespace Catch {
++ namespace Benchmark {
++ template <typename Duration>
++ struct SampleAnalysis {
++ std::vector<Duration> samples;
++ Estimate<Duration> mean;
++ Estimate<Duration> standard_deviation;
++ OutlierClassification outliers;
++ double outlier_variance;
++
++ template <typename Duration2>
++ operator SampleAnalysis<Duration2>() const {
++ std::vector<Duration2> samples2;
++ samples2.reserve(samples.size());
++ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
++ return {
++ std::move(samples2),
++ mean,
++ standard_deviation,
++ outliers,
++ outlier_variance,
++ };
++ }
++ };
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_sample_analysis.hpp
++#include <algorithm>
++#include <iterator>
++#include <vector>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename Duration, typename Iterator>
++ SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
++ if (!cfg.benchmarkNoAnalysis()) {
++ std::vector<double> samples;
++ samples.reserve(last - first);
++ std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
++
++ auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
++ auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
++
++ auto wrap_estimate = [](Estimate<double> e) {
++ return Estimate<Duration> {
++ Duration(e.point),
++ Duration(e.lower_bound),
++ Duration(e.upper_bound),
++ e.confidence_interval,
++ };
++ };
++ std::vector<Duration> samples2;
++ samples2.reserve(samples.size());
++ std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
++ return {
++ std::move(samples2),
++ wrap_estimate(analysis.mean),
++ wrap_estimate(analysis.standard_deviation),
++ outliers,
++ analysis.outlier_variance,
++ };
++ } else {
++ std::vector<Duration> samples;
++ samples.reserve(last - first);
++
++ Duration mean = Duration(0);
++ int i = 0;
++ for (auto it = first; it < last; ++it, ++i) {
++ samples.push_back(Duration(*it));
++ mean += Duration(*it);
++ }
++ mean /= i;
++
++ return {
++ std::move(samples),
++ Estimate<Duration>{mean, mean, mean, 0.0},
++ Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
++ OutlierClassification{},
++ 0.0
++ };
++ }
++ }
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++// end catch_analyse.hpp
++#include <algorithm>
++#include <functional>
++#include <string>
++#include <vector>
++#include <cmath>
++
++namespace Catch {
++ namespace Benchmark {
++ struct Benchmark {
++ Benchmark(std::string &&name)
++ : name(std::move(name)) {}
++
++ template <class FUN>
++ Benchmark(std::string &&name, FUN &&func)
++ : fun(std::move(func)), name(std::move(name)) {}
++
++ template <typename Clock>
++ ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
++ auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
++ auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
++ auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
++ int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
++ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
++ }
++
++ template <typename Clock = default_clock>
++ void run() {
++ IConfigPtr cfg = getCurrentContext().getConfig();
++
++ auto env = Detail::measure_environment<Clock>();
++
++ getResultCapture().benchmarkPreparing(name);
++ CATCH_TRY{
++ auto plan = user_code([&] {
++ return prepare<Clock>(*cfg, env);
++ });
++
++ BenchmarkInfo info {
++ name,
++ plan.estimated_duration.count(),
++ plan.iterations_per_sample,
++ cfg->benchmarkSamples(),
++ cfg->benchmarkResamples(),
++ env.clock_resolution.mean.count(),
++ env.clock_cost.mean.count()
++ };
++
++ getResultCapture().benchmarkStarting(info);
++
++ auto samples = user_code([&] {
++ return plan.template run<Clock>(*cfg, env);
++ });
++
++ auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
++ BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
++ getResultCapture().benchmarkEnded(stats);
++
++ } CATCH_CATCH_ALL{
++ if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
++ std::rethrow_exception(std::current_exception());
++ }
++ }
++
++ // sets lambda to be used in fun *and* executes benchmark!
++ template <typename Fun,
++ typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0>
++ Benchmark & operator=(Fun func) {
++ fun = Detail::BenchmarkFunction(func);
++ run();
++ return *this;
++ }
++
++ explicit operator bool() {
++ return true;
++ }
++
++ private:
++ Detail::BenchmarkFunction fun;
++ std::string name;
++ };
++ }
++} // namespace Catch
++
++#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
++#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
++
++#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
++ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
++ BenchmarkName = [&](int benchmarkIndex)
++
++#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
++ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
++ BenchmarkName = [&]
++
++// end catch_benchmark.hpp
++// start catch_constructor.hpp
++
++// Constructor and destructor helpers
++
++
++#include <type_traits>
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++ template <typename T, bool Destruct>
++ struct ObjectStorage
++ {
++ using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
++
++ ObjectStorage() : data() {}
++
++ ObjectStorage(const ObjectStorage& other)
++ {
++ new(&data) T(other.stored_object());
++ }
++
++ ObjectStorage(ObjectStorage&& other)
++ {
++ new(&data) T(std::move(other.stored_object()));
++ }
++
++ ~ObjectStorage() { destruct_on_exit<T>(); }
++
++ template <typename... Args>
++ void construct(Args&&... args)
++ {
++ new (&data) T(std::forward<Args>(args)...);
++ }
++
++ template <bool AllowManualDestruction = !Destruct>
++ typename std::enable_if<AllowManualDestruction>::type destruct()
++ {
++ stored_object().~T();
++ }
++
++ private:
++ // If this is a constructor benchmark, destruct the underlying object
++ template <typename U>
++ void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
++ // Otherwise, don't
++ template <typename U>
++ void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
++
++ T& stored_object() {
++ return *static_cast<T*>(static_cast<void*>(&data));
++ }
++
++ T const& stored_object() const {
++ return *static_cast<T*>(static_cast<void*>(&data));
++ }
++
++ TStorage data;
++ };
++ }
++
++ template <typename T>
++ using storage_for = Detail::ObjectStorage<T, true>;
++
++ template <typename T>
++ using destructable_object = Detail::ObjectStorage<T, false>;
++ }
++}
++
++// end catch_constructor.hpp
++// end catch_benchmarking_all.hpp
++#endif
++
+ #endif // ! CATCH_CONFIG_IMPL_ONLY
+
+ #ifdef CATCH_IMPL
+@@ -5158,23 +7480,37 @@
+ SourceLineInfo location;
+
+ NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
++ friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
++ return lhs.name == rhs.name
++ && lhs.location == rhs.location;
++ }
+ };
+
+- struct ITracker;
++ class ITracker;
+
+ using ITrackerPtr = std::shared_ptr<ITracker>;
+
+- struct ITracker {
+- virtual ~ITracker();
++ class ITracker {
++ NameAndLocation m_nameAndLocation;
++
++ public:
++ ITracker(NameAndLocation const& nameAndLoc) :
++ m_nameAndLocation(nameAndLoc)
++ {}
+
+ // static queries
+- virtual NameAndLocation const& nameAndLocation() const = 0;
++ NameAndLocation const& nameAndLocation() const {
++ return m_nameAndLocation;
++ }
++
++ virtual ~ITracker();
+
+ // dynamic queries
+ virtual bool isComplete() const = 0; // Successfully completed or failed
+ virtual bool isSuccessfullyCompleted() const = 0;
+ virtual bool isOpen() const = 0; // Started but not complete
+ virtual bool hasChildren() const = 0;
++ virtual bool hasStarted() const = 0;
+
+ virtual ITracker& parent() = 0;
+
+@@ -5189,7 +7525,7 @@
+
+ // Debug/ checking
+ virtual bool isSectionTracker() const = 0;
+- virtual bool isIndexTracker() const = 0;
++ virtual bool isGeneratorTracker() const = 0;
+ };
+
+ class TrackerContext {
+@@ -5206,8 +7542,6 @@
+
+ public:
+
+- static TrackerContext& instance();
+-
+ ITracker& startRun();
+ void endRun();
+
+@@ -5231,7 +7565,6 @@
+ };
+
+ using Children = std::vector<ITrackerPtr>;
+- NameAndLocation m_nameAndLocation;
+ TrackerContext& m_ctx;
+ ITracker* m_parent;
+ Children m_children;
+@@ -5240,11 +7573,13 @@
+ public:
+ TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+- NameAndLocation const& nameAndLocation() const override;
+ bool isComplete() const override;
+ bool isSuccessfullyCompleted() const override;
+ bool isOpen() const override;
+ bool hasChildren() const override;
++ bool hasStarted() const override {
++ return m_runState != NotStarted;
++ }
+
+ void addChild( ITrackerPtr const& child ) override;
+
+@@ -5254,7 +7589,7 @@
+ void openChild() override;
+
+ bool isSectionTracker() const override;
+- bool isIndexTracker() const override;
++ bool isGeneratorTracker() const override;
+
+ void open();
+
+@@ -5269,33 +7604,24 @@
+
+ class SectionTracker : public TrackerBase {
+ std::vector<std::string> m_filters;
++ std::string m_trimmed_name;
+ public:
+ SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+ bool isSectionTracker() const override;
+
++ bool isComplete() const override;
++
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
+
+ void tryOpen();
+
+ void addInitialFilters( std::vector<std::string> const& filters );
+ void addNextFilters( std::vector<std::string> const& filters );
+- };
+-
+- class IndexTracker : public TrackerBase {
+- int m_size;
+- int m_index = -1;
+- public:
+- IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size );
+-
+- bool isIndexTracker() const override;
+- void close() override;
+-
+- static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size );
+-
+- int index() const;
+-
+- void moveNext();
++ //! Returns filters active in this tracker
++ std::vector<std::string> const& getFilters() const;
++ //! Returns whitespace-trimmed name of the tracked section
++ std::string const& trimmedName() const;
+ };
+
+ } // namespace TestCaseTracking
+@@ -5303,7 +7629,6 @@
+ using TestCaseTracking::ITracker;
+ using TestCaseTracking::TrackerContext;
+ using TestCaseTracking::SectionTracker;
+-using TestCaseTracking::IndexTracker;
+
+ } // namespace Catch
+
+@@ -5321,6 +7646,217 @@
+ }
+ // end catch_leak_detector.h
+ // Cpp files will be included in the single-header file here
++// start catch_stats.cpp
++
++// Statistical analysis tools
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++
++#include <cassert>
++#include <random>
++
++#if defined(CATCH_CONFIG_USE_ASYNC)
++#include <future>
++#endif
++
++namespace {
++ double erf_inv(double x) {
++ // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
++ double w, p;
++
++ w = -log((1.0 - x) * (1.0 + x));
++
++ if (w < 6.250000) {
++ w = w - 3.125000;
++ p = -3.6444120640178196996e-21;
++ p = -1.685059138182016589e-19 + p * w;
++ p = 1.2858480715256400167e-18 + p * w;
++ p = 1.115787767802518096e-17 + p * w;
++ p = -1.333171662854620906e-16 + p * w;
++ p = 2.0972767875968561637e-17 + p * w;
++ p = 6.6376381343583238325e-15 + p * w;
++ p = -4.0545662729752068639e-14 + p * w;
++ p = -8.1519341976054721522e-14 + p * w;
++ p = 2.6335093153082322977e-12 + p * w;
++ p = -1.2975133253453532498e-11 + p * w;
++ p = -5.4154120542946279317e-11 + p * w;
++ p = 1.051212273321532285e-09 + p * w;
++ p = -4.1126339803469836976e-09 + p * w;
++ p = -2.9070369957882005086e-08 + p * w;
++ p = 4.2347877827932403518e-07 + p * w;
++ p = -1.3654692000834678645e-06 + p * w;
++ p = -1.3882523362786468719e-05 + p * w;
++ p = 0.0001867342080340571352 + p * w;
++ p = -0.00074070253416626697512 + p * w;
++ p = -0.0060336708714301490533 + p * w;
++ p = 0.24015818242558961693 + p * w;
++ p = 1.6536545626831027356 + p * w;
++ } else if (w < 16.000000) {
++ w = sqrt(w) - 3.250000;
++ p = 2.2137376921775787049e-09;
++ p = 9.0756561938885390979e-08 + p * w;
++ p = -2.7517406297064545428e-07 + p * w;
++ p = 1.8239629214389227755e-08 + p * w;
++ p = 1.5027403968909827627e-06 + p * w;
++ p = -4.013867526981545969e-06 + p * w;
++ p = 2.9234449089955446044e-06 + p * w;
++ p = 1.2475304481671778723e-05 + p * w;
++ p = -4.7318229009055733981e-05 + p * w;
++ p = 6.8284851459573175448e-05 + p * w;
++ p = 2.4031110387097893999e-05 + p * w;
++ p = -0.0003550375203628474796 + p * w;
++ p = 0.00095328937973738049703 + p * w;
++ p = -0.0016882755560235047313 + p * w;
++ p = 0.0024914420961078508066 + p * w;
++ p = -0.0037512085075692412107 + p * w;
++ p = 0.005370914553590063617 + p * w;
++ p = 1.0052589676941592334 + p * w;
++ p = 3.0838856104922207635 + p * w;
++ } else {
++ w = sqrt(w) - 5.000000;
++ p = -2.7109920616438573243e-11;
++ p = -2.5556418169965252055e-10 + p * w;
++ p = 1.5076572693500548083e-09 + p * w;
++ p = -3.7894654401267369937e-09 + p * w;
++ p = 7.6157012080783393804e-09 + p * w;
++ p = -1.4960026627149240478e-08 + p * w;
++ p = 2.9147953450901080826e-08 + p * w;
++ p = -6.7711997758452339498e-08 + p * w;
++ p = 2.2900482228026654717e-07 + p * w;
++ p = -9.9298272942317002539e-07 + p * w;
++ p = 4.5260625972231537039e-06 + p * w;
++ p = -1.9681778105531670567e-05 + p * w;
++ p = 7.5995277030017761139e-05 + p * w;
++ p = -0.00021503011930044477347 + p * w;
++ p = -0.00013871931833623122026 + p * w;
++ p = 1.0103004648645343977 + p * w;
++ p = 4.8499064014085844221 + p * w;
++ }
++ return p * x;
++ }
++
++ double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
++ auto m = Catch::Benchmark::Detail::mean(first, last);
++ double variance = std::accumulate(first, last, 0., [m](double a, double b) {
++ double diff = b - m;
++ return a + diff * diff;
++ }) / (last - first);
++ return std::sqrt(variance);
++ }
++
++}
++
++namespace Catch {
++ namespace Benchmark {
++ namespace Detail {
++
++ double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
++ auto count = last - first;
++ double idx = (count - 1) * k / static_cast<double>(q);
++ int j = static_cast<int>(idx);
++ double g = idx - j;
++ std::nth_element(first, first + j, last);
++ auto xj = first[j];
++ if (g == 0) return xj;
++
++ auto xj1 = *std::min_element(first + (j + 1), last);
++ return xj + g * (xj1 - xj);
++ }
++
++ double erfc_inv(double x) {
++ return erf_inv(1.0 - x);
++ }
++
++ double normal_quantile(double p) {
++ static const double ROOT_TWO = std::sqrt(2.0);
++
++ double result = 0.0;
++ assert(p >= 0 && p <= 1);
++ if (p < 0 || p > 1) {
++ return result;
++ }
++
++ result = -erfc_inv(2.0 * p);
++ // result *= normal distribution standard deviation (1.0) * sqrt(2)
++ result *= /*sd * */ ROOT_TWO;
++ // result += normal disttribution mean (0)
++ return result;
++ }
++
++ double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
++ double sb = stddev.point;
++ double mn = mean.point / n;
++ double mg_min = mn / 2.;
++ double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
++ double sg2 = sg * sg;
++ double sb2 = sb * sb;
++
++ auto c_max = [n, mn, sb2, sg2](double x) -> double {
++ double k = mn - x;
++ double d = k * k;
++ double nd = n * d;
++ double k0 = -n * nd;
++ double k1 = sb2 - n * sg2 + nd;
++ double det = k1 * k1 - 4 * sg2 * k0;
++ return (int)(-2. * k0 / (k1 + std::sqrt(det)));
++ };
++
++ auto var_out = [n, sb2, sg2](double c) {
++ double nc = n - c;
++ return (nc / n) * (sb2 - nc * sg2);
++ };
++
++ return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
++ }
++
++ bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
++ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
++ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
++ static std::random_device entropy;
++ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
++
++ auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
++
++ auto mean = &Detail::mean<std::vector<double>::iterator>;
++ auto stddev = &standard_deviation;
++
++#if defined(CATCH_CONFIG_USE_ASYNC)
++ auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
++ auto seed = entropy();
++ return std::async(std::launch::async, [=] {
++ std::mt19937 rng(seed);
++ auto resampled = resample(rng, n_resamples, first, last, f);
++ return bootstrap(confidence_level, first, last, resampled, f);
++ });
++ };
++
++ auto mean_future = Estimate(mean);
++ auto stddev_future = Estimate(stddev);
++
++ auto mean_estimate = mean_future.get();
++ auto stddev_estimate = stddev_future.get();
++#else
++ auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
++ auto seed = entropy();
++ std::mt19937 rng(seed);
++ auto resampled = resample(rng, n_resamples, first, last, f);
++ return bootstrap(confidence_level, first, last, resampled, f);
++ };
++
++ auto mean_estimate = Estimate(mean);
++ auto stddev_estimate = Estimate(stddev);
++#endif // CATCH_USE_ASYNC
++
++ double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
++
++ return { mean_estimate, stddev_estimate, outlier_variance };
++ }
++ } // namespace Detail
++ } // namespace Benchmark
++} // namespace Catch
++
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++// end catch_stats.cpp
+ // start catch_approx.cpp
+
+ #include <cmath>
+@@ -5365,21 +7901,22 @@
+ bool Approx::equalityComparisonImpl(const double other) const {
+ // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
+ // Thanks to Richard Harris for his help refining the scaled margin value
+- return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
++ return marginComparison(m_value, other, m_margin)
++ || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
+ }
+
+- void Approx::setMargin(double margin) {
+- CATCH_ENFORCE(margin >= 0,
+- "Invalid Approx::margin: " << margin << '.'
++ void Approx::setMargin(double newMargin) {
++ CATCH_ENFORCE(newMargin >= 0,
++ "Invalid Approx::margin: " << newMargin << '.'
+ << " Approx::Margin has to be non-negative.");
+- m_margin = margin;
++ m_margin = newMargin;
+ }
+
+- void Approx::setEpsilon(double epsilon) {
+- CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0,
+- "Invalid Approx::epsilon: " << epsilon << '.'
++ void Approx::setEpsilon(double newEpsilon) {
++ CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
++ "Invalid Approx::epsilon: " << newEpsilon << '.'
+ << " Approx::epsilon has to be in [0, 1]");
+- m_epsilon = epsilon;
++ m_epsilon = newEpsilon;
+ }
+
+ } // end namespace Detail
+@@ -5401,58 +7938,6 @@
+ // end catch_approx.cpp
+ // start catch_assertionhandler.cpp
+
+-// start catch_context.h
+-
+-#include <memory>
+-
+-namespace Catch {
+-
+- struct IResultCapture;
+- struct IRunner;
+- struct IConfig;
+- struct IMutableContext;
+-
+- using IConfigPtr = std::shared_ptr<IConfig const>;
+-
+- struct IContext
+- {
+- virtual ~IContext();
+-
+- virtual IResultCapture* getResultCapture() = 0;
+- virtual IRunner* getRunner() = 0;
+- virtual IConfigPtr const& getConfig() const = 0;
+- };
+-
+- struct IMutableContext : IContext
+- {
+- virtual ~IMutableContext();
+- virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+- virtual void setRunner( IRunner* runner ) = 0;
+- virtual void setConfig( IConfigPtr const& config ) = 0;
+-
+- private:
+- static IMutableContext *currentContext;
+- friend IMutableContext& getCurrentMutableContext();
+- friend void cleanUpContext();
+- static void createContext();
+- };
+-
+- inline IMutableContext& getCurrentMutableContext()
+- {
+- if( !IMutableContext::currentContext )
+- IMutableContext::createContext();
+- return *IMutableContext::currentContext;
+- }
+-
+- inline IContext& getCurrentContext()
+- {
+- return getCurrentMutableContext();
+- }
+-
+- void cleanUpContext();
+-}
+-
+-// end catch_context.h
+ // start catch_debugger.h
+
+ namespace Catch {
+@@ -5461,7 +7946,24 @@
+
+ #ifdef CATCH_PLATFORM_MAC
+
+- #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
++ #if defined(__i386__) || defined(__x86_64__)
++ #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
++ #elif defined(__aarch64__)
++ #define CATCH_TRAP() __asm__(".inst 0xd4200000")
++ #endif
++
++#elif defined(CATCH_PLATFORM_IPHONE)
++
++ // use inline assembler
++ #if defined(__i386__) || defined(__x86_64__)
++ #define CATCH_TRAP() __asm__("int $3")
++ #elif defined(__aarch64__)
++ #define CATCH_TRAP() __asm__(".inst 0xd4200000")
++ #elif defined(__arm__) && !defined(__thumb__)
++ #define CATCH_TRAP() __asm__(".inst 0xe7f001f0")
++ #elif defined(__arm__) && defined(__thumb__)
++ #define CATCH_TRAP() __asm__(".inst 0xde01")
++ #endif
+
+ #elif defined(CATCH_PLATFORM_LINUX)
+ // If we can use inline assembler, do it because this allows us to break
+@@ -5481,13 +7983,12 @@
+ #define CATCH_TRAP() DebugBreak()
+ #endif
+
+-#ifdef CATCH_TRAP
+- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+-#else
+- namespace Catch {
+- inline void doNothing() {}
+- }
+- #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing()
++#ifndef CATCH_BREAK_INTO_DEBUGGER
++ #ifdef CATCH_TRAP
++ #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
++ #else
++ #define CATCH_BREAK_INTO_DEBUGGER() []{}()
++ #endif
+ #endif
+
+ // end catch_debugger.h
+@@ -5495,86 +7996,58 @@
+
+ // start catch_fatal_condition.h
+
+-// start catch_windows_h_proxy.h
+-
+-
+-#if defined(CATCH_PLATFORM_WINDOWS)
+-
+-#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+-# define CATCH_DEFINED_NOMINMAX
+-# define NOMINMAX
+-#endif
+-#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+-# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+-# define WIN32_LEAN_AND_MEAN
+-#endif
+-
+-#ifdef __AFXDLL
+-#include <AfxWin.h>
+-#else
+-#include <windows.h>
+-#endif
+-
+-#ifdef CATCH_DEFINED_NOMINMAX
+-# undef NOMINMAX
+-#endif
+-#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+-# undef WIN32_LEAN_AND_MEAN
+-#endif
+-
+-#endif // defined(CATCH_PLATFORM_WINDOWS)
+-
+-// end catch_windows_h_proxy.h
+-#if defined( CATCH_CONFIG_WINDOWS_SEH )
++#include <cassert>
+
+ namespace Catch {
+
+- struct FatalConditionHandler {
+-
+- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
++ // Wrapper for platform-specific fatal error (signals/SEH) handlers
++ //
++ // Tries to be cooperative with other handlers, and not step over
++ // other handlers. This means that unknown structured exceptions
++ // are passed on, previous signal handlers are called, and so on.
++ //
++ // Can only be instantiated once, and assumes that once a signal
++ // is caught, the binary will end up terminating. Thus, there
++ class FatalConditionHandler {
++ bool m_started = false;
++
++ // Install/disengage implementation for specific platform.
++ // Should be if-defed to work on current platform, can assume
++ // engage-disengage 1:1 pairing.
++ void engage_platform();
++ void disengage_platform();
++ public:
++ // Should also have platform-specific implementations as needed
+ FatalConditionHandler();
+- static void reset();
+ ~FatalConditionHandler();
+
+- private:
+- static bool isSet;
+- static ULONG guaranteeSize;
+- static PVOID exceptionHandlerHandle;
+- };
+-
+-} // namespace Catch
+-
+-#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
+-
+-#include <signal.h>
+-
+-namespace Catch {
+-
+- struct FatalConditionHandler {
+-
+- static bool isSet;
+- static struct sigaction oldSigActions[];
+- static stack_t oldSigStack;
+- static char altStackMem[];
+-
+- static void handleSignal( int sig );
++ void engage() {
++ assert(!m_started && "Handler cannot be installed twice.");
++ m_started = true;
++ engage_platform();
++ }
+
+- FatalConditionHandler();
+- ~FatalConditionHandler();
+- static void reset();
++ void disengage() {
++ assert(m_started && "Handler cannot be uninstalled without being installed first");
++ m_started = false;
++ disengage_platform();
++ }
+ };
+
+-} // namespace Catch
+-
+-#else
+-
+-namespace Catch {
+- struct FatalConditionHandler {
+- void reset();
++ //! Simple RAII guard for (dis)engaging the FatalConditionHandler
++ class FatalConditionHandlerGuard {
++ FatalConditionHandler* m_handler;
++ public:
++ FatalConditionHandlerGuard(FatalConditionHandler* handler):
++ m_handler(handler) {
++ m_handler->engage();
++ }
++ ~FatalConditionHandlerGuard() {
++ m_handler->disengage();
++ }
+ };
+-}
+
+-#endif
++} // end namespace Catch
+
+ // end catch_fatal_condition.h
+ #include <string>
+@@ -5634,14 +8107,20 @@
+ void sectionEnded( SectionEndInfo const& endInfo ) override;
+ void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
+
+- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
++ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ void benchmarkPreparing( std::string const& name ) override;
+ void benchmarkStarting( BenchmarkInfo const& info ) override;
+- void benchmarkEnded( BenchmarkStats const& stats ) override;
++ void benchmarkEnded( BenchmarkStats<> const& stats ) override;
++ void benchmarkFailed( std::string const& error ) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void pushScopedMessage( MessageInfo const& message ) override;
+ void popScopedMessage( MessageInfo const& message ) override;
+
++ void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
++
+ std::string getCurrentTestName() const override;
+
+ const AssertionResult* getLastResult() const override;
+@@ -5682,22 +8161,26 @@
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase = nullptr;
+- ITracker* m_testCaseTracker;
++ ITracker* m_testCaseTracker = nullptr;
+ Option<AssertionResult> m_lastResult;
+
+ IConfigPtr m_config;
+ Totals m_totals;
+ IStreamingReporterPtr m_reporter;
+ std::vector<MessageInfo> m_messages;
++ std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<SectionEndInfo> m_unfinishedSections;
+ std::vector<ITracker*> m_activeSections;
+ TrackerContext m_trackerContext;
++ FatalConditionHandler m_fatalConditionhandler;
+ bool m_lastAssertionPassed = false;
+ bool m_shouldReportUnexpected = true;
+ bool m_includeSuccessfulResults;
+ };
+
++ void seedRng(IConfig const& config);
++ unsigned int rngSeed();
+ } // end namespace Catch
+
+ // end catch_run_context.h
+@@ -5844,7 +8327,7 @@
+ }
+
+ bool AssertionResult::hasExpression() const {
+- return m_info.capturedExpression[0] != 0;
++ return !m_info.capturedExpression.empty();
+ }
+
+ bool AssertionResult::hasMessage() const {
+@@ -5852,16 +8335,22 @@
+ }
+
+ std::string AssertionResult::getExpression() const {
+- if( isFalseTest( m_info.resultDisposition ) )
+- return "!(" + m_info.capturedExpression + ")";
+- else
+- return m_info.capturedExpression;
++ // Possibly overallocating by 3 characters should be basically free
++ std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
++ if (isFalseTest(m_info.resultDisposition)) {
++ expr += "!(";
++ }
++ expr += m_info.capturedExpression;
++ if (isFalseTest(m_info.resultDisposition)) {
++ expr += ')';
++ }
++ return expr;
+ }
+
+ std::string AssertionResult::getExpressionInMacro() const {
+ std::string expr;
+- if( m_info.macroName[0] == 0 )
+- expr = m_info.capturedExpression;
++ if( m_info.macroName.empty() )
++ expr = static_cast<std::string>(m_info.capturedExpression);
+ else {
+ expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
+ expr += m_info.macroName;
+@@ -5896,32 +8385,6 @@
+
+ } // end namespace Catch
+ // end catch_assertionresult.cpp
+-// start catch_benchmark.cpp
+-
+-namespace Catch {
+-
+- auto BenchmarkLooper::getResolution() -> uint64_t {
+- return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple();
+- }
+-
+- void BenchmarkLooper::reportStart() {
+- getResultCapture().benchmarkStarting( { m_name } );
+- }
+- auto BenchmarkLooper::needsMoreIterations() -> bool {
+- auto elapsed = m_timer.getElapsedNanoseconds();
+-
+- // Exponentially increasing iterations until we're confident in our timer resolution
+- if( elapsed < m_resolution ) {
+- m_iterationsToRun *= 10;
+- return true;
+- }
+-
+- getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } );
+- return false;
+- }
+-
+-} // end namespace Catch
+-// end catch_benchmark.cpp
+ // start catch_capture_matchers.cpp
+
+ namespace Catch {
+@@ -6067,6 +8530,9 @@
+ m_suffix = false;
+ auto width = m_column.m_width - indent();
+ m_end = m_pos;
++ if (line()[m_pos] == '\n') {
++ ++m_end;
++ }
+ while (m_end < line().size() && line()[m_end] != '\n')
+ ++m_end;
+
+@@ -6325,6 +8791,7 @@
+ // ----------- end of #include from clara_textflow.hpp -----------
+ // ........... back in clara.hpp
+
++#include <cctype>
+ #include <string>
+ #include <memory>
+ #include <set>
+@@ -6617,7 +9084,7 @@
+ }
+ inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
+ std::string srcLC = source;
+- std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } );
++ std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower(c) ); } );
+ if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
+ target = true;
+ else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
+@@ -7265,9 +9732,14 @@
+ if( !line.empty() && !startsWith( line, '#' ) ) {
+ if( !startsWith( line, '"' ) )
+ line = '"' + line + '"';
+- config.testsOrTags.push_back( line + ',' );
++ config.testsOrTags.push_back( line );
++ config.testsOrTags.emplace_back( "," );
+ }
+ }
++ //Remove comma in the end
++ if(!config.testsOrTags.empty())
++ config.testsOrTags.erase( config.testsOrTags.end()-1 );
++
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setTestOrder = [&]( std::string const& order ) {
+@@ -7302,14 +9774,16 @@
+ };
+ auto const setWaitForKeypress = [&]( std::string const& keypress ) {
+ auto keypressLc = toLower( keypress );
+- if( keypressLc == "start" )
++ if (keypressLc == "never")
++ config.waitForKeypress = WaitForKeypress::Never;
++ else if( keypressLc == "start" )
+ config.waitForKeypress = WaitForKeypress::BeforeStart;
+ else if( keypressLc == "exit" )
+ config.waitForKeypress = WaitForKeypress::BeforeExit;
+ else if( keypressLc == "both" )
+ config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
+ else
+- return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" );
++ return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
+ return ParserResult::ok( ParseResultType::Matched );
+ };
+ auto const setVerbosity = [&]( std::string const& verbosity ) {
+@@ -7379,6 +9853,9 @@
+ | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
+ ["-d"]["--durations"]
+ ( "show test durations" )
++ | Opt( config.minDuration, "seconds" )
++ ["-D"]["--min-duration"]
++ ( "show test durations for tests taking at least the given number of seconds" )
+ | Opt( loadTestNamesFromFile, "filename" )
+ ["-f"]["--input-file"]
+ ( "load test names to run from a file" )
+@@ -7409,13 +9886,24 @@
+ | Opt( config.libIdentify )
+ ["--libidentify"]
+ ( "report name and version according to libidentify standard" )
+- | Opt( setWaitForKeypress, "start|exit|both" )
++ | Opt( setWaitForKeypress, "never|start|exit|both" )
+ ["--wait-for-keypress"]
+ ( "waits for a keypress before exiting" )
+- | Opt( config.benchmarkResolutionMultiple, "multiplier" )
+- ["--benchmark-resolution-multiple"]
+- ( "multiple of clock resolution to run benchmarks" )
+-
++ | Opt( config.benchmarkSamples, "samples" )
++ ["--benchmark-samples"]
++ ( "number of samples to collect (default: 100)" )
++ | Opt( config.benchmarkResamples, "resamples" )
++ ["--benchmark-resamples"]
++ ( "number of resamples for the bootstrap (default: 100000)" )
++ | Opt( config.benchmarkConfidenceInterval, "confidence interval" )
++ ["--benchmark-confidence-interval"]
++ ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
++ | Opt( config.benchmarkNoAnalysis )
++ ["--benchmark-no-analysis"]
++ ( "perform only measurements; do not perform any analysis" )
++ | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
++ ["--benchmark-warmup-time"]
++ ( "amount of time in milliseconds spent on warming up each test (default: 100)" )
+ | Arg( config.testsOrTags, "test name|pattern|tags" )
+ ( "which test or tests to use" );
+
+@@ -7431,9 +9919,6 @@
+
+ namespace Catch {
+
+- bool SourceLineInfo::empty() const noexcept {
+- return file[0] == '\0';
+- }
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
+ return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+ }
+@@ -7469,14 +9954,23 @@
+ : m_data( data ),
+ m_stream( openStream() )
+ {
+- TestSpecParser parser(ITagAliasRegistry::get());
+- if (data.testsOrTags.empty()) {
+- parser.parse("~[.]"); // All not hidden tests
++ // We need to trim filter specs to avoid trouble with superfluous
++ // whitespace (esp. important for bdd macros, as those are manually
++ // aligned with whitespace).
++
++ for (auto& elem : m_data.testsOrTags) {
++ elem = trim(elem);
+ }
+- else {
++ for (auto& elem : m_data.sectionsToRun) {
++ elem = trim(elem);
++ }
++
++ TestSpecParser parser(ITagAliasRegistry::get());
++ if (!m_data.testsOrTags.empty()) {
+ m_hasTestFilters = true;
+- for( auto const& testOrTags : data.testsOrTags )
+- parser.parse( testOrTags );
++ for (auto const& testOrTags : m_data.testsOrTags) {
++ parser.parse(testOrTags);
++ }
+ }
+ m_testSpec = parser.testSpec();
+ }
+@@ -7509,15 +10003,21 @@
+ bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); }
+ bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); }
+ ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
++ double Config::minDuration() const { return m_data.minDuration; }
+ RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; }
+ unsigned int Config::rngSeed() const { return m_data.rngSeed; }
+- int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; }
+ UseColour::YesOrNo Config::useColour() const { return m_data.useColour; }
+ bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+ int Config::abortAfter() const { return m_data.abortAfter; }
+ bool Config::showInvisibles() const { return m_data.showInvisibles; }
+ Verbosity Config::verbosity() const { return m_data.verbosity; }
+
++ bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
++ int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
++ double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
++ unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
++ std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
++
+ IStream const* Config::openStream() {
+ return Catch::makeStream(m_data.outputFilename);
+ }
+@@ -7546,6 +10046,36 @@
+ }
+
+ // end catch_errno_guard.h
++// start catch_windows_h_proxy.h
++
++
++#if defined(CATCH_PLATFORM_WINDOWS)
++
++#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
++# define CATCH_DEFINED_NOMINMAX
++# define NOMINMAX
++#endif
++#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
++# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
++# define WIN32_LEAN_AND_MEAN
++#endif
++
++#ifdef __AFXDLL
++#include <AfxWin.h>
++#else
++#include <windows.h>
++#endif
++
++#ifdef CATCH_DEFINED_NOMINMAX
++# undef NOMINMAX
++#endif
++#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
++# undef WIN32_LEAN_AND_MEAN
++#endif
++
++#endif // defined(CATCH_PLATFORM_WINDOWS)
++
++// end catch_windows_h_proxy.h
+ #include <sstream>
+
+ namespace Catch {
+@@ -7557,7 +10087,7 @@
+ };
+
+ struct NoColourImpl : IColourImpl {
+- void use( Colour::Code ) {}
++ void use( Colour::Code ) override {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+@@ -7591,7 +10121,7 @@
+ originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+ }
+
+- virtual void use( Colour::Code _colourCode ) override {
++ void use( Colour::Code _colourCode ) override {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalForegroundAttributes );
+ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+@@ -7654,7 +10184,7 @@
+ // https://github.com/philsquared/Catch/pull/131
+ class PosixColourImpl : public IColourImpl {
+ public:
+- virtual void use( Colour::Code _colourCode ) override {
++ void use( Colour::Code _colourCode ) override {
+ switch( _colourCode ) {
+ case Colour::None:
+ case Colour::White: return setColour( "[0m" );
+@@ -7682,13 +10212,14 @@
+
+ private:
+ void setColour( const char* _escapeCode ) {
+- Catch::cout() << '\033' << _escapeCode;
++ getCurrentContext().getConfig()->stream()
++ << '\033' << _escapeCode;
+ }
+ };
+
+ bool useColourOnPlatform() {
+ return
+-#ifdef CATCH_PLATFORM_MAC
++#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+ !isDebuggerActive() &&
+ #endif
+ #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
+@@ -7729,13 +10260,13 @@
+ namespace Catch {
+
+ Colour::Colour( Code _colourCode ) { use( _colourCode ); }
+- Colour::Colour( Colour&& rhs ) noexcept {
+- m_moved = rhs.m_moved;
+- rhs.m_moved = true;
+- }
+- Colour& Colour::operator=( Colour&& rhs ) noexcept {
+- m_moved = rhs.m_moved;
+- rhs.m_moved = true;
++ Colour::Colour( Colour&& other ) noexcept {
++ m_moved = other.m_moved;
++ other.m_moved = true;
++ }
++ Colour& Colour::operator=( Colour&& other ) noexcept {
++ m_moved = other.m_moved;
++ other.m_moved = true;
+ return *this;
+ }
+
+@@ -7743,7 +10274,13 @@
+
+ void Colour::use( Code _colourCode ) {
+ static IColourImpl* impl = platformColourInstance();
+- impl->use( _colourCode );
++ // Strictly speaking, this cannot possibly happen.
++ // However, under some conditions it does happen (see #1626),
++ // and this change is small enough that we can let practicality
++ // triumph over purity in this case.
++ if (impl != nullptr) {
++ impl->use( _colourCode );
++ }
+ }
+
+ std::ostream& operator << ( std::ostream& os, Colour const& ) {
+@@ -7764,27 +10301,27 @@
+ class Context : public IMutableContext, NonCopyable {
+
+ public: // IContext
+- virtual IResultCapture* getResultCapture() override {
++ IResultCapture* getResultCapture() override {
+ return m_resultCapture;
+ }
+- virtual IRunner* getRunner() override {
++ IRunner* getRunner() override {
+ return m_runner;
+ }
+
+- virtual IConfigPtr const& getConfig() const override {
++ IConfigPtr const& getConfig() const override {
+ return m_config;
+ }
+
+- virtual ~Context() override;
++ ~Context() override;
+
+ public: // IMutableContext
+- virtual void setResultCapture( IResultCapture* resultCapture ) override {
++ void setResultCapture( IResultCapture* resultCapture ) override {
+ m_resultCapture = resultCapture;
+ }
+- virtual void setRunner( IRunner* runner ) override {
++ void setRunner( IRunner* runner ) override {
+ m_runner = runner;
+ }
+- virtual void setConfig( IConfigPtr const& config ) override {
++ void setConfig( IConfigPtr const& config ) override {
+ m_config = config;
+ }
+
+@@ -7810,6 +10347,12 @@
+ IContext::~IContext() = default;
+ IMutableContext::~IMutableContext() = default;
+ Context::~Context() = default;
++
++ SimplePcg32& rng() {
++ static SimplePcg32 s_rng;
++ return s_rng;
++ }
++
+ }
+ // end catch_context.cpp
+ // start catch_debug_console.cpp
+@@ -7823,7 +10366,16 @@
+ }
+
+ // end catch_debug_console.h
+-#ifdef CATCH_PLATFORM_WINDOWS
++#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
++#include <android/log.h>
++
++ namespace Catch {
++ void writeToDebugConsole( std::string const& text ) {
++ __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
++ }
++ }
++
++#elif defined(CATCH_PLATFORM_WINDOWS)
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+@@ -7844,25 +10396,28 @@
+ // end catch_debug_console.cpp
+ // start catch_debugger.cpp
+
+-#ifdef CATCH_PLATFORM_MAC
++#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+
+-# include <assert.h>
+-# include <stdbool.h>
++# include <cassert>
+ # include <sys/types.h>
+ # include <unistd.h>
+-# include <sys/sysctl.h>
+ # include <cstddef>
+ # include <ostream>
+
+-namespace Catch {
++#ifdef __apple_build_version__
++ // These headers will only compile with AppleClang (XCode)
++ // For other compilers (Clang, GCC, ... ) we need to exclude them
++# include <sys/sysctl.h>
++#endif
+
++ namespace Catch {
++ #ifdef __apple_build_version__
+ // The following function is taken directly from the following technical note:
+- // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
++ // https://developer.apple.com/library/archive/qa/qa1361/_index.html
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive(){
+-
+ int mib[4];
+ struct kinfo_proc info;
+ std::size_t size;
+@@ -7892,6 +10447,12 @@
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+ }
++ #else
++ bool isDebuggerActive() {
++ // We need to find another way to determine this for non-appleclang compilers on macOS
++ return false;
++ }
++ #endif
+ } // namespace Catch
+
+ #elif defined(CATCH_PLATFORM_LINUX)
+@@ -7962,6 +10523,8 @@
+ // end catch_decomposer.cpp
+ // start catch_enforce.cpp
+
++#include <stdexcept>
++
+ namespace Catch {
+ #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
+ [[noreturn]]
+@@ -7971,8 +10534,116 @@
+ std::terminate();
+ }
+ #endif
++
++ [[noreturn]]
++ void throw_logic_error(std::string const& msg) {
++ throw_exception(std::logic_error(msg));
++ }
++
++ [[noreturn]]
++ void throw_domain_error(std::string const& msg) {
++ throw_exception(std::domain_error(msg));
++ }
++
++ [[noreturn]]
++ void throw_runtime_error(std::string const& msg) {
++ throw_exception(std::runtime_error(msg));
++ }
++
+ } // namespace Catch;
+ // end catch_enforce.cpp
++// start catch_enum_values_registry.cpp
++// start catch_enum_values_registry.h
++
++#include <vector>
++#include <memory>
++
++namespace Catch {
++
++ namespace Detail {
++
++ std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
++
++ class EnumValuesRegistry : public IMutableEnumValuesRegistry {
++
++ std::vector<std::unique_ptr<EnumInfo>> m_enumInfos;
++
++ EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
++ };
++
++ std::vector<StringRef> parseEnums( StringRef enums );
++
++ } // Detail
++
++} // Catch
++
++// end catch_enum_values_registry.h
++
++#include <map>
++#include <cassert>
++
++namespace Catch {
++
++ IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
++
++ namespace Detail {
++
++ namespace {
++ // Extracts the actual name part of an enum instance
++ // In other words, it returns the Blue part of Bikeshed::Colour::Blue
++ StringRef extractInstanceName(StringRef enumInstance) {
++ // Find last occurrence of ":"
++ size_t name_start = enumInstance.size();
++ while (name_start > 0 && enumInstance[name_start - 1] != ':') {
++ --name_start;
++ }
++ return enumInstance.substr(name_start, enumInstance.size() - name_start);
++ }
++ }
++
++ std::vector<StringRef> parseEnums( StringRef enums ) {
++ auto enumValues = splitStringRef( enums, ',' );
++ std::vector<StringRef> parsed;
++ parsed.reserve( enumValues.size() );
++ for( auto const& enumValue : enumValues ) {
++ parsed.push_back(trim(extractInstanceName(enumValue)));
++ }
++ return parsed;
++ }
++
++ EnumInfo::~EnumInfo() {}
++
++ StringRef EnumInfo::lookup( int value ) const {
++ for( auto const& valueToName : m_values ) {
++ if( valueToName.first == value )
++ return valueToName.second;
++ }
++ return "{** unexpected enum value **}"_sr;
++ }
++
++ std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
++ std::unique_ptr<EnumInfo> enumInfo( new EnumInfo );
++ enumInfo->m_name = enumName;
++ enumInfo->m_values.reserve( values.size() );
++
++ const auto valueNames = Catch::Detail::parseEnums( allValueNames );
++ assert( valueNames.size() == values.size() );
++ std::size_t i = 0;
++ for( auto value : values )
++ enumInfo->m_values.emplace_back(value, valueNames[i++]);
++
++ return enumInfo;
++ }
++
++ EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
++ m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
++ return *m_enumInfos.back();
++ }
++
++ } // Detail
++} // Catch
++
++// end catch_enum_values_registry.cpp
+ // start catch_errno_guard.cpp
+
+ #include <cerrno>
+@@ -7996,7 +10667,7 @@
+ public:
+ ~ExceptionTranslatorRegistry();
+ virtual void registerTranslator( const IExceptionTranslator* translator );
+- virtual std::string translateActiveException() const override;
++ std::string translateActiveException() const override;
+ std::string tryTranslators() const;
+
+ private:
+@@ -8061,54 +10732,82 @@
+ }
+ }
+
++ std::string ExceptionTranslatorRegistry::tryTranslators() const {
++ if (m_translators.empty()) {
++ std::rethrow_exception(std::current_exception());
++ } else {
++ return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
++ }
++ }
++
+ #else // ^^ Exceptions are enabled // Exceptions are disabled vv
+ std::string ExceptionTranslatorRegistry::translateActiveException() const {
+ CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+ }
+-#endif
+
+ std::string ExceptionTranslatorRegistry::tryTranslators() const {
+- if( m_translators.empty() )
+- std::rethrow_exception(std::current_exception());
+- else
+- return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
++ CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+ }
++#endif
++
+ }
+ // end catch_exception_translator_registry.cpp
+ // start catch_fatal_condition.cpp
+
+-#if defined(__GNUC__)
+-# pragma GCC diagnostic push
+-# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+-#endif
++#include <algorithm>
++
++#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
++
++namespace Catch {
++
++ // If neither SEH nor signal handling is required, the handler impls
++ // do not have to do anything, and can be empty.
++ void FatalConditionHandler::engage_platform() {}
++ void FatalConditionHandler::disengage_platform() {}
++ FatalConditionHandler::FatalConditionHandler() = default;
++ FatalConditionHandler::~FatalConditionHandler() = default;
++
++} // end namespace Catch
++
++#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
++
++#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
++#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
++#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
+
+ #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+ namespace {
+- // Report the error condition
++ //! Signals fatal error message to the run context
+ void reportFatal( char const * const message ) {
+ Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
+ }
+-}
+
+-#endif // signals/SEH handling
++ //! Minimal size Catch2 needs for its own fatal error handling.
++ //! Picked anecdotally, so it might not be sufficient on all
++ //! platforms, and for all configurations.
++ constexpr std::size_t minStackSizeForErrors = 32 * 1024;
++} // end unnamed namespace
++
++#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
+
+ #if defined( CATCH_CONFIG_WINDOWS_SEH )
+
+ namespace Catch {
++
+ struct SignalDefs { DWORD id; const char* name; };
+
+ // There is no 1-1 mapping between signals and windows exceptions.
+ // Windows can easily distinguish between SO and SigSegV,
+ // but SigInt, SigTerm, etc are handled differently.
+ static SignalDefs signalDefs[] = {
+- { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" },
+- { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+- { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+- { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
++ { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" },
++ { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" },
++ { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" },
++ { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
+ };
+
+- LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
++ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+ for (auto const& def : signalDefs) {
+ if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
+ reportFatal(def.name);
+@@ -8119,38 +10818,50 @@
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
++ // Since we do not support multiple instantiations, we put these
++ // into global variables and rely on cleaning them up in outlined
++ // constructors/destructors
++ static PVOID exceptionHandlerHandle = nullptr;
++
++ // For MSVC, we reserve part of the stack memory for handling
++ // memory overflow structured exception.
+ FatalConditionHandler::FatalConditionHandler() {
+- isSet = true;
+- // 32k seems enough for Catch to handle stack overflow,
+- // but the value was found experimentally, so there is no strong guarantee
+- guaranteeSize = 32 * 1024;
+- exceptionHandlerHandle = nullptr;
++ ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
++ if (!SetThreadStackGuarantee(&guaranteeSize)) {
++ // We do not want to fully error out, because needing
++ // the stack reserve should be rare enough anyway.
++ Catch::cerr()
++ << "Failed to reserve piece of stack."
++ << " Stack overflows will not be reported successfully.";
++ }
++ }
++
++ // We do not attempt to unset the stack guarantee, because
++ // Windows does not support lowering the stack size guarantee.
++ FatalConditionHandler::~FatalConditionHandler() = default;
++
++ void FatalConditionHandler::engage_platform() {
+ // Register as first handler in current chain
+ exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+- // Pass in guarantee size to be filled
+- SetThreadStackGuarantee(&guaranteeSize);
++ if (!exceptionHandlerHandle) {
++ CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
++ }
+ }
+
+- void FatalConditionHandler::reset() {
+- if (isSet) {
+- RemoveVectoredExceptionHandler(exceptionHandlerHandle);
+- SetThreadStackGuarantee(&guaranteeSize);
+- exceptionHandlerHandle = nullptr;
+- isSet = false;
++ void FatalConditionHandler::disengage_platform() {
++ if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
++ CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
+ }
++ exceptionHandlerHandle = nullptr;
+ }
+
+- FatalConditionHandler::~FatalConditionHandler() {
+- reset();
+- }
++} // end namespace Catch
+
+-bool FatalConditionHandler::isSet = false;
+-ULONG FatalConditionHandler::guaranteeSize = 0;
+-PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
++#endif // CATCH_CONFIG_WINDOWS_SEH
+
+-} // namespace Catch
++#if defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+-#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
++#include <signal.h>
+
+ namespace Catch {
+
+@@ -8159,10 +10870,6 @@
+ const char* name;
+ };
+
+- // 32kb for the alternate stack seems to be sufficient. However, this value
+- // is experimentally determined, so that's not guaranteed.
+- constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
+-
+ static SignalDefs signalDefs[] = {
+ { SIGINT, "SIGINT - Terminal interrupt signal" },
+ { SIGILL, "SIGILL - Illegal instruction signal" },
+@@ -8172,7 +10879,32 @@
+ { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+ };
+
+- void FatalConditionHandler::handleSignal( int sig ) {
++// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
++// which is zero initialization, but not explicit. We want to avoid
++// that.
++#if defined(__GNUC__)
++# pragma GCC diagnostic push
++# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
++#endif
++
++ static char* altStackMem = nullptr;
++ static std::size_t altStackSize = 0;
++ static stack_t oldSigStack{};
++ static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
++
++ static void restorePreviousSignalHandlers() {
++ // We set signal handlers back to the previous ones. Hopefully
++ // nobody overwrote them in the meantime, and doesn't expect
++ // their signal handlers to live past ours given that they
++ // installed them after ours..
++ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
++ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
++ }
++ // Return the old stack
++ sigaltstack(&oldSigStack, nullptr);
++ }
++
++ static void handleSignal( int sig ) {
+ char const * name = "<unknown signal>";
+ for (auto const& def : signalDefs) {
+ if (sig == def.id) {
+@@ -8180,16 +10912,33 @@
+ break;
+ }
+ }
+- reset();
+- reportFatal(name);
++ // We need to restore previous signal handlers and let them do
++ // their thing, so that the users can have the debugger break
++ // when a signal is raised, and so on.
++ restorePreviousSignalHandlers();
++ reportFatal( name );
+ raise( sig );
+ }
+
+ FatalConditionHandler::FatalConditionHandler() {
+- isSet = true;
++ assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
++ if (altStackSize == 0) {
++ altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
++ }
++ altStackMem = new char[altStackSize]();
++ }
++
++ FatalConditionHandler::~FatalConditionHandler() {
++ delete[] altStackMem;
++ // We signal that another instance can be constructed by zeroing
++ // out the pointer.
++ altStackMem = nullptr;
++ }
++
++ void FatalConditionHandler::engage_platform() {
+ stack_t sigStack;
+ sigStack.ss_sp = altStackMem;
+- sigStack.ss_size = sigStackSize;
++ sigStack.ss_size = altStackSize;
+ sigStack.ss_flags = 0;
+ sigaltstack(&sigStack, &oldSigStack);
+ struct sigaction sa = { };
+@@ -8201,59 +10950,20 @@
+ }
+ }
+
+- FatalConditionHandler::~FatalConditionHandler() {
+- reset();
+- }
+-
+- void FatalConditionHandler::reset() {
+- if( isSet ) {
+- // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
+- sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+- }
+- // Return the old stack
+- sigaltstack(&oldSigStack, nullptr);
+- isSet = false;
+- }
+- }
+-
+- bool FatalConditionHandler::isSet = false;
+- struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
+- stack_t FatalConditionHandler::oldSigStack = {};
+- char FatalConditionHandler::altStackMem[sigStackSize] = {};
+-
+-} // namespace Catch
+-
+-#else
+-
+-namespace Catch {
+- void FatalConditionHandler::reset() {}
+-}
+-
+-#endif // signals/SEH handling
+-
+ #if defined(__GNUC__)
+ # pragma GCC diagnostic pop
+ #endif
+-// end catch_fatal_condition.cpp
+-// start catch_generators.cpp
+-
+-// start catch_random_number_generator.h
+-
+-#include <algorithm>
+-#include <random>
+-
+-namespace Catch {
+
+- struct IConfig;
++ void FatalConditionHandler::disengage_platform() {
++ restorePreviousSignalHandlers();
++ }
+
+- std::mt19937& rng();
+- void seedRng( IConfig const& config );
+- unsigned int rngSeed();
++} // end namespace Catch
+
+-}
++#endif // CATCH_CONFIG_POSIX_SIGNALS
++// end catch_fatal_condition.cpp
++// start catch_generators.cpp
+
+-// end catch_random_number_generator.h
+ #include <limits>
+ #include <set>
+
+@@ -8261,34 +10971,16 @@
+
+ IGeneratorTracker::~IGeneratorTracker() {}
+
+-namespace Generators {
+-
+- GeneratorBase::~GeneratorBase() {}
+-
+- std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ) {
+-
+- assert( selectionSize <= sourceSize );
+- std::vector<size_t> indices;
+- indices.reserve( selectionSize );
+- std::uniform_int_distribution<size_t> uid( 0, sourceSize-1 );
++const char* GeneratorException::what() const noexcept {
++ return m_msg;
++}
+
+- std::set<size_t> seen;
+- // !TBD: improve this algorithm
+- while( indices.size() < selectionSize ) {
+- auto index = uid( rng() );
+- if( seen.insert( index ).second )
+- indices.push_back( index );
+- }
+- return indices;
+- }
++namespace Generators {
+
+- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+- return getResultCapture().acquireGeneratorTracker( lineInfo );
+- }
++ GeneratorUntypedBase::~GeneratorUntypedBase() {}
+
+- template<>
+- auto all<int>() -> Generator<int> {
+- return range( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() );
++ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
++ return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+ }
+
+ } // namespace Generators
+@@ -8344,10 +11036,16 @@
+
+ void noMatchingTestCases( std::string const& spec ) override;
+
++ void reportInvalidArguments(std::string const&arg) override;
++
+ static std::set<Verbosity> getSupportedVerbosities();
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ void benchmarkPreparing(std::string const& name) override;
+ void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+- void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override;
++ void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
++ void benchmarkFailed(std::string const&) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void testRunStarting( TestRunInfo const& testRunInfo ) override;
+ void testGroupStarting( GroupInfo const& groupInfo ) override;
+@@ -8538,7 +11236,7 @@
+
+ std::size_t listReporters();
+
+- Option<std::size_t> list( Config const& config );
++ Option<std::size_t> list( std::shared_ptr<Config> const& config );
+
+ } // end namespace Catch
+
+@@ -8557,7 +11255,7 @@
+ namespace Catch {
+
+ std::size_t listTests( Config const& config ) {
+- TestSpec testSpec = config.testSpec();
++ TestSpec const& testSpec = config.testSpec();
+ if( config.hasTestFilters() )
+ Catch::cout() << "Matching test cases:\n";
+ else {
+@@ -8591,7 +11289,7 @@
+ }
+
+ std::size_t listTestsNamesOnly( Config const& config ) {
+- TestSpec testSpec = config.testSpec();
++ TestSpec const& testSpec = config.testSpec();
+ std::size_t matchedTests = 0;
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( auto const& testCaseInfo : matchedTestCases ) {
+@@ -8613,14 +11311,23 @@
+ }
+
+ std::string TagInfo::all() const {
+- std::string out;
+- for( auto const& spelling : spellings )
+- out += "[" + spelling + "]";
++ size_t size = 0;
++ for (auto const& spelling : spellings) {
++ // Add 2 for the brackes
++ size += spelling.size() + 2;
++ }
++
++ std::string out; out.reserve(size);
++ for (auto const& spelling : spellings) {
++ out += '[';
++ out += spelling;
++ out += ']';
++ }
+ return out;
+ }
+
+ std::size_t listTags( Config const& config ) {
+- TestSpec testSpec = config.testSpec();
++ TestSpec const& testSpec = config.testSpec();
+ if( config.hasTestFilters() )
+ Catch::cout() << "Tags for matching test cases:\n";
+ else {
+@@ -8676,15 +11383,16 @@
+ return factories.size();
+ }
+
+- Option<std::size_t> list( Config const& config ) {
++ Option<std::size_t> list( std::shared_ptr<Config> const& config ) {
+ Option<std::size_t> listedCount;
+- if( config.listTests() )
+- listedCount = listedCount.valueOr(0) + listTests( config );
+- if( config.listTestNamesOnly() )
+- listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+- if( config.listTags() )
+- listedCount = listedCount.valueOr(0) + listTags( config );
+- if( config.listReporters() )
++ getCurrentMutableContext().setConfig( config );
++ if( config->listTests() )
++ listedCount = listedCount.valueOr(0) + listTests( *config );
++ if( config->listTestNamesOnly() )
++ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config );
++ if( config->listTags() )
++ listedCount = listedCount.valueOr(0) + listTags( *config );
++ if( config->listReporters() )
+ listedCount = listedCount.valueOr(0) + listReporters();
+ return listedCount;
+ }
+@@ -8713,6 +11421,29 @@
+
+ } // namespace Catch
+ // end catch_matchers.cpp
++// start catch_matchers_exception.cpp
++
++namespace Catch {
++namespace Matchers {
++namespace Exception {
++
++bool ExceptionMessageMatcher::match(std::exception const& ex) const {
++ return ex.what() == m_message;
++}
++
++std::string ExceptionMessageMatcher::describe() const {
++ return "exception message matches \"" + m_message + "\"";
++}
++
++}
++Exception::ExceptionMessageMatcher Message(std::string const& message) {
++ return Exception::ExceptionMessageMatcher(message);
++}
++
++// namespace Exception
++} // namespace Matchers
++} // namespace Catch
++// end catch_matchers_exception.cpp
+ // start catch_matchers_floating.cpp
+
+ // start catch_polyfills.hpp
+@@ -8741,74 +11472,101 @@
+ } // end namespace Catch
+
+ // end catch_to_string.hpp
++#include <algorithm>
++#include <cmath>
+ #include <cstdlib>
+ #include <cstdint>
+ #include <cstring>
++#include <sstream>
++#include <type_traits>
++#include <iomanip>
++#include <limits>
+
+ namespace Catch {
+-namespace Matchers {
+-namespace Floating {
+-enum class FloatingPointKind : uint8_t {
+- Float,
+- Double
+-};
+-}
+-}
+-}
+-
+ namespace {
+
+-template <typename T>
+-struct Converter;
+-
+-template <>
+-struct Converter<float> {
+- static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
+- Converter(float f) {
++ int32_t convert(float f) {
++ static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
++ int32_t i;
+ std::memcpy(&i, &f, sizeof(f));
++ return i;
+ }
+- int32_t i;
+-};
+
+-template <>
+-struct Converter<double> {
+- static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
+- Converter(double d) {
++ int64_t convert(double d) {
++ static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
++ int64_t i;
+ std::memcpy(&i, &d, sizeof(d));
++ return i;
+ }
+- int64_t i;
+-};
+
+-template <typename T>
+-auto convert(T t) -> Converter<T> {
+- return Converter<T>(t);
+-}
++ template <typename FP>
++ bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
++ // Comparison with NaN should always be false.
++ // This way we can rule it out before getting into the ugly details
++ if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
++ return false;
++ }
+
+-template <typename FP>
+-bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
+- // Comparison with NaN should always be false.
+- // This way we can rule it out before getting into the ugly details
+- if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
+- return false;
++ auto lc = convert(lhs);
++ auto rc = convert(rhs);
++
++ if ((lc < 0) != (rc < 0)) {
++ // Potentially we can have +0 and -0
++ return lhs == rhs;
++ }
++
++ // static cast as a workaround for IBM XLC
++ auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
++ return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
+ }
+
+- auto lc = convert(lhs);
+- auto rc = convert(rhs);
++#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
++
++ float nextafter(float x, float y) {
++ return ::nextafterf(x, y);
++ }
+
+- if ((lc.i < 0) != (rc.i < 0)) {
+- // Potentially we can have +0 and -0
+- return lhs == rhs;
++ double nextafter(double x, double y) {
++ return ::nextafter(x, y);
+ }
+
+- auto ulpDiff = std::abs(lc.i - rc.i);
+- return ulpDiff <= maxUlpDiff;
++#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
++
++template <typename FP>
++FP step(FP start, FP direction, uint64_t steps) {
++ for (uint64_t i = 0; i < steps; ++i) {
++#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
++ start = Catch::nextafter(start, direction);
++#else
++ start = std::nextafter(start, direction);
++#endif
++ }
++ return start;
++}
++
++// Performs equivalent check of std::fabs(lhs - rhs) <= margin
++// But without the subtraction to allow for INFINITY in comparison
++bool marginComparison(double lhs, double rhs, double margin) {
++ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+ }
+
++template <typename FloatingPoint>
++void write(std::ostream& out, FloatingPoint num) {
++ out << std::scientific
++ << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
++ << num;
+ }
+
+-namespace Catch {
++} // end anonymous namespace
++
+ namespace Matchers {
+ namespace Floating {
++
++ enum class FloatingPointKind : uint8_t {
++ Float,
++ Double
++ };
++
+ WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
+ :m_target{ target }, m_margin{ margin } {
+ CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
+@@ -8825,10 +11583,11 @@
+ return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
+ }
+
+- WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
++ WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
+ :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
+- CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
+- << " ULPs have to be non-negative.");
++ CATCH_ENFORCE(m_type == FloatingPointKind::Double
++ || m_ulps < (std::numeric_limits<uint32_t>::max)(),
++ "Provided ULP is impossibly large for a float comparison.");
+ }
+
+ #if defined(__clang__)
+@@ -8853,16 +11612,59 @@
+ #endif
+
+ std::string WithinUlpsMatcher::describe() const {
+- return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
++ std::stringstream ret;
++
++ ret << "is within " << m_ulps << " ULPs of ";
++
++ if (m_type == FloatingPointKind::Float) {
++ write(ret, static_cast<float>(m_target));
++ ret << 'f';
++ } else {
++ write(ret, m_target);
++ }
++
++ ret << " ([";
++ if (m_type == FloatingPointKind::Double) {
++ write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
++ ret << ", ";
++ write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
++ } else {
++ // We have to cast INFINITY to float because of MinGW, see #1782
++ write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
++ ret << ", ";
++ write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
++ }
++ ret << "])";
++
++ return ret.str();
++ }
++
++ WithinRelMatcher::WithinRelMatcher(double target, double epsilon):
++ m_target(target),
++ m_epsilon(epsilon){
++ CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense.");
++ CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense.");
++ }
++
++ bool WithinRelMatcher::match(double const& matchee) const {
++ const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
++ return marginComparison(matchee, m_target,
++ std::isinf(relMargin)? 0 : relMargin);
++ }
++
++ std::string WithinRelMatcher::describe() const {
++ Catch::ReusableStringStream sstr;
++ sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
++ return sstr.str();
+ }
+
+ }// namespace Floating
+
+-Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
++Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
+ return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
+ }
+
+-Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
++Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
+ return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
+ }
+
+@@ -8870,9 +11672,24 @@
+ return Floating::WithinAbsMatcher(target, margin);
+ }
+
++Floating::WithinRelMatcher WithinRel(double target, double eps) {
++ return Floating::WithinRelMatcher(target, eps);
++}
++
++Floating::WithinRelMatcher WithinRel(double target) {
++ return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
++}
++
++Floating::WithinRelMatcher WithinRel(float target, float eps) {
++ return Floating::WithinRelMatcher(target, eps);
++}
++
++Floating::WithinRelMatcher WithinRel(float target) {
++ return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
++}
++
+ } // namespace Matchers
+ } // namespace Catch
+-
+ // end catch_matchers_floating.cpp
+ // start catch_matchers_generic.cpp
+
+@@ -9030,28 +11847,43 @@
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+- : m_info( builder.m_info )
++ : m_info( builder.m_info ), m_moved()
+ {
+ m_info.message = builder.m_stream.str();
+ getResultCapture().pushScopedMessage( m_info );
+ }
+
++ ScopedMessage::ScopedMessage( ScopedMessage&& old )
++ : m_info( old.m_info ), m_moved()
++ {
++ old.m_moved = true;
++ }
++
+ ScopedMessage::~ScopedMessage() {
+- if ( !uncaught_exceptions() ){
++ if ( !uncaught_exceptions() && !m_moved ){
+ getResultCapture().popScopedMessage(m_info);
+ }
+ }
+
+ Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
+ auto trimmed = [&] (size_t start, size_t end) {
+- while (names[start] == ',' || isspace(names[start])) {
++ while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
+ ++start;
+ }
+- while (names[end] == ',' || isspace(names[end])) {
++ while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) {
+ --end;
+ }
+ return names.substr(start, end - start + 1);
+ };
++ auto skipq = [&] (size_t start, char quote) {
++ for (auto i = start + 1; i < names.size() ; ++i) {
++ if (names[i] == quote)
++ return i;
++ if (names[i] == '\\')
++ ++i;
++ }
++ CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
++ };
+
+ size_t start = 0;
+ std::stack<char> openings;
+@@ -9072,18 +11904,22 @@
+ // case '>':
+ openings.pop();
+ break;
++ case '"':
++ case '\'':
++ pos = skipq(pos, c);
++ break;
+ case ',':
+- if (start != pos && openings.size() == 0) {
++ if (start != pos && openings.empty()) {
+ m_messages.emplace_back(macroName, lineInfo, resultType);
+- m_messages.back().message = trimmed(start, pos);
++ m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
+ m_messages.back().message += " := ";
+ start = pos;
+ }
+ }
+ }
+- assert(openings.size() == 0 && "Mismatched openings");
++ assert(openings.empty() && "Mismatched openings");
+ m_messages.emplace_back(macroName, lineInfo, resultType);
+- m_messages.back().message = trimmed(start, names.size() - 1);
++ m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
+ m_messages.back().message += " := ";
+ }
+ Capturer::~Capturer() {
+@@ -9145,6 +11981,22 @@
+ auto str() const -> std::string;
+ };
+
++ class RedirectedStreams {
++ public:
++ RedirectedStreams(RedirectedStreams const&) = delete;
++ RedirectedStreams& operator=(RedirectedStreams const&) = delete;
++ RedirectedStreams(RedirectedStreams&&) = delete;
++ RedirectedStreams& operator=(RedirectedStreams&&) = delete;
++
++ RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
++ ~RedirectedStreams();
++ private:
++ std::string& m_redirectedCout;
++ std::string& m_redirectedCerr;
++ RedirectedStdOut m_redirectedStdOut;
++ RedirectedStdErr m_redirectedStdErr;
++ };
++
+ #if defined(CATCH_CONFIG_NEW_CAPTURE)
+
+ // Windows's implementation of std::tmpfile is terrible (it tries
+@@ -9236,6 +12088,16 @@
+ {}
+ auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
+
++ RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
++ : m_redirectedCout(redirectedCout),
++ m_redirectedCerr(redirectedCerr)
++ {}
++
++ RedirectedStreams::~RedirectedStreams() {
++ m_redirectedCout += m_redirectedStdOut.str();
++ m_redirectedCerr += m_redirectedStdErr.str();
++ }
++
+ #if defined(CATCH_CONFIG_NEW_CAPTURE)
+
+ #if defined(_MSC_VER)
+@@ -9243,12 +12105,12 @@
+ if (tmpnam_s(m_buffer)) {
+ CATCH_RUNTIME_ERROR("Could not get a temp filename");
+ }
+- if (fopen_s(&m_file, m_buffer, "w")) {
++ if (fopen_s(&m_file, m_buffer, "w+")) {
+ char buffer[100];
+ if (strerror_s(buffer, errno)) {
+ CATCH_RUNTIME_ERROR("Could not translate errno to a string");
+ }
+- CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer);
++ CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
+ }
+ }
+ #else
+@@ -9351,20 +12213,61 @@
+
+ namespace Catch {
+
+- std::mt19937& rng() {
+- static std::mt19937 s_rng;
+- return s_rng;
++namespace {
++
++#if defined(_MSC_VER)
++#pragma warning(push)
++#pragma warning(disable:4146) // we negate uint32 during the rotate
++#endif
++ // Safe rotr implementation thanks to John Regehr
++ uint32_t rotate_right(uint32_t val, uint32_t count) {
++ const uint32_t mask = 31;
++ count &= mask;
++ return (val >> count) | (val << (-count & mask));
++ }
++
++#if defined(_MSC_VER)
++#pragma warning(pop)
++#endif
++
++}
++
++ SimplePcg32::SimplePcg32(result_type seed_) {
++ seed(seed_);
++ }
++
++ void SimplePcg32::seed(result_type seed_) {
++ m_state = 0;
++ (*this)();
++ m_state += seed_;
++ (*this)();
+ }
+
+- void seedRng( IConfig const& config ) {
+- if( config.rngSeed() != 0 ) {
+- std::srand( config.rngSeed() );
+- rng().seed( config.rngSeed() );
++ void SimplePcg32::discard(uint64_t skip) {
++ // We could implement this to run in O(log n) steps, but this
++ // should suffice for our use case.
++ for (uint64_t s = 0; s < skip; ++s) {
++ static_cast<void>((*this)());
+ }
+ }
+
+- unsigned int rngSeed() {
+- return getCurrentContext().getConfig()->rngSeed();
++ SimplePcg32::result_type SimplePcg32::operator()() {
++ // prepare the output value
++ const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
++ const auto output = rotate_right(xorshifted, m_state >> 59u);
++
++ // advance state
++ m_state = m_state * 6364136223846793005ULL + s_inc;
++
++ return output;
++ }
++
++ bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
++ return lhs.m_state == rhs.m_state;
++ }
++
++ bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
++ return lhs.m_state != rhs.m_state;
+ }
+ }
+ // end catch_random_number_generator.cpp
+@@ -9383,6 +12286,8 @@
+ struct IConfig;
+
+ std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
++
++ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
+@@ -9495,11 +12400,13 @@
+ namespace Catch {
+
+ class StartupExceptionRegistry {
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ public:
+ void add(std::exception_ptr const& exception) noexcept;
+ std::vector<std::exception_ptr> const& getExceptions() const noexcept;
+ private:
+ std::vector<std::exception_ptr> m_exceptions;
++#endif
+ };
+
+ } // end namespace Catch
+@@ -9582,7 +12489,14 @@
+ m_tagAliasRegistry.add( alias, tag, lineInfo );
+ }
+ void registerStartupException() noexcept override {
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ m_exceptionRegistry.add(std::current_exception());
++#else
++ CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
++#endif
++ }
++ IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
++ return m_enumValuesRegistry;
+ }
+
+ private:
+@@ -9591,6 +12505,7 @@
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ TagAliasRegistry m_tagAliasRegistry;
+ StartupExceptionRegistry m_exceptionRegistry;
++ Detail::EnumValuesRegistry m_enumValuesRegistry;
+ };
+ }
+
+@@ -9671,7 +12586,6 @@
+
+ namespace Generators {
+ struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
+- size_t m_index = static_cast<size_t>( -1 );
+ GeneratorBasePtr m_generator;
+
+ GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+@@ -9683,39 +12597,110 @@
+ std::shared_ptr<GeneratorTracker> tracker;
+
+ ITracker& currentTracker = ctx.currentTracker();
+- if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
++ // Under specific circumstances, the generator we want
++ // to acquire is also the current tracker. If this is
++ // the case, we have to avoid looking through current
++ // tracker's children, and instead return the current
++ // tracker.
++ // A case where this check is important is e.g.
++ // for (int i = 0; i < 5; ++i) {
++ // int n = GENERATE(1, 2);
++ // }
++ //
++ // without it, the code above creates 5 nested generators.
++ if (currentTracker.nameAndLocation() == nameAndLocation) {
++ auto thisTracker = currentTracker.parent().findChild(nameAndLocation);
++ assert(thisTracker);
++ assert(thisTracker->isGeneratorTracker());
++ tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker);
++ } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+- assert( childTracker->isIndexTracker() );
++ assert( childTracker->isGeneratorTracker() );
+ tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
+- }
+- else {
++ } else {
+ tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
+ currentTracker.addChild( tracker );
+ }
+
+- if( !ctx.completedCycle() && !tracker->isComplete() ) {
+- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+- tracker->moveNext();
++ if( !tracker->isComplete() ) {
+ tracker->open();
+ }
+
+ return *tracker;
+ }
+
+- void moveNext() {
+- m_index++;
+- m_children.clear();
+- }
+-
+ // TrackerBase interface
+- bool isIndexTracker() const override { return true; }
++ bool isGeneratorTracker() const override { return true; }
+ auto hasGenerator() const -> bool override {
+ return !!m_generator;
+ }
+ void close() override {
+ TrackerBase::close();
+- if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 )
++ // If a generator has a child (it is followed by a section)
++ // and none of its children have started, then we must wait
++ // until later to start consuming its values.
++ // This catches cases where `GENERATE` is placed between two
++ // `SECTION`s.
++ // **The check for m_children.empty cannot be removed**.
++ // doing so would break `GENERATE` _not_ followed by `SECTION`s.
++ const bool should_wait_for_child = [&]() {
++ // No children -> nobody to wait for
++ if ( m_children.empty() ) {
++ return false;
++ }
++ // If at least one child started executing, don't wait
++ if ( std::find_if(
++ m_children.begin(),
++ m_children.end(),
++ []( TestCaseTracking::ITrackerPtr tracker ) {
++ return tracker->hasStarted();
++ } ) != m_children.end() ) {
++ return false;
++ }
++
++ // No children have started. We need to check if they _can_
++ // start, and thus we should wait for them, or they cannot
++ // start (due to filters), and we shouldn't wait for them
++ auto* parent = m_parent;
++ // This is safe: there is always at least one section
++ // tracker in a test case tracking tree
++ while ( !parent->isSectionTracker() ) {
++ parent = &( parent->parent() );
++ }
++ assert( parent &&
++ "Missing root (test case) level section" );
++
++ auto const& parentSection =
++ static_cast<SectionTracker&>( *parent );
++ auto const& filters = parentSection.getFilters();
++ // No filters -> no restrictions on running sections
++ if ( filters.empty() ) {
++ return true;
++ }
++
++ for ( auto const& child : m_children ) {
++ if ( child->isSectionTracker() &&
++ std::find( filters.begin(),
++ filters.end(),
++ static_cast<SectionTracker&>( *child )
++ .trimmedName() ) !=
++ filters.end() ) {
++ return true;
++ }
++ }
++ return false;
++ }();
++
++ // This check is a bit tricky, because m_generator->next()
++ // has a side-effect, where it consumes generator's current
++ // value, but we do not want to invoke the side-effect if
++ // this generator is still waiting for any child to start.
++ if ( should_wait_for_child ||
++ ( m_runState == CompletedSuccessfully &&
++ m_generator->next() ) ) {
++ m_children.clear();
+ m_runState = Executing;
++ }
+ }
+
+ // IGeneratorTracker interface
+@@ -9725,9 +12710,6 @@
+ void setGenerator( GeneratorBasePtr&& generator ) override {
+ m_generator = std::move( generator );
+ }
+- auto getIndex() const -> size_t override {
+- return m_index;
+- }
+ };
+ GeneratorTracker::~GeneratorTracker() {}
+ }
+@@ -9825,6 +12807,9 @@
+ // and should be let to clear themselves out.
+ static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
++ if (result.getResultType() != ResultWas::Warning)
++ m_messageScopes.clear();
++
+ // Reset working state
+ resetAssertionInfo();
+ m_lastResult = result;
+@@ -9848,10 +12833,10 @@
+
+ return true;
+ }
+- auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
++ auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+ using namespace Generators;
+- GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) );
+- assert( tracker.isOpen() );
++ GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
++ TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
+ m_lastAssertionInfo.lineInfo = lineInfo;
+ return tracker;
+ }
+@@ -9879,6 +12864,7 @@
+
+ m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
+ m_messages.clear();
++ m_messageScopes.clear();
+ }
+
+ void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
+@@ -9890,12 +12876,21 @@
+
+ m_unfinishedSections.push_back(endInfo);
+ }
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ void RunContext::benchmarkPreparing(std::string const& name) {
++ m_reporter->benchmarkPreparing(name);
++ }
+ void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
+ m_reporter->benchmarkStarting( info );
+ }
+- void RunContext::benchmarkEnded( BenchmarkStats const& stats ) {
++ void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
+ m_reporter->benchmarkEnded( stats );
+ }
++ void RunContext::benchmarkFailed(std::string const & error) {
++ m_reporter->benchmarkFailed(error);
++ }
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void RunContext::pushScopedMessage(MessageInfo const & message) {
+ m_messages.push_back(message);
+@@ -9905,6 +12900,10 @@
+ m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
+ }
+
++ void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
++ m_messageScopes.emplace_back( builder );
++ }
++
+ std::string RunContext::getCurrentTestName() const {
+ return m_activeTestCase
+ ? m_activeTestCase->getTestCaseInfo().name
+@@ -9926,7 +12925,7 @@
+ // Don't rebuild the result -- the stringification itself can cause more fatal errors
+ // Instead, fake a result data.
+ AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
+- tempResult.message = message;
++ tempResult.message = static_cast<std::string>(message);
+ AssertionResult result(m_lastAssertionInfo, tempResult);
+
+ assertionEnded(result);
+@@ -9965,6 +12964,7 @@
+ m_lastAssertionPassed = true;
+ ++m_totals.assertions.passed;
+ resetAssertionInfo();
++ m_messageScopes.clear();
+ }
+
+ bool RunContext::aborting() const {
+@@ -9986,13 +12986,10 @@
+ CATCH_TRY {
+ if (m_reporter->getPreferences().shouldRedirectStdOut) {
+ #if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+- RedirectedStdOut redirectedStdOut;
+- RedirectedStdErr redirectedStdErr;
++ RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
+
+ timer.start();
+ invokeActiveTestCase();
+- redirectedCout += redirectedStdOut.str();
+- redirectedCerr += redirectedStdErr.str();
+ #else
+ OutputRedirect r(redirectedCout, redirectedCerr);
+ timer.start();
+@@ -10019,15 +13016,15 @@
+ m_testCaseTracker->close();
+ handleUnfinishedSections();
+ m_messages.clear();
++ m_messageScopes.clear();
+
+ SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
+ m_reporter->sectionEnded(testCaseSectionStats);
+ }
+
+ void RunContext::invokeActiveTestCase() {
+- FatalConditionHandler fatalConditionHandler; // Handle signals
++ FatalConditionHandlerGuard _(&m_fatalConditionhandler);
+ m_activeTestCase->invoke();
+- fatalConditionHandler.reset();
+ }
+
+ void RunContext::handleUnfinishedSections() {
+@@ -10090,7 +13087,7 @@
+ m_lastAssertionInfo = info;
+
+ AssertionResultData data( resultType, LazyExpression( false ) );
+- data.message = message;
++ data.message = static_cast<std::string>(message);
+ AssertionResult assertionResult{ m_lastAssertionInfo, data };
+ assertionEnded( assertionResult );
+ if( !assertionResult.isOk() )
+@@ -10153,6 +13150,18 @@
+ else
+ CATCH_INTERNAL_ERROR("No result capture instance");
+ }
++
++ void seedRng(IConfig const& config) {
++ if (config.rngSeed() != 0) {
++ std::srand(config.rngSeed());
++ rng().seed(config.rngSeed());
++ }
++ }
++
++ unsigned int rngSeed() {
++ return getCurrentContext().getConfig()->rngSeed();
++ }
++
+ }
+ // end catch_run_context.cpp
+ // start catch_section.cpp
+@@ -10214,7 +13223,7 @@
+ void libIdentify();
+
+ int applyCommandLine( int argc, char const * const * argv );
+- #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
++ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+ int applyCommandLine( int argc, wchar_t const * const * argv );
+ #endif
+
+@@ -10281,6 +13290,8 @@
+ // end catch_version.h
+ #include <cstdlib>
+ #include <iomanip>
++#include <set>
++#include <iterator>
+
+ namespace Catch {
+
+@@ -10299,52 +13310,76 @@
+ return createReporter(config->getReporterName(), config);
+ }
+
+- auto multi = std::unique_ptr<ListeningReporter>(new ListeningReporter);
+-
++ // On older platforms, returning std::unique_ptr<ListeningReporter>
++ // when the return type is std::unique_ptr<IStreamingReporter>
++ // doesn't compile without a std::move call. However, this causes
++ // a warning on newer platforms. Thus, we have to work around
++ // it a bit and downcast the pointer manually.
++ auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter);
++ auto& multi = static_cast<ListeningReporter&>(*ret);
+ auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
+ for (auto const& listener : listeners) {
+- multi->addListener(listener->create(Catch::ReporterConfig(config)));
++ multi.addListener(listener->create(Catch::ReporterConfig(config)));
+ }
+- multi->addReporter(createReporter(config->getReporterName(), config));
+- return std::move(multi);
++ multi.addReporter(createReporter(config->getReporterName(), config));
++ return ret;
+ }
+
+- Catch::Totals runTests(std::shared_ptr<Config> const& config) {
+- auto reporter = makeReporter(config);
+-
+- RunContext context(config, std::move(reporter));
+-
+- Totals totals;
+-
+- context.testGroupStarting(config->name(), 1, 1);
+-
+- TestSpec testSpec = config->testSpec();
+-
+- auto const& allTestCases = getAllTestCasesSorted(*config);
+- for (auto const& testCase : allTestCases) {
+- if (!context.aborting() && matchTest(testCase, testSpec, *config))
+- totals += context.runTest(testCase);
+- else
+- context.reporter().skipTest(testCase);
++ class TestGroup {
++ public:
++ explicit TestGroup(std::shared_ptr<Config> const& config)
++ : m_config{config}
++ , m_context{config, makeReporter(config)}
++ {
++ auto const& allTestCases = getAllTestCasesSorted(*m_config);
++ m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
++ auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
++
++ if (m_matches.empty() && invalidArgs.empty()) {
++ for (auto const& test : allTestCases)
++ if (!test.isHidden())
++ m_tests.emplace(&test);
++ } else {
++ for (auto const& match : m_matches)
++ m_tests.insert(match.tests.begin(), match.tests.end());
++ }
+ }
+
+- if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
+- ReusableStringStream testConfig;
++ Totals execute() {
++ auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
++ Totals totals;
++ m_context.testGroupStarting(m_config->name(), 1, 1);
++ for (auto const& testCase : m_tests) {
++ if (!m_context.aborting())
++ totals += m_context.runTest(*testCase);
++ else
++ m_context.reporter().skipTest(*testCase);
++ }
+
+- bool first = true;
+- for (const auto& input : config->getTestsOrTags()) {
+- if (!first) { testConfig << ' '; }
+- first = false;
+- testConfig << input;
++ for (auto const& match : m_matches) {
++ if (match.tests.empty()) {
++ m_context.reporter().noMatchingTestCases(match.name);
++ totals.error = -1;
++ }
+ }
+
+- context.reporter().noMatchingTestCases(testConfig.str());
+- totals.error = -1;
++ if (!invalidArgs.empty()) {
++ for (auto const& invalidArg: invalidArgs)
++ m_context.reporter().reportInvalidArguments(invalidArg);
++ }
++
++ m_context.testGroupEnded(m_config->name(), totals, 1, 1);
++ return totals;
+ }
+
+- context.testGroupEnded(config->name(), totals, 1, 1);
+- return totals;
+- }
++ private:
++ using Tests = std::set<TestCase const*>;
++
++ std::shared_ptr<Config> m_config;
++ RunContext m_context;
++ Tests m_tests;
++ TestSpec::Matches m_matches;
++ };
+
+ void applyFilenamesAsTags(Catch::IConfig const& config) {
+ auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
+@@ -10381,6 +13416,9 @@
+ #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
+ if ( !exceptions.empty() ) {
++ config();
++ getCurrentMutableContext().setConfig(m_config);
++
+ m_startupExceptions = true;
+ Colour colourGuard( Colour::Red );
+ Catch::cerr() << "Errors occurred during startup!" << '\n';
+@@ -10410,7 +13448,7 @@
+ }
+ void Session::libIdentify() {
+ Catch::cout()
+- << std::left << std::setw(16) << "description: " << "A Catch test executable\n"
++ << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
+ << std::left << std::setw(16) << "category: " << "testframework\n"
+ << std::left << std::setw(16) << "framework: " << "Catch Test\n"
+ << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
+@@ -10422,6 +13460,8 @@
+
+ auto result = m_cli.parse( clara::Args( argc, argv ) );
+ if( !result ) {
++ config();
++ getCurrentMutableContext().setConfig(m_config);
+ Catch::cerr()
+ << Colour( Colour::Red )
+ << "\nError(s) in input:\n"
+@@ -10439,17 +13479,17 @@
+ return 0;
+ }
+
+-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
++#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+ int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
+
+ char **utf8Argv = new char *[ argc ];
+
+ for ( int i = 0; i < argc; ++i ) {
+- int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
++ int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );
+
+ utf8Argv[ i ] = new char[ bufSize ];
+
+- WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
++ WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
+ }
+
+ int returnCode = applyCommandLine( argc, utf8Argv );
+@@ -10513,10 +13553,15 @@
+ applyFilenamesAsTags( *m_config );
+
+ // Handle list request
+- if( Option<std::size_t> listed = list( config() ) )
++ if( Option<std::size_t> listed = list( m_config ) )
+ return static_cast<int>( *listed );
+
+- auto totals = runTests( m_config );
++ TestGroup tests { m_config };
++ auto const totals = tests.execute();
++
++ if( m_config->warnAboutNoTests() && totals.error == -1 )
++ return 2;
++
+ // Note that on unices only the lower 8 bits are usually used, clamping
+ // the return value to 255 prevents false negative when some multiple
+ // of 256 tests has failed
+@@ -10564,6 +13609,7 @@
+ // end catch_singletons.cpp
+ // start catch_startup_exception_registry.cpp
+
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ namespace Catch {
+ void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
+ CATCH_TRY {
+@@ -10579,6 +13625,7 @@
+ }
+
+ } // end namespace Catch
++#endif
+ // end catch_startup_exception_registry.cpp
+ // start catch_stream.cpp
+
+@@ -10593,7 +13640,7 @@
+
+ Catch::IStream::~IStream() = default;
+
+- namespace detail { namespace {
++ namespace Detail { namespace {
+ template<typename WriterF, std::size_t bufferSize=256>
+ class StreamBufImpl : public std::streambuf {
+ char data[bufferSize];
+@@ -10692,15 +13739,15 @@
+
+ auto makeStream( StringRef const &filename ) -> IStream const* {
+ if( filename.empty() )
+- return new detail::CoutStream();
++ return new Detail::CoutStream();
+ else if( filename[0] == '%' ) {
+ if( filename == "%debug" )
+- return new detail::DebugOutStream();
++ return new Detail::DebugOutStream();
+ else
+ CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
+ }
+ else
+- return new detail::FileStream( filename );
++ return new Detail::FileStream( filename );
+ }
+
+ // This class encapsulates the idea of a pool of ostringstreams that can be reused.
+@@ -10757,12 +13804,13 @@
+ #include <ostream>
+ #include <cstring>
+ #include <cctype>
++#include <vector>
+
+ namespace Catch {
+
+ namespace {
+ char toLowerCh(char c) {
+- return static_cast<char>( std::tolower( c ) );
++ return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) );
+ }
+ }
+
+@@ -10797,6 +13845,18 @@
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+ }
+
++ StringRef trim(StringRef ref) {
++ const auto is_ws = [](char c) {
++ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
++ };
++ size_t real_begin = 0;
++ while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; }
++ size_t real_end = ref.size();
++ while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; }
++
++ return ref.substr(real_begin, real_end - real_begin);
++ }
++
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+ bool replaced = false;
+ std::size_t i = str.find( replaceThis );
+@@ -10811,6 +13871,21 @@
+ return replaced;
+ }
+
++ std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) {
++ std::vector<StringRef> subStrings;
++ std::size_t start = 0;
++ for(std::size_t pos = 0; pos < str.size(); ++pos ) {
++ if( str[pos] == delimiter ) {
++ if( pos - start > 1 )
++ subStrings.push_back( str.substr( start, pos-start ) );
++ start = pos+1;
++ }
++ }
++ if( start < str.size() )
++ subStrings.push_back( str.substr( start, str.size()-start ) );
++ return subStrings;
++ }
++
+ pluralise::pluralise( std::size_t count, std::string const& label )
+ : m_count( count ),
+ m_label( label )
+@@ -10827,123 +13902,46 @@
+ // end catch_string_manip.cpp
+ // start catch_stringref.cpp
+
+-#if defined(__clang__)
+-# pragma clang diagnostic push
+-# pragma clang diagnostic ignored "-Wexit-time-destructors"
+-#endif
+-
++#include <algorithm>
+ #include <ostream>
+ #include <cstring>
+ #include <cstdint>
+
+-namespace {
+- const uint32_t byte_2_lead = 0xC0;
+- const uint32_t byte_3_lead = 0xE0;
+- const uint32_t byte_4_lead = 0xF0;
+-}
+-
+ namespace Catch {
+ StringRef::StringRef( char const* rawChars ) noexcept
+ : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
+ {}
+
+- StringRef::operator std::string() const {
+- return std::string( m_start, m_size );
+- }
+-
+- void StringRef::swap( StringRef& other ) noexcept {
+- std::swap( m_start, other.m_start );
+- std::swap( m_size, other.m_size );
+- std::swap( m_data, other.m_data );
+- }
+-
+ auto StringRef::c_str() const -> char const* {
+- if( isSubstring() )
+- const_cast<StringRef*>( this )->takeOwnership();
++ CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
+ return m_start;
+ }
+- auto StringRef::currentData() const noexcept -> char const* {
++ auto StringRef::data() const noexcept -> char const* {
+ return m_start;
+ }
+
+- auto StringRef::isOwned() const noexcept -> bool {
+- return m_data != nullptr;
+- }
+- auto StringRef::isSubstring() const noexcept -> bool {
+- return m_start[m_size] != '\0';
+- }
+-
+- void StringRef::takeOwnership() {
+- if( !isOwned() ) {
+- m_data = new char[m_size+1];
+- memcpy( m_data, m_start, m_size );
+- m_data[m_size] = '\0';
+- m_start = m_data;
+- }
+- }
+ auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
+- if( start < m_size )
+- return StringRef( m_start+start, size );
+- else
++ if (start < m_size) {
++ return StringRef(m_start + start, (std::min)(m_size - start, size));
++ } else {
+ return StringRef();
+- }
+- auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
+- return
+- size() == other.size() &&
+- (std::strncmp( m_start, other.m_start, size() ) == 0);
+- }
+- auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool {
+- return !operator==( other );
+- }
+-
+- auto StringRef::operator[](size_type index) const noexcept -> char {
+- return m_start[index];
+- }
+-
+- auto StringRef::numberOfCharacters() const noexcept -> size_type {
+- size_type noChars = m_size;
+- // Make adjustments for uft encodings
+- for( size_type i=0; i < m_size; ++i ) {
+- char c = m_start[i];
+- if( ( c & byte_2_lead ) == byte_2_lead ) {
+- noChars--;
+- if (( c & byte_3_lead ) == byte_3_lead )
+- noChars--;
+- if( ( c & byte_4_lead ) == byte_4_lead )
+- noChars--;
+- }
+ }
+- return noChars;
+ }
+-
+- auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string {
+- std::string str;
+- str.reserve( lhs.size() + rhs.size() );
+- str += lhs;
+- str += rhs;
+- return str;
+- }
+- auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string {
+- return std::string( lhs ) + std::string( rhs );
+- }
+- auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string {
+- return std::string( lhs ) + std::string( rhs );
++ auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
++ return m_size == other.m_size
++ && (std::memcmp( m_start, other.m_start, m_size ) == 0);
+ }
+
+ auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
+- return os.write(str.currentData(), str.size());
++ return os.write(str.data(), str.size());
+ }
+
+ auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
+- lhs.append(rhs.currentData(), rhs.size());
++ lhs.append(rhs.data(), rhs.size());
+ return lhs;
+ }
+
+ } // namespace Catch
+-
+-#if defined(__clang__)
+-# pragma clang diagnostic pop
+-#endif
+ // end catch_stringref.cpp
+ // start catch_tag_alias.cpp
+
+@@ -11046,7 +14044,7 @@
+ void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ CATCH_ENFORCE( !isReservedTag(tag),
+ "Tag name: [" << tag << "] is not allowed.\n"
+- << "Tag names starting with non alpha-numeric characters are reserved\n"
++ << "Tag names starting with non alphanumeric characters are reserved\n"
+ << _lineInfo );
+ }
+ }
+@@ -11062,8 +14060,7 @@
+ std::vector<std::string> tags;
+ std::string desc, tag;
+ bool inTag = false;
+- std::string _descOrTags = nameAndTags.tags;
+- for (char c : _descOrTags) {
++ for (char c : nameAndTags.tags) {
+ if( !inTag ) {
+ if( c == '[' )
+ inTag = true;
+@@ -11078,6 +14075,12 @@
+ else if( prop == TestCaseInfo::None )
+ enforceNotReservedTag( tag, _lineInfo );
+
++ // Merged hide tags like `[.approvals]` should be added as
++ // `[.][approvals]`. The `[.]` is added at later point, so
++ // we only strip the prefix
++ if (startsWith(tag, '.') && tag.size() > 1) {
++ tag.erase(0, 1);
++ }
+ tags.push_back( tag );
+ tag.clear();
+ inTag = false;
+@@ -11087,10 +14090,11 @@
+ }
+ }
+ if( isHidden ) {
+- tags.push_back( "." );
++ // Add all "hidden" tags to make them behave identically
++ tags.insert( tags.end(), { ".", "!hide" } );
+ }
+
+- TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo );
++ TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo );
+ return TestCase( _testCase, std::move(info) );
+ }
+
+@@ -11182,30 +14186,89 @@
+ // end catch_test_case_info.cpp
+ // start catch_test_case_registry_impl.cpp
+
++#include <algorithm>
+ #include <sstream>
+
+ namespace Catch {
+
+- std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
++ namespace {
++ struct TestHasher {
++ using hash_t = uint64_t;
+
+- std::vector<TestCase> sorted = unsortedTestCases;
++ explicit TestHasher( hash_t hashSuffix ):
++ m_hashSuffix{ hashSuffix } {}
++
++ uint32_t operator()( TestCase const& t ) const {
++ // FNV-1a hash with multiplication fold.
++ const hash_t prime = 1099511628211u;
++ hash_t hash = 14695981039346656037u;
++ for ( const char c : t.name ) {
++ hash ^= c;
++ hash *= prime;
++ }
++ hash ^= m_hashSuffix;
++ hash *= prime;
++ const uint32_t low{ static_cast<uint32_t>( hash ) };
++ const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
++ return low * high;
++ }
++
++ private:
++ hash_t m_hashSuffix;
++ };
++ } // end unnamed namespace
+
++ std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+ switch( config.runOrder() ) {
+- case RunTests::InLexicographicalOrder:
+- std::sort( sorted.begin(), sorted.end() );
+- break;
+- case RunTests::InRandomOrder:
+- seedRng( config );
+- std::shuffle( sorted.begin(), sorted.end(), rng() );
+- break;
+ case RunTests::InDeclarationOrder:
+ // already in declaration order
+ break;
++
++ case RunTests::InLexicographicalOrder: {
++ std::vector<TestCase> sorted = unsortedTestCases;
++ std::sort( sorted.begin(), sorted.end() );
++ return sorted;
++ }
++
++ case RunTests::InRandomOrder: {
++ seedRng( config );
++ TestHasher h{ config.rngSeed() };
++
++ using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
++ std::vector<hashedTest> indexed_tests;
++ indexed_tests.reserve( unsortedTestCases.size() );
++
++ for (auto const& testCase : unsortedTestCases) {
++ indexed_tests.emplace_back(h(testCase), &testCase);
++ }
++
++ std::sort(indexed_tests.begin(), indexed_tests.end(),
++ [](hashedTest const& lhs, hashedTest const& rhs) {
++ if (lhs.first == rhs.first) {
++ return lhs.second->name < rhs.second->name;
++ }
++ return lhs.first < rhs.first;
++ });
++
++ std::vector<TestCase> sorted;
++ sorted.reserve( indexed_tests.size() );
++
++ for (auto const& hashed : indexed_tests) {
++ sorted.emplace_back(*hashed.second);
++ }
++
++ return sorted;
++ }
+ }
+- return sorted;
++ return unsortedTestCases;
++ }
++
++ bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
++ return !testCase.throws() || config.allowThrows();
+ }
++
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+- return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
++ return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
+ }
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+@@ -11222,9 +14285,12 @@
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+ std::vector<TestCase> filtered;
+ filtered.reserve( testCases.size() );
+- for( auto const& testCase : testCases )
+- if( matchTest( testCase, testSpec, config ) )
+- filtered.push_back( testCase );
++ for (auto const& testCase : testCases) {
++ if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
++ (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
++ filtered.push_back(testCase);
++ }
++ }
+ return filtered;
+ }
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+@@ -11263,7 +14329,7 @@
+ }
+
+ std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
+- std::string className = classOrQualifiedMethodName;
++ std::string className(classOrQualifiedMethodName);
+ if( startsWith( className, '&' ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+@@ -11300,11 +14366,6 @@
+
+ ITracker::~ITracker() = default;
+
+- TrackerContext& TrackerContext::instance() {
+- static TrackerContext s_instance;
+- return s_instance;
+- }
+-
+ ITracker& TrackerContext::startRun() {
+ m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
+ m_currentTracker = nullptr;
+@@ -11336,15 +14397,12 @@
+ m_currentTracker = tracker;
+ }
+
+- TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+- : m_nameAndLocation( nameAndLocation ),
++ TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
++ ITracker(nameAndLocation),
+ m_ctx( ctx ),
+ m_parent( parent )
+ {}
+
+- NameAndLocation const& TrackerBase::nameAndLocation() const {
+- return m_nameAndLocation;
+- }
+ bool TrackerBase::isComplete() const {
+ return m_runState == CompletedSuccessfully || m_runState == Failed;
+ }
+@@ -11387,7 +14445,7 @@
+ }
+
+ bool TrackerBase::isSectionTracker() const { return false; }
+- bool TrackerBase::isIndexTracker() const { return false; }
++ bool TrackerBase::isGeneratorTracker() const { return false; }
+
+ void TrackerBase::open() {
+ m_runState = Executing;
+@@ -11410,7 +14468,7 @@
+ m_runState = CompletedSuccessfully;
+ break;
+ case ExecutingChildren:
+- if( m_children.empty() || m_children.back()->isComplete() )
++ if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
+ m_runState = CompletedSuccessfully;
+ break;
+
+@@ -11445,7 +14503,8 @@
+ }
+
+ SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+- : TrackerBase( nameAndLocation, ctx, parent )
++ : TrackerBase( nameAndLocation, ctx, parent ),
++ m_trimmed_name(trim(nameAndLocation.name))
+ {
+ if( parent ) {
+ while( !parent->isSectionTracker() )
+@@ -11456,6 +14515,17 @@
+ }
+ }
+
++ bool SectionTracker::isComplete() const {
++ bool complete = true;
++
++ if (m_filters.empty()
++ || m_filters[0] == ""
++ || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
++ complete = TrackerBase::isComplete();
++ }
++ return complete;
++ }
++
+ bool SectionTracker::isSectionTracker() const { return true; }
+
+ SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+@@ -11477,63 +14547,29 @@
+ }
+
+ void SectionTracker::tryOpen() {
+- if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
++ if( !isComplete() )
+ open();
+ }
+
+ void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
+ if( !filters.empty() ) {
+- m_filters.push_back(""); // Root - should never be consulted
+- m_filters.push_back(""); // Test Case - not a section filter
++ m_filters.reserve( m_filters.size() + filters.size() + 2 );
++ m_filters.emplace_back(""); // Root - should never be consulted
++ m_filters.emplace_back(""); // Test Case - not a section filter
+ m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+ }
+ }
+ void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+ if( filters.size() > 1 )
+- m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
+- }
+-
+- IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+- : TrackerBase( nameAndLocation, ctx, parent ),
+- m_size( size )
+- {}
+-
+- bool IndexTracker::isIndexTracker() const { return true; }
+-
+- IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
+- std::shared_ptr<IndexTracker> tracker;
+-
+- ITracker& currentTracker = ctx.currentTracker();
+- if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+- assert( childTracker );
+- assert( childTracker->isIndexTracker() );
+- tracker = std::static_pointer_cast<IndexTracker>( childTracker );
+- }
+- else {
+- tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, &currentTracker, size );
+- currentTracker.addChild( tracker );
+- }
+-
+- if( !ctx.completedCycle() && !tracker->isComplete() ) {
+- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+- tracker->moveNext();
+- tracker->open();
+- }
+-
+- return *tracker;
++ m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
+ }
+
+- int IndexTracker::index() const { return m_index; }
+-
+- void IndexTracker::moveNext() {
+- m_index++;
+- m_children.clear();
++ std::vector<std::string> const& SectionTracker::getFilters() const {
++ return m_filters;
+ }
+
+- void IndexTracker::close() {
+- TrackerBase::close();
+- if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+- m_runState = Executing;
++ std::string const& SectionTracker::trimmedName() const {
++ return m_trimmed_name;
+ }
+
+ } // namespace TestCaseTracking
+@@ -11541,7 +14577,6 @@
+ using TestCaseTracking::ITracker;
+ using TestCaseTracking::TrackerContext;
+ using TestCaseTracking::SectionTracker;
+-using TestCaseTracking::IndexTracker;
+
+ } // namespace Catch
+
+@@ -11586,47 +14621,81 @@
+
+ namespace Catch {
+
++ TestSpec::Pattern::Pattern( std::string const& name )
++ : m_name( name )
++ {}
++
+ TestSpec::Pattern::~Pattern() = default;
+- TestSpec::NamePattern::~NamePattern() = default;
+- TestSpec::TagPattern::~TagPattern() = default;
+- TestSpec::ExcludedPattern::~ExcludedPattern() = default;
+
+- TestSpec::NamePattern::NamePattern( std::string const& name )
+- : m_wildcardPattern( toLower( name ), CaseSensitive::No )
++ std::string const& TestSpec::Pattern::name() const {
++ return m_name;
++ }
++
++ TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
++ : Pattern( filterString )
++ , m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ {}
++
+ bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
+- return m_wildcardPattern.matches( toLower( testCase.name ) );
++ return m_wildcardPattern.matches( testCase.name );
+ }
+
+- TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
++ TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
++ : Pattern( filterString )
++ , m_tag( toLower( tag ) )
++ {}
++
+ bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
+ return std::find(begin(testCase.lcaseTags),
+ end(testCase.lcaseTags),
+ m_tag) != end(testCase.lcaseTags);
+ }
+
+- TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+- bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
++ TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
++ : Pattern( underlyingPattern->name() )
++ , m_underlyingPattern( underlyingPattern )
++ {}
++
++ bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
++ return !m_underlyingPattern->matches( testCase );
++ }
+
+ bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
+- // All patterns in a filter must match for the filter to be a match
+- for( auto const& pattern : m_patterns ) {
+- if( !pattern->matches( testCase ) )
+- return false;
+- }
+- return true;
++ return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
++ }
++
++ std::string TestSpec::Filter::name() const {
++ std::string name;
++ for( auto const& p : m_patterns )
++ name += p->name();
++ return name;
+ }
+
+ bool TestSpec::hasFilters() const {
+ return !m_filters.empty();
+ }
++
+ bool TestSpec::matches( TestCaseInfo const& testCase ) const {
+- // A TestSpec matches if any filter matches
+- for( auto const& filter : m_filters )
+- if( filter.matches( testCase ) )
+- return true;
+- return false;
++ return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
++ }
++
++ TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const
++ {
++ Matches matches( m_filters.size() );
++ std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
++ std::vector<TestCase const*> currentMatches;
++ for( auto const& test : testCases )
++ if( isThrowSafe( test, config ) && filter.matches( test ) )
++ currentMatches.emplace_back( &test );
++ return FilterMatch{ filter.name(), currentMatches };
++ } );
++ return matches;
++ }
++
++ const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
++ return (m_invalidArgs);
+ }
++
+ }
+ // end catch_test_spec.cpp
+ // start catch_test_spec_parser.cpp
+@@ -11638,64 +14707,136 @@
+ TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+- m_start = std::string::npos;
+ m_arg = m_tagAliases->expandAliases( arg );
+ m_escapeChars.clear();
++ m_substring.reserve(m_arg.size());
++ m_patternName.reserve(m_arg.size());
++ m_realPatternPos = 0;
++
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+- visitChar( m_arg[m_pos] );
+- if( m_mode == Name )
+- addPattern<TestSpec::NamePattern>();
++ //if visitChar fails
++ if( !visitChar( m_arg[m_pos] ) ){
++ m_testSpec.m_invalidArgs.push_back(arg);
++ break;
++ }
++ endMode();
+ return *this;
+ }
+ TestSpec TestSpecParser::testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
++ bool TestSpecParser::visitChar( char c ) {
++ if( (m_mode != EscapedName) && (c == '\\') ) {
++ escape();
++ addCharToPattern(c);
++ return true;
++ }else if((m_mode != EscapedName) && (c == ',') ) {
++ return separate();
++ }
+
+- void TestSpecParser::visitChar( char c ) {
+- if( m_mode == None ) {
+- switch( c ) {
+- case ' ': return;
+- case '~': m_exclusion = true; return;
+- case '[': return startNewMode( Tag, ++m_pos );
+- case '"': return startNewMode( QuotedName, ++m_pos );
+- case '\\': return escape();
+- default: startNewMode( Name, m_pos ); break;
+- }
+- }
+- if( m_mode == Name ) {
+- if( c == ',' ) {
+- addPattern<TestSpec::NamePattern>();
+- addFilter();
+- }
+- else if( c == '[' ) {
+- if( subString() == "exclude:" )
+- m_exclusion = true;
+- else
+- addPattern<TestSpec::NamePattern>();
+- startNewMode( Tag, ++m_pos );
+- }
+- else if( c == '\\' )
+- escape();
++ switch( m_mode ) {
++ case None:
++ if( processNoneChar( c ) )
++ return true;
++ break;
++ case Name:
++ processNameChar( c );
++ break;
++ case EscapedName:
++ endMode();
++ addCharToPattern(c);
++ return true;
++ default:
++ case Tag:
++ case QuotedName:
++ if( processOtherChar( c ) )
++ return true;
++ break;
++ }
++
++ m_substring += c;
++ if( !isControlChar( c ) ) {
++ m_patternName += c;
++ m_realPatternPos++;
++ }
++ return true;
++ }
++ // Two of the processing methods return true to signal the caller to return
++ // without adding the given character to the current pattern strings
++ bool TestSpecParser::processNoneChar( char c ) {
++ switch( c ) {
++ case ' ':
++ return true;
++ case '~':
++ m_exclusion = true;
++ return false;
++ case '[':
++ startNewMode( Tag );
++ return false;
++ case '"':
++ startNewMode( QuotedName );
++ return false;
++ default:
++ startNewMode( Name );
++ return false;
++ }
++ }
++ void TestSpecParser::processNameChar( char c ) {
++ if( c == '[' ) {
++ if( m_substring == "exclude:" )
++ m_exclusion = true;
++ else
++ endMode();
++ startNewMode( Tag );
+ }
+- else if( m_mode == EscapedName )
+- m_mode = Name;
+- else if( m_mode == QuotedName && c == '"' )
+- addPattern<TestSpec::NamePattern>();
+- else if( m_mode == Tag && c == ']' )
+- addPattern<TestSpec::TagPattern>();
+ }
+- void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
++ bool TestSpecParser::processOtherChar( char c ) {
++ if( !isControlChar( c ) )
++ return false;
++ m_substring += c;
++ endMode();
++ return true;
++ }
++ void TestSpecParser::startNewMode( Mode mode ) {
+ m_mode = mode;
+- m_start = start;
++ }
++ void TestSpecParser::endMode() {
++ switch( m_mode ) {
++ case Name:
++ case QuotedName:
++ return addNamePattern();
++ case Tag:
++ return addTagPattern();
++ case EscapedName:
++ revertBackToLastMode();
++ return;
++ case None:
++ default:
++ return startNewMode( None );
++ }
+ }
+ void TestSpecParser::escape() {
+- if( m_mode == None )
+- m_start = m_pos;
++ saveLastMode();
+ m_mode = EscapedName;
+- m_escapeChars.push_back( m_pos );
++ m_escapeChars.push_back(m_realPatternPos);
++ }
++ bool TestSpecParser::isControlChar( char c ) const {
++ switch( m_mode ) {
++ default:
++ return false;
++ case None:
++ return c == '~';
++ case Name:
++ return c == '[';
++ case EscapedName:
++ return true;
++ case QuotedName:
++ return c == '"';
++ case Tag:
++ return c == '[' || c == ']';
++ }
+ }
+- std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+
+ void TestSpecParser::addFilter() {
+ if( !m_currentFilter.m_patterns.empty() ) {
+@@ -11704,6 +14845,86 @@
+ }
+ }
+
++ void TestSpecParser::saveLastMode() {
++ lastMode = m_mode;
++ }
++
++ void TestSpecParser::revertBackToLastMode() {
++ m_mode = lastMode;
++ }
++
++ bool TestSpecParser::separate() {
++ if( (m_mode==QuotedName) || (m_mode==Tag) ){
++ //invalid argument, signal failure to previous scope.
++ m_mode = None;
++ m_pos = m_arg.size();
++ m_substring.clear();
++ m_patternName.clear();
++ m_realPatternPos = 0;
++ return false;
++ }
++ endMode();
++ addFilter();
++ return true; //success
++ }
++
++ std::string TestSpecParser::preprocessPattern() {
++ std::string token = m_patternName;
++ for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
++ token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
++ m_escapeChars.clear();
++ if (startsWith(token, "exclude:")) {
++ m_exclusion = true;
++ token = token.substr(8);
++ }
++
++ m_patternName.clear();
++ m_realPatternPos = 0;
++
++ return token;
++ }
++
++ void TestSpecParser::addNamePattern() {
++ auto token = preprocessPattern();
++
++ if (!token.empty()) {
++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
++ if (m_exclusion)
++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++ m_currentFilter.m_patterns.push_back(pattern);
++ }
++ m_substring.clear();
++ m_exclusion = false;
++ m_mode = None;
++ }
++
++ void TestSpecParser::addTagPattern() {
++ auto token = preprocessPattern();
++
++ if (!token.empty()) {
++ // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
++ // we have to create a separate hide tag and shorten the real one
++ if (token.size() > 1 && token[0] == '.') {
++ token.erase(token.begin());
++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
++ if (m_exclusion) {
++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++ }
++ m_currentFilter.m_patterns.push_back(pattern);
++ }
++
++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
++
++ if (m_exclusion) {
++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++ }
++ m_currentFilter.m_patterns.push_back(pattern);
++ }
++ m_substring.clear();
++ m_exclusion = false;
++ m_mode = None;
++ }
++
+ TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+@@ -11744,7 +14965,7 @@
+ // is terrible and we should move on.
+ // TBD: How to signal that the measured resolution is probably wrong?
+ if (ticks > startTime + 3 * nanosecondsInSecond) {
+- return sum / i;
++ return sum / ( i + 1u );
+ }
+ }
+
+@@ -11805,13 +15026,11 @@
+ enum Arch { Big, Little };
+
+ static Arch which() {
+- union _{
+- int asInt;
+- char asChar[sizeof (int)];
+- } u;
+-
+- u.asInt = 1;
+- return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
++ int one = 1;
++ // If the lowest byte we read is non-zero, we can assume
++ // that little endian format is used.
++ auto value = *reinterpret_cast<char*>(&one);
++ return value ? Little : Big;
+ }
+ };
+ }
+@@ -11935,6 +15154,13 @@
+ }
+ #endif
+
++#if defined(CATCH_CONFIG_CPP17_BYTE)
++#include <cstddef>
++std::string StringMaker<std::byte>::convert(std::byte value) {
++ return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
++}
++#endif // defined(CATCH_CONFIG_CPP17_BYTE)
++
+ std::string StringMaker<int>::convert(int value) {
+ return ::Catch::Detail::stringify(static_cast<long long>(value));
+ }
+@@ -11997,11 +15223,16 @@
+ return "nullptr";
+ }
+
++int StringMaker<float>::precision = 5;
++
+ std::string StringMaker<float>::convert(float value) {
+- return fpToString(value, 5) + 'f';
++ return fpToString(value, precision) + 'f';
+ }
++
++int StringMaker<double>::precision = 10;
++
+ std::string StringMaker<double>::convert(double value) {
+- return fpToString(value, 10);
++ return fpToString(value, precision);
+ }
+
+ std::string ratio_string<std::atto>::symbol() { return "a"; }
+@@ -12075,11 +15306,48 @@
+ // end catch_totals.cpp
+ // start catch_uncaught_exceptions.cpp
+
++// start catch_config_uncaught_exceptions.hpp
++
++// Copyright Catch2 Authors
++// Distributed under the Boost Software License, Version 1.0.
++// (See accompanying file LICENSE_1_0.txt or copy at
++// https://www.boost.org/LICENSE_1_0.txt)
++
++// SPDX-License-Identifier: BSL-1.0
++
++#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++
++#if defined(_MSC_VER)
++# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++# endif
++#endif
++
++#include <exception>
++
++#if defined(__cpp_lib_uncaught_exceptions) \
++ && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++
++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#endif // __cpp_lib_uncaught_exceptions
++
++#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \
++ && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \
++ && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++
++# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#endif
++
++#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++// end catch_config_uncaught_exceptions.hpp
+ #include <exception>
+
+ namespace Catch {
+ bool uncaught_exceptions() {
+-#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
++ return false;
++#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+ return std::uncaught_exceptions() > 0;
+ #else
+ return std::uncaught_exception();
+@@ -12119,7 +15387,7 @@
+ }
+
+ Version const& libraryVersion() {
+- static Version version( 2, 5, 0, "", 0 );
++ static Version version( 2, 13, 8, "", 0 );
+ return version;
+ }
+
+@@ -12127,14 +15395,12 @@
+ // end catch_version.cpp
+ // start catch_wildcard_pattern.cpp
+
+-#include <sstream>
+-
+ namespace Catch {
+
+ WildcardPattern::WildcardPattern( std::string const& pattern,
+ CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+- m_pattern( adjustCase( pattern ) )
++ m_pattern( normaliseString( pattern ) )
+ {
+ if( startsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 1 );
+@@ -12149,28 +15415,27 @@
+ bool WildcardPattern::matches( std::string const& str ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+- return m_pattern == adjustCase( str );
++ return m_pattern == normaliseString( str );
+ case WildcardAtStart:
+- return endsWith( adjustCase( str ), m_pattern );
++ return endsWith( normaliseString( str ), m_pattern );
+ case WildcardAtEnd:
+- return startsWith( adjustCase( str ), m_pattern );
++ return startsWith( normaliseString( str ), m_pattern );
+ case WildcardAtBothEnds:
+- return contains( adjustCase( str ), m_pattern );
++ return contains( normaliseString( str ), m_pattern );
+ default:
+ CATCH_INTERNAL_ERROR( "Unknown enum" );
+ }
+ }
+
+- std::string WildcardPattern::adjustCase( std::string const& str ) const {
+- return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
++ std::string WildcardPattern::normaliseString( std::string const& str ) const {
++ return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
+ }
+ }
+ // end catch_wildcard_pattern.cpp
+ // start catch_xmlwriter.cpp
+
+ #include <iomanip>
+-
+-using uchar = unsigned char;
++#include <type_traits>
+
+ namespace Catch {
+
+@@ -12203,13 +15468,37 @@
+ }
+
+ void hexEscapeChar(std::ostream& os, unsigned char c) {
++ std::ios_base::fmtflags f(os.flags());
+ os << "\\x"
+ << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>(c);
++ os.flags(f);
++ }
++
++ bool shouldNewline(XmlFormatting fmt) {
++ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
++ }
++
++ bool shouldIndent(XmlFormatting fmt) {
++ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
+ }
+
+ } // anonymous namespace
+
++ XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
++ return static_cast<XmlFormatting>(
++ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
++ static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
++ );
++ }
++
++ XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
++ return static_cast<XmlFormatting>(
++ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
++ static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
++ );
++ }
++
+ XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+ : m_str( str ),
+ m_forWhat( forWhat )
+@@ -12220,7 +15509,7 @@
+ // (see: http://www.w3.org/TR/xml/#syntax)
+
+ for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+- uchar c = m_str[idx];
++ unsigned char c = m_str[idx];
+ switch (c) {
+ case '<': os << "&lt;"; break;
+ case '&': os << "&amp;"; break;
+@@ -12280,7 +15569,7 @@
+ bool valid = true;
+ uint32_t value = headerValue(c);
+ for (std::size_t n = 1; n < encBytes; ++n) {
+- uchar nc = m_str[idx + n];
++ unsigned char nc = m_str[idx + n];
+ valid &= ((nc & 0xC0) == 0x80);
+ value = (value << 6) | (nc & 0x3F);
+ }
+@@ -12314,13 +15603,17 @@
+ return os;
+ }
+
+- XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
+- : m_writer( writer )
++ XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt )
++ : m_writer( writer ),
++ m_fmt(fmt)
+ {}
+
+ XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
+- : m_writer( other.m_writer ){
++ : m_writer( other.m_writer ),
++ m_fmt(other.m_fmt)
++ {
+ other.m_writer = nullptr;
++ other.m_fmt = XmlFormatting::None;
+ }
+ XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
+ if ( m_writer ) {
+@@ -12328,16 +15621,19 @@
+ }
+ m_writer = other.m_writer;
+ other.m_writer = nullptr;
++ m_fmt = other.m_fmt;
++ other.m_fmt = XmlFormatting::None;
+ return *this;
+ }
+
+ XmlWriter::ScopedElement::~ScopedElement() {
+- if( m_writer )
+- m_writer->endElement();
++ if (m_writer) {
++ m_writer->endElement(m_fmt);
++ }
+ }
+
+- XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
+- m_writer->writeText( text, indent );
++ XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
++ m_writer->writeText( text, fmt );
+ return *this;
+ }
+
+@@ -12347,37 +15643,47 @@
+ }
+
+ XmlWriter::~XmlWriter() {
+- while( !m_tags.empty() )
++ while (!m_tags.empty()) {
+ endElement();
++ }
++ newlineIfNecessary();
+ }
+
+- XmlWriter& XmlWriter::startElement( std::string const& name ) {
++ XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+- m_os << m_indent << '<' << name;
++ if (shouldIndent(fmt)) {
++ m_os << m_indent;
++ m_indent += " ";
++ }
++ m_os << '<' << name;
+ m_tags.push_back( name );
+- m_indent += " ";
+ m_tagIsOpen = true;
++ applyFormatting(fmt);
+ return *this;
+ }
+
+- XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
+- ScopedElement scoped( this );
+- startElement( name );
++ XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
++ ScopedElement scoped( this, fmt );
++ startElement( name, fmt );
+ return scoped;
+ }
+
+- XmlWriter& XmlWriter::endElement() {
+- newlineIfNecessary();
+- m_indent = m_indent.substr( 0, m_indent.size()-2 );
++ XmlWriter& XmlWriter::endElement(XmlFormatting fmt) {
++ m_indent = m_indent.substr(0, m_indent.size() - 2);
++
+ if( m_tagIsOpen ) {
+ m_os << "/>";
+ m_tagIsOpen = false;
++ } else {
++ newlineIfNecessary();
++ if (shouldIndent(fmt)) {
++ m_os << m_indent;
++ }
++ m_os << "</" << m_tags.back() << ">";
+ }
+- else {
+- m_os << m_indent << "</" << m_tags.back() << ">";
+- }
+- m_os << std::endl;
++ m_os << std::flush;
++ applyFormatting(fmt);
+ m_tags.pop_back();
+ return *this;
+ }
+@@ -12393,22 +15699,26 @@
+ return *this;
+ }
+
+- XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
++ XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+- if( tagWasOpen && indent )
++ if (tagWasOpen && shouldIndent(fmt)) {
+ m_os << m_indent;
++ }
+ m_os << XmlEncode( text );
+- m_needsNewline = true;
++ applyFormatting(fmt);
+ }
+ return *this;
+ }
+
+- XmlWriter& XmlWriter::writeComment( std::string const& text ) {
++ XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
+ ensureTagClosed();
+- m_os << m_indent << "<!--" << text << "-->";
+- m_needsNewline = true;
++ if (shouldIndent(fmt)) {
++ m_os << m_indent;
++ }
++ m_os << "<!--" << text << "-->";
++ applyFormatting(fmt);
+ return *this;
+ }
+
+@@ -12424,11 +15734,16 @@
+
+ void XmlWriter::ensureTagClosed() {
+ if( m_tagIsOpen ) {
+- m_os << ">" << std::endl;
++ m_os << '>' << std::flush;
++ newlineIfNecessary();
+ m_tagIsOpen = false;
+ }
+ }
+
++ void XmlWriter::applyFormatting(XmlFormatting fmt) {
++ m_needsNewline = shouldNewline(fmt);
++ }
++
+ void XmlWriter::writeDeclaration() {
+ m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ }
+@@ -12469,11 +15784,37 @@
+ #ifdef _MSC_VER
+ sprintf_s(buffer, "%.3f", duration);
+ #else
+- sprintf(buffer, "%.3f", duration);
++ std::sprintf(buffer, "%.3f", duration);
+ #endif
+ return std::string(buffer);
+ }
+
++ bool shouldShowDuration( IConfig const& config, double duration ) {
++ if ( config.showDurations() == ShowDurations::Always ) {
++ return true;
++ }
++ if ( config.showDurations() == ShowDurations::Never ) {
++ return false;
++ }
++ const double min = config.minDuration();
++ return min >= 0 && duration >= min;
++ }
++
++ std::string serializeFilters( std::vector<std::string> const& container ) {
++ ReusableStringStream oss;
++ bool first = true;
++ for (auto&& filter : container)
++ {
++ if (!first)
++ oss << ' ';
++ else
++ first = false;
++
++ oss << filter;
++ }
++ return oss.str();
++ }
++
+ TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)
+ :StreamingReporterBase(_config) {}
+
+@@ -12689,24 +16030,25 @@
+ if (itMessage == messages.end())
+ return;
+
+- // using messages.end() directly yields (or auto) compilation error:
+- std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+- const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
++ const auto itEnd = messages.cend();
++ const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+
+ {
+ Colour colourGuard(colour);
+ stream << " with " << pluralise(N, "message") << ':';
+ }
+
+- for (; itMessage != itEnd; ) {
++ while (itMessage != itEnd) {
+ // If this assertion is a warning ignore any INFO messages
+ if (printInfoMessages || itMessage->type != ResultWas::Info) {
+- stream << " '" << itMessage->message << '\'';
+- if (++itMessage != itEnd) {
++ printMessage();
++ if (itMessage != itEnd) {
+ Colour colourGuard(dimColour());
+ stream << " and";
+ }
++ continue;
+ }
++ ++itMessage;
+ }
+ }
+
+@@ -12724,10 +16066,6 @@
+ return "Reports test results on a single line, suitable for IDEs";
+ }
+
+- ReporterPreferences CompactReporter::getPreferences() const {
+- return m_reporterPrefs;
+- }
+-
+ void CompactReporter::noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+@@ -12754,8 +16092,9 @@
+ }
+
+ void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
+- if (m_config->showDurations() == ShowDurations::Always) {
+- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
++ double dur = _sectionStats.durationInSeconds;
++ if ( shouldShowDuration( *m_config, dur ) ) {
++ stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ }
+
+@@ -12779,8 +16118,13 @@
+ #if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+- // Note that 4062 (not all labels are handled
+- // and default is missing) is enabled
++ // Note that 4062 (not all labels are handled and default is missing) is enabled
++#endif
++
++#if defined(__clang__)
++# pragma clang diagnostic push
++// For simplicity, benchmarking-only helpers are always enabled
++# pragma clang diagnostic ignored "-Wunused-function"
+ #endif
+
+ namespace Catch {
+@@ -12962,11 +16306,11 @@
+ static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
+ static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
+
+- uint64_t m_inNanoseconds;
++ double m_inNanoseconds;
+ Unit m_units;
+
+ public:
+- explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
++ explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
+ : m_inNanoseconds(inNanoseconds),
+ m_units(units) {
+ if (m_units == Unit::Auto) {
+@@ -12995,7 +16339,7 @@
+ case Unit::Minutes:
+ return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
+ default:
+- return static_cast<double>(m_inNanoseconds);
++ return m_inNanoseconds;
+ }
+ }
+ auto unitsAsString() const -> std::string {
+@@ -13003,7 +16347,7 @@
+ case Unit::Nanoseconds:
+ return "ns";
+ case Unit::Microseconds:
+- return "µs";
++ return "us";
+ case Unit::Milliseconds:
+ return "ms";
+ case Unit::Seconds:
+@@ -13016,7 +16360,7 @@
+
+ }
+ friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
+- return os << duration.value() << " " << duration.unitsAsString();
++ return os << duration.value() << ' ' << duration.unitsAsString();
+ }
+ };
+ } // end anon namespace
+@@ -13041,10 +16385,16 @@
+ if (!m_isOpen) {
+ m_isOpen = true;
+ *this << RowBreak();
+- for (auto const& info : m_columnInfos)
+- *this << info.name << ColumnBreak();
+- *this << RowBreak();
+- m_os << Catch::getLineOfChars<'-'>() << "\n";
++
++ Columns headerCols;
++ Spacer spacer(2);
++ for (auto const& info : m_columnInfos) {
++ headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
++ headerCols += spacer;
++ }
++ m_os << headerCols << '\n';
++
++ m_os << Catch::getLineOfChars<'-'>() << '\n';
+ }
+ }
+ void close() {
+@@ -13063,30 +16413,29 @@
+
+ friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
+ auto colStr = tp.m_oss.str();
+- // This takes account of utf8 encodings
+- auto strSize = Catch::StringRef(colStr).numberOfCharacters();
++ const auto strSize = colStr.size();
+ tp.m_oss.str("");
+ tp.open();
+ if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
+ tp.m_currentColumn = -1;
+- tp.m_os << "\n";
++ tp.m_os << '\n';
+ }
+ tp.m_currentColumn++;
+
+ auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
+- auto padding = (strSize + 2 < static_cast<std::size_t>(colInfo.width))
+- ? std::string(colInfo.width - (strSize + 2), ' ')
++ auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
++ ? std::string(colInfo.width - (strSize + 1), ' ')
+ : std::string();
+ if (colInfo.justification == ColumnInfo::Left)
+- tp.m_os << colStr << padding << " ";
++ tp.m_os << colStr << padding << ' ';
+ else
+- tp.m_os << padding << colStr << " ";
++ tp.m_os << padding << colStr << ' ';
+ return tp;
+ }
+
+ friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
+ if (tp.m_currentColumn > 0) {
+- tp.m_os << "\n";
++ tp.m_os << '\n';
+ tp.m_currentColumn = -1;
+ }
+ return tp;
+@@ -13096,12 +16445,26 @@
+ ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
+ : StreamingReporterBase(config),
+ m_tablePrinter(new TablePrinter(config.stream(),
+- {
+- { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
+- { "iters", 8, ColumnInfo::Right },
+- { "elapsed ns", 14, ColumnInfo::Right },
+- { "average", 14, ColumnInfo::Right }
+- })) {}
++ [&config]() -> std::vector<ColumnInfo> {
++ if (config.fullConfig()->benchmarkNoAnalysis())
++ {
++ return{
++ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
++ { " samples", 14, ColumnInfo::Right },
++ { " iterations", 14, ColumnInfo::Right },
++ { " mean", 14, ColumnInfo::Right }
++ };
++ }
++ else
++ {
++ return{
++ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
++ { "samples mean std dev", 14, ColumnInfo::Right },
++ { "iterations low mean low std dev", 14, ColumnInfo::Right },
++ { "estimated high mean high std dev", 14, ColumnInfo::Right }
++ };
++ }
++ }())) {}
+ ConsoleReporter::~ConsoleReporter() = default;
+
+ std::string ConsoleReporter::getDescription() {
+@@ -13112,6 +16475,10 @@
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+
++void ConsoleReporter::reportInvalidArguments(std::string const&arg){
++ stream << "Invalid Filter: " << arg << std::endl;
++}
++
+ void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
+
+ bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+@@ -13132,6 +16499,7 @@
+ }
+
+ void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
++ m_tablePrinter->close();
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting(_sectionInfo);
+ }
+@@ -13146,8 +16514,9 @@
+ stream << "\nNo assertions in test case";
+ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ }
+- if (m_config->showDurations() == ShowDurations::Always) {
+- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
++ double dur = _sectionStats.durationInSeconds;
++ if (shouldShowDuration(*m_config, dur)) {
++ stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ if (m_headerPrinted) {
+ m_headerPrinted = false;
+@@ -13155,28 +16524,53 @@
+ StreamingReporterBase::sectionEnded(_sectionStats);
+ }
+
+-void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
+- lazyPrintWithoutClosingBenchmarkTable();
+-
+- auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) );
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++void ConsoleReporter::benchmarkPreparing(std::string const& name) {
++ lazyPrintWithoutClosingBenchmarkTable();
++
++ auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
++
++ bool firstLine = true;
++ for (auto line : nameCol) {
++ if (!firstLine)
++ (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
++ else
++ firstLine = false;
+
+- bool firstLine = true;
+- for (auto line : nameCol) {
+- if (!firstLine)
+- (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
+- else
+- firstLine = false;
++ (*m_tablePrinter) << line << ColumnBreak();
++ }
++}
+
+- (*m_tablePrinter) << line << ColumnBreak();
++void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
++ (*m_tablePrinter) << info.samples << ColumnBreak()
++ << info.iterations << ColumnBreak();
++ if (!m_config->benchmarkNoAnalysis())
++ (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
++}
++void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
++ if (m_config->benchmarkNoAnalysis())
++ {
++ (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
++ }
++ else
++ {
++ (*m_tablePrinter) << ColumnBreak()
++ << Duration(stats.mean.point.count()) << ColumnBreak()
++ << Duration(stats.mean.lower_bound.count()) << ColumnBreak()
++ << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
++ << Duration(stats.standardDeviation.point.count()) << ColumnBreak()
++ << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
++ << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
+ }
+ }
+-void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) {
+- Duration average(stats.elapsedTimeInNanoseconds / stats.iterations);
++
++void ConsoleReporter::benchmarkFailed(std::string const& error) {
++ Colour colour(Colour::Red);
+ (*m_tablePrinter)
+- << stats.iterations << ColumnBreak()
+- << stats.elapsedTimeInNanoseconds << ColumnBreak()
+- << average << ColumnBreak();
++ << "Benchmark failed (" << error << ')'
++ << ColumnBreak() << RowBreak();
+ }
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+ void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
+ m_tablePrinter->close();
+@@ -13198,6 +16592,10 @@
+ stream << std::endl;
+ StreamingReporterBase::testRunEnded(_testRunStats);
+ }
++void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
++ StreamingReporterBase::testRunStarting(_testInfo);
++ printTestFilters();
++}
+
+ void ConsoleReporter::lazyPrint() {
+
+@@ -13251,11 +16649,9 @@
+
+ SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+- if (!lineInfo.empty()) {
+- stream << getLineOfChars<'-'>() << '\n';
+- Colour colourGuard(Colour::FileName);
+- stream << lineInfo << '\n';
+- }
++ stream << getLineOfChars<'-'>() << '\n';
++ Colour colourGuard(Colour::FileName);
++ stream << lineInfo << '\n';
+ stream << getLineOfChars<'.'>() << '\n' << std::endl;
+ }
+
+@@ -13379,6 +16775,13 @@
+ stream << getLineOfChars<'-'>() << '\n';
+ }
+
++void ConsoleReporter::printTestFilters() {
++ if (m_config->testSpec().hasFilters()) {
++ Colour guard(Colour::BrightYellow);
++ stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
++ }
++}
++
+ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
+
+ } // end namespace Catch
+@@ -13386,6 +16789,10 @@
+ #if defined(_MSC_VER)
+ #pragma warning(pop)
+ #endif
++
++#if defined(__clang__)
++# pragma clang diagnostic pop
++#endif
+ // end catch_reporter_console.cpp
+ // start catch_reporter_junit.cpp
+
+@@ -13393,6 +16800,7 @@
+ #include <sstream>
+ #include <ctime>
+ #include <algorithm>
++#include <iomanip>
+
+ namespace Catch {
+
+@@ -13420,7 +16828,7 @@
+ #else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+ #endif
+- return std::string(timeStamp);
++ return std::string(timeStamp, timeStampSize-1);
+ }
+
+ std::string fileNameTag(const std::vector<std::string> &tags) {
+@@ -13431,6 +16839,17 @@
+ return it->substr(1);
+ return std::string();
+ }
++
++ // Formats the duration in seconds to 3 decimal places.
++ // This is done because some genius defined Maven Surefire schema
++ // in a way that only accepts 3 decimal places, and tools like
++ // Jenkins use that schema for validation JUnit reporter output.
++ std::string formatDuration( double seconds ) {
++ ReusableStringStream rss;
++ rss << std::fixed << std::setprecision( 3 ) << seconds;
++ return rss.str();
++ }
++
+ } // anonymous namespace
+
+ JunitReporter::JunitReporter( ReporterConfig const& _config )
+@@ -13490,6 +16909,7 @@
+
+ void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
++
+ TestGroupStats const& stats = groupNode.value;
+ xml.writeAttribute( "name", stats.groupInfo.name );
+ xml.writeAttribute( "errors", unexpectedExceptions );
+@@ -13499,15 +16919,30 @@
+ if( m_config->showDurations() == ShowDurations::Never )
+ xml.writeAttribute( "time", "" );
+ else
+- xml.writeAttribute( "time", suiteTime );
++ xml.writeAttribute( "time", formatDuration( suiteTime ) );
+ xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+
++ // Write properties if there are any
++ if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
++ auto properties = xml.scopedElement("properties");
++ if (m_config->hasTestFilters()) {
++ xml.scopedElement("property")
++ .writeAttribute("name", "filters")
++ .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
++ }
++ if (m_config->rngSeed() != 0) {
++ xml.scopedElement("property")
++ .writeAttribute("name", "random-seed")
++ .writeAttribute("value", m_config->rngSeed());
++ }
++ }
++
+ // Write test cases
+ for( auto const& child : groupNode.children )
+ writeTestCase( *child );
+
+- xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false );
+- xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false );
++ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
++ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
+ }
+
+ void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
+@@ -13529,12 +16964,13 @@
+ if ( !m_config->name().empty() )
+ className = m_config->name() + "." + className;
+
+- writeSection( className, "", rootSection );
++ writeSection( className, "", rootSection, stats.testInfo.okToFail() );
+ }
+
+- void JunitReporter::writeSection( std::string const& className,
+- std::string const& rootName,
+- SectionNode const& sectionNode ) {
++ void JunitReporter::writeSection( std::string const& className,
++ std::string const& rootName,
++ SectionNode const& sectionNode,
++ bool testOkToFail) {
+ std::string name = trim( sectionNode.stats.sectionInfo.name );
+ if( !rootName.empty() )
+ name = rootName + '/' + name;
+@@ -13551,20 +16987,30 @@
+ xml.writeAttribute( "classname", className );
+ xml.writeAttribute( "name", name );
+ }
+- xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
++ xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) );
++ // This is not ideal, but it should be enough to mimic gtest's
++ // junit output.
++ // Ideally the JUnit reporter would also handle `skipTest`
++ // events and write those out appropriately.
++ xml.writeAttribute( "status", "run" );
++
++ if (sectionNode.stats.assertions.failedButOk) {
++ xml.scopedElement("skipped")
++ .writeAttribute("message", "TEST_CASE tagged with !mayfail");
++ }
+
+ writeAssertions( sectionNode );
+
+ if( !sectionNode.stdOut.empty() )
+- xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
++ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
+ if( !sectionNode.stdErr.empty() )
+- xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
++ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
+ }
+ for( auto const& childNode : sectionNode.childSections )
+ if( className.empty() )
+- writeSection( name, "", *childNode );
++ writeSection( name, "", *childNode, testOkToFail );
+ else
+- writeSection( className, name, *childNode );
++ writeSection( className, name, *childNode, testOkToFail );
+ }
+
+ void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
+@@ -13582,11 +17028,7 @@
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+- elementName = "failure";
+- break;
+ case ResultWas::ExpressionFailed:
+- elementName = "failure";
+- break;
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+@@ -13604,10 +17046,25 @@
+
+ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+- xml.writeAttribute( "message", result.getExpandedExpression() );
++ xml.writeAttribute( "message", result.getExpression() );
+ xml.writeAttribute( "type", result.getTestMacroName() );
+
+ ReusableStringStream rss;
++ if (stats.totals.assertions.total() > 0) {
++ rss << "FAILED" << ":\n";
++ if (result.hasExpression()) {
++ rss << " ";
++ rss << result.getExpressionInMacro();
++ rss << '\n';
++ }
++ if (result.hasExpandedExpression()) {
++ rss << "with expansion:\n";
++ rss << Column(result.getExpandedExpression()).indent(2) << '\n';
++ }
++ } else {
++ rss << '\n';
++ }
++
+ if( !result.getMessage().empty() )
+ rss << result.getMessage() << '\n';
+ for( auto const& msg : stats.infoMessages )
+@@ -13615,7 +17072,7 @@
+ rss << msg.message << '\n';
+
+ rss << "at " << result.getSourceInfo();
+- xml.writeText( rss.str(), false );
++ xml.writeText( rss.str(), XmlFormatting::Newline );
+ }
+ }
+
+@@ -13659,19 +17116,41 @@
+ m_reporter->noMatchingTestCases( spec );
+ }
+
++ void ListeningReporter::reportInvalidArguments(std::string const&arg){
++ for ( auto const& listener : m_listeners ) {
++ listener->reportInvalidArguments( arg );
++ }
++ m_reporter->reportInvalidArguments( arg );
++ }
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ void ListeningReporter::benchmarkPreparing( std::string const& name ) {
++ for (auto const& listener : m_listeners) {
++ listener->benchmarkPreparing(name);
++ }
++ m_reporter->benchmarkPreparing(name);
++ }
+ void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->benchmarkStarting( benchmarkInfo );
+ }
+ m_reporter->benchmarkStarting( benchmarkInfo );
+ }
+- void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
++ void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->benchmarkEnded( benchmarkStats );
+ }
+ m_reporter->benchmarkEnded( benchmarkStats );
+ }
+
++ void ListeningReporter::benchmarkFailed( std::string const& error ) {
++ for (auto const& listener : m_listeners) {
++ listener->benchmarkFailed(error);
++ }
++ m_reporter->benchmarkFailed(error);
++ }
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
+ for ( auto const& listener : m_listeners ) {
+ listener->testRunStarting( testRunInfo );
+@@ -13802,6 +17281,8 @@
+ m_xml.startElement( "Catch" );
+ if( !m_config->name().empty() )
+ m_xml.writeAttribute( "name", m_config->name() );
++ if (m_config->testSpec().hasFilters())
++ m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
+ if( m_config->rngSeed() != 0 )
+ m_xml.scopedElement( "Randomness" )
+ .writeAttribute( "seed", m_config->rngSeed() );
+@@ -13937,9 +17418,9 @@
+ e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+ if( !testCaseStats.stdOut.empty() )
+- m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
++ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
+ if( !testCaseStats.stdErr.empty() )
+- m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
++ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
+
+ m_xml.endElement();
+ }
+@@ -13951,6 +17432,10 @@
+ .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+ .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
++ m_xml.scopedElement( "OverallResultsCases")
++ .writeAttribute( "successes", testGroupStats.totals.testCases.passed )
++ .writeAttribute( "failures", testGroupStats.totals.testCases.failed )
++ .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk );
+ m_xml.endElement();
+ }
+
+@@ -13960,9 +17445,58 @@
+ .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+ .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
++ m_xml.scopedElement( "OverallResultsCases")
++ .writeAttribute( "successes", testRunStats.totals.testCases.passed )
++ .writeAttribute( "failures", testRunStats.totals.testCases.failed )
++ .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk );
++ m_xml.endElement();
++ }
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++ void XmlReporter::benchmarkPreparing(std::string const& name) {
++ m_xml.startElement("BenchmarkResults")
++ .writeAttribute("name", name);
++ }
++
++ void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
++ m_xml.writeAttribute("samples", info.samples)
++ .writeAttribute("resamples", info.resamples)
++ .writeAttribute("iterations", info.iterations)
++ .writeAttribute("clockResolution", info.clockResolution)
++ .writeAttribute("estimatedDuration", info.estimatedDuration)
++ .writeComment("All values in nano seconds");
++ }
++
++ void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
++ m_xml.startElement("mean")
++ .writeAttribute("value", benchmarkStats.mean.point.count())
++ .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count())
++ .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count())
++ .writeAttribute("ci", benchmarkStats.mean.confidence_interval);
++ m_xml.endElement();
++ m_xml.startElement("standardDeviation")
++ .writeAttribute("value", benchmarkStats.standardDeviation.point.count())
++ .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count())
++ .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count())
++ .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval);
++ m_xml.endElement();
++ m_xml.startElement("outliers")
++ .writeAttribute("variance", benchmarkStats.outlierVariance)
++ .writeAttribute("lowMild", benchmarkStats.outliers.low_mild)
++ .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe)
++ .writeAttribute("highMild", benchmarkStats.outliers.high_mild)
++ .writeAttribute("highSevere", benchmarkStats.outliers.high_severe);
++ m_xml.endElement();
+ m_xml.endElement();
+ }
+
++ void XmlReporter::benchmarkFailed(std::string const &error) {
++ m_xml.scopedElement("failed").
++ writeAttribute("message", error);
++ m_xml.endElement();
++ }
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+ } // end namespace Catch
+@@ -13988,7 +17522,7 @@
+
+ #ifndef __OBJC__
+
+-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
++#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+ // Standard C/C++ Win32 Unicode wmain entry point
+ extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+ #else
+@@ -14036,7 +17570,7 @@
+ #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+ #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+
+-#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ )
++#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+ #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+ #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+ #if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+@@ -14050,7 +17584,7 @@
+ #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+
+-#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ )
++#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+ #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+ #if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+@@ -14065,6 +17599,7 @@
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+ #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
++#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
+ #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
+
+@@ -14082,10 +17617,22 @@
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+ #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+ #else
+ #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+ #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+ #endif
+
+ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+@@ -14106,6 +17653,13 @@
+ #define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
+ #define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++#define CATCH_BENCHMARK(...) \
++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
++#define CATCH_BENCHMARK_ADVANCED(name) \
++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+ #else
+
+@@ -14141,6 +17695,7 @@
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+ #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
++#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
+ #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )
+
+@@ -14157,10 +17712,26 @@
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+ #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
++#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
++#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #else
+ #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+ #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
++#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
++#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+ #endif
+
+ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+@@ -14186,6 +17757,13 @@
+ #define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
+ #define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
+
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++#define BENCHMARK(...) \
++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
++#define BENCHMARK_ADVANCED(name) \
++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ using Catch::Detail::Approx;
+
+ #else // CATCH_CONFIG_DISABLE
+@@ -14225,12 +17803,13 @@
+ #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+-#define CATCH_INFO( msg ) (void)(0)
+-#define CATCH_WARN( msg ) (void)(0)
+-#define CATCH_CAPTURE( msg ) (void)(0)
++#define CATCH_INFO( msg ) (void)(0)
++#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
++#define CATCH_WARN( msg ) (void)(0)
++#define CATCH_CAPTURE( msg ) (void)(0)
+
+-#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+-#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
++#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
++#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+ #define CATCH_METHOD_AS_TEST_CASE( method, ... )
+ #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
+ #define CATCH_SECTION( ... )
+@@ -14239,19 +17818,31 @@
+ #define CATCH_FAIL_CHECK( ... ) (void)(0)
+ #define CATCH_SUCCEED( ... ) (void)(0)
+
+-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
++#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
+-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
++#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
++#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #else
+-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
+-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
++#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #endif
+
+ // "BDD-style" convenience wrappers
+-#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
++#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
++#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className )
+ #define CATCH_GIVEN( desc )
+ #define CATCH_AND_GIVEN( desc )
+ #define CATCH_WHEN( desc )
+@@ -14297,11 +17888,12 @@
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+ #define INFO( msg ) (void)(0)
++#define UNSCOPED_INFO( msg ) (void)(0)
+ #define WARN( msg ) (void)(0)
+ #define CAPTURE( msg ) (void)(0)
+
+-#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+-#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
++#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
++#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+ #define METHOD_AS_TEST_CASE( method, ... )
+ #define REGISTER_TEST_CASE( Function, ... ) (void)(0)
+ #define SECTION( ... )
+@@ -14309,14 +17901,26 @@
+ #define FAIL( ... ) (void)(0)
+ #define FAIL_CHECK( ... ) (void)(0)
+ #define SUCCEED( ... ) (void)(0)
+-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
++#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
+-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
++#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
++#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #else
+-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
+-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
++#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
++#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #endif
+
+ #define STATIC_REQUIRE( ... ) (void)(0)
+@@ -14327,8 +17931,8 @@
+ #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+ // "BDD-style" convenience wrappers
+-#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) )
+-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
++#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ) )
++#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className )
+
+ #define GIVEN( desc )
+ #define AND_GIVEN( desc )
diff --git a/patches/use-packaged-streamvbyte.patch b/patches/use-packaged-streamvbyte.patch
new file mode 100644
index 0000000..5586f96
--- /dev/null
+++ b/patches/use-packaged-streamvbyte.patch
@@ -0,0 +1,79 @@
+Description: Use packaged streamvbyte instead of third_party
+Author: Nilesh Patra <nilesh@debian.org>
+Forwarded: not-needed
+Last-Update: 2021-05-22
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -138,34 +138,8 @@
+ find_package(HDF5 1.8.16)
+ find_package(zstd 1.3.1 REQUIRED)
+ find_package(Sanitizers)
+-
+-get_filename_component(STREAMVBYTE_SOURCE_DIR
+- third_party/streamvbyte
+- ABSOLUTE
+-)
+-set(STREAMVBYTE_INSTALL_DIR "${CMAKE_BINARY_DIR}/streamvbyte_lib/")
+-set(STREAMVBYTE_PREFIX ${CMAKE_BINARY_DIR}/streamvbyte)
+-set(STREAMVBYTE_STATIC_LIB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}streamvbyte_static${CMAKE_STATIC_LIBRARY_SUFFIX})
+-set(STREAMVBYTE_STATIC_LIB_SUBDIR ".")
+-get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+-if (is_multi_config)
+- set(STREAMVBYTE_STATIC_LIB_SUBDIR "$<CONFIG>")
+-endif()
+-ExternalProject_Add(
+- streamvbyte
+- SOURCE_DIR ${STREAMVBYTE_SOURCE_DIR}
+- UPDATE_DISCONNECTED TRUE
+- PREFIX ${STREAMVBYTE_PREFIX}
+- CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${STREAMVBYTE_INSTALL_DIR} -DSTREAMVBYTE_DISABLE_NATIVE=ON -DSTD_FLAGS="-std=c99"
+- # We need to tell CMake about libraries we will link to, otherwise some generators (eg: Ninja)
+- # complain about how knowing how to build libstreamvbyte_static.a, for example.
+- # Ideally, we'd set a hypothetical INSTALL_BYPRODUCTS, and link to the installed file, but that
+- # doesn't seem to be an option.
+- BUILD_BYPRODUCTS "<BINARY_DIR>/${STREAMVBYTE_STATIC_LIB_NAME}"
+-)
+-ExternalProject_Get_Property(streamvbyte BINARY_DIR)
+-set(STREAMVBYTE_STATIC_LIB "${BINARY_DIR}/${STREAMVBYTE_STATIC_LIB_SUBDIR}/${STREAMVBYTE_STATIC_LIB_NAME}")
+-
++find_library(STREAMVBYTE_STATIC_LIB NAMES libstreamvbyte.a PATHS /usr/lib/$ENV{DEB_HOST_MULTIARCH})
++message("INFO: The library that was found is: ${STREAMVBYTE_STATIC_LIB}")
+
+ ########################################################################
+ #
+--- a/vbz/CMakeLists.txt
++++ b/vbz/CMakeLists.txt
+@@ -22,14 +22,16 @@
+ PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+- ${STREAMVBYTE_INSTALL_DIR}/include
++)
++
++target_link_libraries(vbz
++ PUBLIC
++ ${STREAMVBYTE_STATIC_LIB}
++ zstd::zstd
+ )
+
+ # This explicit dependency is required to make sure the header files are installed into the right
+ # location before we try building against them.
+-add_dependencies(vbz
+- streamvbyte
+-)
+
+ if (WIN32 OR APPLE OR "${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64")
+ if(NOT MSVC)
+@@ -37,12 +39,6 @@
+ endif()
+ endif()
+
+-target_link_libraries(vbz
+- PUBLIC
+- ${STREAMVBYTE_STATIC_LIB}
+- zstd::zstd
+-)
+-
+ if (BUILD_TESTING)
+ add_subdirectory(fuzzing)
+ add_subdirectory(test)
diff --git a/rules b/rules
new file mode 100755
index 0000000..916af7f
--- /dev/null
+++ b/rules
@@ -0,0 +1,24 @@
+#!/usr/bin/make -f
+
+# DH_VERBOSE := 1
+export LC_ALL=C.UTF-8
+
+include /usr/share/dpkg/default.mk
+
+%:
+ dh $@
+
+override_dh_auto_configure:
+ dh_auto_configure -- -DCMAKE_BUILD_TYPE=Release \
+ -DENABLE_CONAN=OFF \
+ -DENABLE_PERF_TESTING=OFF \
+ -DENABLE_PYTHON=OFF
+
+override_dh_install__not_possible_since_no_static_lib:
+ dh_install
+ d-shlibmove --commit \
+ --multiarch \
+ --devunversioned \
+ --exclude-la \
+ --override s/libzstd1-dev/libzstd-dev/ \
+ debian/tmp/usr/lib/*/*.so
diff --git a/salsa-ci.yml b/salsa-ci.yml
new file mode 100644
index 0000000..1e7946b
--- /dev/null
+++ b/salsa-ci.yml
@@ -0,0 +1,7 @@
+---
+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_REPROTEST: 1
diff --git a/source/format b/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/upstream/metadata b/upstream/metadata
new file mode 100644
index 0000000..585b9ef
--- /dev/null
+++ b/upstream/metadata
@@ -0,0 +1,7 @@
+---
+Archive: GitHub
+Bug-Database: https://github.com/nanoporetech/vbz_compression/issues
+Bug-Submit: https://github.com/nanoporetech/vbz_compression/issues/new
+Changelog: https://github.com/nanoporetech/vbz_compression/tags
+Repository: https://github.com/nanoporetech/vbz_compression.git
+Repository-Browse: https://github.com/nanoporetech/vbz_compression
diff --git a/watch b/watch
new file mode 100644
index 0000000..5a5cb19
--- /dev/null
+++ b/watch
@@ -0,0 +1,4 @@
+version=4
+
+opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%@PACKAGE@-$1.tar.gz%" \
+https://github.com/nanoporetech/vbz_compression//releases .*/.*/v?@ANY_VERSION@\.tar\.gz