diff options
author | Nilesh Patra <nilesh@debian.org> | 2022-04-14 20:28:47 +0200 |
---|---|---|
committer | Nilesh Patra <nilesh@debian.org> | 2022-04-14 20:28:47 +0200 |
commit | 9ab78b5e3fc5ebc4c8e658ae05ecde18966d1a10 (patch) | |
tree | 2589a1710df571064a3a654e3aaaf7d937831a7b |
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-- | changelog | 26 | ||||
-rw-r--r-- | control | 55 | ||||
-rw-r--r-- | copyright | 254 | ||||
-rw-r--r-- | libvbz-hdf-plugin-dev.install | 4 | ||||
-rw-r--r-- | libvbz-hdf-plugin0.install | 1 | ||||
-rw-r--r-- | patches/fix-install-location.patch | 15 | ||||
-rw-r--r-- | patches/series | 4 | ||||
-rw-r--r-- | patches/soversion.patch | 13 | ||||
-rw-r--r-- | patches/update-catch2.patch | 9997 | ||||
-rw-r--r-- | patches/use-packaged-streamvbyte.patch | 79 | ||||
-rwxr-xr-x | rules | 24 | ||||
-rw-r--r-- | salsa-ci.yml | 7 | ||||
-rw-r--r-- | source/format | 1 | ||||
-rw-r--r-- | upstream/metadata | 7 | ||||
-rw-r--r-- | watch | 4 |
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 @@ -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, ¤tTracker ); + currentTracker.addChild( tracker ); + } + +- if( !ctx.completedCycle() && !tracker->isComplete() ) { +- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) +- tracker->moveNext(); ++ if( !tracker->isComplete() ) { + tracker->open(); + } + + return *tracker; + } + +- void moveNext() { +- m_index++; +- m_children.clear(); +- } +- + // TrackerBase interface +- bool isIndexTracker() const override { return true; } ++ bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); +- if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 ) ++ // If a generator has a child (it is followed by a section) ++ // and none of its children have started, then we must wait ++ // until later to start consuming its values. ++ // This catches cases where `GENERATE` is placed between two ++ // `SECTION`s. ++ // **The check for m_children.empty cannot be removed**. ++ // doing so would break `GENERATE` _not_ followed by `SECTION`s. ++ const bool should_wait_for_child = [&]() { ++ // No children -> nobody to wait for ++ if ( m_children.empty() ) { ++ return false; ++ } ++ // If at least one child started executing, don't wait ++ if ( std::find_if( ++ m_children.begin(), ++ m_children.end(), ++ []( TestCaseTracking::ITrackerPtr tracker ) { ++ return tracker->hasStarted(); ++ } ) != m_children.end() ) { ++ return false; ++ } ++ ++ // No children have started. We need to check if they _can_ ++ // start, and thus we should wait for them, or they cannot ++ // start (due to filters), and we shouldn't wait for them ++ auto* parent = m_parent; ++ // This is safe: there is always at least one section ++ // tracker in a test case tracking tree ++ while ( !parent->isSectionTracker() ) { ++ parent = &( parent->parent() ); ++ } ++ assert( parent && ++ "Missing root (test case) level section" ); ++ ++ auto const& parentSection = ++ static_cast<SectionTracker&>( *parent ); ++ auto const& filters = parentSection.getFilters(); ++ // No filters -> no restrictions on running sections ++ if ( filters.empty() ) { ++ return true; ++ } ++ ++ for ( auto const& child : m_children ) { ++ if ( child->isSectionTracker() && ++ std::find( filters.begin(), ++ filters.end(), ++ static_cast<SectionTracker&>( *child ) ++ .trimmedName() ) != ++ filters.end() ) { ++ return true; ++ } ++ } ++ return false; ++ }(); ++ ++ // This check is a bit tricky, because m_generator->next() ++ // has a side-effect, where it consumes generator's current ++ // value, but we do not want to invoke the side-effect if ++ // this generator is still waiting for any child to start. ++ if ( should_wait_for_child || ++ ( m_runState == CompletedSuccessfully && ++ m_generator->next() ) ) { ++ m_children.clear(); + m_runState = Executing; ++ } + } + + // IGeneratorTracker interface +@@ -9725,9 +12710,6 @@ + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } +- auto getIndex() const -> size_t override { +- return m_index; +- } + }; + GeneratorTracker::~GeneratorTracker() {} + } +@@ -9825,6 +12807,9 @@ + // and should be let to clear themselves out. + static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + ++ if (result.getResultType() != ResultWas::Warning) ++ m_messageScopes.clear(); ++ + // Reset working state + resetAssertionInfo(); + m_lastResult = result; +@@ -9848,10 +12833,10 @@ + + return true; + } +- auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { ++ auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; +- GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); +- assert( tracker.isOpen() ); ++ GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, ++ TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } +@@ -9879,6 +12864,7 @@ + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); ++ m_messageScopes.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { +@@ -9890,12 +12876,21 @@ + + m_unfinishedSections.push_back(endInfo); + } ++ ++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) ++ void RunContext::benchmarkPreparing(std::string const& name) { ++ m_reporter->benchmarkPreparing(name); ++ } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } +- void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { ++ void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { + m_reporter->benchmarkEnded( stats ); + } ++ void RunContext::benchmarkFailed(std::string const & error) { ++ m_reporter->benchmarkFailed(error); ++ } ++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); +@@ -9905,6 +12900,10 @@ + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + ++ void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { ++ m_messageScopes.emplace_back( builder ); ++ } ++ + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name +@@ -9926,7 +12925,7 @@ + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); +- tempResult.message = message; ++ tempResult.message = static_cast<std::string>(message); + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); +@@ -9965,6 +12964,7 @@ + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); ++ m_messageScopes.clear(); + } + + bool RunContext::aborting() const { +@@ -9986,13 +12986,10 @@ + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { + #if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +- RedirectedStdOut redirectedStdOut; +- RedirectedStdErr redirectedStdErr; ++ RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +- redirectedCout += redirectedStdOut.str(); +- redirectedCerr += redirectedStdErr.str(); + #else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); +@@ -10019,15 +13016,15 @@ + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); ++ m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { +- FatalConditionHandler fatalConditionHandler; // Handle signals ++ FatalConditionHandlerGuard _(&m_fatalConditionhandler); + m_activeTestCase->invoke(); +- fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { +@@ -10090,7 +13087,7 @@ + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); +- data.message = message; ++ data.message = static_cast<std::string>(message); + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) +@@ -10153,6 +13150,18 @@ + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } ++ ++ void seedRng(IConfig const& config) { ++ if (config.rngSeed() != 0) { ++ std::srand(config.rngSeed()); ++ rng().seed(config.rngSeed()); ++ } ++ } ++ ++ unsigned int rngSeed() { ++ return getCurrentContext().getConfig()->rngSeed(); ++ } ++ + } + // end catch_run_context.cpp + // start catch_section.cpp +@@ -10214,7 +13223,7 @@ + void libIdentify(); + + int applyCommandLine( int argc, char const * const * argv ); +- #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) ++ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int applyCommandLine( int argc, wchar_t const * const * argv ); + #endif + +@@ -10281,6 +13290,8 @@ + // end catch_version.h + #include <cstdlib> + #include <iomanip> ++#include <set> ++#include <iterator> + + namespace Catch { + +@@ -10299,52 +13310,76 @@ + return createReporter(config->getReporterName(), config); + } + +- auto multi = std::unique_ptr<ListeningReporter>(new ListeningReporter); +- ++ // On older platforms, returning std::unique_ptr<ListeningReporter> ++ // when the return type is std::unique_ptr<IStreamingReporter> ++ // doesn't compile without a std::move call. However, this causes ++ // a warning on newer platforms. Thus, we have to work around ++ // it a bit and downcast the pointer manually. ++ auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter); ++ auto& multi = static_cast<ListeningReporter&>(*ret); + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { +- multi->addListener(listener->create(Catch::ReporterConfig(config))); ++ multi.addListener(listener->create(Catch::ReporterConfig(config))); + } +- multi->addReporter(createReporter(config->getReporterName(), config)); +- return std::move(multi); ++ multi.addReporter(createReporter(config->getReporterName(), config)); ++ return ret; + } + +- Catch::Totals runTests(std::shared_ptr<Config> const& config) { +- auto reporter = makeReporter(config); +- +- RunContext context(config, std::move(reporter)); +- +- Totals totals; +- +- context.testGroupStarting(config->name(), 1, 1); +- +- TestSpec testSpec = config->testSpec(); +- +- auto const& allTestCases = getAllTestCasesSorted(*config); +- for (auto const& testCase : allTestCases) { +- if (!context.aborting() && matchTest(testCase, testSpec, *config)) +- totals += context.runTest(testCase); +- else +- context.reporter().skipTest(testCase); ++ class TestGroup { ++ public: ++ explicit TestGroup(std::shared_ptr<Config> const& config) ++ : m_config{config} ++ , m_context{config, makeReporter(config)} ++ { ++ auto const& allTestCases = getAllTestCasesSorted(*m_config); ++ m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); ++ auto const& invalidArgs = m_config->testSpec().getInvalidArgs(); ++ ++ if (m_matches.empty() && invalidArgs.empty()) { ++ for (auto const& test : allTestCases) ++ if (!test.isHidden()) ++ m_tests.emplace(&test); ++ } else { ++ for (auto const& match : m_matches) ++ m_tests.insert(match.tests.begin(), match.tests.end()); ++ } + } + +- if (config->warnAboutNoTests() && totals.testCases.total() == 0) { +- ReusableStringStream testConfig; ++ Totals execute() { ++ auto const& invalidArgs = m_config->testSpec().getInvalidArgs(); ++ Totals totals; ++ m_context.testGroupStarting(m_config->name(), 1, 1); ++ for (auto const& testCase : m_tests) { ++ if (!m_context.aborting()) ++ totals += m_context.runTest(*testCase); ++ else ++ m_context.reporter().skipTest(*testCase); ++ } + +- bool first = true; +- for (const auto& input : config->getTestsOrTags()) { +- if (!first) { testConfig << ' '; } +- first = false; +- testConfig << input; ++ for (auto const& match : m_matches) { ++ if (match.tests.empty()) { ++ m_context.reporter().noMatchingTestCases(match.name); ++ totals.error = -1; ++ } + } + +- context.reporter().noMatchingTestCases(testConfig.str()); +- totals.error = -1; ++ if (!invalidArgs.empty()) { ++ for (auto const& invalidArg: invalidArgs) ++ m_context.reporter().reportInvalidArguments(invalidArg); ++ } ++ ++ m_context.testGroupEnded(m_config->name(), totals, 1, 1); ++ return totals; + } + +- context.testGroupEnded(config->name(), totals, 1, 1); +- return totals; +- } ++ private: ++ using Tests = std::set<TestCase const*>; ++ ++ std::shared_ptr<Config> m_config; ++ RunContext m_context; ++ Tests m_tests; ++ TestSpec::Matches m_matches; ++ }; + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); +@@ -10381,6 +13416,9 @@ + #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { ++ config(); ++ getCurrentMutableContext().setConfig(m_config); ++ + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occurred during startup!" << '\n'; +@@ -10410,7 +13448,7 @@ + } + void Session::libIdentify() { + Catch::cout() +- << std::left << std::setw(16) << "description: " << "A Catch test executable\n" ++ << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; +@@ -10422,6 +13460,8 @@ + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { ++ config(); ++ getCurrentMutableContext().setConfig(m_config); + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" +@@ -10439,17 +13479,17 @@ + return 0; + } + +-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) ++#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { +- int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); ++ int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr ); + + utf8Argv[ i ] = new char[ bufSize ]; + +- WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); ++ WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); +@@ -10513,10 +13553,15 @@ + applyFilenamesAsTags( *m_config ); + + // Handle list request +- if( Option<std::size_t> listed = list( config() ) ) ++ if( Option<std::size_t> listed = list( m_config ) ) + return static_cast<int>( *listed ); + +- auto totals = runTests( m_config ); ++ TestGroup tests { m_config }; ++ auto const totals = tests.execute(); ++ ++ if( m_config->warnAboutNoTests() && totals.error == -1 ) ++ return 2; ++ + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed +@@ -10564,6 +13609,7 @@ + // end catch_singletons.cpp + // start catch_startup_exception_registry.cpp + ++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + namespace Catch { + void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { +@@ -10579,6 +13625,7 @@ + } + + } // end namespace Catch ++#endif + // end catch_startup_exception_registry.cpp + // start catch_stream.cpp + +@@ -10593,7 +13640,7 @@ + + Catch::IStream::~IStream() = default; + +- namespace detail { namespace { ++ namespace Detail { namespace { + template<typename WriterF, std::size_t bufferSize=256> + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; +@@ -10692,15 +13739,15 @@ + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) +- return new detail::CoutStream(); ++ return new Detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) +- return new detail::DebugOutStream(); ++ return new Detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else +- return new detail::FileStream( filename ); ++ return new Detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. +@@ -10757,12 +13804,13 @@ + #include <ostream> + #include <cstring> + #include <cctype> ++#include <vector> + + namespace Catch { + + namespace { + char toLowerCh(char c) { +- return static_cast<char>( std::tolower( c ) ); ++ return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) ); + } + } + +@@ -10797,6 +13845,18 @@ + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + ++ StringRef trim(StringRef ref) { ++ const auto is_ws = [](char c) { ++ return c == ' ' || c == '\t' || c == '\n' || c == '\r'; ++ }; ++ size_t real_begin = 0; ++ while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } ++ size_t real_end = ref.size(); ++ while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } ++ ++ return ref.substr(real_begin, real_end - real_begin); ++ } ++ + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); +@@ -10811,6 +13871,21 @@ + return replaced; + } + ++ std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) { ++ std::vector<StringRef> subStrings; ++ std::size_t start = 0; ++ for(std::size_t pos = 0; pos < str.size(); ++pos ) { ++ if( str[pos] == delimiter ) { ++ if( pos - start > 1 ) ++ subStrings.push_back( str.substr( start, pos-start ) ); ++ start = pos+1; ++ } ++ } ++ if( start < str.size() ) ++ subStrings.push_back( str.substr( start, str.size()-start ) ); ++ return subStrings; ++ } ++ + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) +@@ -10827,123 +13902,46 @@ + // end catch_string_manip.cpp + // start catch_stringref.cpp + +-#if defined(__clang__) +-# pragma clang diagnostic push +-# pragma clang diagnostic ignored "-Wexit-time-destructors" +-#endif +- ++#include <algorithm> + #include <ostream> + #include <cstring> + #include <cstdint> + +-namespace { +- const uint32_t byte_2_lead = 0xC0; +- const uint32_t byte_3_lead = 0xE0; +- const uint32_t byte_4_lead = 0xF0; +-} +- + namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) ) + {} + +- StringRef::operator std::string() const { +- return std::string( m_start, m_size ); +- } +- +- void StringRef::swap( StringRef& other ) noexcept { +- std::swap( m_start, other.m_start ); +- std::swap( m_size, other.m_size ); +- std::swap( m_data, other.m_data ); +- } +- + auto StringRef::c_str() const -> char const* { +- if( isSubstring() ) +- const_cast<StringRef*>( this )->takeOwnership(); ++ CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance"); + return m_start; + } +- auto StringRef::currentData() const noexcept -> char const* { ++ auto StringRef::data() const noexcept -> char const* { + return m_start; + } + +- auto StringRef::isOwned() const noexcept -> bool { +- return m_data != nullptr; +- } +- auto StringRef::isSubstring() const noexcept -> bool { +- return m_start[m_size] != '\0'; +- } +- +- void StringRef::takeOwnership() { +- if( !isOwned() ) { +- m_data = new char[m_size+1]; +- memcpy( m_data, m_start, m_size ); +- m_data[m_size] = '\0'; +- m_start = m_data; +- } +- } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { +- if( start < m_size ) +- return StringRef( m_start+start, size ); +- else ++ if (start < m_size) { ++ return StringRef(m_start + start, (std::min)(m_size - start, size)); ++ } else { + return StringRef(); +- } +- auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { +- return +- size() == other.size() && +- (std::strncmp( m_start, other.m_start, size() ) == 0); +- } +- auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { +- return !operator==( other ); +- } +- +- auto StringRef::operator[](size_type index) const noexcept -> char { +- return m_start[index]; +- } +- +- auto StringRef::numberOfCharacters() const noexcept -> size_type { +- size_type noChars = m_size; +- // Make adjustments for uft encodings +- for( size_type i=0; i < m_size; ++i ) { +- char c = m_start[i]; +- if( ( c & byte_2_lead ) == byte_2_lead ) { +- noChars--; +- if (( c & byte_3_lead ) == byte_3_lead ) +- noChars--; +- if( ( c & byte_4_lead ) == byte_4_lead ) +- noChars--; +- } + } +- return noChars; + } +- +- auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { +- std::string str; +- str.reserve( lhs.size() + rhs.size() ); +- str += lhs; +- str += rhs; +- return str; +- } +- auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { +- return std::string( lhs ) + std::string( rhs ); +- } +- auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { +- return std::string( lhs ) + std::string( rhs ); ++ auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { ++ return m_size == other.m_size ++ && (std::memcmp( m_start, other.m_start, m_size ) == 0); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { +- return os.write(str.currentData(), str.size()); ++ return os.write(str.data(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { +- lhs.append(rhs.currentData(), rhs.size()); ++ lhs.append(rhs.data(), rhs.size()); + return lhs; + } + + } // namespace Catch +- +-#if defined(__clang__) +-# pragma clang diagnostic pop +-#endif + // end catch_stringref.cpp + // start catch_tag_alias.cpp + +@@ -11046,7 +14044,7 @@ + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" +- << "Tag names starting with non alpha-numeric characters are reserved\n" ++ << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo ); + } + } +@@ -11062,8 +14060,7 @@ + std::vector<std::string> tags; + std::string desc, tag; + bool inTag = false; +- std::string _descOrTags = nameAndTags.tags; +- for (char c : _descOrTags) { ++ for (char c : nameAndTags.tags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; +@@ -11078,6 +14075,12 @@ + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + ++ // Merged hide tags like `[.approvals]` should be added as ++ // `[.][approvals]`. The `[.]` is added at later point, so ++ // we only strip the prefix ++ if (startsWith(tag, '.') && tag.size() > 1) { ++ tag.erase(0, 1); ++ } + tags.push_back( tag ); + tag.clear(); + inTag = false; +@@ -11087,10 +14090,11 @@ + } + } + if( isHidden ) { +- tags.push_back( "." ); ++ // Add all "hidden" tags to make them behave identically ++ tags.insert( tags.end(), { ".", "!hide" } ); + } + +- TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); ++ TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + +@@ -11182,30 +14186,89 @@ + // end catch_test_case_info.cpp + // start catch_test_case_registry_impl.cpp + ++#include <algorithm> + #include <sstream> + + namespace Catch { + +- std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { ++ namespace { ++ struct TestHasher { ++ using hash_t = uint64_t; + +- std::vector<TestCase> sorted = unsortedTestCases; ++ explicit TestHasher( hash_t hashSuffix ): ++ m_hashSuffix{ hashSuffix } {} ++ ++ uint32_t operator()( TestCase const& t ) const { ++ // FNV-1a hash with multiplication fold. ++ const hash_t prime = 1099511628211u; ++ hash_t hash = 14695981039346656037u; ++ for ( const char c : t.name ) { ++ hash ^= c; ++ hash *= prime; ++ } ++ hash ^= m_hashSuffix; ++ hash *= prime; ++ const uint32_t low{ static_cast<uint32_t>( hash ) }; ++ const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) }; ++ return low * high; ++ } ++ ++ private: ++ hash_t m_hashSuffix; ++ }; ++ } // end unnamed namespace + ++ std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { + switch( config.runOrder() ) { +- case RunTests::InLexicographicalOrder: +- std::sort( sorted.begin(), sorted.end() ); +- break; +- case RunTests::InRandomOrder: +- seedRng( config ); +- std::shuffle( sorted.begin(), sorted.end(), rng() ); +- break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; ++ ++ case RunTests::InLexicographicalOrder: { ++ std::vector<TestCase> sorted = unsortedTestCases; ++ std::sort( sorted.begin(), sorted.end() ); ++ return sorted; ++ } ++ ++ case RunTests::InRandomOrder: { ++ seedRng( config ); ++ TestHasher h{ config.rngSeed() }; ++ ++ using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>; ++ std::vector<hashedTest> indexed_tests; ++ indexed_tests.reserve( unsortedTestCases.size() ); ++ ++ for (auto const& testCase : unsortedTestCases) { ++ indexed_tests.emplace_back(h(testCase), &testCase); ++ } ++ ++ std::sort(indexed_tests.begin(), indexed_tests.end(), ++ [](hashedTest const& lhs, hashedTest const& rhs) { ++ if (lhs.first == rhs.first) { ++ return lhs.second->name < rhs.second->name; ++ } ++ return lhs.first < rhs.first; ++ }); ++ ++ std::vector<TestCase> sorted; ++ sorted.reserve( indexed_tests.size() ); ++ ++ for (auto const& hashed : indexed_tests) { ++ sorted.emplace_back(*hashed.second); ++ } ++ ++ return sorted; ++ } + } +- return sorted; ++ return unsortedTestCases; ++ } ++ ++ bool isThrowSafe( TestCase const& testCase, IConfig const& config ) { ++ return !testCase.throws() || config.allowThrows(); + } ++ + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { +- return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); ++ return testSpec.matches( testCase ) && isThrowSafe( testCase, config ); + } + + void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { +@@ -11222,9 +14285,12 @@ + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector<TestCase> filtered; + filtered.reserve( testCases.size() ); +- for( auto const& testCase : testCases ) +- if( matchTest( testCase, testSpec, config ) ) +- filtered.push_back( testCase ); ++ for (auto const& testCase : testCases) { ++ if ((!testSpec.hasFilters() && !testCase.isHidden()) || ++ (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { ++ filtered.push_back(testCase); ++ } ++ } + return filtered; + } + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { +@@ -11263,7 +14329,7 @@ + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { +- std::string className = classOrQualifiedMethodName; ++ std::string className(classOrQualifiedMethodName); + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); +@@ -11300,11 +14366,6 @@ + + ITracker::~ITracker() = default; + +- TrackerContext& TrackerContext::instance() { +- static TrackerContext s_instance; +- return s_instance; +- } +- + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; +@@ -11336,15 +14397,12 @@ + m_currentTracker = tracker; + } + +- TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) +- : m_nameAndLocation( nameAndLocation ), ++ TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): ++ ITracker(nameAndLocation), + m_ctx( ctx ), + m_parent( parent ) + {} + +- NameAndLocation const& TrackerBase::nameAndLocation() const { +- return m_nameAndLocation; +- } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } +@@ -11387,7 +14445,7 @@ + } + + bool TrackerBase::isSectionTracker() const { return false; } +- bool TrackerBase::isIndexTracker() const { return false; } ++ bool TrackerBase::isGeneratorTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; +@@ -11410,7 +14468,7 @@ + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: +- if( m_children.empty() || m_children.back()->isComplete() ) ++ if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) ) + m_runState = CompletedSuccessfully; + break; + +@@ -11445,7 +14503,8 @@ + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) +- : TrackerBase( nameAndLocation, ctx, parent ) ++ : TrackerBase( nameAndLocation, ctx, parent ), ++ m_trimmed_name(trim(nameAndLocation.name)) + { + if( parent ) { + while( !parent->isSectionTracker() ) +@@ -11456,6 +14515,17 @@ + } + } + ++ bool SectionTracker::isComplete() const { ++ bool complete = true; ++ ++ if (m_filters.empty() ++ || m_filters[0] == "" ++ || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { ++ complete = TrackerBase::isComplete(); ++ } ++ return complete; ++ } ++ + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { +@@ -11477,63 +14547,29 @@ + } + + void SectionTracker::tryOpen() { +- if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) ++ if( !isComplete() ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { + if( !filters.empty() ) { +- m_filters.push_back(""); // Root - should never be consulted +- m_filters.push_back(""); // Test Case - not a section filter ++ m_filters.reserve( m_filters.size() + filters.size() + 2 ); ++ m_filters.emplace_back(""); // Root - should never be consulted ++ m_filters.emplace_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) { + if( filters.size() > 1 ) +- m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); +- } +- +- IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) +- : TrackerBase( nameAndLocation, ctx, parent ), +- m_size( size ) +- {} +- +- bool IndexTracker::isIndexTracker() const { return true; } +- +- IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { +- std::shared_ptr<IndexTracker> tracker; +- +- ITracker& currentTracker = ctx.currentTracker(); +- if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { +- assert( childTracker ); +- assert( childTracker->isIndexTracker() ); +- tracker = std::static_pointer_cast<IndexTracker>( childTracker ); +- } +- else { +- tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, ¤tTracker, size ); +- currentTracker.addChild( tracker ); +- } +- +- if( !ctx.completedCycle() && !tracker->isComplete() ) { +- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) +- tracker->moveNext(); +- tracker->open(); +- } +- +- return *tracker; ++ m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); + } + +- int IndexTracker::index() const { return m_index; } +- +- void IndexTracker::moveNext() { +- m_index++; +- m_children.clear(); ++ std::vector<std::string> const& SectionTracker::getFilters() const { ++ return m_filters; + } + +- void IndexTracker::close() { +- TrackerBase::close(); +- if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) +- m_runState = Executing; ++ std::string const& SectionTracker::trimmedName() const { ++ return m_trimmed_name; + } + + } // namespace TestCaseTracking +@@ -11541,7 +14577,6 @@ + using TestCaseTracking::ITracker; + using TestCaseTracking::TrackerContext; + using TestCaseTracking::SectionTracker; +-using TestCaseTracking::IndexTracker; + + } // namespace Catch + +@@ -11586,47 +14621,81 @@ + + namespace Catch { + ++ TestSpec::Pattern::Pattern( std::string const& name ) ++ : m_name( name ) ++ {} ++ + TestSpec::Pattern::~Pattern() = default; +- TestSpec::NamePattern::~NamePattern() = default; +- TestSpec::TagPattern::~TagPattern() = default; +- TestSpec::ExcludedPattern::~ExcludedPattern() = default; + +- TestSpec::NamePattern::NamePattern( std::string const& name ) +- : m_wildcardPattern( toLower( name ), CaseSensitive::No ) ++ std::string const& TestSpec::Pattern::name() const { ++ return m_name; ++ } ++ ++ TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString ) ++ : Pattern( filterString ) ++ , m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} ++ + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { +- return m_wildcardPattern.matches( toLower( testCase.name ) ); ++ return m_wildcardPattern.matches( testCase.name ); + } + +- TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} ++ TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString ) ++ : Pattern( filterString ) ++ , m_tag( toLower( tag ) ) ++ {} ++ + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + +- TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} +- bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } ++ TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) ++ : Pattern( underlyingPattern->name() ) ++ , m_underlyingPattern( underlyingPattern ) ++ {} ++ ++ bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { ++ return !m_underlyingPattern->matches( testCase ); ++ } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { +- // All patterns in a filter must match for the filter to be a match +- for( auto const& pattern : m_patterns ) { +- if( !pattern->matches( testCase ) ) +- return false; +- } +- return true; ++ return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } ); ++ } ++ ++ std::string TestSpec::Filter::name() const { ++ std::string name; ++ for( auto const& p : m_patterns ) ++ name += p->name(); ++ return name; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } ++ + bool TestSpec::matches( TestCaseInfo const& testCase ) const { +- // A TestSpec matches if any filter matches +- for( auto const& filter : m_filters ) +- if( filter.matches( testCase ) ) +- return true; +- return false; ++ return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); ++ } ++ ++ TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const ++ { ++ Matches matches( m_filters.size() ); ++ std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ ++ std::vector<TestCase const*> currentMatches; ++ for( auto const& test : testCases ) ++ if( isThrowSafe( test, config ) && filter.matches( test ) ) ++ currentMatches.emplace_back( &test ); ++ return FilterMatch{ filter.name(), currentMatches }; ++ } ); ++ return matches; ++ } ++ ++ const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{ ++ return (m_invalidArgs); + } ++ + } + // end catch_test_spec.cpp + // start catch_test_spec_parser.cpp +@@ -11638,64 +14707,136 @@ + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; +- m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); ++ m_substring.reserve(m_arg.size()); ++ m_patternName.reserve(m_arg.size()); ++ m_realPatternPos = 0; ++ + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) +- visitChar( m_arg[m_pos] ); +- if( m_mode == Name ) +- addPattern<TestSpec::NamePattern>(); ++ //if visitChar fails ++ if( !visitChar( m_arg[m_pos] ) ){ ++ m_testSpec.m_invalidArgs.push_back(arg); ++ break; ++ } ++ endMode(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } ++ bool TestSpecParser::visitChar( char c ) { ++ if( (m_mode != EscapedName) && (c == '\\') ) { ++ escape(); ++ addCharToPattern(c); ++ return true; ++ }else if((m_mode != EscapedName) && (c == ',') ) { ++ return separate(); ++ } + +- void TestSpecParser::visitChar( char c ) { +- if( m_mode == None ) { +- switch( c ) { +- case ' ': return; +- case '~': m_exclusion = true; return; +- case '[': return startNewMode( Tag, ++m_pos ); +- case '"': return startNewMode( QuotedName, ++m_pos ); +- case '\\': return escape(); +- default: startNewMode( Name, m_pos ); break; +- } +- } +- if( m_mode == Name ) { +- if( c == ',' ) { +- addPattern<TestSpec::NamePattern>(); +- addFilter(); +- } +- else if( c == '[' ) { +- if( subString() == "exclude:" ) +- m_exclusion = true; +- else +- addPattern<TestSpec::NamePattern>(); +- startNewMode( Tag, ++m_pos ); +- } +- else if( c == '\\' ) +- escape(); ++ switch( m_mode ) { ++ case None: ++ if( processNoneChar( c ) ) ++ return true; ++ break; ++ case Name: ++ processNameChar( c ); ++ break; ++ case EscapedName: ++ endMode(); ++ addCharToPattern(c); ++ return true; ++ default: ++ case Tag: ++ case QuotedName: ++ if( processOtherChar( c ) ) ++ return true; ++ break; ++ } ++ ++ m_substring += c; ++ if( !isControlChar( c ) ) { ++ m_patternName += c; ++ m_realPatternPos++; ++ } ++ return true; ++ } ++ // Two of the processing methods return true to signal the caller to return ++ // without adding the given character to the current pattern strings ++ bool TestSpecParser::processNoneChar( char c ) { ++ switch( c ) { ++ case ' ': ++ return true; ++ case '~': ++ m_exclusion = true; ++ return false; ++ case '[': ++ startNewMode( Tag ); ++ return false; ++ case '"': ++ startNewMode( QuotedName ); ++ return false; ++ default: ++ startNewMode( Name ); ++ return false; ++ } ++ } ++ void TestSpecParser::processNameChar( char c ) { ++ if( c == '[' ) { ++ if( m_substring == "exclude:" ) ++ m_exclusion = true; ++ else ++ endMode(); ++ startNewMode( Tag ); + } +- else if( m_mode == EscapedName ) +- m_mode = Name; +- else if( m_mode == QuotedName && c == '"' ) +- addPattern<TestSpec::NamePattern>(); +- else if( m_mode == Tag && c == ']' ) +- addPattern<TestSpec::TagPattern>(); + } +- void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { ++ bool TestSpecParser::processOtherChar( char c ) { ++ if( !isControlChar( c ) ) ++ return false; ++ m_substring += c; ++ endMode(); ++ return true; ++ } ++ void TestSpecParser::startNewMode( Mode mode ) { + m_mode = mode; +- m_start = start; ++ } ++ void TestSpecParser::endMode() { ++ switch( m_mode ) { ++ case Name: ++ case QuotedName: ++ return addNamePattern(); ++ case Tag: ++ return addTagPattern(); ++ case EscapedName: ++ revertBackToLastMode(); ++ return; ++ case None: ++ default: ++ return startNewMode( None ); ++ } + } + void TestSpecParser::escape() { +- if( m_mode == None ) +- m_start = m_pos; ++ saveLastMode(); + m_mode = EscapedName; +- m_escapeChars.push_back( m_pos ); ++ m_escapeChars.push_back(m_realPatternPos); ++ } ++ bool TestSpecParser::isControlChar( char c ) const { ++ switch( m_mode ) { ++ default: ++ return false; ++ case None: ++ return c == '~'; ++ case Name: ++ return c == '['; ++ case EscapedName: ++ return true; ++ case QuotedName: ++ return c == '"'; ++ case Tag: ++ return c == '[' || c == ']'; ++ } + } +- std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { +@@ -11704,6 +14845,86 @@ + } + } + ++ void TestSpecParser::saveLastMode() { ++ lastMode = m_mode; ++ } ++ ++ void TestSpecParser::revertBackToLastMode() { ++ m_mode = lastMode; ++ } ++ ++ bool TestSpecParser::separate() { ++ if( (m_mode==QuotedName) || (m_mode==Tag) ){ ++ //invalid argument, signal failure to previous scope. ++ m_mode = None; ++ m_pos = m_arg.size(); ++ m_substring.clear(); ++ m_patternName.clear(); ++ m_realPatternPos = 0; ++ return false; ++ } ++ endMode(); ++ addFilter(); ++ return true; //success ++ } ++ ++ std::string TestSpecParser::preprocessPattern() { ++ std::string token = m_patternName; ++ for (std::size_t i = 0; i < m_escapeChars.size(); ++i) ++ token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); ++ m_escapeChars.clear(); ++ if (startsWith(token, "exclude:")) { ++ m_exclusion = true; ++ token = token.substr(8); ++ } ++ ++ m_patternName.clear(); ++ m_realPatternPos = 0; ++ ++ return token; ++ } ++ ++ void TestSpecParser::addNamePattern() { ++ auto token = preprocessPattern(); ++ ++ if (!token.empty()) { ++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring); ++ if (m_exclusion) ++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); ++ m_currentFilter.m_patterns.push_back(pattern); ++ } ++ m_substring.clear(); ++ m_exclusion = false; ++ m_mode = None; ++ } ++ ++ void TestSpecParser::addTagPattern() { ++ auto token = preprocessPattern(); ++ ++ if (!token.empty()) { ++ // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) ++ // we have to create a separate hide tag and shorten the real one ++ if (token.size() > 1 && token[0] == '.') { ++ token.erase(token.begin()); ++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring); ++ if (m_exclusion) { ++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); ++ } ++ m_currentFilter.m_patterns.push_back(pattern); ++ } ++ ++ TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring); ++ ++ if (m_exclusion) { ++ pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); ++ } ++ m_currentFilter.m_patterns.push_back(pattern); ++ } ++ m_substring.clear(); ++ m_exclusion = false; ++ m_mode = None; ++ } ++ + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } +@@ -11744,7 +14965,7 @@ + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { +- return sum / i; ++ return sum / ( i + 1u ); + } + } + +@@ -11805,13 +15026,11 @@ + enum Arch { Big, Little }; + + static Arch which() { +- union _{ +- int asInt; +- char asChar[sizeof (int)]; +- } u; +- +- u.asInt = 1; +- return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; ++ int one = 1; ++ // If the lowest byte we read is non-zero, we can assume ++ // that little endian format is used. ++ auto value = *reinterpret_cast<char*>(&one); ++ return value ? Little : Big; + } + }; + } +@@ -11935,6 +15154,13 @@ + } + #endif + ++#if defined(CATCH_CONFIG_CPP17_BYTE) ++#include <cstddef> ++std::string StringMaker<std::byte>::convert(std::byte value) { ++ return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value)); ++} ++#endif // defined(CATCH_CONFIG_CPP17_BYTE) ++ + std::string StringMaker<int>::convert(int value) { + return ::Catch::Detail::stringify(static_cast<long long>(value)); + } +@@ -11997,11 +15223,16 @@ + return "nullptr"; + } + ++int StringMaker<float>::precision = 5; ++ + std::string StringMaker<float>::convert(float value) { +- return fpToString(value, 5) + 'f'; ++ return fpToString(value, precision) + 'f'; + } ++ ++int StringMaker<double>::precision = 10; ++ + std::string StringMaker<double>::convert(double value) { +- return fpToString(value, 10); ++ return fpToString(value, precision); + } + + std::string ratio_string<std::atto>::symbol() { return "a"; } +@@ -12075,11 +15306,48 @@ + // end catch_totals.cpp + // start catch_uncaught_exceptions.cpp + ++// start catch_config_uncaught_exceptions.hpp ++ ++// Copyright Catch2 Authors ++// Distributed under the Boost Software License, Version 1.0. ++// (See accompanying file LICENSE_1_0.txt or copy at ++// https://www.boost.org/LICENSE_1_0.txt) ++ ++// SPDX-License-Identifier: BSL-1.0 ++ ++#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP ++#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP ++ ++#if defined(_MSC_VER) ++# if _MSC_VER >= 1900 // Visual Studio 2015 or newer ++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS ++# endif ++#endif ++ ++#include <exception> ++ ++#if defined(__cpp_lib_uncaught_exceptions) \ ++ && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) ++ ++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS ++#endif // __cpp_lib_uncaught_exceptions ++ ++#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \ ++ && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ ++ && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) ++ ++# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS ++#endif ++ ++#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP ++// end catch_config_uncaught_exceptions.hpp + #include <exception> + + namespace Catch { + bool uncaught_exceptions() { +-#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) ++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) ++ return false; ++#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; + #else + return std::uncaught_exception(); +@@ -12119,7 +15387,7 @@ + } + + Version const& libraryVersion() { +- static Version version( 2, 5, 0, "", 0 ); ++ static Version version( 2, 13, 8, "", 0 ); + return version; + } + +@@ -12127,14 +15395,12 @@ + // end catch_version.cpp + // start catch_wildcard_pattern.cpp + +-#include <sstream> +- + namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), +- m_pattern( adjustCase( pattern ) ) ++ m_pattern( normaliseString( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); +@@ -12149,28 +15415,27 @@ + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: +- return m_pattern == adjustCase( str ); ++ return m_pattern == normaliseString( str ); + case WildcardAtStart: +- return endsWith( adjustCase( str ), m_pattern ); ++ return endsWith( normaliseString( str ), m_pattern ); + case WildcardAtEnd: +- return startsWith( adjustCase( str ), m_pattern ); ++ return startsWith( normaliseString( str ), m_pattern ); + case WildcardAtBothEnds: +- return contains( adjustCase( str ), m_pattern ); ++ return contains( normaliseString( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + +- std::string WildcardPattern::adjustCase( std::string const& str ) const { +- return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; ++ std::string WildcardPattern::normaliseString( std::string const& str ) const { ++ return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str ); + } + } + // end catch_wildcard_pattern.cpp + // start catch_xmlwriter.cpp + + #include <iomanip> +- +-using uchar = unsigned char; ++#include <type_traits> + + namespace Catch { + +@@ -12203,13 +15468,37 @@ + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { ++ std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast<int>(c); ++ os.flags(f); ++ } ++ ++ bool shouldNewline(XmlFormatting fmt) { ++ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline)); ++ } ++ ++ bool shouldIndent(XmlFormatting fmt) { ++ return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent)); + } + + } // anonymous namespace + ++ XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { ++ return static_cast<XmlFormatting>( ++ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) | ++ static_cast<std::underlying_type<XmlFormatting>::type>(rhs) ++ ); ++ } ++ ++ XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { ++ return static_cast<XmlFormatting>( ++ static_cast<std::underlying_type<XmlFormatting>::type>(lhs) & ++ static_cast<std::underlying_type<XmlFormatting>::type>(rhs) ++ ); ++ } ++ + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) +@@ -12220,7 +15509,7 @@ + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { +- uchar c = m_str[idx]; ++ unsigned char c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; +@@ -12280,7 +15569,7 @@ + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { +- uchar nc = m_str[idx + n]; ++ unsigned char nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } +@@ -12314,13 +15603,17 @@ + return os; + } + +- XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) +- : m_writer( writer ) ++ XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt ) ++ : m_writer( writer ), ++ m_fmt(fmt) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept +- : m_writer( other.m_writer ){ ++ : m_writer( other.m_writer ), ++ m_fmt(other.m_fmt) ++ { + other.m_writer = nullptr; ++ other.m_fmt = XmlFormatting::None; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { +@@ -12328,16 +15621,19 @@ + } + m_writer = other.m_writer; + other.m_writer = nullptr; ++ m_fmt = other.m_fmt; ++ other.m_fmt = XmlFormatting::None; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { +- if( m_writer ) +- m_writer->endElement(); ++ if (m_writer) { ++ m_writer->endElement(m_fmt); ++ } + } + +- XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { +- m_writer->writeText( text, indent ); ++ XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) { ++ m_writer->writeText( text, fmt ); + return *this; + } + +@@ -12347,37 +15643,47 @@ + } + + XmlWriter::~XmlWriter() { +- while( !m_tags.empty() ) ++ while (!m_tags.empty()) { + endElement(); ++ } ++ newlineIfNecessary(); + } + +- XmlWriter& XmlWriter::startElement( std::string const& name ) { ++ XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) { + ensureTagClosed(); + newlineIfNecessary(); +- m_os << m_indent << '<' << name; ++ if (shouldIndent(fmt)) { ++ m_os << m_indent; ++ m_indent += " "; ++ } ++ m_os << '<' << name; + m_tags.push_back( name ); +- m_indent += " "; + m_tagIsOpen = true; ++ applyFormatting(fmt); + return *this; + } + +- XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { +- ScopedElement scoped( this ); +- startElement( name ); ++ XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) { ++ ScopedElement scoped( this, fmt ); ++ startElement( name, fmt ); + return scoped; + } + +- XmlWriter& XmlWriter::endElement() { +- newlineIfNecessary(); +- m_indent = m_indent.substr( 0, m_indent.size()-2 ); ++ XmlWriter& XmlWriter::endElement(XmlFormatting fmt) { ++ m_indent = m_indent.substr(0, m_indent.size() - 2); ++ + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; ++ } else { ++ newlineIfNecessary(); ++ if (shouldIndent(fmt)) { ++ m_os << m_indent; ++ } ++ m_os << "</" << m_tags.back() << ">"; + } +- else { +- m_os << m_indent << "</" << m_tags.back() << ">"; +- } +- m_os << std::endl; ++ m_os << std::flush; ++ applyFormatting(fmt); + m_tags.pop_back(); + return *this; + } +@@ -12393,22 +15699,26 @@ + return *this; + } + +- XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { ++ XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); +- if( tagWasOpen && indent ) ++ if (tagWasOpen && shouldIndent(fmt)) { + m_os << m_indent; ++ } + m_os << XmlEncode( text ); +- m_needsNewline = true; ++ applyFormatting(fmt); + } + return *this; + } + +- XmlWriter& XmlWriter::writeComment( std::string const& text ) { ++ XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) { + ensureTagClosed(); +- m_os << m_indent << "<!--" << text << "-->"; +- m_needsNewline = true; ++ if (shouldIndent(fmt)) { ++ m_os << m_indent; ++ } ++ m_os << "<!--" << text << "-->"; ++ applyFormatting(fmt); + return *this; + } + +@@ -12424,11 +15734,16 @@ + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { +- m_os << ">" << std::endl; ++ m_os << '>' << std::flush; ++ newlineIfNecessary(); + m_tagIsOpen = false; + } + } + ++ void XmlWriter::applyFormatting(XmlFormatting fmt) { ++ m_needsNewline = shouldNewline(fmt); ++ } ++ + void XmlWriter::writeDeclaration() { + m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + } +@@ -12469,11 +15784,37 @@ + #ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); + #else +- sprintf(buffer, "%.3f", duration); ++ std::sprintf(buffer, "%.3f", duration); + #endif + return std::string(buffer); + } + ++ bool shouldShowDuration( IConfig const& config, double duration ) { ++ if ( config.showDurations() == ShowDurations::Always ) { ++ return true; ++ } ++ if ( config.showDurations() == ShowDurations::Never ) { ++ return false; ++ } ++ const double min = config.minDuration(); ++ return min >= 0 && duration >= min; ++ } ++ ++ std::string serializeFilters( std::vector<std::string> const& container ) { ++ ReusableStringStream oss; ++ bool first = true; ++ for (auto&& filter : container) ++ { ++ if (!first) ++ oss << ' '; ++ else ++ first = false; ++ ++ oss << filter; ++ } ++ return oss.str(); ++ } ++ + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + +@@ -12689,24 +16030,25 @@ + if (itMessage == messages.end()) + return; + +- // using messages.end() directly yields (or auto) compilation error: +- std::vector<MessageInfo>::const_iterator itEnd = messages.end(); +- const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd)); ++ const auto itEnd = messages.cend(); ++ const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } + +- for (; itMessage != itEnd; ) { ++ while (itMessage != itEnd) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { +- stream << " '" << itMessage->message << '\''; +- if (++itMessage != itEnd) { ++ printMessage(); ++ if (itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } ++ continue; + } ++ ++itMessage; + } + } + +@@ -12724,10 +16066,6 @@ + return "Reports test results on a single line, suitable for IDEs"; + } + +- ReporterPreferences CompactReporter::getPreferences() const { +- return m_reporterPrefs; +- } +- + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } +@@ -12754,8 +16092,9 @@ + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { +- if (m_config->showDurations() == ShowDurations::Always) { +- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; ++ double dur = _sectionStats.durationInSeconds; ++ if ( shouldShowDuration( *m_config, dur ) ) { ++ stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + +@@ -12779,8 +16118,13 @@ + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch +- // Note that 4062 (not all labels are handled +- // and default is missing) is enabled ++ // Note that 4062 (not all labels are handled and default is missing) is enabled ++#endif ++ ++#if defined(__clang__) ++# pragma clang diagnostic push ++// For simplicity, benchmarking-only helpers are always enabled ++# pragma clang diagnostic ignored "-Wunused-function" + #endif + + namespace Catch { +@@ -12962,11 +16306,11 @@ + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + +- uint64_t m_inNanoseconds; ++ double m_inNanoseconds; + Unit m_units; + + public: +- explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) ++ explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { +@@ -12995,7 +16339,7 @@ + case Unit::Minutes: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute); + default: +- return static_cast<double>(m_inNanoseconds); ++ return m_inNanoseconds; + } + } + auto unitsAsString() const -> std::string { +@@ -13003,7 +16347,7 @@ + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: +- return "µs"; ++ return "us"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: +@@ -13016,7 +16360,7 @@ + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { +- return os << duration.value() << " " << duration.unitsAsString(); ++ return os << duration.value() << ' ' << duration.unitsAsString(); + } + }; + } // end anon namespace +@@ -13041,10 +16385,16 @@ + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); +- for (auto const& info : m_columnInfos) +- *this << info.name << ColumnBreak(); +- *this << RowBreak(); +- m_os << Catch::getLineOfChars<'-'>() << "\n"; ++ ++ Columns headerCols; ++ Spacer spacer(2); ++ for (auto const& info : m_columnInfos) { ++ headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2)); ++ headerCols += spacer; ++ } ++ m_os << headerCols << '\n'; ++ ++ m_os << Catch::getLineOfChars<'-'>() << '\n'; + } + } + void close() { +@@ -13063,30 +16413,29 @@ + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); +- // This takes account of utf8 encodings +- auto strSize = Catch::StringRef(colStr).numberOfCharacters(); ++ const auto strSize = colStr.size(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; +- tp.m_os << "\n"; ++ tp.m_os << '\n'; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; +- auto padding = (strSize + 2 < static_cast<std::size_t>(colInfo.width)) +- ? std::string(colInfo.width - (strSize + 2), ' ') ++ auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width)) ++ ? std::string(colInfo.width - (strSize + 1), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) +- tp.m_os << colStr << padding << " "; ++ tp.m_os << colStr << padding << ' '; + else +- tp.m_os << padding << colStr << " "; ++ tp.m_os << padding << colStr << ' '; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { +- tp.m_os << "\n"; ++ tp.m_os << '\n'; + tp.m_currentColumn = -1; + } + return tp; +@@ -13096,12 +16445,26 @@ + ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), +- { +- { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, +- { "iters", 8, ColumnInfo::Right }, +- { "elapsed ns", 14, ColumnInfo::Right }, +- { "average", 14, ColumnInfo::Right } +- })) {} ++ [&config]() -> std::vector<ColumnInfo> { ++ if (config.fullConfig()->benchmarkNoAnalysis()) ++ { ++ return{ ++ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left }, ++ { " samples", 14, ColumnInfo::Right }, ++ { " iterations", 14, ColumnInfo::Right }, ++ { " mean", 14, ColumnInfo::Right } ++ }; ++ } ++ else ++ { ++ return{ ++ { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left }, ++ { "samples mean std dev", 14, ColumnInfo::Right }, ++ { "iterations low mean low std dev", 14, ColumnInfo::Right }, ++ { "estimated high mean high std dev", 14, ColumnInfo::Right } ++ }; ++ } ++ }())) {} + ConsoleReporter::~ConsoleReporter() = default; + + std::string ConsoleReporter::getDescription() { +@@ -13112,6 +16475,10 @@ + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + ++void ConsoleReporter::reportInvalidArguments(std::string const&arg){ ++ stream << "Invalid Filter: " << arg << std::endl; ++} ++ + void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + + bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { +@@ -13132,6 +16499,7 @@ + } + + void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { ++ m_tablePrinter->close(); + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); + } +@@ -13146,8 +16514,9 @@ + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } +- if (m_config->showDurations() == ShowDurations::Always) { +- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; ++ double dur = _sectionStats.durationInSeconds; ++ if (shouldShowDuration(*m_config, dur)) { ++ stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; +@@ -13155,28 +16524,53 @@ + StreamingReporterBase::sectionEnded(_sectionStats); + } + +-void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { +- lazyPrintWithoutClosingBenchmarkTable(); +- +- auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) ); ++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) ++void ConsoleReporter::benchmarkPreparing(std::string const& name) { ++ lazyPrintWithoutClosingBenchmarkTable(); ++ ++ auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2)); ++ ++ bool firstLine = true; ++ for (auto line : nameCol) { ++ if (!firstLine) ++ (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); ++ else ++ firstLine = false; + +- bool firstLine = true; +- for (auto line : nameCol) { +- if (!firstLine) +- (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); +- else +- firstLine = false; ++ (*m_tablePrinter) << line << ColumnBreak(); ++ } ++} + +- (*m_tablePrinter) << line << ColumnBreak(); ++void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { ++ (*m_tablePrinter) << info.samples << ColumnBreak() ++ << info.iterations << ColumnBreak(); ++ if (!m_config->benchmarkNoAnalysis()) ++ (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); ++} ++void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { ++ if (m_config->benchmarkNoAnalysis()) ++ { ++ (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); ++ } ++ else ++ { ++ (*m_tablePrinter) << ColumnBreak() ++ << Duration(stats.mean.point.count()) << ColumnBreak() ++ << Duration(stats.mean.lower_bound.count()) << ColumnBreak() ++ << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() ++ << Duration(stats.standardDeviation.point.count()) << ColumnBreak() ++ << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() ++ << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); + } + } +-void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { +- Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); ++ ++void ConsoleReporter::benchmarkFailed(std::string const& error) { ++ Colour colour(Colour::Red); + (*m_tablePrinter) +- << stats.iterations << ColumnBreak() +- << stats.elapsedTimeInNanoseconds << ColumnBreak() +- << average << ColumnBreak(); ++ << "Benchmark failed (" << error << ')' ++ << ColumnBreak() << RowBreak(); + } ++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); +@@ -13198,6 +16592,10 @@ + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } ++void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { ++ StreamingReporterBase::testRunStarting(_testInfo); ++ printTestFilters(); ++} + + void ConsoleReporter::lazyPrint() { + +@@ -13251,11 +16649,9 @@ + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + +- if (!lineInfo.empty()) { +- stream << getLineOfChars<'-'>() << '\n'; +- Colour colourGuard(Colour::FileName); +- stream << lineInfo << '\n'; +- } ++ stream << getLineOfChars<'-'>() << '\n'; ++ Colour colourGuard(Colour::FileName); ++ stream << lineInfo << '\n'; + stream << getLineOfChars<'.'>() << '\n' << std::endl; + } + +@@ -13379,6 +16775,13 @@ + stream << getLineOfChars<'-'>() << '\n'; + } + ++void ConsoleReporter::printTestFilters() { ++ if (m_config->testSpec().hasFilters()) { ++ Colour guard(Colour::BrightYellow); ++ stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; ++ } ++} ++ + CATCH_REGISTER_REPORTER("console", ConsoleReporter) + + } // end namespace Catch +@@ -13386,6 +16789,10 @@ + #if defined(_MSC_VER) + #pragma warning(pop) + #endif ++ ++#if defined(__clang__) ++# pragma clang diagnostic pop ++#endif + // end catch_reporter_console.cpp + // start catch_reporter_junit.cpp + +@@ -13393,6 +16800,7 @@ + #include <sstream> + #include <ctime> + #include <algorithm> ++#include <iomanip> + + namespace Catch { + +@@ -13420,7 +16828,7 @@ + #else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + #endif +- return std::string(timeStamp); ++ return std::string(timeStamp, timeStampSize-1); + } + + std::string fileNameTag(const std::vector<std::string> &tags) { +@@ -13431,6 +16839,17 @@ + return it->substr(1); + return std::string(); + } ++ ++ // Formats the duration in seconds to 3 decimal places. ++ // This is done because some genius defined Maven Surefire schema ++ // in a way that only accepts 3 decimal places, and tools like ++ // Jenkins use that schema for validation JUnit reporter output. ++ std::string formatDuration( double seconds ) { ++ ReusableStringStream rss; ++ rss << std::fixed << std::setprecision( 3 ) << seconds; ++ return rss.str(); ++ } ++ + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) +@@ -13490,6 +16909,7 @@ + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); ++ + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); +@@ -13499,15 +16919,30 @@ + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else +- xml.writeAttribute( "time", suiteTime ); ++ xml.writeAttribute( "time", formatDuration( suiteTime ) ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + ++ // Write properties if there are any ++ if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { ++ auto properties = xml.scopedElement("properties"); ++ if (m_config->hasTestFilters()) { ++ xml.scopedElement("property") ++ .writeAttribute("name", "filters") ++ .writeAttribute("value", serializeFilters(m_config->getTestsOrTags())); ++ } ++ if (m_config->rngSeed() != 0) { ++ xml.scopedElement("property") ++ .writeAttribute("name", "random-seed") ++ .writeAttribute("value", m_config->rngSeed()); ++ } ++ } ++ + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + +- xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); +- xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); ++ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); ++ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { +@@ -13529,12 +16964,13 @@ + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + +- writeSection( className, "", rootSection ); ++ writeSection( className, "", rootSection, stats.testInfo.okToFail() ); + } + +- void JunitReporter::writeSection( std::string const& className, +- std::string const& rootName, +- SectionNode const& sectionNode ) { ++ void JunitReporter::writeSection( std::string const& className, ++ std::string const& rootName, ++ SectionNode const& sectionNode, ++ bool testOkToFail) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; +@@ -13551,20 +16987,30 @@ + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } +- xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); ++ xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) ); ++ // This is not ideal, but it should be enough to mimic gtest's ++ // junit output. ++ // Ideally the JUnit reporter would also handle `skipTest` ++ // events and write those out appropriately. ++ xml.writeAttribute( "status", "run" ); ++ ++ if (sectionNode.stats.assertions.failedButOk) { ++ xml.scopedElement("skipped") ++ .writeAttribute("message", "TEST_CASE tagged with !mayfail"); ++ } + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) +- xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); ++ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); + if( !sectionNode.stdErr.empty() ) +- xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); ++ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) +- writeSection( name, "", *childNode ); ++ writeSection( name, "", *childNode, testOkToFail ); + else +- writeSection( className, name, *childNode ); ++ writeSection( className, name, *childNode, testOkToFail ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { +@@ -13582,11 +17028,7 @@ + elementName = "error"; + break; + case ResultWas::ExplicitFailure: +- elementName = "failure"; +- break; + case ResultWas::ExpressionFailed: +- elementName = "failure"; +- break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; +@@ -13604,10 +17046,25 @@ + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + +- xml.writeAttribute( "message", result.getExpandedExpression() ); ++ xml.writeAttribute( "message", result.getExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; ++ if (stats.totals.assertions.total() > 0) { ++ rss << "FAILED" << ":\n"; ++ if (result.hasExpression()) { ++ rss << " "; ++ rss << result.getExpressionInMacro(); ++ rss << '\n'; ++ } ++ if (result.hasExpandedExpression()) { ++ rss << "with expansion:\n"; ++ rss << Column(result.getExpandedExpression()).indent(2) << '\n'; ++ } ++ } else { ++ rss << '\n'; ++ } ++ + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) +@@ -13615,7 +17072,7 @@ + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); +- xml.writeText( rss.str(), false ); ++ xml.writeText( rss.str(), XmlFormatting::Newline ); + } + } + +@@ -13659,19 +17116,41 @@ + m_reporter->noMatchingTestCases( spec ); + } + ++ void ListeningReporter::reportInvalidArguments(std::string const&arg){ ++ for ( auto const& listener : m_listeners ) { ++ listener->reportInvalidArguments( arg ); ++ } ++ m_reporter->reportInvalidArguments( arg ); ++ } ++ ++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) ++ void ListeningReporter::benchmarkPreparing( std::string const& name ) { ++ for (auto const& listener : m_listeners) { ++ listener->benchmarkPreparing(name); ++ } ++ m_reporter->benchmarkPreparing(name); ++ } + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); + } +- void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { ++ void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); + } + ++ void ListeningReporter::benchmarkFailed( std::string const& error ) { ++ for (auto const& listener : m_listeners) { ++ listener->benchmarkFailed(error); ++ } ++ m_reporter->benchmarkFailed(error); ++ } ++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING ++ + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); +@@ -13802,6 +17281,8 @@ + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); ++ if (m_config->testSpec().hasFilters()) ++ m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) ); + if( m_config->rngSeed() != 0 ) + m_xml.scopedElement( "Randomness" ) + .writeAttribute( "seed", m_config->rngSeed() ); +@@ -13937,9 +17418,9 @@ + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) +- m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); ++ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); + if( !testCaseStats.stdErr.empty() ) +- m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); ++ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); + + m_xml.endElement(); + } +@@ -13951,6 +17432,10 @@ + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); ++ m_xml.scopedElement( "OverallResultsCases") ++ .writeAttribute( "successes", testGroupStats.totals.testCases.passed ) ++ .writeAttribute( "failures", testGroupStats.totals.testCases.failed ) ++ .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk ); + m_xml.endElement(); + } + +@@ -13960,9 +17445,58 @@ + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); ++ m_xml.scopedElement( "OverallResultsCases") ++ .writeAttribute( "successes", testRunStats.totals.testCases.passed ) ++ .writeAttribute( "failures", testRunStats.totals.testCases.failed ) ++ .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk ); ++ m_xml.endElement(); ++ } ++ ++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) ++ void XmlReporter::benchmarkPreparing(std::string const& name) { ++ m_xml.startElement("BenchmarkResults") ++ .writeAttribute("name", name); ++ } ++ ++ void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { ++ m_xml.writeAttribute("samples", info.samples) ++ .writeAttribute("resamples", info.resamples) ++ .writeAttribute("iterations", info.iterations) ++ .writeAttribute("clockResolution", info.clockResolution) ++ .writeAttribute("estimatedDuration", info.estimatedDuration) ++ .writeComment("All values in nano seconds"); ++ } ++ ++ void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { ++ m_xml.startElement("mean") ++ .writeAttribute("value", benchmarkStats.mean.point.count()) ++ .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count()) ++ .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count()) ++ .writeAttribute("ci", benchmarkStats.mean.confidence_interval); ++ m_xml.endElement(); ++ m_xml.startElement("standardDeviation") ++ .writeAttribute("value", benchmarkStats.standardDeviation.point.count()) ++ .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count()) ++ .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count()) ++ .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval); ++ m_xml.endElement(); ++ m_xml.startElement("outliers") ++ .writeAttribute("variance", benchmarkStats.outlierVariance) ++ .writeAttribute("lowMild", benchmarkStats.outliers.low_mild) ++ .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe) ++ .writeAttribute("highMild", benchmarkStats.outliers.high_mild) ++ .writeAttribute("highSevere", benchmarkStats.outliers.high_severe); ++ m_xml.endElement(); + m_xml.endElement(); + } + ++ void XmlReporter::benchmarkFailed(std::string const &error) { ++ m_xml.scopedElement("failed"). ++ writeAttribute("message", error); ++ m_xml.endElement(); ++ } ++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING ++ + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + + } // end namespace Catch +@@ -13988,7 +17522,7 @@ + + #ifndef __OBJC__ + +-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) ++#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) + // Standard C/C++ Win32 Unicode wmain entry point + extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { + #else +@@ -14036,7 +17570,7 @@ + #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +-#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) ++#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) + #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) + #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +@@ -14050,7 +17584,7 @@ + #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +-#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) ++#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) + #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) + #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +@@ -14065,6 +17599,7 @@ + #endif // CATCH_CONFIG_DISABLE_MATCHERS + + #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) ++#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) + #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ ) + +@@ -14082,10 +17617,22 @@ + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) + #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) + #else + #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) ) + #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) + #endif + + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +@@ -14106,6 +17653,13 @@ + #define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) + #define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + ++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) ++#define CATCH_BENCHMARK(...) \ ++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) ++#define CATCH_BENCHMARK_ADVANCED(name) \ ++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) ++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING ++ + // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required + #else + +@@ -14141,6 +17695,7 @@ + #endif // CATCH_CONFIG_DISABLE_MATCHERS + + #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) ++#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) + #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ ) + +@@ -14157,10 +17712,26 @@ + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) + #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ++#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__) ++#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #else + #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) ++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) ) + #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) ++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) ++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) ++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) ++#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) ) ++#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) + #endif + + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +@@ -14186,6 +17757,13 @@ + #define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) + #define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + ++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) ++#define BENCHMARK(...) \ ++ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) ++#define BENCHMARK_ADVANCED(name) \ ++ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) ++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING ++ + using Catch::Detail::Approx; + + #else // CATCH_CONFIG_DISABLE +@@ -14225,12 +17803,13 @@ + #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) + #endif // CATCH_CONFIG_DISABLE_MATCHERS + +-#define CATCH_INFO( msg ) (void)(0) +-#define CATCH_WARN( msg ) (void)(0) +-#define CATCH_CAPTURE( msg ) (void)(0) ++#define CATCH_INFO( msg ) (void)(0) ++#define CATCH_UNSCOPED_INFO( msg ) (void)(0) ++#define CATCH_WARN( msg ) (void)(0) ++#define CATCH_CAPTURE( msg ) (void)(0) + +-#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +-#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) ++#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) ++#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) + #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) + #define CATCH_SECTION( ... ) +@@ -14239,19 +17818,31 @@ + #define CATCH_FAIL_CHECK( ... ) (void)(0) + #define CATCH_SUCCEED( ... ) (void)(0) + +-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) ++#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ++#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ++#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) ++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #else +-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) ++#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) ++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) ++#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #endif + + // "BDD-style" convenience wrappers +-#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) ++#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) ++#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className ) + #define CATCH_GIVEN( desc ) + #define CATCH_AND_GIVEN( desc ) + #define CATCH_WHEN( desc ) +@@ -14297,11 +17888,12 @@ + #endif // CATCH_CONFIG_DISABLE_MATCHERS + + #define INFO( msg ) (void)(0) ++#define UNSCOPED_INFO( msg ) (void)(0) + #define WARN( msg ) (void)(0) + #define CAPTURE( msg ) (void)(0) + +-#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +-#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) ++#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) ++#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) + #define METHOD_AS_TEST_CASE( method, ... ) + #define REGISTER_TEST_CASE( Function, ... ) (void)(0) + #define SECTION( ... ) +@@ -14309,14 +17901,26 @@ + #define FAIL( ... ) (void)(0) + #define FAIL_CHECK( ... ) (void)(0) + #define SUCCEED( ... ) (void)(0) +-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) ++#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ++#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ++#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) ++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #else +-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) ++#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) ++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) ++#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) ++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) ++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #endif + + #define STATIC_REQUIRE( ... ) (void)(0) +@@ -14327,8 +17931,8 @@ + #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + + // "BDD-style" convenience wrappers +-#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) ++#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ) ) ++#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className ) + + #define GIVEN( desc ) + #define AND_GIVEN( desc ) 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) @@ -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 @@ -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 |