Browse Source

Update Catch2 v3.1.1

Re-initialize subrepo
Yuri D'Elia 1 year ago
parent
commit
ae17cef676
100 changed files with 29015 additions and 2188 deletions
  1. 10 0
      lib/Catch2/.bazelrc
  2. 4 4
      lib/Catch2/.conan/build.py
  3. 5 4
      lib/Catch2/.conan/test_package/CMakeLists.txt
  4. 3 2
      lib/Catch2/.conan/test_package/conanfile.py
  5. 1 3
      lib/Catch2/.conan/test_package/test_package.cpp
  6. 1 0
      lib/Catch2/.github/FUNDING.yml
  7. 43 0
      lib/Catch2/.github/workflows/linux-meson-builds.yml
  8. 103 0
      lib/Catch2/.github/workflows/linux-other-builds.yml
  9. 105 0
      lib/Catch2/.github/workflows/linux-simple-builds.yml
  10. 49 0
      lib/Catch2/.github/workflows/mac-builds.yml
  11. 36 0
      lib/Catch2/.github/workflows/validate-header-guards.yml
  12. 11 5
      lib/Catch2/.gitignore
  13. 6 6
      lib/Catch2/.gitrepo
  14. 0 339
      lib/Catch2/.travis.yml
  15. 81 8
      lib/Catch2/BUILD.bazel
  16. 78 0
      lib/Catch2/CMake/CatchConfigOptions.cmake
  17. 120 0
      lib/Catch2/CMake/CatchMiscFunctions.cmake
  18. 0 26
      lib/Catch2/CMake/MiscFunctions.cmake
  19. 10 0
      lib/Catch2/CMake/catch2-with-main.pc.in
  20. 5 1
      lib/Catch2/CMake/catch2.pc.in
  21. 96 152
      lib/Catch2/CMakeLists.txt
  22. 25 0
      lib/Catch2/CMakePresets.json
  23. 2484 0
      lib/Catch2/Doxyfile
  24. 32 17
      lib/Catch2/README.md
  25. 19 0
      lib/Catch2/SECURITY.md
  26. 0 0
      lib/Catch2/WORKSPACE
  27. 15 0
      lib/Catch2/WORKSPACE.bazel
  28. 101 73
      lib/Catch2/appveyor.yml
  29. 3 6
      lib/Catch2/codecov.yml
  30. 44 14
      lib/Catch2/conanfile.py
  31. 0 0
      lib/Catch2/data/artwork/catch2-c-logo.png
  32. 0 0
      lib/Catch2/data/artwork/catch2-hand-logo.png
  33. 0 0
      lib/Catch2/data/artwork/catch2-logo-small.png
  34. 16 15
      lib/Catch2/docs/Readme.md
  35. 45 64
      lib/Catch2/docs/assertions.md
  36. 3 6
      lib/Catch2/docs/benchmarks.md
  37. 5 34
      lib/Catch2/docs/ci-and-misc.md
  38. 142 31
      lib/Catch2/docs/cmake-integration.md
  39. 227 63
      lib/Catch2/docs/command-line.md
  40. 14 13
      lib/Catch2/docs/commercial-users.md
  41. 192 0
      lib/Catch2/docs/comparing-floating-point-numbers.md
  42. 35 54
      lib/Catch2/docs/configuration.md
  43. 123 36
      lib/Catch2/docs/contributing.md
  44. 7 120
      lib/Catch2/docs/deprecations.md
  45. 31 62
      lib/Catch2/docs/event-listeners.md
  46. 74 0
      lib/Catch2/docs/faq.md
  47. 9 9
      lib/Catch2/docs/generators.md
  48. 31 33
      lib/Catch2/docs/limitations.md
  49. 0 3
      lib/Catch2/docs/list-of-examples.md
  50. 2 2
      lib/Catch2/docs/logging.md
  51. 309 122
      lib/Catch2/docs/matchers.md
  52. 98 0
      lib/Catch2/docs/migrate-v2-to-v3.md
  53. 37 13
      lib/Catch2/docs/opensource-users.md
  54. 19 18
      lib/Catch2/docs/other-macros.md
  55. 50 49
      lib/Catch2/docs/own-main.md
  56. 276 5
      lib/Catch2/docs/release-notes.md
  57. 9 16
      lib/Catch2/docs/release-process.md
  58. 175 0
      lib/Catch2/docs/reporter-events.md
  59. 191 25
      lib/Catch2/docs/reporters.md
  60. 0 106
      lib/Catch2/docs/slow-compiles.md
  61. 80 17
      lib/Catch2/docs/test-cases-and-sections.md
  62. 28 9
      lib/Catch2/docs/test-fixtures.md
  63. 3 3
      lib/Catch2/docs/tostring.md
  64. 87 139
      lib/Catch2/docs/tutorial.md
  65. 100 0
      lib/Catch2/docs/usage-tips.md
  66. 24 11
      lib/Catch2/docs/why-catch.md
  67. 0 15
      lib/Catch2/examples/000-CatchMain.cpp
  68. 4 7
      lib/Catch2/examples/010-TestCase.cpp
  69. 3 9
      lib/Catch2/examples/020-TestCase-1.cpp
  70. 2 2
      lib/Catch2/examples/020-TestCase-2.cpp
  71. 5 5
      lib/Catch2/examples/030-Asn-Require-Check.cpp
  72. 5 4
      lib/Catch2/examples/100-Fix-Section.cpp
  73. 7 4
      lib/Catch2/examples/110-Fix-ClassFixture.cpp
  74. 4 4
      lib/Catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp
  75. 0 27
      lib/Catch2/examples/200-Rpt-CatchMain.cpp
  76. 0 171
      lib/Catch2/examples/207-Rpt-TeamCityReporter.cpp
  77. 51 44
      lib/Catch2/examples/210-Evt-EventListeners.cpp
  78. 3 4
      lib/Catch2/examples/231-Cfg-OutputStreams.cpp
  79. 13 3
      lib/Catch2/examples/300-Gen-OwnGenerator.cpp
  80. 9 7
      lib/Catch2/examples/301-Gen-MapTypeConversion.cpp
  81. 4 3
      lib/Catch2/examples/302-Gen-Table.cpp
  82. 3 1
      lib/Catch2/examples/310-Gen-VariablesInGenerators.cpp
  83. 3 1
      lib/Catch2/examples/311-Gen-CustomCapture.cpp
  84. 31 126
      lib/Catch2/examples/CMakeLists.txt
  85. 15 1
      lib/Catch2/contrib/Catch.cmake
  86. 33 12
      lib/Catch2/contrib/CatchAddTests.cmake
  87. 66 0
      lib/Catch2/extras/CatchShardTests.cmake
  88. 52 0
      lib/Catch2/extras/CatchShardTestsImpl.cmake
  89. 0 0
      lib/Catch2/extras/ParseAndAddCatchTests.cmake
  90. 10395 0
      lib/Catch2/extras/catch_amalgamated.cpp
  91. 12260 0
      lib/Catch2/extras/catch_amalgamated.hpp
  92. 0 0
      lib/Catch2/extras/gdbinit
  93. 0 0
      lib/Catch2/extras/lldbinit
  94. 20 0
      lib/Catch2/fuzzing/CMakeLists.txt
  95. 10 0
      lib/Catch2/fuzzing/NullOStream.cpp
  96. 20 0
      lib/Catch2/fuzzing/NullOStream.h
  97. 33 0
      lib/Catch2/fuzzing/build_fuzzers.sh
  98. 16 0
      lib/Catch2/fuzzing/fuzz_TestSpecParser.cpp
  99. 16 0
      lib/Catch2/fuzzing/fuzz_XmlWriter.cpp
  100. 0 0
      lib/Catch2/fuzzing/fuzz_textflow.cpp

+ 10 - 0
lib/Catch2/.bazelrc

@@ -0,0 +1,10 @@
+build --enable_platform_specific_config
+
+build:gcc9 --cxxopt=-std=c++2a
+build:gcc11 --cxxopt=-std=c++2a
+build:clang13 --cxxopt=-std=c++17
+build:vs2019 --cxxopt=/std:c++17
+build:vs2022 --cxxopt=/std:c++17
+
+build:windows --config=vs2022
+build:linux --config=gcc11

+ 4 - 4
lib/Catch2/.conan/build.py

@@ -26,8 +26,8 @@ class BuilderSettings(object):
         """ Set Catch2 repository to be used on upload.
             The upload server address could be customized by env var
             CONAN_UPLOAD. If not defined, the method will check the branch name.
-            Only master or CONAN_STABLE_BRANCH_PATTERN will be accepted.
-            The master branch will be pushed to testing channel, because it does
+            Only devel or CONAN_STABLE_BRANCH_PATTERN will be accepted.
+            The devel branch will be pushed to testing channel, because it does
             not match the stable pattern. Otherwise it will upload to stable
             channel.
         """
@@ -49,7 +49,7 @@ class BuilderSettings(object):
     def reference(self):
         """ Read project version from branch create Conan reference
         """
-        return os.getenv("CONAN_REFERENCE", "Catch2/{}".format(self._version))
+        return os.getenv("CONAN_REFERENCE", "catch2/{}".format(self._version))
 
     @property
     def channel(self):
@@ -85,7 +85,7 @@ if __name__ == "__main__":
         reference=settings.reference,
         channel=settings.channel,
         upload=settings.upload,
-        upload_only_when_stable=settings.upload_only_when_stable,
+        upload_only_when_stable=False,
         stable_branch_pattern=settings.stable_branch_pattern,
         login_username=settings.login_username,
         username=settings.username,

+ 5 - 4
lib/Catch2/.conan/test_package/CMakeLists.txt

@@ -1,11 +1,12 @@
 cmake_minimum_required(VERSION 3.2.0)
 project(test_package CXX)
 
-include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
-conan_basic_setup(TARGETS)
+include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
+conan_basic_setup()
 
 find_package(Catch2 REQUIRED CONFIG)
 
 add_executable(${PROJECT_NAME} test_package.cpp)
-target_link_libraries(${PROJECT_NAME} CONAN_PKG::Catch2)
-set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 11)
+
+target_link_libraries(${PROJECT_NAME} Catch2::Catch2WithMain)
+set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 14)

+ 3 - 2
lib/Catch2/.conan/test_package/conanfile.py

@@ -6,7 +6,7 @@ import os
 
 class TestPackageConan(ConanFile):
     settings = "os", "compiler", "build_type", "arch"
-    generators = "cmake"
+    generators = "cmake_find_package_multi", "cmake"
 
     def build(self):
         cmake = CMake(self)
@@ -14,6 +14,7 @@ class TestPackageConan(ConanFile):
         cmake.build()
 
     def test(self):
-        assert os.path.isfile(os.path.join(self.deps_cpp_info["Catch2"].rootpath, "licenses", "LICENSE.txt"))
+        assert os.path.isfile(os.path.join(
+            self.deps_cpp_info["catch2"].rootpath, "licenses", "LICENSE.txt"))
         bin_path = os.path.join("bin", "test_package")
         self.run("%s -s" % bin_path, run_environment=True)

+ 1 - 3
lib/Catch2/.conan/test_package/test_package.cpp

@@ -1,6 +1,4 @@
-#define CATCH_CONFIG_MAIN
-
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
 
 int Factorial( int number ) {
     return number <= 1 ? 1 : Factorial( number - 1 ) * number;

+ 1 - 0
lib/Catch2/.github/FUNDING.yml

@@ -1 +1,2 @@
+github: "horenmar"
 custom: "https://www.paypal.me/horenmar"

+ 43 - 0
lib/Catch2/.github/workflows/linux-meson-builds.yml

@@ -0,0 +1,43 @@
+name: Linux builds (basic) using meson build system
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    name: meson ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}}
+    runs-on: ubuntu-22.04
+    strategy:
+      matrix:
+        cxx:
+          - g++-11
+          - clang++-11
+        build_type: [debug, release]
+        std: [14, 17]
+        include:
+          - cxx: clang++-11
+            other_pkgs: clang-11
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Prepare environment
+      run: sudo apt-get install -y meson ninja-build ${{matrix.other_pkgs}}
+
+    - name: Configure build
+      env:
+        CXX: ${{matrix.cxx}}
+        CXXFLAGS: -std=c++${{matrix.std}} ${{matrix.cxxflags}}
+      # Note: $GITHUB_WORKSPACE is distinct from ${{runner.workspace}}.
+      #       This is important
+      run: |
+        meson -Dbuildtype=${{matrix.build_type}} ${{runner.workspace}}/meson-build
+
+    - name: Build tests + lib
+      working-directory: ${{runner.workspace}}/meson-build
+      run: ninja
+
+    - name: Run tests
+      working-directory: ${{runner.workspace}}/meson-build
+      # Hardcode 2 cores we know are there
+      run: |
+        meson test --verbose

+ 103 - 0
lib/Catch2/.github/workflows/linux-other-builds.yml

@@ -0,0 +1,103 @@
+# The builds in this file are more complex (e.g. they need custom CMake
+# configuration) and thus are unsuitable to the simple build matrix
+# approach used in simple-builds
+name: Linux builds (complex)
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    name: ${{matrix.build_description}}, ${{matrix.cxx}}, C++${{matrix.std}} ${{matrix.build_type}}
+    runs-on: ubuntu-20.04
+    strategy:
+      matrix:
+        # We add builds one by one in this case, because there are no
+        # dimensions that are shared across the builds
+        include:
+
+          # Single surrogate header build
+          - cxx: clang++-10
+            build_description: Surrogates build
+            build_type: Debug
+            std: 14
+            other_pkgs: clang-10
+            cmake_configurations: -DCATCH_BUILD_SURROGATES=ON
+
+          # Extras and examples with gcc-7
+          - cxx: g++-7
+            build_description: Extras + Examples
+            build_type: Debug
+            std: 14
+            other_pkgs: g++-7
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+          - cxx: g++-7
+            build_description: Extras + Examples
+            build_type: Release
+            std: 14
+            other_pkgs: g++-7
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+
+          # Extras and examples with Clang-10
+          - cxx: clang++-10
+            build_description: Extras + Examples
+            build_type: Debug
+            std: 17
+            other_pkgs: clang-10
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+          - cxx: clang++-10
+            build_description: Extras + Examples
+            build_type: Release
+            std: 17
+            other_pkgs: clang-10
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+
+          # Configure tests with Clang-10
+          - cxx: clang++-10
+            build_description: CMake configuration tests
+            build_type: Debug
+            std: 14
+            other_pkgs: clang-10
+            cmake_configurations: -DCATCH_ENABLE_CONFIGURE_TESTS=ON
+
+          # Valgrind test Clang-10
+          - cxx: clang++-10
+            build_description: Valgrind tests
+            build_type: Debug
+            std: 14
+            other_pkgs: clang-10 valgrind
+            cmake_configurations: -DMEMORYCHECK_COMMAND=`which valgrind` -DMEMORYCHECK_COMMAND_OPTIONS="-q --track-origins=yes --leak-check=full --num-callers=50 --show-leak-kinds=definite --error-exitcode=1"
+            other_ctest_args: -T memcheck -LE uses-python
+
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Prepare environment
+      run: sudo apt-get install -y ninja-build ${{matrix.other_pkgs}}
+
+    - name: Configure build
+      working-directory: ${{runner.workspace}}
+      env:
+        CXX: ${{matrix.cxx}}
+        CXXFLAGS: ${{matrix.cxxflags}}
+      # Note: $GITHUB_WORKSPACE is distinct from ${{runner.workspace}}.
+      #       This is important
+      run: |
+        cmake -Bbuild -H$GITHUB_WORKSPACE \
+              -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
+              -DCMAKE_CXX_STANDARD=${{matrix.std}} \
+              -DCMAKE_CXX_EXTENSIONS=OFF \
+              -DCATCH_DEVELOPMENT_BUILD=ON \
+              ${{matrix.cmake_configurations}} \
+              -G Ninja
+
+    - name: Build tests + lib
+      working-directory: ${{runner.workspace}}/build
+      run: ninja
+
+    - name: Run tests
+      env:
+          CTEST_OUTPUT_ON_FAILURE: 1
+      working-directory: ${{runner.workspace}}/build
+      # Hardcode 2 cores we know are there
+      run: ctest -C ${{matrix.build_type}} -j 2 ${{matrix.other_ctest_args}}

+ 105 - 0
lib/Catch2/.github/workflows/linux-simple-builds.yml

@@ -0,0 +1,105 @@
+name: Linux builds (basic)
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    name: ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}}
+    runs-on: ubuntu-20.04
+    strategy:
+      matrix:
+        cxx:
+          - g++-5
+          - g++-6
+          - g++-7
+          - g++-8
+          - g++-9
+          - g++-10
+          - clang++-6.0
+          - clang++-7
+          - clang++-8
+          - clang++-9
+          - clang++-10
+        build_type: [Debug, Release]
+        std: [14]
+        include:
+          - cxx: g++-5
+            other_pkgs: g++-5
+          - cxx: g++-6
+            other_pkgs: g++-6
+          - cxx: g++-7
+            other_pkgs: g++-7
+          - cxx: g++-8
+            other_pkgs: g++-8
+          - cxx: g++-9
+            other_pkgs: g++-9
+          - cxx: g++-10
+            other_pkgs: g++-10
+          - cxx: clang++-6.0
+            other_pkgs: clang-6.0
+          - cxx: clang++-7
+            other_pkgs: clang-7
+          - cxx: clang++-8
+            other_pkgs: clang-8
+          - cxx: clang++-9
+            other_pkgs: clang-9
+          - cxx: clang++-10
+            other_pkgs: clang-10
+          # Clang 6 + C++17
+          # does not work with the default libstdc++ version thanks
+          # to a disagreement on variant implementation.
+          # - cxx: clang++-6.0
+          #   build_type: Debug
+          #   std: 17
+          #   other_pkgs: clang-6.0
+          # - cxx: clang++-6.0
+          #   build_type: Release
+          #   std: 17
+          #   other_pkgs: clang-6.0
+          # Clang 10 + C++17
+          - cxx: clang++-10
+            build_type: Debug
+            std: 17
+            other_pkgs: clang-10
+          - cxx: clang++-10
+            build_type: Release
+            std: 17
+            other_pkgs: clang-10
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Add repositories for older GCC
+      run: |
+        sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ bionic main'
+        sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ bionic universe'
+      if: ${{ matrix.cxx == 'g++-5' || matrix.cxx == 'g++-6' }}
+
+    - name: Prepare environment
+      run: sudo apt-get install -y ninja-build ${{matrix.other_pkgs}}
+
+    - name: Configure build
+      working-directory: ${{runner.workspace}}
+      env:
+        CXX: ${{matrix.cxx}}
+        CXXFLAGS: ${{matrix.cxxflags}}
+      # Note: $GITHUB_WORKSPACE is distinct from ${{runner.workspace}}.
+      #       This is important
+      run: |
+        cmake -Bbuild -H$GITHUB_WORKSPACE \
+              -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
+              -DCMAKE_CXX_STANDARD=${{matrix.std}} \
+              -DCMAKE_CXX_EXTENSIONS=OFF \
+              -DCATCH_DEVELOPMENT_BUILD=ON \
+              -G Ninja
+
+    - name: Build tests + lib
+      working-directory: ${{runner.workspace}}/build
+      run: ninja
+
+    - name: Run tests
+      env:
+          CTEST_OUTPUT_ON_FAILURE: 1
+      working-directory: ${{runner.workspace}}/build
+      # Hardcode 2 cores we know are there
+      run: ctest -C ${{matrix.build_type}} -j 2

+ 49 - 0
lib/Catch2/.github/workflows/mac-builds.yml

@@ -0,0 +1,49 @@
+name: Mac builds
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    # macos-12 updated to a toolchain that crashes when linking the
+    # test binary. This seems to be a known bug in that version,
+    # and will eventually get fixed in an update. After that, we can go
+    # back to newer macos images.
+    runs-on: macos-11
+    strategy:
+      matrix:
+        cxx:
+          - g++-11
+          - clang++
+        build_type: [Debug, Release]
+        std: [14, 17]
+        include:
+          - build_type: Debug
+            examples: ON
+            extra_tests: ON
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Configure build
+      working-directory: ${{runner.workspace}}
+      env:
+        CXX: ${{matrix.cxx}}
+        CXXFLAGS: ${{matrix.cxxflags}}
+      # Note: $GITHUB_WORKSPACE is distinct from ${{runner.workspace}}.
+      #       This is important
+      run: |
+        cmake -Bbuild -H$GITHUB_WORKSPACE \
+              -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} \
+              -DCATCH_DEVELOPMENT_BUILD=ON -DCATCH_BUILD_EXAMPLES=${{matrix.examples}} \
+              -DCATCH_BUILD_EXTRA_TESTS=${{matrix.examples}}
+
+    - name: Build tests + lib
+      working-directory: ${{runner.workspace}}/build
+      run: make -j 2
+
+    - name: Run tests
+      env:
+          CTEST_OUTPUT_ON_FAILURE: 1
+      working-directory: ${{runner.workspace}}/build
+      # Hardcode 2 cores we know are there
+      run: ctest -C ${{matrix.build_type}} -j 2

+ 36 - 0
lib/Catch2/.github/workflows/validate-header-guards.yml

@@ -0,0 +1,36 @@
+name: Check header guards
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    # Set the type of machine to run on
+    runs-on: ubuntu-20.04
+    steps:
+
+      - name: Checkout source code
+        uses: actions/checkout@v2
+
+      - name: Setup Dependencies
+        uses: actions/setup-python@v2
+        with:
+            python-version: '3.7'
+      - name: Install checkguard
+        run: pip install guardonce
+
+      - name: Check that include guards are properly named
+        run: |
+          wrong_files=$(checkguard -r src/catch2/ -p "name | append _INCLUDED | upper")
+          if [[ $wrong_files ]]; then
+            echo "Files with wrong header guard:"
+            echo $wrong_files
+            exit 1
+          fi
+
+      - name: Check that there are no duplicated filenames
+        run: |
+          ./tools/scripts/checkDuplicateFilenames.py
+
+      - name: Check that all source files have the correct license header
+        run: |
+          ./tools/scripts/checkLicense.py

+ 11 - 5
lib/Catch2/.gitignore

@@ -1,4 +1,5 @@
 *.build
+!meson.build
 *.pbxuser
 *.mode1v3
 *.ncb
@@ -11,11 +12,6 @@ Release
 xcuserdata
 CatchSelfTest.xcscheme
 Breakpoints.xcbkptlist
-projects/VS2010/TestCatch/_UpgradeReport_Files/
-projects/VS2010/TestCatch/TestCatch/TestCatch.vcxproj.filters
-projects/VisualStudio/TestCatch/UpgradeLog.XML
-projects/CMake/.idea
-projects/CMake/cmake-build-debug
 UpgradeLog.XML
 Resources/DWARF
 projects/Generated
@@ -25,7 +21,17 @@ DerivedData
 Build
 .idea
 .vs
+.vscode
 cmake-build-*
 benchmark-dir
 .conan/test_package/build
 bazel-*
+build-fuzzers
+debug-build
+.vscode
+msvc-sln*
+# Currently we use Doxygen for dep graphs and the full docs are only slowly
+# being filled in, so we definitely do not want git to deal with the docs.
+docs/doxygen
+*.cache
+compile_commands.json

+ 6 - 6
lib/Catch2/.gitrepo

@@ -1,12 +1,12 @@
 ; DO NOT EDIT (unless you know what you are doing)
 ;
 ; This subdirectory is a git "subrepo", and this file is maintained by the
-; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
+; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme
 ;
 [subrepo]
-	remote = git@github.com:catchorg/Catch2.git
-	branch = v2.x
-	commit = 5c88067bd339465513af4aec606bd2292f1b594a
-	parent = c58a5298d683f9b137f23a929eac699cd776f66c
+	remote = https://github.com/catchorg/Catch2
+	branch = devel
+	commit = 5df88da16e276f853cc0c45f4b570419be77dd43
+	parent = edc5d013ddca9613e29f70d87d7b68f64919eda9
 	method = merge
-	cmdver = 0.4.3
+	cmdver = 0.4.5

+ 0 - 339
lib/Catch2/.travis.yml

@@ -1,339 +0,0 @@
-language: cpp
-
-branches:
-  except:
-  - /dev-appveyor.*/
-
-common_sources: &all_sources
-  - ubuntu-toolchain-r-test
-  - llvm-toolchain-trusty
-  - llvm-toolchain-trusty-3.9
-  - llvm-toolchain-trusty-4.0
-  - llvm-toolchain-xenial-5.0
-  - llvm-toolchain-xenial-6.0
-
-matrix:
-  include:
-
-    # 1/ Linux Clang Builds
-    - os: linux
-      compiler: clang
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['clang-3.5']
-      env: COMPILER='clang++-3.5'
-
-    - os: linux
-      compiler: clang
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['clang-3.6']
-      env: COMPILER='clang++-3.6'
-
-    # Clang 3.7 is intentionally skipped as we cannot get it easily on
-    # TravisCI container
-
-    - os: linux
-      compiler: clang
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['lcov', 'clang-3.8']
-      env: COMPILER='clang++-3.8'
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-3.9']
-      env: COMPILER='clang++-3.9'
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-4.0']
-      env: COMPILER='clang++-4.0'
-
-    - os: linux
-      dist: xenial
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-5.0']
-      env: COMPILER='clang++-5.0'
-
-    - os: linux
-      dist: xenial
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-6.0']
-      env: COMPILER='clang++-6.0'
-
-    # 2/ Linux GCC Builds
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-         sources: *all_sources
-         packages: ['g++-4.8']
-      env: COMPILER='g++-4.8'
-
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['g++-4.9']
-      env: COMPILER='g++-4.9'
-
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['g++-5']
-      env: COMPILER='g++-5'
-
-    - os: linux
-      compiler: gcc
-      addons: &gcc6
-        apt:
-          sources: *all_sources
-          packages: ['g++-6']
-      env: COMPILER='g++-6'
-
-    - os: linux
-      compiler: gcc
-      addons: &gcc7
-        apt:
-          sources: *all_sources
-          packages: ['g++-7']
-      env: COMPILER='g++-7'
-
-    - os: linux
-      compiler: gcc
-      addons: &gcc8
-        apt:
-          sources: *all_sources
-          packages: ['g++-8']
-      env: COMPILER='g++-8'
-
-    # 3b/ Linux C++14 Clang builds
-    # Note that we need newer libstdc++ for C++14 support
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              packages: ['clang-3.8', 'libstdc++-6-dev']
-              sources:
-                  - ubuntu-toolchain-r-test
-                  - llvm-toolchain-trusty
-      env: COMPILER='clang++-3.8' CPP14=1
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-3.9', 'libstdc++-6-dev']
-      env: COMPILER='clang++-3.9' CPP14=1
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-4.0', 'libstdc++-6-dev']
-      env: COMPILER='clang++-4.0' CPP14=1
-
-    - os: linux
-      dist: xenial
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-5.0', 'libstdc++-6-dev']
-      env: COMPILER='clang++-5.0' CPP14=1
-
-    - os: linux
-      dist: xenial
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-6.0', 'libstdc++-6-dev']
-      env: COMPILER='clang++-6.0' CPP14=1
-
-
-    # 4a/ Linux C++14 GCC builds
-    - os: linux
-      compiler: gcc
-      addons: *gcc6
-      env: COMPILER='g++-6' CPP14=1
-
-    - os: linux
-      compiler: gcc
-      addons: *gcc7
-      env: COMPILER='g++-7' CPP14=1
-
-    - os: linux
-      compiler: gcc
-      addons: *gcc8
-      env: COMPILER='g++-8' CPP14=1
-
-    # 5/ OSX Clang Builds
-    - os: osx
-      osx_image: xcode7.3
-      compiler: clang
-      env: COMPILER='clang++'
-
-    - os: osx
-      osx_image: xcode8
-      compiler: clang
-      env: COMPILER='clang++'
-
-    - os: osx
-      osx_image: xcode9
-      compiler: clang
-      env: COMPILER='clang++'
-
-    - os: osx
-      osx_image: xcode9.1
-      compiler: clang
-      env: COMPILER='clang++'
-
-    - os: osx
-      osx_image: xcode9.1
-      compiler: clang
-      env: COMPILER='clang++' CPP14=1
-
-    # 6/ Special builds -- examples, coverage, valgrind, etc.
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['lcov', 'g++-7']
-      env: COMPILER='g++-7' CPP14=1 EXAMPLES=1 COVERAGE=1 EXTRAS=1
-
-    - os: linux
-      compiler: clang
-      addons:
-        apt:
-          packages: ['clang-3.8', 'lcov']
-          sources:
-            - ubuntu-toolchain-r-test
-            - llvm-toolchain-trusty
-      env: COMPILER='clang++-3.8' EXAMPLES=1 COVERAGE=1 EXTRAS=1
-
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['valgrind', 'lcov', 'g++-7']
-      env: COMPILER='g++-7' CPP14=1 VALGRIND=1
-
-    - os: osx
-      osx_image: xcode9.1
-      compiler: clang
-      env: COMPILER='clang++' CPP14=1 EXAMPLES=1 COVERAGE=1 EXTRAS=1
-
-    # 7/ C++17 builds
-    - os: linux
-      compiler: gcc
-      addons: *gcc7
-      env: COMPILER='g++-7' CPP17=1
-
-    - os: linux
-      compiler: gcc
-      addons: *gcc7
-      env: COMPILER='g++-7' EXAMPLES=1 COVERAGE=1 EXTRAS=1 CPP17=1
-
-    - os: linux
-      dist: xenial
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-6.0', 'libstdc++-8-dev']
-      env: COMPILER='clang++-6.0' CPP17=1
-
-    - os: linux
-      dist: xenial
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-6.0', 'libstdc++-8-dev']
-      env: COMPILER='clang++-6.0' CPP17=1 EXAMPLES=1 COVERAGE=1 EXTRAS=1
-
-    # 8/ Conan
-    - language: python
-      python:
-        - "3.7"
-      dist: xenial
-      install:
-        - pip install conan-package-tools
-      env:
-        - CONAN_GCC_VERSIONS=8
-        - CONAN_DOCKER_IMAGE=conanio/gcc8
-      script:
-        - python .conan/build.py
-
-install:
-  - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
-  - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
-  - |
-    if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
-      CMAKE_URL="http://cmake.org/files/v3.8/cmake-3.8.2-Linux-x86_64.tar.gz"
-      mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
-      export PATH=${DEPS_DIR}/cmake/bin:${PATH}
-    elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
-        which cmake || brew install cmake;
-    fi
-
-before_script:
-  - export CXX=${COMPILER}
-  - cd ${TRAVIS_BUILD_DIR}
-  # Regenerate single header file, so it is tested in the examples...
-  - python scripts/generateSingleHeader.py
-
-  - |
-    if [[ ${CPP17} -eq 1 ]]; then
-      export CPP_STANDARD=17
-    elif [[ ${CPP14} -eq 1 ]]; then
-      export CPP_STANDARD=14
-    else
-      export CPP_STANDARD=11
-    fi
-
-    # Use Debug builds for running Valgrind and building examples
-  - cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DCATCH_USE_VALGRIND=${VALGRIND} -DCATCH_BUILD_EXAMPLES=${EXAMPLES} -DCATCH_ENABLE_COVERAGE=${COVERAGE} -DCATCH_BUILD_EXTRA_TESTS=${EXTRAS} -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF
-    # Don't bother with release build for coverage build
-  - cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF
-
-
-script:
-  - cd Build-Debug
-  - make -j 2
-  - CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
-    # Coverage collection does not work for OS X atm
-  - |
-    if [[ "${TRAVIS_OS_NAME}" == "linux" ]] && [[ "${COVERAGE}" == "1" ]]; then
-      make gcov
-      make lcov
-      bash <(curl -s https://codecov.io/bash) -X gcov || echo "Codecov did not collect coverage reports"
-    fi
-  - # Go to release build
-  - cd ../Build-Release
-  - make -j 2
-  - CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2

+ 81 - 8
lib/Catch2/BUILD.bazel

@@ -1,17 +1,90 @@
-# Load the cc_library rule.
-load("@rules_cc//cc:defs.bzl", "cc_library")
+load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
 
-# Header-only rule to export catch2/catch.hpp.
+expand_template(
+    name = "catch_user_config",
+    out = "catch2/catch_user_config.hpp",
+    substitutions = {
+        "@CATCH_CONFIG_CONSOLE_WIDTH@": "80",
+        "@CATCH_CONFIG_DEFAULT_REPORTER@": "console",
+        "#cmakedefine CATCH_CONFIG_ANDROID_LOGWRITE": "",
+        "#cmakedefine CATCH_CONFIG_BAZEL_SUPPORT": "#define CATCH_CONFIG_BAZEL_SUPPORT",
+        "#cmakedefine CATCH_CONFIG_COLOUR_WIN32": "",
+        "#cmakedefine CATCH_CONFIG_COUNTER": "",
+        "#cmakedefine CATCH_CONFIG_CPP11_TO_STRING": "",
+        "#cmakedefine CATCH_CONFIG_CPP17_BYTE": "",
+        "#cmakedefine CATCH_CONFIG_CPP17_OPTIONAL": "",
+        "#cmakedefine CATCH_CONFIG_CPP17_STRING_VIEW": "",
+        "#cmakedefine CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS": "",
+        "#cmakedefine CATCH_CONFIG_CPP17_VARIANT": "",
+        "#cmakedefine CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER": "",
+        "#cmakedefine CATCH_CONFIG_DISABLE_EXCEPTIONS": "",
+        "#cmakedefine CATCH_CONFIG_DISABLE_STRINGIFICATION": "",
+        "#cmakedefine CATCH_CONFIG_DISABLE": "",
+        "#cmakedefine CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS": "",
+        "#cmakedefine CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER": "",
+        "#cmakedefine CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER": "",
+        "#cmakedefine CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER": "",
+        "#cmakedefine CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER": "",
+        "#cmakedefine CATCH_CONFIG_EXPERIMENTAL_REDIRECT": "",
+        "#cmakedefine CATCH_CONFIG_FALLBACK_STRINGIFIER @CATCH_CONFIG_FALLBACK_STRINGIFIER@": "",
+        "#cmakedefine CATCH_CONFIG_FAST_COMPILE": "",
+        "#cmakedefine CATCH_CONFIG_GLOBAL_NEXTAFTER": "",
+        "#cmakedefine CATCH_CONFIG_NO_ANDROID_LOGWRITE": "",
+        "#cmakedefine CATCH_CONFIG_NO_COLOUR_WIN32": "",
+        "#cmakedefine CATCH_CONFIG_NO_COUNTER": "",
+        "#cmakedefine CATCH_CONFIG_NO_CPP11_TO_STRING": "",
+        "#cmakedefine CATCH_CONFIG_NO_CPP17_BYTE": "",
+        "#cmakedefine CATCH_CONFIG_NO_CPP17_OPTIONAL": "",
+        "#cmakedefine CATCH_CONFIG_NO_CPP17_STRING_VIEW": "",
+        "#cmakedefine CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS": "",
+        "#cmakedefine CATCH_CONFIG_NO_CPP17_VARIANT": "",
+        "#cmakedefine CATCH_CONFIG_NO_GLOBAL_NEXTAFTER": "",
+        "#cmakedefine CATCH_CONFIG_NO_POSIX_SIGNALS": "",
+        "#cmakedefine CATCH_CONFIG_NO_USE_ASYNC": "",
+        "#cmakedefine CATCH_CONFIG_NO_WCHAR": "",
+        "#cmakedefine CATCH_CONFIG_NO_WINDOWS_SEH": "",
+        "#cmakedefine CATCH_CONFIG_NOSTDOUT": "",
+        "#cmakedefine CATCH_CONFIG_POSIX_SIGNALS": "",
+        "#cmakedefine CATCH_CONFIG_PREFIX_ALL": "",
+        "#cmakedefine CATCH_CONFIG_SHARED_LIBRARY": "",
+        "#cmakedefine CATCH_CONFIG_USE_ASYNC": "",
+        "#cmakedefine CATCH_CONFIG_WCHAR": "",
+        "#cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG": "",
+        "#cmakedefine CATCH_CONFIG_WINDOWS_SEH": "",
+    },
+    template = "src/catch2/catch_user_config.hpp.in",
+)
+
+# Generated header library, modifies the include prefix to account for
+# generation path so that we can include <catch2/catch_user_config.hpp>
+# correctly.
+cc_library(
+    name = "catch2_generated",
+    hdrs = ["catch2/catch_user_config.hpp"],
+    include_prefix = ".",  # to manipulate -I of dependenices
+    visibility = ["//visibility:public"],
+)
+
+# Static library, without main.
 cc_library(
     name = "catch2",
-    hdrs = ["single_include/catch2/catch.hpp"],
-    includes = ["single_include/"],
+    srcs = glob(
+        ["src/catch2/**/*.cpp"],
+        exclude = ["src/catch2/internal/catch_main.cpp"],
+    ),
+    hdrs = glob(["src/catch2/**/*.hpp"]),
+    includes = ["src/"],
+    linkstatic = True,
     visibility = ["//visibility:public"],
+    deps = [":catch2_generated"],
 )
 
+# Static library, with main.
 cc_library(
-    name = "catch2_with_main",
-    srcs = ["src/catch_with_main.cpp"],
+    name = "catch2_main",
+    srcs = ["src/catch2/internal/catch_main.cpp"],
+    includes = ["src/"],
+    linkstatic = True,
     visibility = ["//visibility:public"],
-    deps = ["//:catch2"],
+    deps = [":catch2"],
 )

+ 78 - 0
lib/Catch2/CMake/CatchConfigOptions.cmake

@@ -0,0 +1,78 @@
+
+#              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
+
+##
+# This file contains options that are materialized into the Catch2
+# compiled library. All of them default to OFF, as even the positive
+# forms correspond to the user _forcing_ them to ON, while being OFF
+# means that Catch2 can use its own autodetection.
+#
+# For detailed docs look into docs/configuration.md
+
+
+macro(AddOverridableConfigOption OptionBaseName)
+  option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF)
+  option(CATCH_CONFIG_NO_${OptionBaseName} "Read docs/configuration.md for details" OFF)
+endmacro()
+
+macro(AddConfigOption OptionBaseName)
+  option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF)
+endmacro()
+
+set(_OverridableOptions
+  "ANDROID_LOGWRITE"
+  "BAZEL_SUPPORT"
+  "COLOUR_WIN32"
+  "COUNTER"
+  "CPP11_TO_STRING"
+  "CPP17_BYTE"
+  "CPP17_OPTIONAL"
+  "CPP17_STRING_VIEW"
+  "CPP17_UNCAUGHT_EXCEPTIONS"
+  "CPP17_VARIANT"
+  "GLOBAL_NEXTAFTER"
+  "POSIX_SIGNALS"
+  "USE_ASYNC"
+  "WCHAR"
+  "WINDOWS_SEH"
+)
+
+foreach(OptionName ${_OverridableOptions})
+  AddOverridableConfigOption(${OptionName})
+endforeach()
+
+set(_OtherConfigOptions
+  "DISABLE_EXCEPTIONS"
+  "DISABLE_EXCEPTIONS_CUSTOM_HANDLER"
+  "DISABLE"
+  "DISABLE_STRINGIFICATION"
+  "ENABLE_ALL_STRINGMAKERS"
+  "ENABLE_OPTIONAL_STRINGMAKER"
+  "ENABLE_PAIR_STRINGMAKER"
+  "ENABLE_TUPLE_STRINGMAKER"
+  "ENABLE_VARIANT_STRINGMAKER"
+  "EXPERIMENTAL_REDIRECT"
+  "FAST_COMPILE"
+  "NOSTDOUT"
+  "PREFIX_ALL"
+  "WINDOWS_CRTDBG"
+)
+
+
+foreach(OptionName ${_OtherConfigOptions})
+  AddConfigOption(${OptionName})
+endforeach()
+set(CATCH_CONFIG_SHARED_LIBRARY ${BUILD_SHARED_LIBS})
+
+set(CATCH_CONFIG_DEFAULT_REPORTER "console" CACHE STRING "Read docs/configuration.md for details. The name of the reporter should be without quotes.")
+set(CATCH_CONFIG_CONSOLE_WIDTH "80" CACHE STRING "Read docs/configuration.md for details. Must form a valid integer literal.")
+
+# There is no good way to both turn this into a CMake cache variable,
+# and keep reasonable default semantics inside the project. Thus we do
+# not define it and users have to provide it as an outside variable.
+#set(CATCH_CONFIG_FALLBACK_STRINGIFIER "" CACHE STRING "Read docs/configuration.md for details.")

+ 120 - 0
lib/Catch2/CMake/CatchMiscFunctions.cmake

@@ -0,0 +1,120 @@
+
+#              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
+
+include(CheckCXXCompilerFlag)
+function(add_cxx_flag_if_supported_to_targets flagname targets)
+    string(MAKE_C_IDENTIFIER ${flagname} flag_identifier )
+    check_cxx_compiler_flag("${flagname}" HAVE_FLAG_${flag_identifier})
+
+    if (HAVE_FLAG_${flag_identifier})
+        foreach(target ${targets})
+            target_compile_options(${target} PUBLIC ${flagname})
+        endforeach()
+    endif()
+endfunction()
+
+# Assumes that it is only called for development builds, where warnings
+# and Werror is desired, so it also enables Werror.
+function(add_warnings_to_targets targets)
+    LIST(LENGTH targets TARGETS_LEN)
+    # For now we just assume 2 possibilities: msvc and msvc-like compilers,
+    # and other.
+    if (MSVC)
+        foreach(target ${targets})
+            # Force MSVC to consider everything as encoded in utf-8
+            target_compile_options( ${target} PRIVATE /utf-8 )
+            # Enable Werror equivalent
+            if (CATCH_ENABLE_WERROR)
+                target_compile_options( ${target} PRIVATE /WX )
+            endif()
+
+            # MSVC is currently handled specially
+            if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
+                STRING(REGEX REPLACE "/W[0-9]" "/W4" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # override default warning level
+                target_compile_options( ${target} PRIVATE /w44265 /w44061 /w44062 /w45038 )
+            endif()
+        endforeach()
+
+    endif()
+
+    if (NOT MSVC)
+        set(CHECKED_WARNING_FLAGS
+          "-Wabsolute-value"
+          "-Wall"
+          "-Wc++20-compat"
+          "-Wcall-to-pure-virtual-from-ctor-dtor"
+          "-Wcast-align"
+          "-Wcatch-value"
+          "-Wdangling"
+          "-Wdeprecated"
+          "-Wdeprecated-register"
+          "-Wexceptions"
+          "-Wexit-time-destructors"
+          "-Wextra"
+          "-Wextra-semi"
+          "-Wfloat-equal"
+          "-Wglobal-constructors"
+          "-Winit-self"
+          "-Wmisleading-indentation"
+          "-Wmismatched-new-delete"
+          "-Wmismatched-return-types"
+          "-Wmismatched-tags"
+          "-Wmissing-braces"
+          "-Wmissing-declarations"
+          "-Wmissing-noreturn"
+          "-Wmissing-prototypes"
+          "-Wmissing-variable-declarations"
+          "-Wnull-dereference"
+          "-Wold-style-cast"
+          "-Woverloaded-virtual"
+          "-Wparentheses"
+          "-Wpedantic"
+          "-Wreorder"
+          "-Wreturn-std-move"
+          "-Wshadow"
+          "-Wstrict-aliasing"
+          "-Wsuggest-destructor-override"
+          "-Wsuggest-override"
+          "-Wundef"
+          "-Wuninitialized"
+          "-Wunneeded-internal-declaration"
+          "-Wunreachable-code"
+          "-Wunused"
+          "-Wunused-function"
+          "-Wunused-parameter"
+          "-Wvla"
+          "-Wweak-vtables"
+
+          # This is a useful warning, but our tests sometimes rely on
+          # functions being present, but not picked (e.g. various checks
+          # for stringification implementation ordering).
+          # Ergo, we should use it every now and then, but we cannot
+          # enable it by default.
+          # "-Wunused-member-function"
+        )
+        foreach(warning ${CHECKED_WARNING_FLAGS})
+            add_cxx_flag_if_supported_to_targets(${warning} "${targets}")
+        endforeach()
+
+        if (CATCH_ENABLE_WERROR)
+            foreach(target ${targets})
+                # Enable Werror equivalent
+                target_compile_options( ${target} PRIVATE -Werror )
+            endforeach()
+        endif()
+    endif()
+endfunction()
+
+# Adds flags required for reproducible build to the target
+# Currently only supports GCC and Clang
+function(add_build_reproducibility_settings target)
+  # Make the build reproducible on versions of g++ and clang that supports -ffile-prefix-map
+  if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
+    add_cxx_flag_if_supported_to_targets("-ffile-prefix-map=${CATCH_DIR}/=" "${target}")
+  endif()
+endfunction()

+ 0 - 26
lib/Catch2/CMake/MiscFunctions.cmake

@@ -1,26 +0,0 @@
-#checks that the given hard-coded list contains all headers + sources in the given folder
-function(CheckFileList LIST_VAR FOLDER)
-  set(MESSAGE " should be added to the variable ${LIST_VAR}")
-  set(MESSAGE "${MESSAGE} in ${CMAKE_CURRENT_LIST_FILE}\n")
-  file(GLOB GLOBBED_LIST "${FOLDER}/*.cpp"
-                         "${FOLDER}/*.hpp"
-                         "${FOLDER}/*.h")
-  list(REMOVE_ITEM GLOBBED_LIST ${${LIST_VAR}})
-  foreach(EXTRA_ITEM ${GLOBBED_LIST})
-    string(REPLACE "${CATCH_DIR}/" "" RELATIVE_FILE_NAME "${EXTRA_ITEM}")
-    message(AUTHOR_WARNING "The file \"${RELATIVE_FILE_NAME}\"${MESSAGE}")
-  endforeach()
-endfunction()
-
-function(CheckFileListRec LIST_VAR FOLDER)
-  set(MESSAGE " should be added to the variable ${LIST_VAR}")
-  set(MESSAGE "${MESSAGE} in ${CMAKE_CURRENT_LIST_FILE}\n")
-  file(GLOB_RECURSE GLOBBED_LIST "${FOLDER}/*.cpp"
-                                 "${FOLDER}/*.hpp"
-                                 "${FOLDER}/*.h")
-  list(REMOVE_ITEM GLOBBED_LIST ${${LIST_VAR}})
-  foreach(EXTRA_ITEM ${GLOBBED_LIST})
-    string(REPLACE "${CATCH_DIR}/" "" RELATIVE_FILE_NAME "${EXTRA_ITEM}")
-    message(AUTHOR_WARNING "The file \"${RELATIVE_FILE_NAME}\"${MESSAGE}")
-  endforeach()
-endfunction()

+ 10 - 0
lib/Catch2/CMake/catch2-with-main.pc.in

@@ -0,0 +1,10 @@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+pkg_version=@Catch2_VERSION@
+
+Name: Catch2-With-Main
+Description: A modern, C++-native test framework for C++14 and above (links in default main)
+Version: ${pkg_version}
+Requires: catch2 = ${pkg_version}
+Cflags: -I${includedir}
+Libs: -L${libdir} -lCatch2Main

+ 5 - 1
lib/Catch2/CMake/catch2.pc.in

@@ -1,7 +1,11 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
 includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
 
 Name: Catch2
-Description: A modern, C++-native, header-only, test framework for C++11
+Description: A modern, C++-native, test framework for C++14 and above
 URL: https://github.com/catchorg/Catch2
 Version: @Catch2_VERSION@
 Cflags: -I${includedir}
+Libs: -L${libdir} -lCatch2

+ 96 - 152
lib/Catch2/CMakeLists.txt

@@ -1,13 +1,28 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.10)
 
 # detect if Catch is being bundled,
 # disable testsuite in that case
 if(NOT DEFINED PROJECT_NAME)
-    set(NOT_SUBPROJECT ON)
+  set(NOT_SUBPROJECT ON)
 else()
-    set(NOT_SUBPROJECT OFF)
+  set(NOT_SUBPROJECT OFF)
 endif()
 
+option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON)
+option(CATCH_INSTALL_EXTRAS "Install extras (CMake scripts, debugger helpers) alongside library" ON)
+option(CATCH_DEVELOPMENT_BUILD "Build tests, enable warnings, enable Werror, etc" OFF)
+
+include(CMakeDependentOption)
+cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_BUILD_EXAMPLES "Build code examples" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_BUILD_FUZZERS "Build fuzzers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_ENABLE_CONFIGURE_TESTS "Enable CMake configuration tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
+
+
 # Catch2's build breaks if done in-tree. You probably should not build
 # things in tree anyway, but we can allow projects that include Catch2
 # as a subproject to build in-tree as long as it is not in our tree.
@@ -15,44 +30,63 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
     message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
 endif()
 
+project(Catch2
+  VERSION 3.1.1 # CML version placeholder, don't delete
+  LANGUAGES CXX
+  # HOMEPAGE_URL is not supported until CMake version 3.12, which
+  # we do not target yet.
+  # HOMEPAGE_URL "https://github.com/catchorg/Catch2"
+  DESCRIPTION "A modern, C++-native, unit test framework."
+)
 
-project(Catch2 LANGUAGES CXX VERSION 2.13.6)
 
-# Provide path for scripts
+# Provide path for scripts. We first add path to the scripts we don't use,
+# but projects including us might, and set the path up to parent scope.
+# Then we also add path that we use to configure the project, but is of
+# no use to top level projects.
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/extras")
+if (NOT NOT_SUBPROJECT)
+  set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE)
+endif()
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
 
 include(GNUInstallDirs)
+include(CMakePackageConfigHelpers)
+include(CatchConfigOptions)
+if(CATCH_DEVELOPMENT_BUILD)
+  include(CTest)
+endif()
 
-option(CATCH_USE_VALGRIND "Perform SelfTests with Valgrind" OFF)
-option(CATCH_BUILD_TESTING "Build SelfTest project" ON)
-option(CATCH_BUILD_EXAMPLES "Build documentation examples" OFF)
-option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF)
-option(CATCH_BUILD_STATIC_LIBRARY "Builds static library from the main implementation. EXPERIMENTAL" OFF)
-option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF)
-option(CATCH_ENABLE_WERROR "Enable all warnings as errors" ON)
-option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON)
-option(CATCH_INSTALL_HELPERS "Install contrib alongside library" ON)
-
-# define some folders
-set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest)
-set(BENCHMARK_DIR ${CATCH_DIR}/projects/Benchmark)
-set(HEADER_DIR ${CATCH_DIR}/include)
+# This variable is used in some subdirectories, so we need it here, rather
+# than later in the install block
+set(CATCH_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Catch2")
 
-if(USE_WMAIN)
+# We have some Windows builds that test `wmain` entry point,
+# and we need this change to be present in all binaries that
+# are built during these tests, so this is required here, before
+# the subdirectories are added.
+if(CATCH_TEST_USE_WMAIN)
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup")
 endif()
 
-if(NOT_SUBPROJECT)
-    include(CTest)
-    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
-    if(BUILD_TESTING AND CATCH_BUILD_TESTING)
-        find_package(PythonInterp)
-        if (NOT PYTHONINTERP_FOUND)
-            message(FATAL_ERROR "Python not found, but required for tests")
-        endif()
-        add_subdirectory(projects)
+
+# Basic paths
+set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(SOURCES_DIR ${CATCH_DIR}/src/catch2)
+set(SELF_TEST_DIR ${CATCH_DIR}/tests/SelfTest)
+set(BENCHMARK_DIR ${CATCH_DIR}/tests/Benchmark)
+set(EXAMPLES_DIR ${CATCH_DIR}/examples)
+
+# We need to bring-in the variables defined there to this scope
+add_subdirectory(src)
+
+# Build tests only if requested
+if (BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT)
+    find_package(PythonInterp 3 REQUIRED)
+    if (NOT PYTHONINTERP_FOUND)
+        message(FATAL_ERROR "Python not found, but required for tests")
     endif()
+    add_subdirectory(tests)
 endif()
 
 if(CATCH_BUILD_EXAMPLES)
@@ -60,71 +94,21 @@ if(CATCH_BUILD_EXAMPLES)
 endif()
 
 if(CATCH_BUILD_EXTRA_TESTS)
-    add_subdirectory(projects/ExtraTests)
+    add_subdirectory(tests/ExtraTests)
 endif()
 
-# add catch as a 'linkable' target
-add_library(Catch2 INTERFACE)
-
-
-
-# depend on some obvious c++11 features so the dependency is transitively added dependents
-target_compile_features(Catch2
-  INTERFACE
-    cxx_alignas
-    cxx_alignof
-    cxx_attributes
-    cxx_auto_type
-    cxx_constexpr
-    cxx_defaulted_functions
-    cxx_deleted_functions
-    cxx_final
-    cxx_lambdas
-    cxx_noexcept
-    cxx_override
-    cxx_range_for
-    cxx_rvalue_references
-    cxx_static_assert
-    cxx_strong_enums
-    cxx_trailing_return_types
-    cxx_unicode_literals
-    cxx_user_literals
-    cxx_variadic_macros
-)
-
-target_include_directories(Catch2
-  INTERFACE
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/single_include>
-    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
-)
-
-if (ANDROID)
-    target_link_libraries(Catch2 INTERFACE log)
+if(CATCH_BUILD_FUZZERS)
+    add_subdirectory(fuzzing)
 endif()
 
-# provide a namespaced alias for clients to 'link' against if catch is included as a sub-project
-add_library(Catch2::Catch2 ALIAS Catch2)
-
-# Hacky support for compiling the impl into a static lib
-if (CATCH_BUILD_STATIC_LIBRARY)
-  add_library(Catch2WithMain ${CMAKE_CURRENT_LIST_DIR}/src/catch_with_main.cpp)
-  target_link_libraries(Catch2WithMain PUBLIC Catch2)
-  add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain)
-
-  # Make the build reproducible on versions of g++ and clang that supports -ffile-prefix-map
-  if(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 8) OR
-     ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 10))
-    target_compile_options(Catch2WithMain PRIVATE "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
-  endif()
-endif(CATCH_BUILD_STATIC_LIBRARY)
+if (CATCH_DEVELOPMENT_BUILD)
+    add_warnings_to_targets("${CATCH_WARNING_TARGETS}")
+endif()
 
 # Only perform the installation steps when Catch is not being used as
 # a subproject via `add_subdirectory`, or the destinations will break,
 # see https://github.com/catchorg/Catch2/issues/1373
 if (NOT_SUBPROJECT)
-    include(CMakePackageConfigHelpers)
-    set(CATCH_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Catch2")
-
     configure_package_config_file(
         ${CMAKE_CURRENT_LIST_DIR}/CMake/Catch2Config.cmake.in
         ${CMAKE_CURRENT_BINARY_DIR}/Catch2Config.cmake
@@ -132,58 +116,11 @@ if (NOT_SUBPROJECT)
           ${CATCH_CMAKE_CONFIG_DESTINATION}
     )
 
-    # Workaround lack of generator expressions in install(TARGETS
-    set(InstallationTargets Catch2)
-    if (TARGET Catch2WithMain)
-      list(APPEND InstallationTargets Catch2WithMain)
-    endif()
-
-
-    # create and install an export set for catch target as Catch2::Catch
-    install(
-      TARGETS
-        ${InstallationTargets}
-      EXPORT
-        Catch2Targets
-      DESTINATION
-        ${CMAKE_INSTALL_LIBDIR}
-    )
-
-
-    install(
-      EXPORT
-        Catch2Targets
-      NAMESPACE
-        Catch2::
-      DESTINATION
-        ${CATCH_CMAKE_CONFIG_DESTINATION}
-    )
-
-    # By default, FooConfigVersion is tied to architecture that it was
-    # generated on. Because Catch2 is header-only, it is arch-independent
-    # and thus Catch2ConfigVersion should not be tied to the architecture
-    # it was generated on.
-    #
-    # CMake does not provide a direct customization point for this in
-    # `write_basic_package_version_file`, but it can be accomplished
-    # indirectly by temporarily redefining `CMAKE_SIZEOF_VOID_P` to an
-    # empty string. Note that just undefining the variable could be
-    # insufficient in cases where the variable was already in CMake cache
-    set(CATCH2_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
-    set(CMAKE_SIZEOF_VOID_P "")
     write_basic_package_version_file(
       "${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake"
       COMPATIBILITY
         SameMajorVersion
     )
-    set(CMAKE_SIZEOF_VOID_P ${CATCH2_CMAKE_SIZEOF_VOID_P})
-
-    install(
-      DIRECTORY
-        "single_include/"
-      DESTINATION
-        "${CMAKE_INSTALL_INCLUDEDIR}"
-    )
 
     install(
       FILES
@@ -200,28 +137,29 @@ if (NOT_SUBPROJECT)
           docs/
         DESTINATION
           "${CMAKE_INSTALL_DOCDIR}"
+        PATTERN "doxygen" EXCLUDE
       )
     endif()
 
-    if(CATCH_INSTALL_HELPERS)
-    # Install CMake scripts
-    install(
-      FILES
-        "contrib/ParseAndAddCatchTests.cmake"
-        "contrib/Catch.cmake"
-        "contrib/CatchAddTests.cmake"
-      DESTINATION
-        ${CATCH_CMAKE_CONFIG_DESTINATION}
-    )
-
-    # Install debugger helpers
-    install(
-      FILES
-        "contrib/gdbinit"
-        "contrib/lldbinit"
-      DESTINATION
-        ${CMAKE_INSTALL_DATAROOTDIR}/Catch2
-    )
+    if(CATCH_INSTALL_EXTRAS)
+        # Install CMake scripts
+        install(
+          FILES
+            "extras/ParseAndAddCatchTests.cmake"
+            "extras/Catch.cmake"
+            "extras/CatchAddTests.cmake"
+          DESTINATION
+            ${CATCH_CMAKE_CONFIG_DESTINATION}
+        )
+    
+        # Install debugger helpers
+        install(
+          FILES
+            "extras/gdbinit"
+            "extras/lldbinit"
+          DESTINATION
+            ${CMAKE_INSTALL_DATAROOTDIR}/Catch2
+        )
     endif()
 
     ## Provide some pkg-config integration
@@ -234,9 +172,15 @@ if (NOT_SUBPROJECT)
       ${CMAKE_CURRENT_BINARY_DIR}/catch2.pc
       @ONLY
     )
+    configure_file(
+      ${CMAKE_CURRENT_SOURCE_DIR}/CMake/catch2-with-main.pc.in
+      ${CMAKE_CURRENT_BINARY_DIR}/catch2-with-main.pc
+      @ONLY
+    )
     install(
       FILES
         "${CMAKE_CURRENT_BINARY_DIR}/catch2.pc"
+        "${CMAKE_CURRENT_BINARY_DIR}/catch2-with-main.pc"
       DESTINATION
         ${PKGCONFIG_INSTALL_DIR}
     )

+ 25 - 0
lib/Catch2/CMakePresets.json

@@ -0,0 +1,25 @@
+{
+    "version": 3,
+    "configurePresets": [
+        {
+            "name": "basic-tests",
+            "displayName": "Basic development build",
+            "description": "Enables development build with basic tests that are cheap to build and run",
+            "cacheVariables": {
+                "CATCH_DEVELOPMENT_BUILD": "ON"
+            }
+        },
+        {
+            "name": "all-tests",
+            "inherits": "basic-tests",
+            "displayName": "Full development build",
+            "description": "Enables development build with examples and ALL tests",
+            "cacheVariables": {
+                "CATCH_BUILD_EXAMPLES": "ON",
+                "CATCH_BUILD_EXTRA_TESTS": "ON",
+                "CATCH_BUILD_SURROGATES": "ON",
+                "CATCH_ENABLE_CONFIGURE_TESTS": "ON"
+            }
+        }
+    ]   
+}

+ 2484 - 0
lib/Catch2/Doxyfile

@@ -0,0 +1,2484 @@
+# Doxyfile 1.8.16
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "Catch2"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "Popular C++ unit testing framework"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER         = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES                = "complexity=@par Complexity:" \
+                         "noexcept=**Noexcept**"
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is
+# Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 5
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = YES
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = YES
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL   = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# (including Cygwin) ands Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = YES
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = YES
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = YES
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           = doxygen.errors
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT  = "src/catch2"
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+
+# FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = */lib/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = NO
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via Javascript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have Javascript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = YES
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     = TeX/AMSmath \
+                         TeX/AMSsymbols
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = YES
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = NO
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = NO
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = NO
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = NO
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = YES
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 100
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES

+ 32 - 17
lib/Catch2/README.md

@@ -1,28 +1,41 @@
 <a id="top"></a>
-![catch logo](artwork/catch2-logo-small.png)
+![Catch2 logo](data/artwork/catch2-logo-small.png)
 
 [![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases)
-[![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=v2.x)](https://travis-ci.org/catchorg/Catch2)
-[![Build status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true)](https://ci.appveyor.com/project/catchorg/catch2)
-[![codecov](https://codecov.io/gh/catchorg/Catch2/branch/v2.x/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/6JUH8Eybx4CtvkJS)
+[![Linux build status](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml)
+[![Linux build status](https://github.com/catchorg/Catch2/actions/workflows/linux-other-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/linux-other-builds.yml)
+[![MacOS build status](https://github.com/catchorg/Catch2/actions/workflows/mac-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/mac-builds.yml)
+[![Build Status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true&branch=devel)](https://ci.appveyor.com/project/catchorg/catch2)
+[![Code Coverage](https://codecov.io/gh/catchorg/Catch2/branch/devel/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://godbolt.org/z/EdoY15q9G)
 [![Join the chat in Discord: https://discord.gg/4CWS9zD](https://img.shields.io/badge/Discord-Chat!-brightgreen.svg)](https://discord.gg/4CWS9zD)
 
 
-<a href="https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
+## What's the Catch2?
 
-## Catch2 is released!
+Catch2 is mainly a unit testing framework for C++, but it also
+provides basic micro-benchmarking features, and simple BDD macros.
 
-If you've been using an earlier version of Catch, please see the
-Breaking Changes section of [the release notes](https://github.com/catchorg/Catch2/releases/tag/v2.0.1)
-before moving to Catch2. You might also like to read [this blog post](https://levelofindirection.com/blog/catch2-released.html) for more details.
+Catch2's main advantage is that using it is both simple and natural.
+Tests autoregister themselves and do not have to be named with valid
+identifiers, assertions look like normal C++ code, and sections provide
+a nice way to share set-up and tear-down code in tests.
 
-## What's the Catch?
 
-Catch2 is a multi-paradigm test framework for C++. which also supports
-Objective-C (and maybe C).
-It is primarily distributed as a single header file, although certain
-extensions may require additional headers.
+## Catch2 v3 is being developed!
+
+You are on the `devel` branch, where the next major version, v3, of
+Catch2 is being developed. As it is a significant rework, you will
+find that parts of this documentation are likely still stuck on v2.
+
+For stable (and documentation-matching) version of Catch2, [go to the
+`v2.x` branch](https://github.com/catchorg/Catch2/tree/v2.x).
+
+For migrating from the v2 releases to v3, you should look at [our
+documentation](docs/migrate-v2-to-v3.md#top). It provides a simple
+guidelines on getting started, and collects most common migration
+problems.
+
 
 ## How to use it
 This documentation comprises these three parts:
@@ -31,7 +44,9 @@ This documentation comprises these three parts:
 * [Tutorial](docs/tutorial.md#top) - getting started
 * [Reference section](docs/Readme.md#top) - all the details
 
+
 ## More
 * Issues and bugs can be raised on the [Issue tracker on GitHub](https://github.com/catchorg/Catch2/issues)
-* For discussion or questions please use [the dedicated Google Groups forum](https://groups.google.com/forum/?fromgroups#!forum/catch-forum) or our [Discord](https://discord.gg/4CWS9zD)
-* See [who else is using Catch2](docs/opensource-users.md#top)
+* For discussion or questions please use [our Discord](https://discord.gg/4CWS9zD)
+* See who else is using Catch2 in [Open Source Software](docs/opensource-users.md#top)
+or [commercially](docs/commercial-users.md#top).

+ 19 - 0
lib/Catch2/SECURITY.md

@@ -0,0 +1,19 @@
+# Security Policy
+
+## Supported Versions
+
+* Versions 1.x (branch Catch1.x) are no longer supported.
+* Versions 2.x (branch v2.x) are currently supported.
+* `devel` branch serves for stable-ish development and is supported,
+  but branches `devel-*` are considered short lived and are not supported separately.
+
+
+## Reporting a Vulnerability
+
+Due to its nature as a _unit_ test framework, Catch2 shouldn't interact
+with untrusted inputs and there shouldn't be many security vulnerabilities
+in it.
+
+However, if you find one you send email to martin <dot> horenovsky <at>
+gmail <dot> com. If you want to encrypt the email, my pgp key is
+`E29C 46F3 B8A7 5028 6079 3B7D ECC9 C20E 314B 2360`.

+ 0 - 0
lib/Catch2/WORKSPACE


+ 15 - 0
lib/Catch2/WORKSPACE.bazel

@@ -0,0 +1,15 @@
+workspace(name = "catch2")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "bazel_skylib",
+    urls = [
+        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
+        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
+    ],
+    sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
+)
+
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+bazel_skylib_workspace()

+ 101 - 73
lib/Catch2/appveyor.yml

@@ -1,93 +1,37 @@
-# version string format -- This will be overwritten later anyway
-version: "{build}"
-
-# We need a more up to date pip because Python 2.7 is EOL soon
-init:
-  - set PATH=C:\Python35\Scripts;%PATH%
+version: "{build}-{branch}"
 
+# If we ever get a backlog larger than clone_depth, builds will fail
+# spuriously. I do not think we will ever get 20 deep commits deep though.
+clone_depth: 20
 
+# We want to build everything, except for branches that are explicitly
+# for messing around with travis.
 branches:
   except:
     - /dev-travis.+/
 
-os:
-  - Visual Studio 2017
-  - Visual Studio 2015
-
-environment:
-    matrix:
-        - additional_flags: "/permissive- /std:c++latest"
-          wmain: 0
-
-        - additional_flags: ""
-          wmain: 0
-
-        - additional_flags: "/D_UNICODE /DUNICODE"
-          wmain: 1
-          coverage: 0
-
-        # Have a coverage dimension
-        - additional_flags: ""
-          wmain: 0
-          coverage: 1
-
-        # Have an examples dimension
-        - additional_flags: ""
-          wmain: 0
-          examples: 1
-
-
-matrix:
-    exclude:
-        - os: Visual Studio 2015
-          additional_flags: "/permissive- /std:c++latest"
-
-        - os: Visual Studio 2015
-          additional_flags: "/D_UNICODE /DUNICODE"
 
-        # Exclude unwanted coverage configurations
-        - coverage: 1
-          platform: Win32
-
-        - coverage: 1
-          os: Visual Studio 2015
-
-        - coverage: 1
-          configuration: Release
-
-        # Exclude unwanted examples configurations
-        - examples: 1
-          platform: Win32
-
-        - examples: 1
-          os: Visual Studio 2015
-
-        - examples: 1
-          configuration: Release
+# We need a more up to date pip because Python 2.7 is EOL soon
+init:
+  - set PATH=C:\Python35;C:\Python35\Scripts;%PATH%
 
 
 install:
   - ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { pip --disable-pip-version-check install codecov }
-  - ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { .\misc\installOpenCppCoverage.ps1 }
-
-# Win32 and x64 are CMake-compatible solution platform names.
-# This allows us to pass %PLATFORM% to CMake -A.
-platform:
-  - Win32
-  - x64
+  # This removes our changes to PATH. Keep this step last!
+  - ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { .\tools\misc\installOpenCppCoverage.ps1 }
 
-# build Configurations, i.e. Debug, Release, etc.
-configuration:
-  - Debug
-  - Release
 
-#Cmake will autodetect the compiler, but we set the arch
 before_build:
+  # We need to modify PATH again, because it was reset since the "init" step
+  - set PATH=C:\Python35;C:\Python35\Scripts;%PATH%
   - set CXXFLAGS=%additional_flags%
+  # If we are building examples/extra-tests, we need to regenerate the amalgamated files
+  - cmd: if "%examples%"=="1" ( python .\tools\scripts\generateAmalgamatedFiles.py )
   # Indirection because appveyor doesn't handle multiline batch scripts properly
   # https://stackoverflow.com/questions/37627248/how-to-split-a-command-over-multiple-lines-in-appveyor-yml/37647169#37647169
   # https://help.appveyor.com/discussions/questions/3888-multi-line-cmd-or-powershell-warning-ignore
-  - cmd: .\misc\appveyorBuildConfigurationScript.bat
+  - cmd: .\tools\misc\appveyorBuildConfigurationScript.bat
 
 
 # build with MSBuild
@@ -98,4 +42,88 @@ build:
 
 test_script:
   - set CTEST_OUTPUT_ON_FAILURE=1
-  - cmd: .\misc\appveyorTestRunScript.bat
+  - cmd: .\tools\misc\appveyorTestRunScript.bat
+
+
+# Sadly we cannot use the standard "dimensions" based approach towards
+# specifying the different builds, as there is no way to add one-offs
+# builds afterwards. This means that we will painfully specify each
+# build explicitly.
+environment:
+  matrix:
+    - FLAVOR: VS 2019 x64 Debug Surrogates Configure Tests
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      surrogates: 1
+      configure_tests: 1
+      platform: x64
+      configuration: Debug
+    
+    - FLAVOR: VS 2019 x64 Release
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      platform: x64
+      configuration: Release
+
+    - FLAVOR: VS 2019 x64 Debug Coverage Examples
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      examples: 1
+      coverage: 1
+      platform: x64
+      configuration: Debug
+
+    - FLAVOR: VS 2019 x64 Debug WMain
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      wmain: 1
+      additional_flags: "/D_UNICODE /DUNICODE"
+      platform: x64
+      configuration: Debug
+
+    - FLAVOR: VS 2019 Win32 Debug
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      platform: Win32
+      configuration: Debug
+
+    - FLAVOR: VS 2019 x64 Debug Latest Strict
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      additional_flags: "/permissive- /std:c++latest"
+      platform: x64
+      configuration: Debug
+
+    - FLAVOR: VS 2017 x64 Debug
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      platform: x64
+      configuration: Debug
+    
+    - FLAVOR: VS 2017 x64 Release
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      platform: x64
+      configuration: Release
+
+    - FLAVOR: VS 2017 x64 Release Coverage
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      coverage: 1
+      platform: x64
+      configuration: Debug
+
+    - FLAVOR: VS 2017 Win32 Debug
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      platform: Win32
+      configuration: Debug
+
+    - FLAVOR: VS 2017 Win32 Debug Examples
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      examples: 1
+      platform: Win32
+      configuration: Debug
+
+    - FLAVOR: VS 2017 Win32 Debug WMain
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      wmain: 1
+      additional_flags: "/D_UNICODE /DUNICODE"
+      platform: Win32
+      configuration: Debug
+
+    - FLAVOR: VS 2017 x64 Debug Latest Strict
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      additional_flags: "/permissive- /std:c++latest"
+      platform: x64
+      configuration: Debug

+ 3 - 6
lib/Catch2/codecov.yml

@@ -10,16 +10,13 @@ coverage:
       default:
         target: 80%
   ignore:
-    - "projects/SelfTest"
-    - "**/catch_reporter_tap.hpp"
-    - "**/catch_reporter_automake.hpp"
-    - "**/catch_reporter_teamcity.hpp"
-    - "**/catch_reporter_sonarqube.hpp"
     - "**/external/clara.hpp"
+    - "tests"
 
 
 codecov:
-  branch: v2.x
+  branch: devel
+  max_report_age: off
 
 comment:
   layout: "diff"

+ 44 - 14
lib/Catch2/conanfile.py

@@ -1,30 +1,60 @@
 #!/usr/bin/env python
-from conans import ConanFile, CMake
-
+from conans import ConanFile, CMake, tools
 
 class CatchConan(ConanFile):
-    name = "Catch2"
-    description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD"
-    topics = ("conan", "catch2", "header-only", "unit-test", "tdd", "bdd")
+    name = "catch2"
+    description = "A modern, C++-native, framework for unit-tests, TDD and BDD"
+    topics = ("conan", "catch2", "unit-test", "tdd", "bdd")
     url = "https://github.com/catchorg/Catch2"
     homepage = url
     license = "BSL-1.0"
+
     exports = "LICENSE.txt"
-    exports_sources = ("single_include/*", "CMakeLists.txt", "CMake/*", "contrib/*", "src/*")
+    exports_sources = ("src/*", "CMakeLists.txt", "CMake/*", "extras/*")
+
+    settings = "os", "compiler", "build_type", "arch"
+
     generators = "cmake"
 
-    def package(self):
+    def _configure_cmake(self):
         cmake = CMake(self)
         cmake.definitions["BUILD_TESTING"] = "OFF"
         cmake.definitions["CATCH_INSTALL_DOCS"] = "OFF"
-        cmake.definitions["CATCH_INSTALL_HELPERS"] = "ON"
-        cmake.configure(build_folder='build')
-        cmake.install()
+        cmake.definitions["CATCH_INSTALL_EXTRAS"] = "ON"
+        cmake.configure(build_folder="build")
+        return cmake
 
-        self.copy(pattern="LICENSE.txt", dst="licenses")
+    def build(self):
+        # We need this workaround until the toolchains feature
+        # to inject stuff like MD/MT
+        line_to_replace = 'list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")'
+        tools.replace_in_file("CMakeLists.txt", line_to_replace,
+                              '''{}
+include("{}/conanbuildinfo.cmake")
+conan_basic_setup()'''.format(line_to_replace, self.install_folder.replace("\\", "/")))
+
+        cmake = self._configure_cmake()
+        cmake.build()
 
-    def package_id(self):
-        self.info.header_only()
+    def package(self):
+        self.copy(pattern="LICENSE.txt", dst="licenses")
+        cmake = self._configure_cmake()
+        cmake.install()
 
     def package_info(self):
-        self.cpp_info.builddirs.append("lib/cmake/Catch2")
+        lib_suffix = "d" if self.settings.build_type == "Debug" else ""
+
+        self.cpp_info.names["cmake_find_package"] = "Catch2"
+        self.cpp_info.names["cmake_find_package_multi"] = "Catch2"
+        # Catch2
+        self.cpp_info.components["catch2base"].names["cmake_find_package"] = "Catch2"
+        self.cpp_info.components["catch2base"].names["cmake_find_package_multi"] = "Catch2"
+        self.cpp_info.components["catch2base"].names["pkg_config"] = "Catch2"
+        self.cpp_info.components["catch2base"].libs = ["Catch2" + lib_suffix]
+        self.cpp_info.components["catch2base"].builddirs.append("lib/cmake/Catch2")
+        # Catch2WithMain
+        self.cpp_info.components["catch2main"].names["cmake_find_package"] = "Catch2WithMain"
+        self.cpp_info.components["catch2main"].names["cmake_find_package_multi"] = "Catch2WithMain"
+        self.cpp_info.components["catch2main"].names["pkg_config"] = "Catch2WithMain"
+        self.cpp_info.components["catch2main"].libs = ["Catch2Main" + lib_suffix]
+        self.cpp_info.components["catch2main"].requires = ["catch2base"]

lib/Catch2/artwork/catch2-c-logo.png → lib/Catch2/data/artwork/catch2-c-logo.png


lib/Catch2/artwork/catch2-hand-logo.png → lib/Catch2/data/artwork/catch2-hand-logo.png


lib/Catch2/artwork/catch2-logo-small.png → lib/Catch2/data/artwork/catch2-logo-small.png


+ 16 - 15
lib/Catch2/docs/Readme.md

@@ -4,38 +4,39 @@
 To get the most out of Catch2, start with the [tutorial](tutorial.md#top).
 Once you're up and running consider the following reference material.
 
-Writing tests:
+**Writing tests:**
 * [Assertion macros](assertions.md#top)
-* [Matchers](matchers.md#top)
+* [Matchers (asserting complex properties)](matchers.md#top)
+* [Comparing floating point numbers](comparing-floating-point-numbers.md#top)
 * [Logging macros](logging.md#top)
 * [Test cases and sections](test-cases-and-sections.md#top)
 * [Test fixtures](test-fixtures.md#top)
-* [Reporters](reporters.md#top)
+* [Reporters (output customization)](reporters.md#top)
 * [Event Listeners](event-listeners.md#top)
-* [Data Generators](generators.md#top)
+* [Data Generators (value parameterized tests)](generators.md#top)
 * [Other macros](other-macros.md#top)
 * [Micro benchmarking](benchmarks.md#top)
 
-Fine tuning:
+**Fine tuning:**
 * [Supplying your own main()](own-main.md#top)
 * [Compile-time configuration](configuration.md#top)
 * [String Conversions](tostring.md#top)
 
-Running:
+**Running:**
 * [Command line](command-line.md#top)
 
-Odds and ends:
+**Odds and ends:**
+* [Frequently Asked Questions (FAQ)](faq.md#top)
+* [Best practices and other tips](usage-tips.md#top)
 * [CMake integration](cmake-integration.md#top)
 * [CI and other miscellaneous pieces](ci-and-misc.md#top)
-
-FAQ:
-* [Why are my tests slow to compile?](slow-compiles.md#top)
 * [Known limitations](limitations.md#top)
- 
-Other:
-* [Why Catch?](why-catch.md#top)
-* [Open Source Projects using Catch](opensource-users.md#top)
-* [Commercial Projects using Catch](commercial-users.md#top)
+
+**Other:**
+* [Why Catch2?](why-catch.md#top)
+* [Migrating from v2 to v3](migrate-v2-to-v3.md#top)
+* [Open Source Projects using Catch2](opensource-users.md#top)
+* [Commercial Projects using Catch2](commercial-users.md#top)
 * [Contributing](contributing.md#top)
 * [Release Notes](release-notes.md#top)
 * [Deprecations and incoming changes](deprecations.md#top)

+ 45 - 64
lib/Catch2/docs/assertions.md

@@ -3,6 +3,7 @@
 
 **Contents**<br>
 [Natural Expressions](#natural-expressions)<br>
+[Floating point comparisons](#floating-point-comparisons)<br>
 [Exceptions](#exceptions)<br>
 [Matcher expressions](#matcher-expressions)<br>
 [Thread Safety](#thread-safety)<br>
@@ -19,7 +20,7 @@ Most of these macros come in two forms:
 The ```REQUIRE``` family of macros tests an expression and aborts the test case if it fails.
 The ```CHECK``` family are equivalent but execution continues in the same test case even if the assertion fails. This is useful if you have a series of essentially orthogonal assertions and it is useful to see all the results rather than stopping at the first failure.
 
-* **REQUIRE(** _expression_ **)** and  
+* **REQUIRE(** _expression_ **)** and
 * **CHECK(** _expression_ **)**
 
 Evaluates the expression and records the result. If an exception is thrown, it is caught, reported, and counted as a failure. These are the macros you will use most of the time.
@@ -31,98 +32,78 @@ CHECK( thisReturnsTrue() );
 REQUIRE( i == 42 );
 ```
 
-* **REQUIRE_FALSE(** _expression_ **)** and  
+Expressions prefixed with `!` cannot be decomposed. If you have a type
+that is convertible to bool and you want to assert that it evaluates to
+false, use the two forms below:
+
+
+* **REQUIRE_FALSE(** _expression_ **)** and
 * **CHECK_FALSE(** _expression_ **)**
 
-Evaluates the expression and records the _logical NOT_ of the result. If an exception is thrown it is caught, reported, and counted as a failure.
-(these forms exist as a workaround for the fact that ! prefixed expressions cannot be decomposed).
+Note that there is no reason to use these forms for plain bool variables,
+because there is no added value in decomposing them.
 
 Example:
+```cpp
+Status ret = someFunction();
+REQUIRE_FALSE(ret); // ret must evaluate to false, and Catch2 will print
+                    // out the value of ret if possibly
 ```
-REQUIRE_FALSE( thisReturnsFalse() );
-```
-
-Do note that "overly complex" expressions cannot be decomposed and thus will not compile. This is done partly for practical reasons (to keep the underlying expression template machinery to minimum) and partly for philosophical reasons (assertions should be simple and deterministic).
-
-Examples:
-* `CHECK(a == 1 && b == 2);`
-This expression is too complex because of the `&&` operator. If you want to check that 2 or more properties hold, you can either put the expression into parenthesis, which stops decomposition from working, or you need to decompose the expression into two assertions: `CHECK( a == 1 ); CHECK( b == 2);`
-* `CHECK( a == 2 || b == 1 );`
-This expression is too complex because of the `||` operator. If you want to check that one of several properties hold, you can put the expression into parenthesis (unlike with `&&`, expression decomposition into several `CHECK`s is not possible).
-
 
-### Floating point comparisons
 
-When comparing floating point numbers - especially if at least one of them has been computed - great care must be taken to allow for rounding errors and inexact representations.
+### Other limitations
 
-Catch provides a way to perform tolerant comparisons of floating point values through use of a wrapper class called `Approx`. `Approx` can be used on either side of a comparison expression. It overloads the comparisons operators to take a tolerance into account. Here's a simple example:
+Note that expressions containing either of the binary logical operators,
+`&&` or `||`, cannot be decomposed and will not compile. The reason behind
+this is that it is impossible to overload `&&` and `||` in a way that
+keeps their short-circuiting semantics, and expression decomposition
+relies on overloaded operators to work.
 
-```cpp
-REQUIRE( performComputation() == Approx( 2.1 ) );
-```
+Simple example of an issue with overloading binary logical operators
+is a common pointer idiom, `p && p->foo == 2`. Using the built-in `&&`
+operator, `p` is only dereferenced if it is not null. With overloaded
+`&&`, `p` is always dereferenced, thus causing a segfault if
+`p == nullptr`.
 
-Catch also provides a user-defined literal for `Approx`; `_a`. It resides in
-the `Catch::literals` namespace and can be used like so:
-```cpp
-using namespace Catch::literals;
-REQUIRE( performComputation() == 2.1_a );
-```
+If you want to test expression that contains `&&` or `||`, you have two
+options.
 
-`Approx` is constructed with defaults that should cover most simple cases.
-For the more complex cases, `Approx` provides 3 customization points:
+1) Enclose it in parentheses. Parentheses force evaluation of the expression
+   before the expression decomposition can touch it, and thus it cannot
+   be used.
 
-* __epsilon__ - epsilon serves to set the coefficient by which a result
-can differ from `Approx`'s value before it is rejected.
-_By default set to `std::numeric_limits<float>::epsilon()*100`._
-* __margin__ - margin serves to set the the absolute value by which
-a result can differ from `Approx`'s value before it is rejected.
-_By default set to `0.0`._
-* __scale__ - scale is used to change the magnitude of `Approx` for relative check.
-_By default set to `0.0`._
+2) Rewrite the expression. `REQUIRE(a == 1 && b == 2)` can always be split
+   into `REQUIRE(a == 1); REQUIRE(b == 2);`. Alternatively, if this is a
+   common pattern in your tests, think about using [Matchers](#matcher-expressions).
+   instead. There is no simple rewrite rule for `||`, but I generally
+   believe that if you have `||` in your test expression, you should rethink
+   your tests.
 
-#### epsilon example
-```cpp
-Approx target = Approx(100).epsilon(0.01);
-100.0 == target; // Obviously true
-200.0 == target; // Obviously still false
-100.5 == target; // True, because we set target to allow up to 1% difference
-```
 
-#### margin example
-```cpp
-Approx target = Approx(100).margin(5);
-100.0 == target; // Obviously true
-200.0 == target; // Obviously still false
-104.0 == target; // True, because we set target to allow absolute difference of at most 5
-```
+## Floating point comparisons
 
-#### scale
-Scale can be useful if the computation leading to the result worked
-on different scale than is used by the results. Since allowed difference
-between Approx's value and compared value is based primarily on Approx's value
-(the allowed difference is computed as
-`(Approx::scale + Approx::value) * epsilon`), the resulting comparison could
-need rescaling to be correct.
+Comparing floating point numbers is complex, and [so it has its own
+documentation page](comparing-floating-point-numbers.md#top).
 
 
 ## Exceptions
 
-* **REQUIRE_NOTHROW(** _expression_ **)** and  
+* **REQUIRE_NOTHROW(** _expression_ **)** and
 * **CHECK_NOTHROW(** _expression_ **)**
 
 Expects that no exception is thrown during evaluation of the expression.
 
-* **REQUIRE_THROWS(** _expression_ **)** and  
+* **REQUIRE_THROWS(** _expression_ **)** and
 * **CHECK_THROWS(** _expression_ **)**
 
 Expects that an exception (of any type) is be thrown during evaluation of the expression.
 
-* **REQUIRE_THROWS_AS(** _expression_, _exception type_ **)** and  
+* **REQUIRE_THROWS_AS(** _expression_, _exception type_ **)** and
 * **CHECK_THROWS_AS(** _expression_, _exception type_ **)**
 
 Expects that an exception of the _specified type_ is thrown during evaluation of the expression. Note that the _exception type_ is extended with `const&` and you should not include it yourself.
 
-* **REQUIRE_THROWS_WITH(** _expression_, _string or string matcher_ **)** and  
+* **REQUIRE_THROWS_WITH(** _expression_, _string or string matcher_ **)** and
 * **CHECK_THROWS_WITH(** _expression_, _string or string matcher_ **)**
 
 Expects that an exception is thrown that, when converted to a string, matches the _string_ or _string matcher_ provided (see next section for Matchers).
@@ -158,8 +139,8 @@ REQUIRE_NOTHROW([&](){
 
 To support Matchers a slightly different form is used. Matchers have [their own documentation](matchers.md#top).
 
-* **REQUIRE_THAT(** _lhs_, _matcher expression_ **)** and  
-* **CHECK_THAT(** _lhs_, _matcher expression_ **)**  
+* **REQUIRE_THAT(** _lhs_, _matcher expression_ **)** and
+* **CHECK_THAT(** _lhs_, _matcher expression_ **)**
 
 Matchers can be composed using `&&`, `||` and `!` operators.
 

+ 3 - 6
lib/Catch2/docs/benchmarks.md

@@ -1,11 +1,7 @@
 <a id="top"></a>
 # Authoring benchmarks
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
-
-_Note that benchmarking support is disabled by default and to enable it,
-you need to define `CATCH_CONFIG_ENABLE_BENCHMARKING`. For more details,
-see the [compile-time configuration documentation](configuration.md#top)._
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 Writing benchmarks is not easy. Catch simplifies certain aspects but you'll
 always need to take care about various aspects. Understanding a few things about
@@ -15,7 +11,8 @@ First off, let's go over some terminology that will be used throughout this
 guide.
 
 - *User code*: user code is the code that the user provides to be measured.
-- *Run*: one run is one execution of the user code.
+- *Run*: one run is one execution of the user code. Sometimes also referred
+  to as an _iteration_.
 - *Sample*: one sample is one data point obtained by measuring the time it takes
   to perform a certain number of runs. One sample can consist of more than one
   run if the clock available does not have enough resolution to accurately

+ 5 - 34
lib/Catch2/docs/ci-and-misc.md

@@ -1,13 +1,7 @@
 <a id="top"></a>
 # CI and other odd pieces
 
-**Contents**<br>
-[Continuous Integration systems](#continuous-integration-systems)<br>
-[Other reporters](#other-reporters)<br>
-[Low-level tools](#low-level-tools)<br>
-[CMake](#cmake)<br>
-
-This page talks about how Catch integrates with Continuous Integration 
+This page talks about how Catch integrates with Continuous Integration
 Build Systems may refer to low-level tools, like CMake, or larger systems that run on servers, like Jenkins or TeamCity. This page will talk about both.
 
 ## Continuous Integration systems
@@ -17,9 +11,9 @@ Probably the most important aspect to using Catch with a build server is the use
 Two of these reporters are built in (XML and JUnit) and the third (TeamCity) is included as a separate header. It's possible that the other two may be split out in the future too - as that would make the core of Catch smaller for those that don't need them.
 
 ### XML Reporter
-```-r xml``` 
+```-r xml```
 
-The XML Reporter writes in an XML format that is specific to Catch. 
+The XML Reporter writes in an XML format that is specific to Catch.
 
 The advantage of this format is that it corresponds well to the way Catch works (especially the more unusual features, such as nested sections) and is a fully streaming format - that is it writes output as it goes, without having to store up all its results before it can start writing.
 
@@ -34,19 +28,6 @@ The advantage of this format is that the JUnit Ant schema is widely understood b
 
 The disadvantage is that this schema was designed to correspond to how JUnit works - and there is a significant mismatch with how Catch works. Additionally the format is not streamable (because opening elements hold counts of failed and passing tests as attributes) - so the whole test run must complete before it can be written.
 
-## Other reporters
-Other reporters are not part of the single-header distribution and need
-to be downloaded and included separately. All reporters are stored in
-`single_include` directory in the git repository, and are named
-`catch_reporter_*.hpp`. For example, to use the TeamCity reporter you
-need to download `single_include/catch_reporter_teamcity.hpp` and include
-it after Catch itself.
-
-```cpp
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
-#include "catch_reporter_teamcity.hpp"
-```
 
 ### TeamCity Reporter
 ```-r teamcity```
@@ -69,18 +50,8 @@ Because of the incremental nature of Catch's test suites and ability to run spec
 ```-r sonarqube```
 [SonarQube Generic Test Data](https://docs.sonarqube.org/latest/analysis/generic-test/) XML format for tests metrics.
 
-## Low-level tools
-
-### Precompiled headers (PCHs)
-
-Catch offers prototypal support for being included in precompiled headers, but because of its single-header nature it does need some actions by the user:
-* The precompiled header needs to define `CATCH_CONFIG_ALL_PARTS`
-* The implementation file needs to
-  * undefine `TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED`
-  * define `CATCH_CONFIG_IMPL_ONLY`
-  * define `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`
-  * include "catch.hpp" again
 
+## Low-level tools
 
 ### CodeCoverage module (GCOV, LCOV...)
 
@@ -95,7 +66,7 @@ can use `pkg-config` to get its include path: `pkg-config --cflags catch2`.
 
 ### gdb and lldb scripts
 
-Catch2's `contrib` folder also contains two simple debugger scripts,
+Catch2's `extras` folder also contains two simple debugger scripts,
 `gdbinit` for `gdb` and `lldbinit` for `lldb`. If loaded into their
 respective debugger, these will tell it to step over Catch2's internals
 when stepping through code.

+ 142 - 31
lib/Catch2/docs/cmake-integration.md

@@ -2,9 +2,10 @@
 # CMake integration
 
 **Contents**<br>
-[CMake target](#cmake-target)<br>
+[CMake targets](#cmake-targets)<br>
 [Automatic test registration](#automatic-test-registration)<br>
 [CMake project options](#cmake-project-options)<br>
+[`CATCH_CONFIG_*` customization options in CMake](#catch_config_-customization-options-in-cmake)<br>
 [Installing Catch2 from git repository](#installing-catch2-from-git-repository)<br>
 [Installing Catch2 from vcpkg](#installing-catch2-from-vcpkg)<br>
 
@@ -15,26 +16,33 @@ integration points for our users.
 2) Catch2's repository contains CMake scripts for automatic registration
 of `TEST_CASE`s in CTest
 
-## CMake target
+## CMake targets
 
-Catch2's CMake build exports an interface target `Catch2::Catch2`. Linking
-against it will add the proper include path and all necessary capabilities
-to the resulting binary.
+Catch2's CMake build exports two targets, `Catch2::Catch2`, and
+`Catch2::Catch2WithMain`. If you do not need custom `main` function,
+you should be using the latter (and only the latter). Linking against
+it will add the proper include paths and link your target together with
+2 static libraries that implement Catch2 and its main respectively.
+If you need custom `main`, you should link only against `Catch2::Catch2`.
 
-This means that if Catch2 has been installed on the system, it should be
-enough to do:
+This means that if Catch2 has been installed on the system, it should
+be enough to do
 ```cmake
-find_package(Catch2 REQUIRED)
-target_link_libraries(tests Catch2::Catch2)
+find_package(Catch2 3 REQUIRED)
+# These tests can use the Catch2-provided main
+add_executable(tests test.cpp)
+target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
+
+# These tests need their own main
+add_executable(custom-main-tests test.cpp test-main.cpp)
+target_link_libraries(custom-main-tests PRIVATE Catch2::Catch2)
 ```
 
+These targets are also provided when Catch2 is used as a subdirectory.
+Assuming Catch2 has been cloned to `lib/Catch2`, you only need to replace
+the `find_package` call with `add_subdirectory(lib/Catch2)` and the snippet
+above still works.
 
-This target is also provided when Catch2 is used as a subdirectory.
-Assuming that Catch2 has been cloned to `lib/Catch2`:
-```cmake
-add_subdirectory(lib/Catch2)
-target_link_libraries(tests Catch2::Catch2)
-```
 
 Another possibility is to use [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html):
 ```cmake
@@ -43,26 +51,31 @@ Include(FetchContent)
 FetchContent_Declare(
   Catch2
   GIT_REPOSITORY https://github.com/catchorg/Catch2.git
-  GIT_TAG        v2.13.1)
+  GIT_TAG        v3.0.1 # or a later release
+)
 
 FetchContent_MakeAvailable(Catch2)
 
-target_link_libraries(tests Catch2::Catch2)
+add_executable(tests test.cpp)
+target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
 ```
 
+
 ## Automatic test registration
 
-Catch2's repository also contains two CMake scripts that help users
+Catch2's repository also contains three CMake scripts that help users
 with automatically registering their `TEST_CASE`s with CTest. They
-can be found in the `contrib` folder, and are
+can be found in the `extras` folder, and are
 
 1) `Catch.cmake` (and its dependency `CatchAddTests.cmake`)
 2) `ParseAndAddCatchTests.cmake` (deprecated)
+3) `CatchShardTests.cmake` (and its dependency `CatchShardTestsImpl.cmake`)
 
 If Catch2 has been installed in system, both of these can be used after
 doing `find_package(Catch2 REQUIRED)`. Otherwise you need to add them
 to your CMake module path.
 
+<a id="catch_discover_tests"></a>
 ### `Catch.cmake` and `CatchAddTests.cmake`
 
 `Catch.cmake` provides function `catch_discover_tests` to get tests from
@@ -78,16 +91,28 @@ project(baz LANGUAGES CXX VERSION 0.0.1)
 
 find_package(Catch2 REQUIRED)
 add_executable(foo test.cpp)
-target_link_libraries(foo Catch2::Catch2)
+target_link_libraries(foo PRIVATE Catch2::Catch2)
 
 include(CTest)
 include(Catch)
 catch_discover_tests(foo)
 ```
 
+When using `FetchContent`, `include(Catch)` will fail unless
+`CMAKE_MODULE_PATH` is explicitly updated to include the extras
+directory.
+
+```cmake
+# ... FetchContent ...
+#
+list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
+include(CTest)
+include(Catch)
+catch_discover_tests()
+```
 
 #### Customization
-`catch_discover_tests` can be given several extra argumets:
+`catch_discover_tests` can be given several extra arguments:
 ```cmake
 catch_discover_tests(target
                      [TEST_SPEC arg1...]
@@ -177,7 +202,7 @@ the output file name e.g. ".xml".
 ### `ParseAndAddCatchTests.cmake`
 
 ⚠ This script is [deprecated](https://github.com/catchorg/Catch2/pull/2120)
-in Catch 2.13.4 and superseded by the above approach using `catch_discover_tests`.
+in Catch2 2.13.4 and superseded by the above approach using `catch_discover_tests`.
 See [#2092](https://github.com/catchorg/Catch2/issues/2092) for details.
 
 `ParseAndAddCatchTests` works by parsing all implementation files
@@ -198,7 +223,7 @@ project(baz LANGUAGES CXX VERSION 0.0.1)
 
 find_package(Catch2 REQUIRED)
 add_executable(foo test.cpp)
-target_link_libraries(foo Catch2::Catch2)
+target_link_libraries(foo PRIVATE Catch2::Catch2)
 
 include(CTest)
 include(ParseAndAddCatchTests)
@@ -212,7 +237,7 @@ ParseAndAddCatchTests(foo)
 * `PARSE_CATCH_TESTS_VERBOSE` -- When `ON`, the script prints debug
 messages. Defaults to `OFF`.
 * `PARSE_CATCH_TESTS_NO_HIDDEN_TESTS` -- When `ON`, hidden tests (tests
-tagged with any of `[!hide]`, `[.]` or `[.foo]`) will not be registered.
+tagged with either of `[.]` or `[.foo]`) will not be registered.
 Defaults to `OFF`.
 * `PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME` -- When `ON`, adds fixture
 class name to the test name in CTest. Defaults to `ON`.
@@ -234,10 +259,68 @@ unset(OptionalCatchTestLauncher)
 ParseAndAddCatchTests(bar)
 ```
 
+
+### `CatchShardTests.cmake`
+
+> `CatchShardTests.cmake` was introduced in Catch2 3.1.0.
+
+`CatchShardTests.cmake` provides a function
+`catch_add_sharded_tests(TEST_BINARY)` that splits tests from `TEST_BINARY`
+into multiple shards. The tests in each shard and their order is randomized,
+and the seed changes every invocation of CTest.
+
+Currently there are 3 customization points for this script:
+
+ * SHARD_COUNT - number of shards to split target's tests into
+ * REPORTER    - reporter spec to use for tests
+ * TEST_SPEC   - test spec used for filtering tests
+
+Example usage:
+
+```
+include(CatchShardTests)
+
+catch_add_sharded_tests(foo-tests
+  SHARD_COUNT 4
+  REPORTER "xml::out=-"
+  TEST_SPEC "A"
+)
+
+catch_add_sharded_tests(tests
+  SHARD_COUNT 8
+  REPORTER "xml::out=-"
+  TEST_SPEC "B"
+)
+```
+
+This registers total of 12 CTest tests (4 + 8 shards) to run shards
+from `foo-tests` test binary, filtered by a test spec.
+
+_Note that this script is currently a proof-of-concept for reseeding
+shards per CTest run, and thus does not support (nor does it currently
+aim to support) all customization points from
+[`catch_discover_tests`](#catch_discover_tests)._
+
+
 ## CMake project options
 
 Catch2's CMake project also provides some options for other projects
-that consume it. These are
+that consume it. These are:
+
+* `BUILD_TESTING` -- When `ON` and the project is not used as a subproject,
+Catch2's test binary will be built. Defaults to `ON`.
+* `CATCH_INSTALL_DOCS` -- When `ON`, Catch2's documentation will be
+included in the installation. Defaults to `ON`.
+* `CATCH_INSTALL_EXTRAS` -- When `ON`, Catch2's extras folder (the CMake
+scripts mentioned above, debugger helpers) will be included in the
+installation. Defaults to `ON`.
+* `CATCH_DEVELOPMENT_BUILD` -- When `ON`, configures the build for development
+of Catch2. This means enabling test projects, warnings and so on.
+Defaults to `OFF`.
+
+
+Enabling `CATCH_DEVELOPMENT_BUILD` also enables further configuration
+customization options:
 
 * `CATCH_BUILD_TESTING` -- When `ON`, Catch2's SelfTest project will be
 built. Defaults to `ON`. Note that Catch2 also obeys `BUILD_TESTING` CMake
@@ -245,12 +328,40 @@ variable, so _both_ of them need to be `ON` for the SelfTest to be built,
 and either of them can be set to `OFF` to disable building SelfTest.
 * `CATCH_BUILD_EXAMPLES` -- When `ON`, Catch2's usage examples will be
 built. Defaults to `OFF`.
-* `CATCH_INSTALL_DOCS` -- When `ON`, Catch2's documentation will be
-included in the installation. Defaults to `ON`.
-* `CATCH_INSTALL_HELPERS` -- When `ON`, Catch2's contrib folder will be
-included in the installation. Defaults to `ON`.
-* `BUILD_TESTING` -- When `ON` and the project is not used as a subproject,
-Catch2's test binary will be built. Defaults to `ON`.
+* `CATCH_BUILD_EXTRA_TESTS` -- When `ON`, Catch2's extra tests will be
+built. Defaults to `OFF`.
+* `CATCH_BUILD_FUZZERS` -- When `ON`, Catch2 fuzzing entry points will
+be built. Defaults to `OFF`.
+* `CATCH_ENABLE_WERROR` -- When `ON`, adds `-Werror` or equivalent flag
+to the compilation. Defaults to `ON`.
+* `CATCH_BUILD_SURROGATES` -- When `ON`, each header in Catch2 will be
+compiled separately to ensure that they are self-sufficient.
+Defaults to `OFF`.
+
+
+## `CATCH_CONFIG_*` customization options in CMake
+
+> CMake support for `CATCH_CONFIG_*` options was introduced in Catch2 3.0.1
+
+Due to the new separate compilation model, all the options from the
+[Compile-time configuration docs](configuration.md#top) can also be set
+through Catch2's CMake. To set them, define the option you want as `ON`,
+e.g. `-DCATCH_CONFIG_NOSTDOUT=ON`.
+
+Note that setting the option to `OFF` doesn't disable it. To force disable
+an option, you need to set the `_NO_` form of it to `ON`, e.g.
+`-DCATCH_CONFIG_NO_COLOUR_WIN32=ON`.
+
+
+To summarize the configuration option behaviour with an example:
+
+| `-DCATCH_CONFIG_COLOUR_WIN32` | `-DCATCH_CONFIG_NO_COLOUR_WIN32` |      Result |
+|-------------------------------|----------------------------------|-------------|
+|                          `ON` |                             `ON` |       error |
+|                          `ON` |                            `OFF` |    force-on |
+|                         `OFF` |                             `ON` |   force-off |
+|                         `OFF` |                            `OFF` | auto-detect |
+
 
 
 ## Installing Catch2 from git repository

+ 227 - 63
lib/Catch2/docs/command-line.md

@@ -15,11 +15,11 @@
 [Warnings](#warnings)<br>
 [Reporting timings](#reporting-timings)<br>
 [Load test names to run from a file](#load-test-names-to-run-from-a-file)<br>
-[Just test names](#just-test-names)<br>
 [Specify the order test cases are run](#specify-the-order-test-cases-are-run)<br>
 [Specify a seed for the Random Number Generator](#specify-a-seed-for-the-random-number-generator)<br>
 [Identify framework and version according to the libIdentify standard](#identify-framework-and-version-according-to-the-libidentify-standard)<br>
 [Wait for key before continuing](#wait-for-key-before-continuing)<br>
+[Skip all benchmarks](#skip-all-benchmarks)<br>
 [Specify the number of benchmark samples to collect](#specify-the-number-of-benchmark-samples-to-collect)<br>
 [Specify the number of resamples for bootstrapping](#specify-the-number-of-resamples-for-bootstrapping)<br>
 [Specify the confidence-interval for bootstrapping](#specify-the-confidence-interval-for-bootstrapping)<br>
@@ -29,14 +29,15 @@
 [Specify the section to run](#specify-the-section-to-run)<br>
 [Filenames as tags](#filenames-as-tags)<br>
 [Override output colouring](#override-output-colouring)<br>
+[Test Sharding](#test-sharding)<br>
+[Allow running the binary without tests](#allow-running-the-binary-without-tests)<br>
+[Output verbosity](#output-verbosity)<br>
 
 Catch works quite nicely without any command line options at all - but for those times when you want greater control the following options are available.
 Click one of the following links to take you straight to that option - or scroll on to browse the available options.
 
 <a href="#specifying-which-tests-to-run">               `    <test-spec> ...`</a><br />
 <a href="#usage">                                       `    -h, -?, --help`</a><br />
-<a href="#listing-available-tests-tags-or-reporters">   `    -l, --list-tests`</a><br />
-<a href="#listing-available-tests-tags-or-reporters">   `    -t, --list-tags`</a><br />
 <a href="#showing-results-for-successful-tests">        `    -s, --success`</a><br />
 <a href="#breaking-into-the-debugger">                  `    -b, --break`</a><br />
 <a href="#eliding-assertions-expected-to-throw">        `    -e, --nothrow`</a><br />
@@ -55,18 +56,25 @@ Click one of the following links to take you straight to that option - or scroll
 
 </br>
 
-<a href="#list-test-names-only">                        `    --list-test-names-only`</a><br />
+<a href="#listing-available-tests-tags-or-reporters">   `    --list-tests`</a><br />
+<a href="#listing-available-tests-tags-or-reporters">   `    --list-tags`</a><br />
 <a href="#listing-available-tests-tags-or-reporters">   `    --list-reporters`</a><br />
+<a href="#listing-available-tests-tags-or-reporters">   `    --list-listeners`</a><br />
 <a href="#order">                                       `    --order`</a><br />
 <a href="#rng-seed">                                    `    --rng-seed`</a><br />
 <a href="#libidentify">                                 `    --libidentify`</a><br />
 <a href="#wait-for-keypress">                           `    --wait-for-keypress`</a><br />
+<a href="#skip-benchmarks">                             `    --skip-benchmarks`</a><br />
 <a href="#benchmark-samples">                           `    --benchmark-samples`</a><br />
 <a href="#benchmark-resamples">                         `    --benchmark-resamples`</a><br />
 <a href="#benchmark-confidence-interval">               `    --benchmark-confidence-interval`</a><br />
 <a href="#benchmark-no-analysis">                       `    --benchmark-no-analysis`</a><br />
 <a href="#benchmark-warmup-time">                       `    --benchmark-warmup-time`</a><br />
-<a href="#use-colour">                                  `    --use-colour`</a><br />
+<a href="#colour-mode">                                 `    --colour-mode`</a><br />
+<a href="#test-sharding">                               `    --shard-count`</a><br />
+<a href="#test-sharding">                               `    --shard-index`</a><br />
+<a href=#no-tests-override>                             `    --allow-running-no-tests`</a><br />
+<a href=#output-verbosity>                              `    --verbosity`</a><br />
 
 </br>
 
@@ -93,7 +101,8 @@ Inclusions and exclusions are evaluated in left-to-right order.
 
 Test case examples:
 
-<pre>thisTestOnly            Matches the test case called, 'thisTestOnly'
+```
+thisTestOnly            Matches the test case called, 'thisTestOnly'
 "this test only"        Matches the test case called, 'this test only'
 these*                  Matches all cases starting with 'these'
 exclude:notThis         Matches all tests except, 'notThis'
@@ -101,8 +110,9 @@ exclude:notThis         Matches all tests except, 'notThis'
 ~*private*              Matches all tests except those that contain 'private'
 a* ~ab* abc             Matches all tests that start with 'a', except those that
                         start with 'ab', except 'abc', which is included
+~[tag1]                 Matches all tests except those tagged with '[tag1]'
 -# [#somefile]          Matches all tests from the file 'somefile.cpp'
-</pre>
+```
 
 Names within square brackets are interpreted as tags.
 A series of tags form an AND expression whereas a comma-separated sequence forms an OR expression. e.g.:
@@ -116,18 +126,48 @@ Test names containing special characters, such as `,` or `[` can specify them on
 <a id="choosing-a-reporter-to-use"></a>
 ## Choosing a reporter to use
 
-<pre>-r, --reporter &lt;reporter></pre>
+<pre>-r, --reporter &lt;reporter[::key=value]*&gt;</pre>
 
-A reporter is an object that formats and structures the output of running tests, and potentially summarises the results. By default a console reporter is used that writes, IDE friendly, textual output. Catch comes bundled with some alternative reporters, but more can be added in client code.<br />
-The bundled reporters are:
+Reporters are how the output from Catch2 (results of assertions, tests,
+benchmarks and so on) is formatted and written out. The default reporter
+is called the "Console" reporter and is intended to provide relatively
+verbose and human-friendly output.
 
-<pre>-r console
--r compact
--r xml
--r junit
-</pre>
+Reporters are also individually configurable. To pass configuration options
+to the reporter, you append `::key=value` to the reporter specification
+as many times as you want, e.g. `--reporter xml::out=someFile.xml`.
+
+The keys must either be prefixed by "X", in which case they are not parsed
+by Catch2 and are only passed down to the reporter, or one of options
+hardcoded into Catch2. Currently there are only 2,
+["out"](#sending-output-to-a-file), and ["colour-mode"](#colour-mode).
+
+_Note that the reporter might still check the X-prefixed options for
+validity, and throw an error if they are wrong._
+
+> Support for passing arguments to reporters through the `-r`, `--reporter` flag was introduced in Catch2 3.0.1
+
+There are multiple built-in reporters, you can see what they do by using the
+[`--list-reporter`](command-line.md#listing-available-tests-tags-or-reporters)
+flag. If you need a reporter providing custom format outside of the already
+provided ones, look at the ["write your own reporter" part of the reporter
+documentation](reporters.md#writing-your-own-reporter).
+
+This option may be passed multiple times to use multiple (different)
+reporters  at the same time. See the [reporter documentation](reporters.md#multiple-reporters)
+for details on what the resulting behaviour is. Also note that at most one
+reporter can be provided without the output-file part of reporter spec.
+This reporter will use the "default" output destination, based on
+the [`-o`, `--out`](#sending-output-to-a-file) option.
+
+> Support for using multiple different reporters at the same time was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 3.0.1
+
+
+_Note: There is currently no way to escape `::` in the reporter spec,
+and thus the reporter names, or configuration keys and values, cannot
+contain `::`. As `::` in paths is relatively obscure (unlike ':'), we do
+not consider this an issue._
 
-The JUnit reporter is an xml format that follows the structure of the JUnit XML Report ANT task, as consumed by a number of third-party tools, including Continuous Integration servers such as Hudson. If not otherwise needed, the standard XML reporter is preferred as this is a streaming reporter, whereas the Junit reporter needs to hold all its results until the end so it can write the overall results into attributes of the root node.
 
 <a id="breaking-into-the-debugger"></a>
 ## Breaking into the debugger
@@ -157,24 +197,62 @@ Sometimes this results in a flood of failure messages and you'd rather just see
 
 <a id="listing-available-tests-tags-or-reporters"></a>
 ## Listing available tests, tags or reporters
-<pre>-l, --list-tests
--t, --list-tags
+```
+--list-tests
+--list-tags
 --list-reporters
-</pre>
+--list-listeners
+```
 
-```-l``` or ```--list-tests``` will list all registered tests, along with any tags.
-If one or more test-specs have been supplied too then only the matching tests will be listed.
+> The `--list*` options became customizable through reporters in Catch2 3.0.1
 
-```-t``` or ```--list-tags``` lists all available tags, along with the number of test cases they match. Again, supplying test specs limits the tags that match.
+> The `--list-listeners` option was added in Catch2 3.0.1
 
-```--list-reporters``` lists the available reporters.
+`--list-tests` lists all registered tests matching specified test spec.
+Usually this listing also includes tags, and potentially also other
+information, like source location, based on verbosity and reporter's design.
+
+`--list-tags` lists all tags from registered tests matching specified test
+spec. Usually this also includes number of tests cases they match and
+similar information.
+
+`--list-reporters` lists all available reporters and their descriptions.
+
+`--list-listeners` lists all registered listeners and their descriptions.
+
+The [`--verbosity` argument](#output-verbosity) modifies the level of detail provided by the default `--list*` options
+as follows:
+
+| Option             | `normal` (default)              | `quiet`             | `high`                                  |
+|--------------------|---------------------------------|---------------------|-----------------------------------------|
+| `--list-tests`     | Test names and tags             | Test names only     | Same as `normal`, plus source code line |
+| `--list-tags`      | Tags and counts                 | Same as `normal`    | Same as `normal`                        |
+| `--list-reporters` | Reporter names and descriptions | Reporter names only | Same as `normal`                        |
+| `--list-listeners` | Listener names and descriptions | Same as `normal`    | Same as `normal`                        |
 
 <a id="sending-output-to-a-file"></a>
 ## Sending output to a file
-<pre>-o, --out &lt;filename>
+<pre>-o, --out &lt;filename&gt;
 </pre>
 
-Use this option to send all output to a file. By default output is sent to stdout (note that uses of stdout and stderr *from within test cases* are redirected and included in the report - so even stderr will effectively end up on stdout).
+Use this option to send all output to a file, instead of stdout. You can
+use `-` as the filename to explicitly send the output to stdout (this is
+useful e.g. when using multiple reporters).
+
+> Support for `-` as the filename was introduced in Catch2 3.0.1
+
+Filenames starting with "%" (percent symbol) are reserved by Catch2 for
+meta purposes, e.g. using `%debug` as the filename opens stream that
+writes to platform specific debugging/logging mechanism.
+
+Catch2 currently recognizes 3 meta streams:
+
+* `%debug` - writes to platform specific debugging/logging output
+* `%stdout` - writes to stdout
+* `%stderr` - writes to stderr
+
+> Support for `%stdout` and `%stderr` was introduced in Catch2 3.0.1
+
 
 <a id="naming-a-test-run"></a>
 ## Naming a test run
@@ -205,16 +283,24 @@ This option transforms tabs and newline characters into ```\t``` and ```\n``` re
 ## Warnings
 <pre>-w, --warn &lt;warning name></pre>
 
-Enables reporting of suspicious test states. There are currently two
-available warnings
+You can think of Catch2's warnings as the equivalent of `-Werror` (`/WX`)
+flag for C++ compilers. It turns some suspicious occurrences, like a section
+without assertions, into errors. Because these might be intended, warnings
+are not enabled by default, but user can opt in.
+
+You can enable multiple warnings at the same time.
+
+There are currently two warnings implemented:
 
 ```
-    NoAssertions   // Fail test case / leaf section if no assertions
-                   // (e.g. `REQUIRE`) is encountered.
-    NoTests        // Return non-zero exit code when no test cases were run
-                   // Also calls reporter's noMatchingTestCases method
+    NoAssertions        // Fail test case / leaf section if no assertions
+                        // (e.g. `REQUIRE`) is encountered.
+    UnmatchedTestSpec   // Fail test run if any of the CLI test specs did
+                        // not match any tests.
 ```
 
+> `UnmatchedTestSpec` was introduced in Catch2 3.0.1.
+
 
 <a id="reporting-timings"></a>
 ## Reporting timings
@@ -224,10 +310,10 @@ When set to ```yes``` Catch will report the duration of each test case, in milli
 
 <pre>-D, --min-duration &lt;value></pre>
 
-> `--min-duration` was [introduced](https://github.com/catchorg/Catch2/pull/1910) in Catch 2.13.0
+> `--min-duration` was [introduced](https://github.com/catchorg/Catch2/pull/1910) in Catch2 2.13.0
 
 When set, Catch will report the duration of each test case that took more
-than &lt;value> seconds, in milliseconds. This option is overriden by both
+than &lt;value> seconds, in milliseconds. This option is overridden by both
 `-d yes` and `-d no`, so that either all durations are reported, or none
 are.
 
@@ -236,15 +322,13 @@ are.
 ## Load test names to run from a file
 <pre>-f, --input-file &lt;filename></pre>
 
-Provide the name of a file that contains a list of test case names - one per line. Blank lines are skipped and anything after the comment character, ```#```, is ignored.
+Provide the name of a file that contains a list of test case names,
+one per line. Blank lines are skipped.
 
-A useful way to generate an initial instance of this file is to use the <a href="#list-test-names-only">list-test-names-only</a> option. This can then be manually curated to specify a specific subset of tests - or in a specific order.
-
-<a id="list-test-names-only"></a>
-## Just test names
-<pre>--list-test-names-only</pre>
-
-This option lists all available tests in a non-indented form, one on each line. This makes it ideal for saving to a file and feeding back into the <a href="#input-file">```-f``` or ```--input-file```</a> option.
+A useful way to generate an initial instance of this file is to combine
+the [`--list-tests`](#listing-available-tests-tags-or-reporters) flag with
+the [`--verbosity quiet`](#output-verbosity) option. You can also
+use test specs to filter this list down to what you want first.
 
 
 <a id="order"></a>
@@ -255,8 +339,8 @@ Test cases are ordered one of three ways:
 
 ### decl
 Declaration order (this is the default order if no --order argument is provided).
-Tests in the same TU are sorted using their declaration orders, different
-TUs are in an implementation (linking) dependent order.
+Tests in the same translation unit are sorted using their declaration orders,
+different TUs are sorted in an implementation (linking) dependent order.
 
 
 ### lex
@@ -265,23 +349,36 @@ Lexicographic order. Tests are sorted by their name, their tags are ignored.
 
 ### rand
 
-Randomly sorted. The order is dependent on Catch2's random seed (see
+Randomly ordered. The order is dependent on Catch2's random seed (see
 [`--rng-seed`](#rng-seed)), and is subset invariant. What this means
 is that as long as the random seed is fixed, running only some tests
 (e.g. via tag) does not change their relative order.
 
 > The subset stability was introduced in Catch2 v2.12.0
 
+Since the random order was made subset stable, we promise that given
+the same random seed, the order of test cases will be the same across
+different platforms, as long as the tests were compiled against identical
+version of Catch2. We reserve the right to change the relative order
+of tests cases between Catch2 versions, but it is unlikely to happen often.
+
 
 <a id="rng-seed"></a>
 ## Specify a seed for the Random Number Generator
-<pre>--rng-seed &lt;'time'|number&gt;</pre>
+<pre>--rng-seed &lt;'time'|'random-device'|number&gt;</pre>
+
+Sets the seed for random number generators used by Catch2. These are used
+e.g. to shuffle tests when user asks for tests to be in random order.
 
-Sets a seed for the random number generator using ```std::srand()```. 
-If a number is provided this is used directly as the seed so the random pattern is repeatable.
-Alternatively if the keyword ```time``` is provided then the result of calling ```std::time(0)``` is used and so the pattern becomes unpredictable. In some cases, you might need to pass the keyword ```time``` in double quotes instead of single quotes.
+Using `time` as the argument asks Catch2 generate the seed through call
+to `std::time(nullptr)`. This provides very weak randomness and multiple
+runs of the binary can generate the same seed if they are started close
+to each other.
+
+Using `random-device` asks for `std::random_device` to be used instead.
+If your implementation provides working `std::random_device`, it should
+be preferred to using `time`. Catch2 uses `std::random_device` by default.
 
-In either case the actual value for the seed is printed as part of Catch's output so if an issue is discovered that is sensitive to test ordering the ordering can be reproduced - even if it was originally seeded from ```std::time(0)```.
 
 <a id="libidentify"></a>
 ## Identify framework and version according to the libIdentify standard
@@ -296,11 +393,21 @@ See [The LibIdentify repo for more information and examples](https://github.com/
 Will cause the executable to print a message and wait until the return/ enter key is pressed before continuing -
 either before running any tests, after running all tests - or both, depending on the argument.
 
+<a id="skip-benchmarks"></a>
+## Skip all benchmarks
+<pre>--skip-benchmarks</pre>
+
+> [Introduced](https://github.com/catchorg/Catch2/issues/2408) in Catch2 3.0.1.
+
+This flag tells Catch2 to skip running all benchmarks. Benchmarks in this
+case mean code blocks in `BENCHMARK` and `BENCHMARK_ADVANCED` macros, not
+test cases with the `[!benchmark]` tag.
+
 <a id="benchmark-samples"></a>
 ## Specify the number of benchmark samples to collect
 <pre>--benchmark-samples &lt;# of samples&gt;</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 When running benchmarks a number of "samples" is collected. This is the base data for later statistical analysis.
 Per sample a clock resolution dependent number of iterations of the user code is run, which is independent of the number of samples. Defaults to 100.
@@ -309,7 +416,7 @@ Per sample a clock resolution dependent number of iterations of the user code is
 ## Specify the number of resamples for bootstrapping
 <pre>--benchmark-resamples &lt;# of resamples&gt;</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 After the measurements are performed, statistical [bootstrapping] is performed
 on the samples. The number of resamples for that bootstrapping is configurable
@@ -324,7 +431,7 @@ defaults to 95%).
 ## Specify the confidence-interval for bootstrapping
 <pre>--benchmark-confidence-interval &lt;confidence-interval&gt;</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 The confidence-interval is used for statistical bootstrapping on the samples to
 calculate the upper and lower bounds of mean and standard deviation.
@@ -334,7 +441,7 @@ Must be between 0 and 1 and defaults to 0.95.
 ## Disable statistical analysis of collected benchmark samples
 <pre>--benchmark-no-analysis</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 When this flag is specified no bootstrapping or any other statistical analysis is performed.
 Instead the user code is only measured and the plain mean from the samples is reported.
@@ -343,7 +450,7 @@ Instead the user code is only measured and the plain mean from the samples is re
 ## Specify the amount of time in milliseconds spent on warming up each test
 <pre>--benchmark-warmup-time</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/pull/1844) in Catch 2.11.2.
+> [Introduced](https://github.com/catchorg/Catch2/pull/1844) in Catch2 2.11.2.
 
 Configure the amount of time spent warming up each test.
 
@@ -392,7 +499,7 @@ There are some limitations of this feature to be aware of:
 - Code outside of sections being skipped will still be executed - e.g. any set-up code in the TEST_CASE before the
 start of the first section.</br>
 - At time of writing, wildcards are not supported in section names.
-- If you specify a section without narrowing to a test case first then all test cases will be executed 
+- If you specify a section without narrowing to a test case first then all test cases will be executed
 (but only matching sections within them).
 
 
@@ -400,21 +507,78 @@ start of the first section.</br>
 ## Filenames as tags
 <pre>-#, --filenames-as-tags</pre>
 
-When this option is used then every test is given an additional tag which is formed of the unqualified 
+When this option is used then every test is given an additional tag which is formed of the unqualified
 filename it is found in, with any extension stripped, prefixed with the `#` character.
 
 So, for example,  tests within the file `~\Dev\MyProject\Ferrets.cpp` would be tagged `[#Ferrets]`.
 
-<a id="use-colour"></a>
+<a id="colour-mode"></a>
 ## Override output colouring
-<pre>--use-colour &lt;yes|no|auto&gt;</pre>
+<pre>--colour-mode &lt;ansi|win32|none|default&gt;</pre>
+
+> The `--colour-mode` option replaced the old `--colour` option in Catch2 3.0.1
+
+
+Catch2 support two different ways of colouring terminal output, and by
+default it attempts to make a good guess on which implementation to use
+(and whether to even use it, e.g. Catch2 tries to avoid writing colour
+codes when writing the results into a file).
+
+`--colour-mode` allows the user to explicitly select what happens.
+
+* `--colour-mode ansi` tells Catch2 to always use ANSI colour codes, even
+when writing to a file
+* `--colour-mode win32` tells Catch2 to use colour implementation based
+  on Win32 terminal API
+* `--colour-mode none` tells Catch2 to disable colours completely
+* `--colour-mode default` lets Catch2 decide
+
+`--colour-mode default` is the default setting.
+
+
+<a id="test-sharding"></a>
+## Test Sharding
+<pre>--shard-count <#number of shards>, --shard-index <#shard index to run></pre>
+
+> [Introduced](https://github.com/catchorg/Catch2/pull/2257) in Catch2 3.0.1.
+
+When `--shard-count <#number of shards>` is used, the tests to execute
+will be split evenly in to the given number of sets, identified by indices
+starting at 0. The tests in the set given by
+`--shard-index <#shard index to run>` will be executed. The default shard
+count is `1`, and the default index to run is `0`.
+
+_Shard index must be less than number of shards. As the name suggests,
+it is treated as an index of the shard to run._
+
+Sharding is useful when you want to split test execution across multiple
+processes, as is done with the [Bazel test sharding](https://docs.bazel.build/versions/main/test-encyclopedia.html#test-sharding).
+
+
+<a id="no-tests-override"></a>
+## Allow running the binary without tests
+<pre>--allow-running-no-tests</pre>
+
+> Introduced in Catch2 3.0.1.
+
+By default, Catch2 test binaries return non-0 exit code if no tests were
+run, e.g. if the binary was compiled with no tests, or the provided test
+spec matched no tests. This flag overrides that, so a test run with no
+tests still returns 0.
+
+## Output verbosity
+```
+-v, --verbosity <quiet|normal|high>
+```
+
+Changing verbosity might change how many details Catch2's reporters output.
+However, you should consider changing the verbosity level as a _suggestion_.
+Not all reporters support all verbosity levels, e.g. because the reporter's
+format cannot meaningfully change. In that case, the verbosity level is
+ignored.
 
-Catch colours output for terminals, but omits colouring when it detects that
-output is being sent to a pipe. This is done to avoid interfering with automated
-processing of output.
+Verbosity defaults to _normal_.
 
-`--use-colour yes` forces coloured output, `--use-colour no` disables coloured
-output. The default behaviour is `--use-colour auto`.
 
 ---
 

+ 14 - 13
lib/Catch2/docs/commercial-users.md

@@ -1,22 +1,23 @@
 <a id="top"></a>
-# Commercial users of Catch
+# Commercial users of Catch2
 
-As well as [Open Source](opensource-users.md#top) users Catch is widely used within proprietary code bases too.
-Many organisations like to keep this information internal, and that's fine, 
-but if you're more open it would be great if we could list the names of as
-many organisations as possible that use Catch somewhere in their codebase. 
-Enterprise environments often tend to be far more conservative in their tool adoption - 
-and being aware that other companies are using Catch can ease the path in.
+Catch2 is also widely used in proprietary code bases. This page contains
+some of them that are willing to share this information.
+
+If you want to add your organisation, please check that there is no issue
+with you sharing this fact.
 
-So if you are aware of Catch usage in your organisation, and are fairly confident there is no issue with sharing this
-fact then please let us know - either directly, via a PR or 
-[issue](https://github.com/philsquared/Catch/issues), or on the [forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum).
- 
  - Bloomberg
  - [Bloomlife](https://bloomlife.com)
- - NASA
  - [Inscopix Inc.](https://www.inscopix.com/)
+ - Locksley.CZ
  - [Makimo](https://makimo.pl/)
+ - NASA
+ - [Nexus Software Systems](https://nexwebsites.com)
  - [UX3D](https://ux3d.io)
  - [King](https://king.com)
- 
+
+
+---
+
+[Home](Readme.md#top)

+ 192 - 0
lib/Catch2/docs/comparing-floating-point-numbers.md

@@ -0,0 +1,192 @@
+<a id="top"></a>
+# Comparing floating point numbers with Catch2
+
+If you are not deeply familiar with them, floating point numbers can be
+unintuitive. This also applies to comparing floating point numbers for
+(in)equality.
+
+This page assumes that you have some understanding of both FP, and the
+meaning of different kinds of comparisons, and only goes over what
+functionality Catch2 provides to help you with comparing floating point
+numbers. If you do not have this understanding, we recommend that you first
+study up on floating point numbers and their comparisons, e.g. by [reading
+this blog post](https://codingnest.com/the-little-things-comparing-floating-point-numbers/).
+
+
+## Floating point matchers
+
+```
+#include <catch2/matchers/catch_matchers_floating.hpp
+```
+
+[Matchers](matchers.md#top) are the preferred way of comparing floating
+point numbers in Catch2. We provide 3 of them:
+
+* `WithinAbs(double target, double margin)`,
+* `WithinRel(FloatingPoint target, FloatingPoint eps)`, and
+* `WithinULP(FloatingPoint target, uint64_t maxUlpDiff)`.
+
+> `WithinRel` matcher was introduced in Catch2 2.10.0
+
+As with all matchers, you can combine multiple floating point matchers
+in a single assertion. For example, to check that some computation matches
+a known good value within 0.1% or is close enough (no different to 5
+decimal places) to zero, we would write this assertion:
+
+```cpp
+    REQUIRE_THAT( computation(input),
+        Catch::Matchers::WithinRel(expected, 0.001)
+     || Catch::Matchers::WithinAbs(0, 0.000001) );
+```
+
+
+### WithinAbs
+
+`WithinAbs` creates a matcher that accepts floating point numbers whose
+difference with `target` is less-or-equal to the `margin`. Since `float`
+can be converted to `double` without losing precision, only `double`
+overload exists.
+
+```cpp
+REQUIRE_THAT(1.0, WithinAbs(1.2, 0.2));
+REQUIRE_THAT(0.f, !WithinAbs(1.0, 0.5));
+// Notice that infinity == infinity for WithinAbs
+REQUIRE_THAT(INFINITY, WithinAbs(INFINITY, 0));
+```
+
+
+### WithinRel
+
+`WithinRel` creates a matcher that accepts floating point numbers that
+are _approximately equal_ to the `target` with a tolerance of `eps.`
+Specifically, it matches if
+`|arg - target| <= eps * max(|arg|, |target|)` holds. If you do not
+specify `eps`, `std::numeric_limits<FloatingPoint>::epsilon * 100`
+is used as the default.
+
+```cpp
+// Notice that WithinRel comparison is symmetric, unlike Approx's.
+REQUIRE_THAT(1.0, WithinRel(1.1, 0.1));
+REQUIRE_THAT(1.1, WithinRel(1.0, 0.1));
+// Notice that inifnity == infinity for WithinRel
+REQUIRE_THAT(INFINITY, WithinRel(INFINITY));
+```
+
+
+### WithinULP
+
+`WithinULP` creates a matcher that accepts floating point numbers that
+are no more than `maxUlpDiff`
+[ULPs](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
+away from the `target` value. The short version of what this means
+is that there is no more than `maxUlpDiff - 1` representable floating
+point numbers between the argument for matching and the `target` value.
+
+When using the ULP matcher in Catch2, it is important to keep in mind
+that Catch2 interprets ULP distance slightly differently than
+e.g. `std::nextafter` does.
+
+Catch2's ULP calculation obeys these relations:
+  * `ulpDistance(-x, x) == 2 * ulpDistance(x, 0)`
+  * `ulpDistance(-0, 0) == 0` (due to the above)
+  * `ulpDistance(DBL_MAX, INFINITY) == 1`
+  * `ulpDistancE(NaN, x) == infinity`
+
+
+**Important**: The WithinULP matcher requires the platform to use the
+[IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) representation for
+floating point numbers.
+
+```cpp
+REQUIRE_THAT( -0.f, WithinULP( 0.f, 0 ) );
+```
+
+
+## `Approx`
+
+```
+#include <catch2/catch_approx.hpp>
+```
+
+**We strongly recommend against using `Approx` when writing new code.**
+You should be using floating point matchers instead.
+
+Catch2 provides one more way to handle floating point comparisons. It is
+`Approx`, a special type with overloaded comparison operators, that can
+be used in standard assertions, e.g.
+
+```cpp
+REQUIRE(0.99999 == Catch::Approx(1));
+```
+
+`Approx` supports four comparison operators, `==`, `!=`, `<=`, `>=`, and can
+also be used with strong typedefs over `double`s. It can be used for both
+relative and margin comparisons by using its three customization points.
+Note that the semantics of this is always that of an _or_, so if either
+the relative or absolute margin comparison passes, then the whole comparison
+passes.
+
+The downside to `Approx` is that it has a couple of issues that we cannot
+fix without breaking backwards compatibility. Because Catch2 also provides
+complete set of matchers that implement different floating point comparison
+methods, `Approx` is left as-is, is considered deprecated, and should
+not be used in new code.
+
+The issues are
+  * All internal computation is done in `double`s, leading to slightly
+    different results if the inputs were floats.
+  * `Approx`'s relative margin comparison is not symmetric. This means
+    that `Approx( 10 ).epsilon(0.1) != 11.1` but `Approx( 11.1 ).epsilon(0.1) == 10`.
+  * By default, `Approx` only uses relative margin comparison. This means
+    that `Approx(0) == X` only passes for `X == 0`.
+
+
+### Approx details
+
+If you still want/need to know more about `Approx`, read on.
+
+Catch2 provides a UDL for `Approx`; `_a`. It resides in the `Catch::literals`
+namespace, and can be used like this:
+
+```cpp
+using namespace Catch::literals;
+REQUIRE( performComputation() == 2.1_a );
+```
+
+`Approx` has three customization points for the comparison:
+
+* **epsilon** - epsilon sets the coefficient by which a result
+can differ from `Approx`'s value before it is rejected.
+_Defaults to `std::numeric_limits<float>::epsilon()*100`._
+
+```cpp
+Approx target = Approx(100).epsilon(0.01);
+100.0 == target; // Obviously true
+200.0 == target; // Obviously still false
+100.5 == target; // True, because we set target to allow up to 1% difference
+```
+
+
+* **margin** - margin sets the absolute value by which
+a result can differ from `Approx`'s value before it is rejected.
+_Defaults to `0.0`._
+
+```cpp
+Approx target = Approx(100).margin(5);
+100.0 == target; // Obviously true
+200.0 == target; // Obviously still false
+104.0 == target; // True, because we set target to allow absolute difference of at most 5
+```
+
+* **scale** - scale is used to change the magnitude of `Approx` for the relative check.
+_By default, set to `0.0`._
+
+Scale could be useful if the computation leading to the result worked
+on a different scale than is used by the results. Approx's scale is added
+to Approx's value when computing the allowed relative margin from the
+Approx's value.
+
+
+---
+
+[Home](Readme.md#top)

+ 35 - 54
lib/Catch2/docs/configuration.md

@@ -2,40 +2,26 @@
 # Compile-time configuration
 
 **Contents**<br>
-[main()/ implementation](#main-implementation)<br>
-[Reporter / Listener interfaces](#reporter--listener-interfaces)<br>
 [Prefixing Catch macros](#prefixing-catch-macros)<br>
 [Terminal colour](#terminal-colour)<br>
 [Console width](#console-width)<br>
 [stdout](#stdout)<br>
 [Fallback stringifier](#fallback-stringifier)<br>
 [Default reporter](#default-reporter)<br>
+[Bazel support](#bazel-support)<br>
 [C++11 toggles](#c11-toggles)<br>
 [C++17 toggles](#c17-toggles)<br>
 [Other toggles](#other-toggles)<br>
-[Windows header clutter](#windows-header-clutter)<br>
 [Enabling stringification](#enabling-stringification)<br>
 [Disabling exceptions](#disabling-exceptions)<br>
 [Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
 
-Catch is designed to "just work" as much as possible. For most people the only configuration needed is telling Catch which source file should host all the implementation code (```CATCH_CONFIG_MAIN```).
+Catch2 is designed to "just work" as much as possible, and most of the
+configuration options below are changed automatically during compilation,
+according to the detected environment. However, this detection can also
+be overridden by users, using macros documented below, and/or CMake options
+with the same name.
 
-Nonetheless there are still some occasions where finer control is needed. For these occasions Catch exposes a set of macros for configuring how it is built.
-
-## main()/ implementation
-
-    CATCH_CONFIG_MAIN      // Designates this as implementation file and defines main()
-    CATCH_CONFIG_RUNNER    // Designates this as implementation file
-
-Although Catch is header only it still, internally, maintains a distinction between interface headers and headers that contain implementation. Only one source file in your test project should compile the implementation headers and this is controlled through the use of one of these macros - one of these identifiers should be defined before including Catch in *exactly one implementation file in your project*.
-
-## Reporter / Listener interfaces
-
-    CATCH_CONFIG_EXTERNAL_INTERFACES  // Brings in necessary headers for Reporter/Listener implementation
-
-Brings in various parts of Catch that are required for user defined Reporters and Listeners. This means that new Reporters and Listeners can be defined in this file as well as in the main file.
-
-Implied by both `CATCH_CONFIG_MAIN` and `CATCH_CONFIG_RUNNER`.
 
 ## Prefixing Catch macros
 
@@ -46,19 +32,18 @@ To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TE
 
 ## Terminal colour
 
-    CATCH_CONFIG_COLOUR_NONE      // completely disables all text colouring
-    CATCH_CONFIG_COLOUR_WINDOWS   // forces the Win32 console API to be used
-    CATCH_CONFIG_COLOUR_ANSI      // forces ANSI colour codes to be used
-
-Yes, I am English, so I will continue to spell "colour" with a 'u'.
+    CATCH_CONFIG_COLOUR_WIN32     // Force enables compiling colouring impl based on Win32 console API
+    CATCH_CONFIG_NO_COLOUR_WIN32  // Force disables ...
 
-When sending output to the terminal, if it detects that it can, Catch will use colourised text. On Windows the Win32 API, ```SetConsoleTextAttribute```, is used. On POSIX systems ANSI colour escape codes are inserted into the stream.
+Yes, Catch2 uses the british spelling of colour.
 
-For finer control you can define one of the above identifiers (these are mutually exclusive - but that is not checked so may behave unexpectedly if you mix them):
+Catch2 attempts to autodetect whether the Win32 console colouring API,
+`SetConsoleTextAttribute`, is available, and if it is available it compiles
+in a console colouring implementation that uses it.
 
-Note that when ANSI colour codes are used "unistd.h" must be includable - along with a definition of ```isatty()```
+This option can be used to override Catch2's autodetection and force the
+compilation either ON or OFF.
 
-Typically you should place the ```#define``` before #including "catch.hpp" in your main source file - but if you prefer you can define it for your whole project by whatever your IDE or build system provides for you to do so.
 
 ## Console width
 
@@ -72,7 +57,7 @@ By default a console width of 80 is assumed but this can be controlled by defini
     CATCH_CONFIG_NOSTDOUT
 
 To support platforms that do not provide `std::cout`, `std::cerr` and
-`std::clog`, Catch does not usem the directly, but rather calls
+`std::clog`, Catch does not use them directly, but rather calls
 `Catch::cout`, `Catch::cerr` and `Catch::clog`. You can replace their
 implementation by defining `CATCH_CONFIG_NOSTDOUT` and implementing
 them yourself, their signatures are:
@@ -112,6 +97,14 @@ This means that defining `CATCH_CONFIG_DEFAULT_REPORTER` to `"console"`
 is equivalent with the out-of-the-box experience.
 
 
+## Bazel support
+When `CATCH_CONFIG_BAZEL_SUPPORT` is defined or when `BAZEL_TEST=1` (which is set by the Bazel inside of a test environment),
+Catch2 will register a `JUnit` reporter writing to a path pointed by `XML_OUTPUT_FILE` provided by Bazel.
+
+> `CATCH_CONFIG_BAZEL_SUPPORT` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 3.0.1.
+
+> `CATCH_CONFIG_BAZEL_SUPPORT` was [deprecated](https://github.com/catchorg/Catch2/pull/2459) in Catch2 3.1.0.
+
 ## C++11 toggles
 
     CATCH_CONFIG_CPP11_TO_STRING // Use `std::to_string`
@@ -127,13 +120,13 @@ Catch's selection, by defining either `CATCH_CONFIG_CPP11_TO_STRING` or
 
 ## C++17 toggles
 
-    CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS  // Use std::uncaught_exceptions instead of std::uncaught_exception
-    CATCH_CONFIG_CPP17_STRING_VIEW          // Override std::string_view support detection(Catch provides a StringMaker specialization by default)
+    CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS  // Override std::uncaught_exceptions (instead of std::uncaught_exception) support detection
+    CATCH_CONFIG_CPP17_STRING_VIEW          // Override std::string_view support detection (Catch provides a StringMaker specialization by default)
     CATCH_CONFIG_CPP17_VARIANT              // Override std::variant support detection (checked by CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER)
     CATCH_CONFIG_CPP17_OPTIONAL             // Override std::optional support detection (checked by CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER)
     CATCH_CONFIG_CPP17_BYTE                 // Override std::byte support detection (Catch provides a StringMaker specialization by default)
 
-> `CATCH_CONFIG_CPP17_STRING_VIEW` was [introduced](https://github.com/catchorg/Catch2/issues/1376) in Catch 2.4.1.
+> `CATCH_CONFIG_CPP17_STRING_VIEW` was [introduced](https://github.com/catchorg/Catch2/issues/1376) in Catch2 2.4.1.
 
 Catch contains basic compiler/standard detection and attempts to use
 some C++17 features whenever appropriate. This automatic detection
@@ -147,25 +140,26 @@ by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`.
     CATCH_CONFIG_COUNTER                    // Use __COUNTER__ to generate unique names for test cases
     CATCH_CONFIG_WINDOWS_SEH                // Enable SEH handling on Windows
     CATCH_CONFIG_FAST_COMPILE               // Sacrifices some (rather minor) features for compilation speed
-    CATCH_CONFIG_DISABLE_MATCHERS           // Do not compile Matchers in this compilation unit
     CATCH_CONFIG_POSIX_SIGNALS              // Enable handling POSIX signals
     CATCH_CONFIG_WINDOWS_CRTDBG             // Enable leak checking using Windows's CRT Debug Heap
     CATCH_CONFIG_DISABLE_STRINGIFICATION    // Disable stringifying the original expression
     CATCH_CONFIG_DISABLE                    // Disables assertions and test case registration
     CATCH_CONFIG_WCHAR                      // Enables use of wchart_t
     CATCH_CONFIG_EXPERIMENTAL_REDIRECT      // Enables the new (experimental) way of capturing stdout/stderr
-    CATCH_CONFIG_ENABLE_BENCHMARKING        // Enables the integrated benchmarking features (has a significant effect on compilation speed)
     CATCH_CONFIG_USE_ASYNC                  // Force parallel statistical processing of samples during benchmarking
     CATCH_CONFIG_ANDROID_LOGWRITE           // Use android's logging system for debug output
     CATCH_CONFIG_GLOBAL_NEXTAFTER           // Use nextafter{,f,l} instead of std::nextafter
 
-> [`CATCH_CONFIG_ANDROID_LOGWRITE`](https://github.com/catchorg/Catch2/issues/1743) and [`CATCH_CONFIG_GLOBAL_NEXTAFTER`](https://github.com/catchorg/Catch2/pull/1739) were introduced in Catch 2.10.0
+> [`CATCH_CONFIG_ANDROID_LOGWRITE`](https://github.com/catchorg/Catch2/issues/1743) and [`CATCH_CONFIG_GLOBAL_NEXTAFTER`](https://github.com/catchorg/Catch2/pull/1739) were introduced in Catch2 2.10.0
 
 Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support.
 
 `CATCH_CONFIG_POSIX_SIGNALS` is on by default, except when Catch is compiled under `Cygwin`, where it is disabled by default (but can be force-enabled by defining `CATCH_CONFIG_POSIX_SIGNALS`).
 
-`CATCH_CONFIG_WINDOWS_CRTDBG` is off by default. If enabled, Windows's CRT is used to check for memory leaks, and displays them after the tests finish running.
+`CATCH_CONFIG_WINDOWS_CRTDBG` is off by default. If enabled, Windows's
+CRT is used to check for memory leaks, and displays them after the tests
+finish running. This option only works when linking against the default
+main, and must be defined for the whole library build.
 
 `CATCH_CONFIG_WCHAR` is on by default, but can be disabled. Currently
 it is only used in support for DJGPP cross-compiler.
@@ -184,11 +178,6 @@ should not lead to false negatives.
 `CATCH_CONFIG_FAST_COMPILE` has to be either defined, or not defined,
 in all translation units that are linked into single test binary.
 
-### `CATCH_CONFIG_DISABLE_MATCHERS`
-When `CATCH_CONFIG_DISABLE_MATCHERS` is defined, all mentions of Catch's Matchers are ifdef-ed away from the translation unit. Doing so will speed up compilation of that TU.
-
-_Note: If you define `CATCH_CONFIG_DISABLE_MATCHERS` in the same file as Catch's main is implemented, your test executable will fail to link if you use Matchers anywhere._
-
 ### `CATCH_CONFIG_DISABLE_STRINGIFICATION`
 This toggle enables a workaround for VS 2017 bug. For details see [known limitations](limitations.md#visual-studio-2017----raw-string-literal-in-assert-fails-to-compile).
 
@@ -199,13 +188,6 @@ This feature is considered experimental and might change at any point.
 
 _Inspired by Doctest's `DOCTEST_CONFIG_DISABLE`_
 
-## Windows header clutter
-
-On Windows Catch includes `windows.h`. To minimize global namespace clutter in the implementation file, it defines `NOMINMAX` and `WIN32_LEAN_AND_MEAN` before including it. You can control this behaviour via two macros:
-
-    CATCH_CONFIG_NO_NOMINMAX            // Stops Catch from using NOMINMAX macro 
-    CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN // Stops Catch from using WIN32_LEAN_AND_MEAN macro
-
 
 ## Enabling stringification
 
@@ -213,18 +195,17 @@ By default, Catch does not stringify some types from the standard library. This
 
     CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER     // Provide StringMaker specialization for std::pair
     CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER    // Provide StringMaker specialization for std::tuple
-    CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER   // Provide StringMaker specialization for std::chrono::duration, std::chrono::timepoint
     CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER  // Provide StringMaker specialization for std::variant, std::monostate (on C++17)
     CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Provide StringMaker specialization for std::optional (on C++17)
     CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS     // Defines all of the above
 
-> `CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1380) in Catch 2.4.1.
+> `CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1380) in Catch2 2.4.1.
 
-> `CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1510) in Catch 2.6.0.
+> `CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1510) in Catch2 2.6.0.
 
 ## Disabling exceptions
 
-> Introduced in Catch 2.4.0.
+> Introduced in Catch2 2.4.0.
 
 By default, Catch2 uses exceptions to signal errors and to abort tests
 when an assertion from the `REQUIRE` family of assertions fails. We also
@@ -260,7 +241,7 @@ namespace Catch {
 
 ## Overriding Catch's debug break (`-b`)
 
-> [Introduced](https://github.com/catchorg/Catch2/pull/1846) in Catch 2.11.2.
+> [Introduced](https://github.com/catchorg/Catch2/pull/1846) in Catch2 2.11.2.
 
 You can override Catch2's break-into-debugger code by defining the
 `CATCH_BREAK_INTO_DEBUGGER()` macro. This can be used if e.g. Catch2 does

+ 123 - 36
lib/Catch2/docs/contributing.md

@@ -21,8 +21,8 @@ to the codebase itself.
 
 ## Using Git(Hub)
 
-Ongoing development happens in the `v2.x` branch for Catch2 v2, and in
-`devel` for the next major version, v3.
+Ongoing development happens in the `devel` branch for Catch2 v3, and in
+`v2.x` for maintenance updates to the v2 versions.
 
 Commits should be small and atomic. A commit is atomic when, after it is
 applied, the codebase, tests and all, still works as expected. Small
@@ -30,8 +30,8 @@ commits are also preferred, as they make later operations with git history,
 whether it is bisecting, reverting, or something else, easier.
 
 _When submitting a pull request please do not include changes to the
-single include. This means do not include them in your git commits!_
-
+amalgamated distribution files. This means do not include them in your
+git commits!_
 
 When addressing review comments in a MR, please do not rebase/squash the
 commits immediately. Doing so makes it harder to review the new changes,
@@ -62,43 +62,60 @@ test using an external check script. Catch2 integration tests are written
 using CTest, either as a direct command invocation + pass/fail regex,
 or by delegating the check to a Python script.
 
-There are also two more kinds of tests, examples and "ExtraTests".
-Examples serve as a compilation test on the single-header distribution,
-and present a small and self-contained snippets of using Catch2 for
-writing tests. ExtraTests then are tests that either take a long time
-to run, or require separate compilation, e.g. because of testing compile
-time configuration options, and take a long time because of that.
+Catch2 is slowly gaining more and more types of tests, currently Catch2
+project also has buildable examples, "ExtraTests", and CMake config tests.
+Examples present a small and self-contained snippets of code that
+use Catch2's facilities for specific purpose. Currently they are assumed
+passing if they compile.
+
+ExtraTests then are expensive tests, that we do not want to run all the
+time. This can be either because they take a long time to run, or because
+they take a long time to compile, e.g. because they test compile time
+configuration and require separate compilation.
 
-Both of these are compiled against the single-header distribution of
-Catch2, and thus might require you to regenerate it manually. This is
-done by calling the `generateSingleHeader.py` script in `scripts`.
+Finally, CMake config tests test that you set Catch2's compile-time
+configuration options through CMake, using CMake options of the same name.
 
-Examples and ExtraTests are not compiled by default. To compile them,
-add `-DCATCH_BUILD_EXAMPLES=ON` and `-DCATCH_BUILD_EXTRA_TESTS=ON` to
-the invocation of CMake configuration step.
+None of these tests are enabled by default. To enable them, add
+`-DCATCH_BUILD_EXAMPLES=ON`, `-DCATCH_BUILD_EXTRA_TESTS=ON`, and
+`-DCATCH_ENABLE_CONFIGURE_TESTS=ON` when configuration the CMake build.
 
 Bringing this all together, the steps below should configure, build,
 and run all tests in the `Debug` compilation.
 
-1. Regenerate the single header distribution
-```
-$ cd Catch2
-$ ./scripts/generateSingleHeader.py
-```
-2. Configure the full test build
-```
-$ cmake -Bdebug-build -H. -DCMAKE_BUILD_TYPE=Debug -DCATCH_BUILD_EXAMPLES=ON -DCATCH_BUILD_EXTRA_TESTS=ON
-```
-3. Run the actual build
-```
-$ cmake --build debug-build
-```
-4. Run the tests using CTest
+<!-- snippet: catch2-build-and-test -->
+<a id='snippet-catch2-build-and-test'></a>
+```sh
+# 1. Regenerate the amalgamated distribution
+./tools/scripts/generateAmalgamatedFiles.py
+
+# 2. Configure the full test build
+cmake -Bdebug-build -H. -DCMAKE_BUILD_TYPE=Debug -DCATCH_DEVELOPMENT_BUILD=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_BUILD_EXTRA_TESTS=ON
+
+# 3. Run the actual build
+cmake --build debug-build
+
+# 4. Run the tests using CTest
+cd debug-build
+ctest -j 4 --output-on-failure -C Debug
 ```
-$ cd debug-build
-$ ctest -j 4 --output-on-failure -C Debug
+<sup><a href='/tools/scripts/buildAndTest.sh#L6-L19' title='File snippet `catch2-build-and-test` was extracted from'>snippet source</a> | <a href='#snippet-catch2-build-and-test' title='Navigate to start of snippet `catch2-build-and-test`'>anchor</a></sup>
+<!-- endSnippet -->
+
+For convenience, the above commands are in the script `tools/scripts/buildAndTest.sh`, and can be run like this:
+
+```bash
+cd Catch2
+./tools/scripts/buildAndTest.sh
 ```
 
+A Windows version of the script is available at `tools\scripts\buildAndTest.cmd`.
+
+If you added new tests, you will likely see `ApprovalTests` failure.
+After you check that the output difference is expected, you should
+run `tools/scripts/approve.py` to confirm them, and include these changes
+in your commit.
+
 
 ## Writing documentation
 
@@ -107,7 +124,8 @@ other people can use it as well. This section collects some technical
 information that you will need for updating Catch2's documentation, and
 possibly some generic advise as well.
 
-### Technicalities 
+
+### Technicalities
 
 First, the technicalities:
 
@@ -118,6 +136,8 @@ should use. It provides you with the top anchor mentioned to link to
 <a id="top"></a>
 # Cool feature
 
+> [Introduced](https://github.com/catchorg/Catch2/pull/123456) in Catch2 X.Y.Z
+
 Text that explains how to use the cool feature.
 
 
@@ -136,9 +156,9 @@ be replaced with the actual version upon release. There are 2 styles
 of placeholders used through the documentation, you should pick one that
 fits your text better (if in doubt, take a look at the existing version
 tags for other features).
-  * `> [Introduced](link-to-issue-or-PR) in Catch X.Y.Z` - this
+  * `> [Introduced](link-to-issue-or-PR) in Catch2 X.Y.Z` - this
   placeholder is usually used after a section heading
-  * `> X (Y and Z) was [introduced](link-to-issue-or-PR) in Catch X.Y.Z`
+  * `> X (Y and Z) was [introduced](link-to-issue-or-PR) in Catch2 X.Y.Z`
   - this placeholder is used when you need to tag a subpart of something,
   e.g. a list
 
@@ -179,6 +199,24 @@ If want to contribute code, this section contains some simple rules
 and tips on things like code formatting, code constructions to avoid,
 and so on.
 
+### C++ standard version
+
+Catch2 currently targets C++14 as the minimum supported C++ version.
+Features from higher language versions should be used only sparingly,
+when the benefits from using them outweigh the maintenance overhead.
+
+Example of good use of polyfilling features is our use of `conjunction`,
+where if available we use `std::conjunction` and otherwise provide our
+own implementation. The reason it is good is that the surface area for
+maintenance is quite small, and `std::conjunction` can directly use
+compiler built-ins, thus providing significant compilation benefits.
+
+Example of bad use of polyfilling features would be to keep around two
+sets of metaprogramming in the stringification implementation, once
+using C++14 compliant TMP and once using C++17's `if constexpr`. While
+the C++17 would provide significant compilation speedups, the maintenance
+cost would be too high.
+
 
 ### Formatting
 
@@ -199,7 +237,7 @@ are problematic and are not always caught by our CI infrastructure.
 #### Naked exceptions and exceptions-related function
 
 If you are throwing an exception, it should be done via `CATCH_ERROR`
-or `CATCH_RUNTIME_ERROR` in `catch_enforce.h`. These macros will handle
+or `CATCH_RUNTIME_ERROR` in `internal/catch_enforce.hpp`. These macros will handle
 the differences between compilation with or without exceptions for you.
 However, some platforms (IAR) also have problems with exceptions-related
 functions, such as `std::current_exceptions`. We do not have IAR in our
@@ -208,6 +246,18 @@ However, if you do, they should be kept behind a
 `CATCH_CONFIG_DISABLE_EXCEPTIONS` macro.
 
 
+#### Avoid `std::move` and `std::forward`
+
+`std::move` and `std::forward` provide nice semantic name for a specific
+`static_cast`. However, being function templates they have surprisingly
+high cost during compilation, and can also have a negative performance
+impact for low-optimization builds.
+
+You should be using `CATCH_MOVE` and `CATCH_FORWARD` macros from
+`internal/catch_move_and_forward.hpp` instead. They expand into the proper
+`static_cast`, and avoid the overhead of `std::move` and `std::forward`.
+
+
 #### Unqualified usage of functions from C's stdlib
 
 If you are using a function from C's stdlib, please include the header
@@ -216,6 +266,43 @@ there is no difference is wrong, QNX and VxWorks won't compile if you
 include the header as `<cfoo>` and call the function unqualified.
 
 
+#### User-Defined Literals (UDL) for Catch2' types
+
+Due to messy standardese and ... not great ... implementation of
+`-Wreserved-identifier` in Clang, avoid declaring UDLs as
+```cpp
+Approx operator "" _a(long double);
+```
+and instead declare them as
+```cpp
+Approx operator ""_a(long double);
+```
+
+Notice that the second version does not have a space between the `""` and
+the literal suffix.
+
+
+
+### New source file template
+
+If you are adding new source file, there is a template you should use.
+Specifically, every source file should start with the licence header:
+```cpp
+
+    //              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
+```
+
+The include guards for header files should follow the pattern `{FILENAME}_INCLUDED`.
+This means that for file `catch_matchers_foo.hpp`, the include guard should
+be `CATCH_MATCHERS_FOO_HPP_INCLUDED`, for `catch_generators_bar.hpp`, the include
+guard should be `CATCH_GENERATORS_BAR_HPP_INCLUDED`, and so on.
+
+
 ## CoC
 
 This project has a [CoC](../CODE_OF_CONDUCT.md). Please adhere to it

+ 7 - 120
lib/Catch2/docs/deprecations.md

@@ -9,69 +9,6 @@ either of these is a breaking change, and thus will not happen until
 at least the next major release.
 
 
-## Deprecations
-
-### `--list-*` return values
-
-The return codes of the `--list-*` family of command line arguments
-will no longer be equal to the number of tests/tags/etc found, instead
-it will be 0 for success and non-zero for failure.
-
-
-### `--list-test-names-only`
-
-`--list-test-names-only` command line argument will be removed.
-
-
-### `ANON_TEST_CASE`
-
-`ANON_TEST_CASE` is scheduled for removal, as it can be fully replaced
-by a `TEST_CASE` with no arguments.
-
-
-### Secondary description amongst tags
-
-Currently, the tags part of `TEST_CASE` (and others) macro can also
-contain text that is not part of tags. This text is then separated into
-a "description" of the test case, but the description is then never used
-apart from writing it out for `--list-tests -v high`.
-
-Because it isn't actually used nor documented, and brings complications
-to Catch2's internals, description support will be removed.
-
-### SourceLineInfo::empty()
-
-There should be no reason to ever have an empty `SourceLineInfo`, so the
-method will be removed.
-
-
-### Composing lvalues of already composed matchers
-
-Because a significant bug in this use case has persisted for 2+ years
-without a bug report, and to simplify the implementation, code that
-composes lvalues of composed matchers will not compile. That is,
-this code will no longer work:
-
-```cpp
-            auto m1 = Contains("string");
-            auto m2 = Contains("random");
-            auto composed1 = m1 || m2;
-            auto m3 = Contains("different");
-            auto composed2 = composed1 || m3;
-            REQUIRE_THAT(foo(), !composed1);
-            REQUIRE_THAT(foo(), composed2);
-```
-
-Instead you will have to write this:
-
-```cpp
-            auto m1 = Contains("string");
-            auto m2 = Contains("random");
-            auto m3 = Contains("different");
-            REQUIRE_THAT(foo(), !(m1 || m2));
-            REQUIRE_THAT(foo(), m1 || m2 || m3);
-```
-
 ### `ParseAndAddCatchTests.cmake`
 
 The CMake/CTest integration using `ParseAndAddCatchTests.cmake` is deprecated,
@@ -80,64 +17,14 @@ as it can be replaced by `Catch.cmake` that provides the function
 command line interface instead of parsing C++ code with regular expressions.
 
 
-## Planned changes
-
-
-### Reporter verbosities
-
-The current implementation of verbosities, where the reporter is checked
-up-front whether it supports the requested verbosity, is fundamentally
-misguided and will be changed. The new implementation will no longer check
-whether the specified reporter supports the requested verbosity, instead
-it will be up to the reporters to deal with verbosities as they see fit
-(with an expectation that unsupported verbosities will be, at most,
-warnings, but not errors).
-
-
-### Output format of `--list-*` command line parameters
-
-The various list operations will be piped through reporters. This means
-that e.g. XML reporter will write the output as machine-parseable XML,
-while the Console reporter will keep the current, human-oriented output.
-
-
-### `CHECKED_IF` and `CHECKED_ELSE`
-
-To make the `CHECKED_IF` and `CHECKED_ELSE` macros more useful, they will
-be marked as "OK to fail" (`Catch::ResultDisposition::SuppressFail` flag
-will be added), which means that their failure will not fail the test,
-making the `else` actually useful.
-
-
-### Change semantics of `[.]` and tag exclusion
-
-Currently, given these 2 tests
-```cpp
-TEST_CASE("A", "[.][foo]") {}
-TEST_CASE("B", "[.][bar]") {}
-```
-specifying `[foo]` as the testspec will run test "A" and specifying
-`~[foo]` will run test "B", even though it is hidden. Also, specifying
-`~[baz]` will run both tests. This behaviour is often surprising and will
-be changed so that hidden tests are included in a run only if they
-positively match a testspec.
-
-
-### Console Colour API
-
-The API for Catch2's console colour will be changed to take an extra
-argument, the stream to which the colour code should be applied.
-
-
-### Type erasure in the `PredicateMatcher`
-
-Currently, the `PredicateMatcher` uses `std::function` for type erasure,
-so that type of the matcher is always `PredicateMatcher<T>`, regardless
-of the type of the predicate. Because of the high compilation overhead
-of `std::function`, and the fact that the type erasure is used only rarely,
-`PredicateMatcher` will no longer be type erased in the future. Instead,
-the predicate type will be made part of the PredicateMatcher's type.
+### `CATCH_CONFIG_BAZEL_SUPPORT`
 
+Catch2 supports writing the Bazel JUnit XML output file when it is aware
+that is within a bazel testing environment. Originally there was no way
+to accurately probe the environment for this information so the flag
+`CATCH_CONFIG_BAZEL_SUPPORT` was added. This now deprecated. Bazel has now had a change
+where it will export `BAZEL_TEST=1` for purposes like the above. Catch2
+will now instead inspect the environment instead of relying on build configuration.
 
 ---
 

+ 31 - 62
lib/Catch2/docs/event-listeners.md

@@ -1,74 +1,43 @@
 <a id="top"></a>
 # Event Listeners
 
-A `Listener` is a class you can register with Catch that will then be passed events,
-such as a test case starting or ending, as they happen during a test run.
-`Listeners` are actually types of `Reporters`, with a few small differences:
- 
-1. Once registered in code they are automatically used - you don't need to specify them on the command line
-2. They are called in addition to (just before) any reporters, and you can register multiple listeners.
-3. They derive from `Catch::TestEventListenerBase`, which has default stubs for all the events,
-so you are not forced to implement events you're not interested in.
-4. You register a listener with `CATCH_REGISTER_LISTENER`
-
-
-## Implementing a Listener
-Simply derive a class from `Catch::TestEventListenerBase` and implement the methods you are interested in, either in
-the main source file (i.e. the one that defines `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`), or in a
-file that defines `CATCH_CONFIG_EXTERNAL_INTERFACES`.
-
-Then register it using `CATCH_REGISTER_LISTENER`.
-
-For example ([complete source code](../examples/210-Evt-EventListeners.cpp)):
-
-```c++
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
-
-struct MyListener : Catch::TestEventListenerBase {
-
-    using TestEventListenerBase::TestEventListenerBase; // inherit constructor
-
-    void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
-        // Perform some setup before a test case is run
-    }
-    
-    void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
-        // Tear-down after a test case is run
+An event listener is a bit like a reporter, in that it responds to various
+reporter events in Catch2, but it is not expected to write any output.
+Instead, an event listener performs actions within the test process, such
+as performing global initialization (e.g. of a C library), or cleaning out
+in-memory logs if they are not needed (the test case passed).
+
+Unlike reporters, each registered event listener is always active. Event
+listeners are always notified before reporter(s).
+
+To write your own event listener, you should derive from `Catch::TestEventListenerBase`,
+as it provides empty stubs for all reporter events, allowing you to
+only override events you care for. Afterwards you have to register it
+with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows
+about it and instantiates it before running tests.
+
+Example event listener:
+```cpp
+#include <catch2/reporters/catch_reporter_event_listener.hpp>
+#include <catch2/reporters/catch_reporter_registrars.hpp>
+
+class testRunListener : public Catch::EventListenerBase {
+public:
+    using Catch::EventListenerBase::EventListenerBase;
+
+    void testRunStarting(Catch::TestRunInfo const&) override {
+        lib_foo_init();
     }
 };
-CATCH_REGISTER_LISTENER( MyListener )
-```
-
-_Note that you should not use any assertion macros within a Listener!_ 
-
-## Events that can be hooked
-
-The following are the methods that can be overridden in the Listener:
 
-```c++
-// The whole test run, starting and ending
-virtual void testRunStarting( TestRunInfo const& testRunInfo );
-virtual void testRunEnded( TestRunStats const& testRunStats );
-
-// Test cases starting and ending
-virtual void testCaseStarting( TestCaseInfo const& testInfo );
-virtual void testCaseEnded( TestCaseStats const& testCaseStats );
-
-// Sections starting and ending
-virtual void sectionStarting( SectionInfo const& sectionInfo );
-virtual void sectionEnded( SectionStats const& sectionStats );
+CATCH_REGISTER_LISTENER(testRunListener)
+```
 
-// Assertions before/ after
-virtual void assertionStarting( AssertionInfo const& assertionInfo );
-virtual bool assertionEnded( AssertionStats const& assertionStats );
+_Note that you should not use any assertion macros within a Listener!_
 
-// A test is being skipped (because it is "hidden")
-virtual void skipTest( TestCaseInfo const& testInfo );
-```
+[You can find the list of events that the listeners can react to on its
+own page](reporter-events.md#top).
 
-More information about the events (e.g. name of the test case) is contained in the structs passed as arguments -
-just look in the source code to see what fields are available. 
 
 ---
 

+ 74 - 0
lib/Catch2/docs/faq.md

@@ -0,0 +1,74 @@
+<a id="top"></a>
+# Frequently Asked Questions (FAQ)
+
+**Contents**<br>
+[How do I run global setup/teardown only if tests will be run?](#how-do-i-run-global-setupteardown-only-if-tests-will-be-run)<br>
+[How do I clean up global state between running different tests?](#how-do-i-clean-up-global-state-between-running-different-tests)<br>
+[Why cannot I derive from the built-in reporters?](#why-cannot-i-derive-from-the-built-in-reporters)<br>
+[What is Catch2's ABI stability policy?](#what-is-catch2s-abi-stability-policy)<br>
+[What is Catch2's API stability policy?](#what-is-catch2s-api-stability-policy)<br>
+[Does Catch2 support running tests in parallel?](#does-catch2-support-running-tests-in-parallel)<br>
+[Can I compile Catch2 into a dynamic library?](#can-i-compile-catch2-into-a-dynamic-library)<br>
+
+## How do I run global setup/teardown only if tests will be run?
+
+Write a custom [event listener](event-listeners.md#top) and place the
+global setup/teardown code into the `testRun*` events.
+
+
+## How do I clean up global state between running different tests?
+
+Write a custom [event listener](event-listeners.md#top) and place the
+cleanup code into either `testCase*` or `testCasePartial*` events,
+depending on how often the cleanup needs to happen.
+
+
+## Why cannot I derive from the built-in reporters?
+
+They are not made to be overridden, in that we do not attempt to maintain
+a consistent internal state if a member function is overriden, and by
+forbidding users from using them as a base class, we can refactor them
+as needed later.
+
+
+## What is Catch2's ABI stability policy?
+
+Catch2 provides no ABI stability guarantees whatsoever. Catch2 provides
+rich C++ interface, and trying to freeze its ABI would take a lot of
+pointless work.
+
+Catch2 is not designed to be distributed as dynamic library, and you
+should really be able to compile everything with the same compiler binary.
+
+
+## What is Catch2's API stability policy?
+
+Catch2 follows [semver](https://semver.org/) to the best of our ability.
+This means that we will not knowingly make backwards-incompatible changes
+without incrementing the major version number.
+
+
+## Does Catch2 support running tests in parallel?
+
+Not natively, no. We see running tests in parallel as the job of an
+external test runner, that can also run them in separate processes,
+support test execution timeouts and so on.
+
+However, Catch2 provides some tools that make the job of external test
+runners easier. [See the relevant section in our page on best
+practices](usage-tips.md#parallel-tests).
+
+
+## Can I compile Catch2 into a dynamic library?
+
+Yes, Catch2 supports the [standard CMake `BUILD_SHARED_LIBS`
+option](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html).
+However, the dynamic library support is provided as-is. Catch2 does not
+provide API export annotations, and so you can only use it as a dynamic
+library on platforms that default to public visibility, or with tooling
+support to force export Catch2's API.
+
+
+---
+
+[Home](Readme.md#top)

+ 9 - 9
lib/Catch2/docs/generators.md

@@ -1,7 +1,7 @@
 <a id="top"></a>
 # Data Generators
 
-> Introduced in Catch 2.6.0.
+> Introduced in Catch2 2.6.0.
 
 Data generators (also known as _data driven/parametrized test cases_)
 let you reuse the same set of assertions across different input values.
@@ -92,7 +92,7 @@ TEST_CASE("Complex mix of sections and generates") {
 }
 ```
 
-> The ability to place `GENERATE` between two `SECTION`s was [introduced](https://github.com/catchorg/Catch2/issues/1938) in Catch 2.13.0.
+> The ability to place `GENERATE` between two `SECTION`s was [introduced](https://github.com/catchorg/Catch2/issues/1938) in Catch2 2.13.0.
 
 ## Provided generators
 
@@ -114,12 +114,12 @@ a test case,
 * 4 specific purpose generators
   * `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
   * `RandomFloatGenerator<Float>` -- generates random Floats from range
-  * `RangeGenerator<T>` -- generates all values inside an arithmetic range
+  * `RangeGenerator<T>(first, last)` -- generates all values inside a `[first, last)` arithmetic range
   * `IteratorGenerator<T>` -- copies and returns values from an iterator range
 
-> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch 2.7.0.
+> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch2 2.7.0.
 
-> `IteratorGenerator<T>` was introduced in Catch 2.10.0.
+> `IteratorGenerator<T>` was introduced in Catch2 2.10.0.
 
 The generators also have associated helper functions that infer their
 type, making their usage much nicer. These are
@@ -139,11 +139,11 @@ type, making their usage much nicer. These are
 * `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator<T>`
 * `from_range(Container const&)` for `IteratorGenerator<T>`
 
-> `chunk()`, `random()` and both `range()` functions were introduced in Catch 2.7.0.
+> `chunk()`, `random()` and both `range()` functions were introduced in Catch2 2.7.0.
 
-> `from_range` has been introduced in Catch 2.10.0
+> `from_range` has been introduced in Catch2 2.10.0
 
-> `range()` for floating point numbers has been introduced in Catch 2.11.0
+> `range()` for floating point numbers has been introduced in Catch2 2.11.0
 
 And can be used as shown in the example below to create a generator
 that returns 100 odd random number:
@@ -176,7 +176,7 @@ scope and thus capturing references is dangerous. If you need to use
 variables inside the generator expression, make sure you thought through
 the lifetime implications and use `GENERATE_COPY` or `GENERATE_REF`.**
 
-> `GENERATE_COPY` and `GENERATE_REF` were introduced in Catch 2.7.1.
+> `GENERATE_COPY` and `GENERATE_REF` were introduced in Catch2 2.7.1.
 
 You can also override the inferred type by using `as<type>` as the first
 argument to the macro. This can be useful when dealing with string literals,

+ 31 - 33
lib/Catch2/docs/limitations.md

@@ -94,39 +94,57 @@ to remove this limitation in the future.
 ### Process isolation in a test
 Catch does not support running tests in isolated (forked) processes. While this might in the future, the fact that Windows does not support forking and only allows full-on process creation and the desire to keep code as similar as possible across platforms, mean that this is likely to take significant development time, that is not currently available.
 
+
 ### Running multiple tests in parallel
-Catch's test execution is strictly serial. If you find yourself with a test suite that takes too long to run and you want to make it parallel, there are 2 feasible solutions
- * You can split your tests into multiple binaries and then run these binaries in parallel.
- * You can have Catch list contained test cases and then run the same test binary multiple times in parallel, passing each instance list of test cases it should run.
 
-Both of these solutions have their problems, but should let you wring parallelism out of your test suite.
+Catch2 keeps test execution in one process strictly serial, and there
+are no plans to change this. If you find yourself with a test suite
+that takes too long to run and you want to make it parallel, you have
+to run multiple processes side by side.
+
+There are 2 basic ways to do that,
+* you can split your tests into multiple binaries, and run those binaries
+  in parallel
+* you can run the same test binary multiple times, but run a different
+  subset of the tests in each process
+
+There are multiple ways to achieve the latter, the easiest way is to use
+[test sharding](command-line.md#test-sharding).
+
 
 ## 3rd party bugs
+
 This section outlines known bugs in 3rd party components (this means compilers, standard libraries, standard runtimes).
 
+
 ### Visual Studio 2017 -- raw string literal in assert fails to compile
-There is a known bug in Visual Studio 2017 (VC 15), that causes compilation error when preprocessor attempts to stringize a raw string literal (`#` preprocessor is applied to it). This snippet is sufficient to trigger the compilation error:
+
+There is a known bug in Visual Studio 2017 (VC 15), that causes compilation
+error when preprocessor attempts to stringize a raw string literal
+(`#` preprocessor directive is applied to it). This snippet is sufficient
+to trigger the compilation error:
+
 ```cpp
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
+#include <catch2/catch_test_macros.hpp>
 
 TEST_CASE("test") {
     CHECK(std::string(R"("\)") == "\"\\");
 }
 ```
 
-Catch provides a workaround, it is possible to disable stringification of original expressions by defining `CATCH_CONFIG_DISABLE_STRINGIFICATION`:
+Catch2 provides a workaround, by letting the user disable stringification
+of the original expression by defining `CATCH_CONFIG_DISABLE_STRINGIFICATION`,
+like so:
 ```cpp
-#define CATCH_CONFIG_FAST_COMPILE
 #define CATCH_CONFIG_DISABLE_STRINGIFICATION
-#include "catch.hpp"
+#include <catch2/catch_test_macros.hpp>
 
 TEST_CASE("test") {
     CHECK(std::string(R"("\)") == "\"\\");
 }
 ```
 
-_Do note that this changes the output somewhat_
+_Do note that this changes the output:_
 ```
 catchwork\test1.cpp(6):
 PASSED:
@@ -135,26 +153,11 @@ with expansion:
   ""\" == ""\"
 ```
 
-### Visual Studio 2015 -- Alignment compilation error (C2718)
-
-VS 2015 has a known bug, where `declval<T>` can cause compilation error
-if `T` has alignment requirements that it cannot meet.
-
-
-A workaround is to explicitly specialize `Catch::is_range` for given
-type (this avoids code path that uses `declval<T>` in a SFINAE context).
-
-
-### Visual Studio 2015 -- Wrong line number reported in debug mode
-VS 2015 has a known bug where `__LINE__` macro can be improperly expanded under certain circumstances, while compiling multi-file project in Debug mode.
-
-A workaround is to compile the binary in Release mode.
 
 ### Clang/G++ -- skipping leaf sections after an exception
 Some versions of `libc++` and `libstdc++` (or their runtimes) have a bug with `std::uncaught_exception()` getting stuck returning `true` after rethrow, even if there are no active exceptions. One such case is this snippet, which skipped the sections "a" and "b", when compiled against `libcxxrt` from master
 ```cpp
-#define CATCH_CONFIG_MAIN
-#include <catch.hpp>
+#include <catch2/catch_test_macros.hpp>
 
 TEST_CASE("a") {
     CHECK_THROWS(throw 3);
@@ -168,12 +171,7 @@ TEST_CASE("b") {
 }
 ```
 
-If you are seeing a problem like this, i.e. a weird test paths that trigger only under Clang with `libc++`, or only under very specific version of `libstdc++`, it is very likely you are seeing this. The only known workaround is to use a fixed version of your standard library.
-
-### Clang/G++ -- `Matches` string matcher always returns false
-This is a bug in `libstdc++-4.8`, where all matching methods from `<regex>` return false. Since `Matches` uses `<regex>` internally, if the underlying implementation does not work, it doesn't work either.
-
-Workaround: Use newer version of `libstdc++`.
+If you are seeing a problem like this, i.e. weird test paths that trigger only under Clang with `libc++`, or only under very specific version of `libstdc++`, it is very likely you are seeing this. The only known workaround is to use a fixed version of your standard library.
 
 
 ### libstdc++, `_GLIBCXX_DEBUG` macro and random ordering of tests

+ 0 - 3
lib/Catch2/docs/list-of-examples.md

@@ -3,15 +3,12 @@
 
 ## Already available
 
-- Catch main: [Catch-provided main](../examples/000-CatchMain.cpp)
 - Test Case: [Single-file](../examples/010-TestCase.cpp)
 - Test Case: [Multiple-file 1](../examples/020-TestCase-1.cpp), [2](../examples/020-TestCase-2.cpp)
 - Assertion: [REQUIRE, CHECK](../examples/030-Asn-Require-Check.cpp)
 - Fixture: [Sections](../examples/100-Fix-Section.cpp)
 - Fixture: [Class-based fixtures](../examples/110-Fix-ClassFixture.cpp)
 - BDD: [SCENARIO, GIVEN, WHEN, THEN](../examples/120-Bdd-ScenarioGivenWhenThen.cpp)
-- Report: [Catch-provided main](../examples/200-Rpt-CatchMain.cpp)
-- Report: [TeamCity reporter](../examples/207-Rpt-TeamCityReporter.cpp)
 - Listener: [Listeners](../examples/210-Evt-EventListeners.cpp)
 - Configuration: [Provide your own output streams](../examples/231-Cfg-OutputStreams.cpp)
 - Generators: [Create your own generator](../examples/300-Gen-OwnGenerator.cpp)

+ 2 - 2
lib/Catch2/docs/logging.md

@@ -30,7 +30,7 @@ When the last `CHECK` fails in the "Bar" test case, then only one message will b
 
 ## Logging without local scope
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
 
 `UNSCOPED_INFO` is similar to `INFO` with two key differences:
 
@@ -106,7 +106,7 @@ This semicolon will be removed with next major version. It is highly advised to
 
 **UNSCOPED_INFO(** _message expression_ **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
 
 Similar to `INFO`, but messages are not limited to their own scope: They are removed from the buffer after each assertion, section or test case, whichever comes first.
 

+ 309 - 122
lib/Catch2/docs/matchers.md

@@ -1,77 +1,123 @@
 <a id="top"></a>
 # Matchers
 
-Matchers are an alternative way to do assertions which are easily extensible and composable.
-This makes them well suited to use with more complex types (such as collections) or your own custom types.
-Matchers were first popularised by the [Hamcrest](https://en.wikipedia.org/wiki/Hamcrest) family of frameworks.
+**Contents**<br>
+[Using Matchers](#using-matchers)<br>
+[Built-in matchers](#built-in-matchers)<br>
+[Writing custom matchers (old style)](#writing-custom-matchers-old-style)<br>
+[Writing custom matchers (new style)](#writing-custom-matchers-new-style)<br>
 
-## In use
+Matchers, as popularized by the [Hamcrest](https://en.wikipedia.org/wiki/Hamcrest)
+framework are an alternative way to write assertions, useful for tests
+where you work with complex types or need to assert more complex
+properties. Matchers are easily composable and users can write their
+own and combine them with the Catch2-provided matchers seamlessly.
 
-Matchers are introduced with the `REQUIRE_THAT` or `CHECK_THAT` macros, which take two arguments.
-The first argument is the thing (object or value) under test. The second part is a match _expression_,
-which consists of either a single matcher or one or more matchers combined using `&&`, `||` or `!` operators.
 
-For example, to assert that a string ends with a certain substring:
+## Using Matchers
 
- ```c++
-using Catch::Matchers::EndsWith; // or Catch::EndsWith
-std::string str = getStringFromSomewhere();
-REQUIRE_THAT( str, EndsWith( "as a service" ) );
-```
+Matchers are most commonly used in tandem with the `REQUIRE_THAT` or
+`CHECK_THAT` macros. The `REQUIRE_THAT` macro takes two arguments,
+the first one is the input (object/value) to test, the second argument
+is the matcher itself.
 
-The matcher objects can take multiple arguments, allowing more fine tuning.
-The built-in string matchers, for example, take a second argument specifying whether the comparison is
-case sensitive or not:
+For example, to assert that a string ends with the "as a service"
+substring, you can write the following assertion
 
-```c++
-REQUIRE_THAT( str, EndsWith( "as a service", Catch::CaseSensitive::No ) );
- ```
+```cpp
+using Catch::Matchers::EndsWith;
 
-And matchers can be combined:
+REQUIRE_THAT( getSomeString(), EndsWith("as a service") );
+```
 
-```c++
-REQUIRE_THAT( str,
-    EndsWith( "as a service" ) ||
-    (StartsWith( "Big data" ) && !Contains( "web scale" ) ) );
+Individual matchers can also be combined using the C++ logical
+operators, that is `&&`, `||`, and `!`, like so:
+
+```cpp
+using Catch::Matchers::EndsWith;
+using Catch::Matchers::ContainsSubstring;
+
+REQUIRE_THAT( getSomeString(),
+              EndsWith("as a service") && ContainsSubstring("web scale"));
 ```
 
-_The combining operators do not take ownership of the matcher objects.
-This means that if you store the combined object, you have to ensure that
-the matcher objects outlive its last use. What this means is that code
-like this leads to a use-after-free and (hopefully) a crash:_
+The example above asserts that the string returned from `getSomeString`
+_both_ ends with the suffix "as a service" _and_ contains the string
+"web scale" somewhere.
+
+
+Both of the string matchers used in the examples above live in the
+`catch_matchers_string.hpp` header, so to compile the code above also
+requires `#include <catch2/matchers/catch_matchers_string.hpp>`.
+
+**IMPORTANT**: The combining operators do not take ownership of the
+matcher objects being combined. This means that if you store combined
+matcher object, you have to ensure that the matchers being combined
+outlive its last use. What this means is that the following code leads
+to a use-after-free (UAF):
 
 ```cpp
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_string.hpp>
+
 TEST_CASE("Bugs, bugs, bugs", "[Bug]"){
     std::string str = "Bugs as a service";
 
-    auto match_expression = Catch::EndsWith( "as a service" ) ||
-        (Catch::StartsWith( "Big data" ) && !Catch::Contains( "web scale" ) );
+    auto match_expression = Catch::Matchers::EndsWith( "as a service" ) ||
+        (Catch::Matchers::StartsWith( "Big data" ) && !Catch::Matchers::ContainsSubstring( "web scale" ) );
     REQUIRE_THAT(str, match_expression);
 }
 ```
 
 
-## Built in matchers
-Catch2 provides some matchers by default. They can be found in the
-`Catch::Matchers::foo` namespace and are imported into the `Catch`
-namespace as well.
+## Built-in matchers
+
+Every matcher provided by Catch2 is split into 2 parts, a factory
+function that lives in the `Catch::Matchers` namespace, and the actual
+matcher type that is in some deeper namespace and should not be used by
+the user. In the examples above, we used `Catch::Matchers::Contains`.
+This is the factory function for the
+`Catch::Matchers::StdString::ContainsMatcher` type that does the actual
+matching.
+
+Out of the box, Catch2 provides the following matchers:
+
+
+### `std::string` matchers
+
+Catch2 provides 5 different matchers that work with `std::string`,
+* `StartsWith(std::string str, CaseSensitive)`,
+* `EndsWith(std::string str, CaseSensitive)`,
+* `ContainsSubstring(std::string str, CaseSensitive)`,
+* `Equals(std::string str, CaseSensitive)`, and
+* `Matches(std::string str, CaseSensitive)`.
 
-There are two parts to each of the built-in matchers, the matcher
-type itself and a helper function that provides template argument
-deduction when creating templated matchers. As an example, the matcher
-for checking that two instances of `std::vector` are identical is
-`EqualsMatcher<T>`, but the user is expected to use the `Equals`
-helper function instead.
+The first three should be fairly self-explanatory, they succeed if
+the argument starts with `str`, ends with `str`, or contains `str`
+somewhere inside it.
 
+The `Equals` matcher matches a string if (and only if) the argument
+string is equal to `str`.
 
-### String matchers
-The string matchers are `StartsWith`, `EndsWith`, `Contains`, `Equals` and `Matches`. The first four match a literal (sub)string against a result, while `Matches` takes and matches an ECMAScript regex. Do note that `Matches` matches the string as a whole, meaning that "abc" will not match against "abcd", but "abc.*" will.
+Finally, the `Matches` matcher performs an ECMAScript regex match using
+`str` against the argument string. It is important to know that
+the match is performed against the string as a whole, meaning that
+the regex `"abc"` will not match input string `"abcd"`. To match
+`"abcd"`, you need to use e.g. `"abc.*"` as your regex.
 
-Each of the provided `std::string` matchers also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive).
+The second argument sets whether the matching should be case-sensitive
+or not. By default, it is case-sensitive.
+
+> `std::string` matchers live in `catch2/matchers/catch_matchers_string.hpp`
 
 
 ### Vector matchers
-Catch2 currently provides 5 built-in matchers that work on `std::vector`.
+
+_Vector matchers have been deprecated in favour of the generic
+range matchers with the same functionality._
+
+Catch2 provides 5 built-in matchers that work on `std::vector`.
+
 These are
 
  * `Contains` which checks whether a specified vector is present in the result
@@ -79,42 +125,48 @@ These are
  * `Equals` which checks whether the result is exactly equal (order matters) to a specific vector
  * `UnorderedEquals` which checks whether the result is equal to a specific vector under a permutation
  * `Approx` which checks whether the result is "approx-equal" (order matters, but comparison is done via `Approx`) to a specific vector
-> Approx matcher was [introduced](https://github.com/catchorg/Catch2/issues/1499) in Catch 2.7.2.
+> Approx matcher was [introduced](https://github.com/catchorg/Catch2/issues/1499) in Catch2 2.7.2.
+
+An example usage:
+```cpp
+    std::vector<int> some_vec{ 1, 2, 3 };
+    REQUIRE_THAT(some_vec, Catch::Matchers::UnorderedEquals(std::vector<int>{ 3, 2, 1 }));
+```
+
+This assertions will pass, because the elements given to the matchers
+are a permutation of the ones in `some_vec`.
+
+> vector matchers live in `catch2/matchers/catch_matchers_vector.hpp`
 
 
 ### Floating point matchers
-Catch2 provides 3 matchers for working with floating point numbers. These
-are `WithinAbsMatcher`, `WithinUlpsMatcher` and `WithinRelMatcher`.
-
-The `WithinAbsMatcher` matcher accepts floating point numbers that are
-within a certain distance of target. It should be constructed with the
-`WithinAbs(double target, double margin)` helper.
-
-The `WithinUlpsMatcher` matcher accepts floating point numbers that are
-within a certain number of [ULPs](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
-of the target. Because ULP comparisons need to be done differently for
-`float`s and for `double`s, there are two overloads of the helpers for
-this matcher, `WithinULP(float target, int64_t ULPs)`, and
-`WithinULP(double target, int64_t ULPs)`.
-
-The `WithinRelMatcher` matcher accepts floating point numbers that are
-_approximately equal_ with the target number with some specific tolerance.
-In other words, it checks that `|lhs - rhs| <= epsilon * max(|lhs|, |rhs|)`,
-with special casing for `INFINITY` and `NaN`. There are _4_ overloads of
-the helpers for this matcher, `WithinRel(double target, double margin)`,
-`WithinRel(float target, float margin)`, `WithinRel(double target)`, and
-`WithinRel(float target)`. The latter two provide a default epsilon of
-machine epsilon * 100.
-
-> `WithinRel` matcher was introduced in Catch 2.10.0
-
-### Generic matchers
-Catch also aims to provide a set of generic matchers. Currently this set
-contains only a matcher that takes arbitrary callable predicate and applies
-it onto the provided object.
-
-Because of type inference limitations, the argument type of the predicate
-has to be provided explicitly. Example:
+
+Catch2 provides 3 matchers that target floating point numbers. These
+are:
+
+* `WithinAbs(double target, double margin)`,
+* `WithinULP(FloatingPoint target, uint64_t maxUlpDiff)`, and
+* `WithinRel(FloatingPoint target, FloatingPoint eps)`.
+
+> `WithinRel` matcher was introduced in Catch2 2.10.0
+
+For more details, read [the docs on comparing floating point
+numbers](comparing-floating-point-numbers.md#floating-point-matchers).
+
+
+### Miscellaneous matchers
+
+Catch2 also provides some matchers and matcher utilities that do not
+quite fit into other categories.
+
+The first one of them is the `Predicate(Callable pred, std::string description)`
+matcher. It creates a matcher object that calls `pred` for the provided
+argument. The `description` argument allows users to set what the
+resulting matcher should self-describe as if required.
+
+Do note that you will need to explicitly specify the type of the
+argument, like in this example:
+
 ```cpp
 REQUIRE_THAT("Hello olleH",
              Predicate<std::string>(
@@ -123,84 +175,219 @@ REQUIRE_THAT("Hello olleH",
 );
 ```
 
-The second argument is an optional description of the predicate, and is
-used only during reporting of the result.
+> the predicate matcher lives in `catch2/matchers/catch_matchers_predicate.hpp`
+
+
+The other miscellaneous matcher utility is exception matching.
 
 
-### Exception matchers
-Catch2 also provides an exception matcher that can be used to verify
-that an exception's message exactly matches desired string. The matcher
-is `ExceptionMessageMatcher`, and we also provide a helper function
-`Message`.
+#### Matching exceptions
 
-The matched exception must publicly derive from `std::exception` and
-the message matching is done _exactly_, including case.
+Catch2 provides a utility macro for asserting that an expression
+throws exception of specific type, and that the exception has desired
+properties. The macro is `REQUIRE_THROWS_MATCHES(expr, ExceptionType, Matcher)`.
 
-> `ExceptionMessageMatcher` was introduced in Catch 2.10.0
+> `REQUIRE_THROWS_MATCHES` macro lives in `catch2/matchers/catch_matchers.hpp`
+
+
+Catch2 currently provides only one matcher for exceptions,
+`Message(std::string message)`. `Message` checks that the exception's
+message, as returned from `what` is exactly equal to `message`.
 
 Example use:
 ```cpp
 REQUIRE_THROWS_MATCHES(throwsDerivedException(),  DerivedException,  Message("DerivedException::what"));
 ```
 
-## Custom matchers
-It's easy to provide your own matchers to extend Catch or just to work with your own types.
+Note that `DerivedException` in the example above has to derive from
+`std::exception` for the example to work.
+
+> the exception message matcher lives in `catch2/matchers/catch_matchers_exception.hpp`
+
+
+### Generic range Matchers
 
-You need to provide two things:
-1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the type being tested.
-The constructor takes and stores any arguments needed (e.g. something to compare against) and you must
-override two methods: `match()` and `describe()`.
-2. A simple builder function. This is what is actually called from the test code and allows overloading.
+> Generic range matchers were introduced in Catch2 3.0.1
 
-Here's an example for asserting that an integer falls within a given range
-(note that it is all inline for the sake of keeping the example short):
+Catch2 also provides some matchers that use the new style matchers
+definitions to handle generic range-like types. These are:
+
+* `IsEmpty()`
+* `SizeIs(size_t target_size)`
+* `SizeIs(Matcher size_matcher)`
+* `Contains(T&& target_element, Comparator = std::equal_to<>{})`
+* `Contains(Matcher element_matcher)`
+* `AllMatch(Matcher element_matcher)`
+* `NoneMatch(Matcher element_matcher)`
+* `AnyMatch(Matcher element_matcher)`
+* `AllTrue()`
+* `NoneTrue()`
+* `AnyTrue()`
+
+`IsEmpty` should be self-explanatory. It successfully matches objects
+that are empty according to either `std::empty`, or ADL-found `empty`
+free function.
+
+`SizeIs` checks range's size. If constructed with `size_t` arg, the
+matchers accepts ranges whose size is exactly equal to the arg. If
+constructed from another matcher, then the resulting matcher accepts
+ranges whose size is accepted by the provided matcher.
+
+`Contains` accepts ranges that contain specific element. There are
+again two variants, one that accepts the desired element directly,
+in which case a range is accepted if any of its elements is equal to
+the target element. The other variant is constructed from a matcher,
+in which case a range is accepted if any of its elements is accepted
+by the provided matcher.
+
+`AllMatch`, `NoneMatch`, and `AnyMatch` match ranges for which either
+all, none, or any of the contained elements matches the given matcher,
+respectively.
+
+`AllTrue`, `NoneTrue`, and `AnyTrue` match ranges for which either
+all, none, or any of the contained elements are `true`, respectively.
+It works for ranges of `bool`s and ranges of elements (explicitly)
+convertible to `bool`.
+
+## Writing custom matchers (old style)
+
+The old style of writing matchers has been introduced back in Catch
+Classic. To create an old-style matcher, you have to create your own
+type that derives from `Catch::Matchers::MatcherBase<ArgT>`, where
+`ArgT` is the type your matcher works for. Your type has to override
+two methods, `bool match(ArgT const&) const`,
+and `std::string describe() const`.
+
+As the name suggests, `match` decides whether the provided argument
+is matched (accepted) by the matcher. `describe` then provides a
+human-oriented description of what the matcher does.
+
+We also recommend that you create factory function, just like Catch2
+does, but that is mostly useful for template argument deduction for
+templated matchers (assuming you do not have CTAD available).
+
+To combine these into an example, let's say that you want to write
+a matcher that decides whether the provided argument is a number
+within certain range. We will call it `IsBetweenMatcher<T>`:
 
 ```c++
-// The matcher class
-class IntRange : public Catch::MatcherBase<int> {
-    int m_begin, m_end;
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers.hpp>
+// ...
+
+
+template <typename T>
+class IsBetweenMatcher : public Catch::Matchers::MatcherBase<T> {
+    T m_begin, m_end;
 public:
-    IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {}
+    IsBetweenMatcher(T begin, T end) : m_begin(begin), m_end(end) {}
 
-    // Performs the test for this matcher
-    bool match( int const& i ) const override {
-        return i >= m_begin && i <= m_end;
+    bool match(T const& in) const override {
+        return in >= m_begin && in <= m_end;
     }
 
-    // Produces a string describing what this matcher does. It should
-    // include any provided data (the begin/ end in this case) and
-    // be written as if it were stating a fact (in the output it will be
-    // preceded by the value under test).
-    virtual std::string describe() const override {
+    std::string describe() const override {
         std::ostringstream ss;
         ss << "is between " << m_begin << " and " << m_end;
         return ss.str();
     }
 };
 
-// The builder function
-inline IntRange IsBetween( int begin, int end ) {
-    return IntRange( begin, end );
+template <typename T>
+IsBetweenMatcher<T> IsBetween(T begin, T end) {
+    return { begin, end };
 }
 
 // ...
 
-// Usage
-TEST_CASE("Integers are within a range")
-{
-    CHECK_THAT( 3, IsBetween( 1, 10 ) );
-    CHECK_THAT( 100, IsBetween( 1, 10 ) );
+TEST_CASE("Numbers are within range") {
+    // infers `double` for the argument type of the matcher
+    CHECK_THAT(3., IsBetween(1., 10.));
+    // infers `int` for the argument type of the matcher
+    CHECK_THAT(100, IsBetween(1, 10));
 }
 ```
 
-Running this test gives the following in the console:
+Obviously, the code above can be improved somewhat, for example you
+might want to `static_assert` over the fact that `T` is an arithmetic
+type... or generalize the matcher to cover any type for which the user
+can provide a comparison function object.
 
+Note that while any matcher written using the old style can also be
+written using the new style, combining old style matchers should
+generally compile faster. Also note that you can combine old and new
+style matchers arbitrarily.
+
+> `MatcherBase` lives in `catch2/matchers/catch_matchers.hpp`
+
+
+## Writing custom matchers (new style)
+
+> New style matchers were introduced in Catch2 3.0.1
+
+To create a new-style matcher, you have to create your own type that
+derives from `Catch::Matchers::MatcherGenericBase`. Your type has to
+also provide two methods, `bool match( ... ) const` and overridden
+`std::string describe() const`.
+
+Unlike with old-style matchers, there are no requirements on how
+the `match` member function takes its argument. This means that the
+argument can be taken by value or by mutating reference, but also that
+the matcher's `match` member function can be templated.
+
+This allows you to write more complex matcher, such as a matcher that
+can compare one range-like (something that responds to `begin` and
+`end`) object to another, like in the following example:
+
+```cpp
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_templated.hpp>
+// ...
+
+template<typename Range>
+struct EqualsRangeMatcher : Catch::Matchers::MatcherGenericBase {
+    EqualsRangeMatcher(Range const& range):
+        range{ range }
+    {}
+
+    template<typename OtherRange>
+    bool match(OtherRange const& other) const {
+        using std::begin; using std::end;
+
+        return std::equal(begin(range), end(range), begin(other), end(other));
+    }
+
+    std::string describe() const override {
+        return "Equals: " + Catch::rangeToString(range);
+    }
+
+private:
+    Range const& range;
+};
+
+template<typename Range>
+auto EqualsRange(const Range& range) -> EqualsRangeMatcher<Range> {
+    return EqualsRangeMatcher<Range>{range};
+}
+
+TEST_CASE("Combining templated matchers", "[matchers][templated]") {
+    std::array<int, 3> container{{ 1,2,3 }};
+
+    std::array<int, 3> a{{ 1,2,3 }};
+    std::vector<int> b{ 0,1,2 };
+    std::list<int> c{ 4,5,6 };
+
+    REQUIRE_THAT(container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c));
+}
 ```
-/**/TestFile.cpp:123: FAILED:
-  CHECK_THAT( 100, IsBetween( 1, 10 ) )
-with expansion:
-  100 is between 1 and 10
-```
+
+Do note that while you can rewrite any matcher from the old style to
+a new style matcher, combining new style matchers is more expensive
+in terms of compilation time. Also note that you can combine old style
+and new style matchers arbitrarily.
+
+> `MatcherGenericBase` lives in `catch2/matchers/catch_matchers_templated.hpp`
+
 
 ---
 

+ 98 - 0
lib/Catch2/docs/migrate-v2-to-v3.md

@@ -0,0 +1,98 @@
+<a id="top"></a>
+# Migrating from v2 to v3
+
+v3 is the next major version of Catch2 and brings three significant changes:
+ * Catch2 is now split into multiple headers
+ * Catch2 is now compiled as a static library
+ * C++14 is the minimum required C++ version
+
+There are many reasons why we decided to go from the old single-header
+distribution model to a more standard library distribution model. The
+big one is compile-time performance, but moving over to a split header
+distribution model also improves the future maintainability and
+extendability of the codebase. For example v3 adds a new kind of matchers
+without impacting the compilation times of users that do not use matchers
+in their tests. The new model is also more friendly towards package
+managers, such as vcpkg and Conan.
+
+The result of this move is a significant improvement in compilation
+times, e.g. the inclusion overhead of Catch2 in the common case has been
+reduced by roughly 80%. The improved ease of maintenance also led to
+various runtime performance improvements and the introduction of new features.
+For details, look at [the release notes of 3.0.1](release-notes.md#301).
+
+_Note that we still provide one header + one translation unit (TU)
+distribution but do not consider it the primarily supported option. You
+should also expect that the compilation times will be worse if you use
+this option._
+
+
+## How to migrate projects from v2 to v3
+
+To migrate to v3, there are two basic approaches to do so.
+
+1. Use `catch_amalgamated.hpp` and `catch_amalgamated.cpp`.
+2. Build Catch2 as a proper (static) library, and move to piecewise headers
+
+Doing 1 means downloading the [amalgamated header](/extras/catch_amalgamated.hpp)
+and the [amalgamated sources](/extras/catch_amalgamated.cpp) from `extras`,
+dropping them into your test project, and rewriting your includes from
+`<catch2/catch.hpp>` to `"catch_amalgamated.hpp"` (or something similar,
+based on how you set up your paths).
+
+The disadvantage of using this approach are increased compilation times,
+at least compared to the second approach, but it does let you avoid
+dealing with consuming libraries in your build system of choice.
+
+
+However, we recommend doing 2, and taking extra time to migrate to v3
+properly. This lets you reap the benefits of significantly improved
+compilation times in the v3 version. The basic steps to do so are:
+
+1. Change your CMakeLists.txt to link against `Catch2WithMain` target if
+you use Catch2's default main. (If you do not, keep linking against
+the `Catch2` target.). If you use pkg-config, change `pkg-config catch2` to
+`pkg-config catch2-with-main`.
+2. Delete TU with `CATCH_CONFIG_RUNNER` or `CATCH_CONFIG_MAIN` defined,
+as it is no longer needed.
+3. Change `#include <catch2/catch.hpp>` to `#include <catch2/catch_all.hpp>`
+4. Check that everything compiles. You might have to modify namespaces,
+or perform some other changes (see the
+[Things that can break during porting](#things-that-can-break-during-porting)
+section for the most common things).
+5. Start migrating your test TUs from including `<catch2/catch_all.hpp>`
+to piecemeal includes. You will likely want to start by including
+`<catch2/catch_test_macros.hpp>`, and then go from there. (see
+[other notes](#other-notes) for further ideas)
+
+## Other notes
+
+* The main test include is now `<catch2/catch_test_macros.hpp>`
+
+* Big "subparts" like Matchers, or Generators, have their own folder, and
+also their own "big header", so if you just want to include all matchers,
+you can include `<catch2/matchers/catch_matchers_all.hpp>`,
+or `<catch2/generators/catch_generators_all.hpp>`
+
+
+## Things that can break during porting
+
+* The namespaces of Matchers were flattened and cleaned up.
+
+Matchers are no longer declared deep within an internal namespace and
+then brought up into `Catch` namespace. All Matchers now live in the
+`Catch::Matchers` namespace.
+
+* The `Contains` string matcher was renamed to `ContainsSubstring`.
+
+* The reporter interfaces changed in a breaking manner.
+
+If you are using a custom reporter or listener, you will likely need to
+modify them to conform to the new interfaces. Unlike before in v2,
+the [interfaces](reporters.md#top) and the [events](reporter-events.md#top)
+are now documented.
+
+
+---
+
+[Home](Readme.md#top)

+ 37 - 13
lib/Catch2/docs/opensource-users.md

@@ -1,22 +1,28 @@
 <a id="top"></a>
-# Open Source projects using Catch
+# Open Source projects using Catch2
 
-Catch is great for open source. With its [liberal license](../LICENSE.txt) and single-header, dependency-free, distribution
-it's easy to just drop the header into your project and start writing tests - what's not to like?
+Catch2 is great for open source. It is licensed under the [Boost Software
+License (BSL)](../LICENSE.txt), has no further dependencies and supports
+two file distribution.
 
-As a result Catch is now being used in many Open Source projects, including some quite well known ones.
-This page is an attempt to track those projects. Obviously it can never be complete.
-This effort largely relies on the maintainers of the projects themselves updating this page and submitting a PR
-(or, if you prefer contact one of the maintainers of Catch directly, use the
-[forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum)), or raise an [issue](https://github.com/philsquared/Catch/issues) to let us know).
-Of course users of those projects might want to update this page too. That's fine - as long you're confident the project maintainers won't mind.
-If you're an Open Source project maintainer and see your project listed here but would rather it wasn't -
-just let us know via any of the previously mentioned means - although I'm sure there won't be many who feel that way.
+As a result, Catch2 is used for testing in many different Open Source
+projects. This page lists at least some of them, even though it will
+obviously never be complete (and does not have the ambition to be
+complete). Note that the list below is intended to be in alphabetical
+order, to avoid implications of relative importance of the projects.
+
+_Please only add projects here if you are their maintainer, or have the
+maintainer's explicit consent._
 
-Listing a project here does not imply endorsement and the plan is to keep these ordered alphabetically to avoid an implication of relative importance.
 
 ## Libraries & Frameworks
 
+### [accessorpp](https://github.com/wqking/accessorpp)
+C++ library for implementing property and data binding.
+
+### [alpaka](https://github.com/alpaka-group/alpaka)
+A header-only C++14 abstraction library for accelerator development.
+
 ### [ApprovalTests.cpp](https://github.com/approvals/ApprovalTests.cpp)
 C++11 implementation of Approval Tests, for quick, convenient testing of legacy code.
 
@@ -47,17 +53,26 @@ Header-only C++11 library to encode/decode base64, base64url, base32, base32hex
 ### [DtCraft](https://github.com/twhuang-uiuc/DtCraft)
 A High-performance Cluster Computing Engine.
 
+### [eventpp](https://github.com/wqking/eventpp)
+C++ event library for callbacks, event dispatcher, and event queue. With eventpp you can easily implement signal and slot mechanism, publisher and subscriber pattern, or observer pattern.
+
 ### [forest](https://github.com/xorz57/forest)
 Template Library of Tree Data Structures.
 
 ### [Fuxedo](https://github.com/fuxedo/fuxedo)
 Open source Oracle Tuxedo-like XATMI middleware for C and C++.
 
+### [HIP CPU Runtime](https://github.com/ROCm-Developer-Tools/HIP-CPU)
+A header-only library that allows CPUs to execute unmodified HIP code. It is generic and does not assume a particular CPU vendor or architecture.
+
 ### [Inja](https://github.com/pantor/inja)
 A header-only template engine for modern C++.
 
+### [LLAMA](https://github.com/alpaka-group/llama)
+A C++17 template header-only library for the abstraction of memory access patterns.
+
 ### [libcluon](https://github.com/chrberger/libcluon)
-A single-header-only library written in C++14 to glue distributed software components (UDP, TCP, shared memory) supporting natively Protobuf, LCM/ZCM, MsgPack, and JSON for dynamic message transformations in-between. 
+A single-header-only library written in C++14 to glue distributed software components (UDP, TCP, shared memory) supporting natively Protobuf, LCM/ZCM, MsgPack, and JSON for dynamic message transformations in-between.
 
 ### [MNMLSTC Core](https://github.com/mnmlstc/core)
 A small and easy to use C++11 library that adds a functionality set that will be available in C++14 and later, as well as some useful additions.
@@ -68,6 +83,9 @@ A small C++ library wrapper for the native C ODBC API.
 ### [Nonius](https://github.com/libnonius/nonius)
 A header-only framework for benchmarking small snippets of C++ code.
 
+### [OpenALpp](https://github.com/Laguna1989/OpenALpp)
+A modern OOP C++14 audio library built on OpenAL for Windows, Linux and web (emscripten).
+
 ### [polymorphic_value](https://github.com/jbcoe/polymorphic_value)
 A polymorphic value-type for C++.
 
@@ -100,6 +118,12 @@ A high available cloud native micro-service application management platform impl
 ### [ArangoDB](https://github.com/arangodb/arangodb)
 ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values.
 
+### [Cytopia](https://github.com/CytopiaTeam/Cytopia)
+Cytopia is a free, open source retro pixel-art city building game with a big focus on mods. It utilizes a custom isometric rendering engine based on SDL2.
+
+### [d-SEAMS](https://github.com/d-SEAMS/seams-core)
+Open source molecular dynamics simulation structure analysis suite of tools in modern C++.
+
 ### [Giada - Your Hardcore Loop Machine](https://github.com/monocasual/giada)
 Minimal, open-source and cross-platform audio tool for live music production.
 

+ 19 - 18
lib/Catch2/docs/other-macros.md

@@ -15,6 +15,8 @@ stringification machinery to the _expr_ and records the result. As with
 evaluates to `true`. `CHECKED_ELSE( expr )` work similarly, but the block
 is entered only if the _expr_ evaluated to `false`.
 
+> `CHECKED_X` macros were changed to not count as failure in Catch2 3.0.1.
+
 Example:
 ```cpp
 int a = ...;
@@ -57,9 +59,9 @@ TEST_CASE( "SUCCEED showcase" ) {
 }
 ```
 
-* `STATIC_REQUIRE`
+* `STATIC_REQUIRE` and `STATIC_CHECK`
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1362) in Catch 2.4.2.
+> `STATIC_REQUIRE` was [introduced](https://github.com/catchorg/Catch2/issues/1362) in Catch2 2.4.2.
 
 `STATIC_REQUIRE( expr )` is a macro that can be used the same way as a
 `static_assert`, but also registers the success with Catch2, so it is
@@ -75,6 +77,20 @@ TEST_CASE("STATIC_REQUIRE showcase", "[traits]") {
 }
 ```
 
+> `STATIC_CHECK` was [introduced](https://github.com/catchorg/Catch2/pull/2318) in Catch2 3.0.1.
+
+`STATIC_CHECK( expr )` is equivalent to `STATIC_REQUIRE( expr )`, with the
+difference that when `CATCH_CONFIG_RUNTIME_STATIC_REQUIRE` is defined, it
+becomes equivalent to `CHECK` instead of `REQUIRE`.
+
+Example:
+```cpp
+TEST_CASE("STATIC_CHECK showcase", "[traits]") {
+    STATIC_CHECK( std::is_void<void>::value );
+    STATIC_CHECK_FALSE( std::is_void<int>::value );
+}
+```
+
 ## Test case related macros
 
 * `METHOD_AS_TEST_CASE`
@@ -117,24 +133,9 @@ is initiated. This means that it either needs to be done in a global
 constructor, or before Catch2's session is created in user's own main._
 
 
-* `ANON_TEST_CASE`
-
-`ANON_TEST_CASE` is a `TEST_CASE` replacement that will autogenerate
-unique name. The advantage of this is that you do not have to think
-of a name for the test case,`the disadvantage is that the name doesn't
-necessarily remain stable across different links, and thus it might be
-hard to run directly.
-
-Example:
-```cpp
-ANON_TEST_CASE() {
-    SUCCEED("Hello from anonymous test case");
-}
-```
-
 * `DYNAMIC_SECTION`
 
-> Introduced in Catch 2.3.0.
+> Introduced in Catch2 2.3.0.
 
 `DYNAMIC_SECTION` is a `SECTION` where the user can use `operator<<` to
 create the final name for that section. This can be useful with e.g.

+ 50 - 49
lib/Catch2/docs/own-main.md

@@ -2,63 +2,69 @@
 # Supplying main() yourself
 
 **Contents**<br>
-[Let Catch take full control of args and config](#let-catch-take-full-control-of-args-and-config)<br>
-[Amending the config](#amending-the-config)<br>
+[Let Catch2 take full control of args and config](#let-catch2-take-full-control-of-args-and-config)<br>
+[Amending the Catch2 config](#amending-the-catch2-config)<br>
 [Adding your own command line options](#adding-your-own-command-line-options)<br>
 [Version detection](#version-detection)<br>
 
-The easiest way to use Catch is to let it supply ```main()``` for you and handle configuring itself from the command line.
+The easiest way to use Catch2 is to use its own `main` function, and let
+it handle the command line arguments. This is done by linking against
+Catch2Main library, e.g. through the [CMake target](cmake-integration.md#cmake-targets),
+or pkg-config files.
 
-This is achieved by writing ```#define CATCH_CONFIG_MAIN``` before the ```#include "catch.hpp"``` in *exactly one* source file.
+If you want to provide your own `main`, then you should link against
+the static library (target) only, without the main part. You will then
+have to write your own `main` and call into Catch2 test runner manually.
 
-Sometimes, though, you need to write your own version of main(). You can do this by writing ```#define CATCH_CONFIG_RUNNER``` instead. Now you are free to write ```main()``` as normal and call into Catch yourself manually. You now have a lot of flexibility - but here are three recipes to get your started:
+Below are some basic recipes on what you can do supplying your own main.
 
-**Important note: you can only provide `main` in the same file you defined `CATCH_CONFIG_RUNNER`.**
 
-## Let Catch take full control of args and config
+## Let Catch2 take full control of args and config
 
-If you just need to have code that executes before and/ or after Catch this is the simplest option.
+This is useful if you just need to have code that executes before/after
+Catch2 runs tests.
 
-```c++
-#define CATCH_CONFIG_RUNNER
-#include "catch.hpp"
+```cpp
+#include <catch2/catch_session.hpp>
 
 int main( int argc, char* argv[] ) {
-  // global setup...
+  // your setup ...
 
   int result = Catch::Session().run( argc, argv );
 
-  // global clean-up...
+  // your clean-up...
 
   return result;
 }
 ```
 
-## Amending the config
+_Note that if you only want to run some set up before tests are run, it
+might be simpler to use [event listeners](event-listeners.md#top) instead._
 
-If you still want Catch to process the command line, but you want to programmatically tweak the config, you can do so in one of two ways:
 
-```c++
-#define CATCH_CONFIG_RUNNER
-#include "catch.hpp"
+## Amending the Catch2 config
 
-int main( int argc, char* argv[] )
-{
+If you want Catch2 to process command line arguments, but also want to
+programmatically change the resulting configuration of Catch2 run,
+you can do it in two ways:
+
+```c++
+int main( int argc, char* argv[] ) {
   Catch::Session session; // There must be exactly one instance
- 
+
   // writing to session.configData() here sets defaults
   // this is the preferred way to set them
-    
+
   int returnCode = session.applyCommandLine( argc, argv );
   if( returnCode != 0 ) // Indicates a command line error
         return returnCode;
- 
-  // writing to session.configData() or session.Config() here 
+
+  // writing to session.configData() or session.Config() here
   // overrides command line args
   // only do this if you know you need to
 
   int numFailed = session.run();
-  
+
   // numFailed is clamped to 255 as some unices only use the lower 8 bits.
   // This clamping has already been applied, so just return it here
   // You can also do any post run clean-up here
@@ -66,38 +72,32 @@ int main( int argc, char* argv[] )
 }
 ```
 
-Take a look at the definitions of Config and ConfigData to see what you can do with them.
+If you want full control of the configuration, don't call `applyCommandLine`.
 
-To take full control of the config simply omit the call to ```applyCommandLine()```.
 
 ## Adding your own command line options
 
-Catch embeds a powerful command line parser called [Clara](https://github.com/philsquared/Clara). 
-As of Catch2 (and Clara 1.0) Clara allows you to write _composable_ option and argument parsers, 
-so extending Catch's own command line options is now easy.
-
-```c++
-#define CATCH_CONFIG_RUNNER
-#include "catch.hpp"
+You can add new command line options to Catch2, by composing the premade
+CLI parser (called Clara), and add your own options.
 
-int main( int argc, char* argv[] )
-{
+```cpp
+int main( int argc, char* argv[] ) {
   Catch::Session session; // There must be exactly one instance
-  
+
   int height = 0; // Some user variable you want to be able to set
-  
-  // Build a new parser on top of Catch's
-  using namespace Catch::clara;
-  auto cli 
-    = session.cli() // Get Catch's composite command line parser
+
+  // Build a new parser on top of Catch2's
+  using namespace Catch::Clara;
+  auto cli
+    = session.cli()           // Get Catch2's command line parser
     | Opt( height, "height" ) // bind variable to a new option, with a hint string
         ["-g"]["--height"]    // the option names it will respond to
         ("how high?");        // description string for the help output
-        
-  // Now pass the new composite back to Catch so it uses that
-  session.cli( cli ); 
-  
-  // Let Catch (using Clara) parse the command line
+
+  // Now pass the new composite back to Catch2 so it uses that
+  session.cli( cli );
+
+  // Let Catch2 (using Clara) parse the command line
   int returnCode = session.applyCommandLine( argc, argv );
   if( returnCode != 0 ) // Indicates a command line error
       return returnCode;
@@ -110,12 +110,13 @@ int main( int argc, char* argv[] )
 }
 ```
 
-See the [Clara documentation](https://github.com/philsquared/Clara/blob/master/README.md) for more details.
+See the [Clara documentation](https://github.com/catchorg/Clara/blob/master/README.md)
+for more details on how to use the Clara parser.
 
 
 ## Version detection
 
-Catch provides a triplet of macros providing the header's version, 
+Catch2 provides a triplet of macros providing the header's version,
 
 * `CATCH_VERSION_MAJOR`
 * `CATCH_VERSION_MINOR`

+ 276 - 5
lib/Catch2/docs/release-notes.md

@@ -2,6 +2,10 @@
 
 # Release notes
 **Contents**<br>
+[3.1.1](#311)<br>
+[3.1.0](#310)<br>
+[3.0.1](#301)<br>
+[2.13.7](#2137)<br>
 [2.13.6](#2136)<br>
 [2.13.5](#2135)<br>
 [2.13.4](#2134)<br>
@@ -47,6 +51,271 @@
 [Even Older versions](#even-older-versions)<br>
 
 
+
+## 3.1.1
+
+### Improvements
+* Added `Catch::getSeed` function that user code can call to retrieve current rng-seed
+* Better detection of compiler support for `-ffile-prefix-map` (#2517)
+* Catch2's shared libraries now have `SOVERSION` set (#2516)
+* `catch2/catch_all.hpp` convenience header no longer transitively includes `windows.h` (#2432, #2526)
+
+
+### Fixes
+* Fixed compilation on Universal Windows Platform
+* Fixed compilation on VxWorks (#2515)
+* Fixed compilation on Cygwin (#2540)
+* Remove unused variable in reporter registration (#2538)
+* Fixed some symbol visibility issues with dynamic library on Windows (#2527)
+* Suppressed `-Wuseless-cast` warnings in `REQUIRE_THROWS*` macros (#2520, #2521)
+  * This was triggered when the potentially throwing expression evaluates to `void`
+* Fixed "warning: storage class is not first" with `nvc++` (#2533)
+* Fixed handling of `DL_PATHS` argument to `catch_discover_tests` on MacOS (#2483)
+* Suppressed `*-avoid-c-arrays` clang-tidy warning in `TEMPLATE_TEST_CASE` (#2095, #2536)
+
+
+### Miscellaneous
+* Fixed CMake install step for Catch2 build as dynamic library (#2485)
+* Raised minimum CMake version to 3.10 (#2523)
+  * Expect the minimum CMake version to increase once more in next few releases.
+* Whole bunch of doc updates and fixes
+  * #1444, #2497, #2547, #2549, and more
+* Added support for building Catch2 with Meson (#2530, #2539)
+
+
+
+## 3.1.0
+
+### Improvements
+* Improved suppression of `-Wparentheses` for older GCCs
+  * Turns out that even GCC 9 does not properly handle `_Pragma`s in the C++ frontend.
+* Added type constraints onto `random` generator (#2433)
+  * These constraints copy what the standard says for the underlying `std::uniform_int_distribution`
+* Suppressed -Wunused-variable from nvcc (#2306, #2427)
+* Suppressed -Wunused-variable from MinGW (#2132)
+* Added All/Any/NoneTrue range matchers (#2319)
+  * These check that all/any/none of boolean values in a range are true.
+* The JUnit reporter now normalizes classnames from C++ namespaces to Java-like namespaces (#2468)
+  * This provides better support for other JUnit based tools.
+* The Bazel support now understands `BAZEL_TEST` environment variable (#2459)
+  * The `CATCH_CONFIG_BAZEL_SUPPORT` configuration option is also still supported.
+* Returned support for compiling Catch2 with GCC 5 (#2448)
+  * This required removing inherited constructors from Catch2's internals.
+  * I recommend updating to a newer GCC anyway.
+* `catch_discover_tests` now has a new options for setting library load path(s) when running the Catch2 binary (#2467)
+
+
+### Fixes
+* Fixed crash when listing listeners without any registered listeners (#2442)
+* Fixed nvcc compilation error in constructor benchmarking helper (#2477)
+* Catch2's CMakeList supports pre-3.12 CMake again (#2428)
+  * The gain from requiring CMake 3.12 was very minor, but y'all should really update to newer CMake
+
+
+### Miscellaneous
+* Fixed SelfTest build on MinGW (#2447)
+* The in-repo conan recipe exports the CMake helper (#2460)
+* Added experimental CMake script to showcase using test case sharding together with CTest
+  * Compared to `catch_discover_tests`, it supports very limited number of options and customization
+* Added documentation page on best practices when running Catch2 tests
+* Catch2 can be built as a dynamic library (#2397, #2398)
+  * Note that Catch2 does not have visibility annotations, and you are responsible for ensuring correct visibility built into the resulting library.
+
+
+
+## 3.0.1
+
+**Catch2 now uses statically compiled library as its distribution model.
+This also means that to get all of Catch2's functionality in a test file,
+you have to include multiple headers.**
+
+You probably want to look into the [migration docs](migrate-v2-to-v3.md#top),
+which were written to help people coming from v2.x.x versions to the
+v3 releases.
+
+
+### FAQ
+
+* Why is Catch2 moving to separate headers?
+  * The short answer is future extensibility and scalability. The long answer is complex and can be found on my blog, but at the most basic level, it is that providing single-header distribution is at odds with providing variety of useful features. When Catch2 was distributed in a single header, adding a new Matcher would cause overhead for everyone, but was useful only to a subset of users. This meant that the barrier to entry for new Matchers/Generators/etc is high in single header model, but much smaller in the new model.
+* Will Catch2 again distribute single-header version in the future?
+  * No. But we do provide sqlite-style amalgamated distribution option. This means that you can download just 1 .cpp file and 1 header and place them next to your own sources. However, doing this has downsides similar to using the `catch_all.hpp` header.
+* Why the big breaking change caused by replacing `catch.hpp` with `catch_all.hpp`?
+  * The convenience header `catch_all.hpp` exists for two reasons. One of them is to provide a way for quick migration from Catch2, the second one is to provide a simple way to test things with Catch2. Using it for migration has one drawback in that it is **big**. This means that including it _will_ cause significant compile time drag, and so using it to migrate should be a conscious decision by the user, not something they can just stumble into unknowingly.
+
+
+### (Potentially) Breaking changes
+* **Catch2 now uses statically compiled library as its distribution model**
+  * **Including `catch.hpp` no longer works**
+* **Catch2 now uses C++14 as the minimum support language version**
+* `ANON_TEST_CASE` has been removed, use `TEST_CASE` with no arguments instead (#1220)
+* `--list*` commands no longer have non-zero return code (#1410)
+* `--list-test-names-only` has been removed (#1190)
+  * You should use verbosity-modifiers for `--list-tests` instead
+* `--list*` commands are now piped through the reporters
+  * The top-level reporter interface provides default implementation that works just as the old one
+  * XmlReporter outputs a machine-parseable XML
+* `TEST_CASE` description support has been removed
+  * If the second argument has text outside tags, the text will be ignored.
+* Hidden test cases are no longer included just because they don't match an exclusion tag
+  * Previously, a `TEST_CASE("A", "[.foo]")` would be included by asking for `~[bar]`.
+* `PredicateMatcher` is no longer type erased.
+  * This means that the type of the provided predicate is part of the `PredicateMatcher`'s type
+* `SectionInfo` no longer contains section description as a member (#1319)
+  * You can still write `SECTION("ShortName", "Long and wordy description")`, but the description is thrown away
+  * The description type now must be a `const char*` or be implicitly convertible to it
+* The `[!hide]` tag has been removed.
+  * Use `[.]` or `[.foo]` instead.
+* Lvalues of composed matchers cannot be composed further
+* Uses of `REGISTER_TEST_CASE` macro need to be followed by a semicolon
+  * This does not change `TEST_CASE` and friends in any way
+* `IStreamingReporter::IsMulti` member function was removed
+  * This is _very_ unlikely to actually affect anyone, as it was default-implemented in the interface, and only used internally
+* Various classes not designed for user-extension have been made final
+  * `ListeningReporter` is now `final`
+  * Concrete Matchers (e.g. `UnorderedEquals` vector matcher) are now `final`
+  * All Generators are now `final`
+* Matcher namespacing has been redone
+  * Matcher types are no longer in deeply nested namespaces
+  * Matcher factory functions are no longer brought into `Catch` namespace
+  * This means that all public-facing matcher-related functionality is now in `Catch::Matchers` namespace
+* Defining `CATCH_CONFIG_MAIN` will no longer create main in that TU.
+  * Link with `libCatch2Main.a`, or the proper CMake/pkg-config target
+  * If you want to write custom main, include `catch2/catch_session.hpp`
+* `CATCH_CONFIG_EXTERNAL_INTERFACES` has been removed.
+  * You should instead include the appropriate headers as needed.
+* `CATCH_CONFIG_IMPL` has been removed.
+  * The implementation is now compiled into a static library.
+* Event Listener interface has changed
+  * `TestEventListenerBase` was renamed to `EventListenerBase`
+  * `EventListenerBase` now directly derives from `IStreamingReporter`, instead of deriving from `StreamingReporterBase`
+* `GENERATE` decays its arguments (#2012, #2040)
+  * This means that `str` in `auto str = GENERATE("aa", "bb", "cc");` is inferred to `char const*` rather than `const char[2]`.
+* `--list-*` flags write their output to file specified by the `-o` flag
+* Many changes to reporter interfaces
+  * With the exception of the XmlReporter, the outputs of first party reporters should remain the same
+  * New pair of events were added
+  * One obsolete event was removed
+  * The base class has been renamed
+  * The built-in reporter class hierarchy has been redone
+* Catch2 generates a random seed if one hasn't been specified by the user
+* The short flag for `--list-tests`, `-l`, has been removed.
+  * This is not a commonly used flag and does not need to use up valuable single-letter space.
+* The short flag for `--list-tags`, `-t`, has been removed.
+  * This is not a commonly used flag and does not need to use up valuable single-letter space.
+* The `--colour` option has been replaced with `--colour-mode` option
+
+
+### Improvements
+* Matchers have been extended with the ability to use different signatures of `match` (#1307, #1553, #1554, #1843)
+  * This includes having templated `match` member function
+  * See the [rewritten Matchers documentation](matchers.md#top) for details
+  * Catch2 currently provides _some_ generic matchers, but there should be more before final release of v3
+    * `IsEmpty`, `SizeIs` which check that the range has specific properties
+    * `Contains`, which checks whether a range contains a specific element
+    * `AllMatch`, `AnyMatch`, `NoneMatch` range matchers, which apply matchers over a range of elements
+* Significant compilation time improvements
+  * including `catch_test_macros.hpp` is 80% cheaper than including `catch.hpp`
+* Some runtime performance optimizations
+  * In all tested cases the v3 branch was faster, so the table below shows the speedup of v3 to v2 at the same task
+<a id="v3-runtime-optimization-table"></a>
+
+|                   task                      |  debug build | release build |
+|:------------------------------------------- | ------------:| -------------:|
+| Run 1M `REQUIRE(true)`                      |  1.10 ± 0.01 |   1.02 ± 0.06 |
+| Run 100 tests, 3^3 sections, 1 REQUIRE each |  1.27 ± 0.01 |   1.04 ± 0.01 |
+| Run 3k tests, no names, no tags             |  1.29 ± 0.01 |   1.05 ± 0.01 |
+| Run 3k tests, names, tags                   |  1.49 ± 0.01 |   1.22 ± 0.01 |
+| Run 1 out of 3k tests no names, no tags     |  1.68 ± 0.02 |   1.19 ± 0.22 |
+| Run 1 out of 3k tests, names, tags          |  1.79 ± 0.02 |   2.06 ± 0.23 |
+
+
+* POSIX platforms use `gmtime_r`, rather than `gmtime` when constructing a date string (#2008, #2165)
+* `--list-*` flags write their output to file specified by the `-o` flag (#2061, #2163)
+* `Approx::operator()` is now properly `const`
+* Catch2's internal helper variables no longer use reserved identifiers (#578)
+* `--rng-seed` now accepts string `"random-device"` to generate random seed using `std::random_device`
+* Catch2 now supports test sharding (#2257)
+  * You can ask for the tests to be split into N groups and only run one of them.
+  * This greatly simplifies parallelization of tests in a binary through external runner.
+* The embedded CLI parser now supports repeatedly callable lambdas
+  * A lambda-based option parser can opt into being repeatedly specifiable.
+* Added `STATIC_CHECK` macro, similar to `STATIC_REQUIRE` (#2318)
+  * When deferred tu runtime, it behaves like `CHECK`, and not like `REQUIRE`.
+* You can have multiple tests with the same name, as long as other parts of the test identity differ (#1915, #1999, #2175)
+  * Test identity includes test's name, test's tags and and test's class name if applicable.
+* Added new warning, `UnmatchedTestSpec`, to error on test specs with no matching tests
+* The `-w`, `--warn` warning flags can now be provided multiple times to enable multiple warnings
+* The case-insensitive handling of tags is now more reliable and takes up less memory
+* Test case and assertion counting can no longer reasonably overflow on 32 bit systems
+  * The count is now kept in `uint64_t` on all platforms, instead of using `size_t` type.
+* The `-o`, `--out` output destination specifiers recognize `-` as stdout
+  * You have to provide it as `--out=-` to avoid CLI error about missing option
+  * The new reporter specification also recognizes `-` as stdout
+* Multiple reporters can now run at the same time and write to different files (#1712, #2183)
+  * To support this, the `-r`, `--reporter` flag now also accepts optional output destination
+  * For full overview of the semantics of using multiple reporters, look into the reporter documentation
+  * To enable the new syntax, reporter names can no longer contain `::`.
+* Console colour support has been rewritten and significantly improved
+  * The colour implementation based on ANSI colour codes is always available
+  * Colour implementations respect their associated stream
+    * previously e.g. Win32 impl would change console colour even if Catch2 was writing to a file
+  * The colour API is resilient against changing evaluation order of expressions
+  * The associated CLI flag and compile-time configuration options have changed
+    * For details see the docs for command-line and compile-time Catch2 configuration
+* Added a support for Bazel integration with `XML_OUTPUT_FILE` env var (#2399)
+  * This has to be enabled during compilation.
+* Added `--skip-benchmarks` flag to run tests without any `BENCHMARK`s (#2392, #2408)
+* Added option to list all listeners in the binary via `--list-listeners`
+
+
+### Fixes
+* The `INFO` macro no longer contains superfluous semicolon (#1456)
+* The `--list*` family of command line flags now return 0 on success (#1410, #1146)
+* Various ways of failing a benchmark are now counted and reporter properly
+* The ULP matcher now handles comparing numbers with different signs properly (#2152)
+* Universal ADL-found operators should no longer break decomposition (#2121)
+* Reporter selection is properly case-insensitive
+  * Previously it forced lower cased name, which would fail for reporters with upper case characters in name
+* The cumulative reporter base stores benchmark results alongside assertion results
+* Catch2's SE handling should no longer interferes with ASan on Windows (#2334)
+* Fixed Windows console colour handling for tests that redirect stdout (#2345)
+* Fixed issue with the `random` generators returning the same value over and over again
+
+
+### Other changes
+* `CATCH_CONFIG_DISABLE_MATCHERS` no longer exists.
+  * If you do not want to use Matchers in a TU, do not include their header.
+* `CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER` no longer exists.
+  * `StringMaker` specializations for `<chrono>` are always provided
+* Catch2's CMake now provides 2 targets, `Catch2` and `Catch2WithMain`.
+  * `Catch2` is the statically compiled implementation by itself
+  * `Catch2WithMain` also links in the default main
+* Catch2's pkg-config integration also provides 2 packages
+  * `catch2` is the statically compiled implementation by itself
+  * `catch2-with-main` also links in the default main
+* Passing invalid test specifications passed to Catch2 are now reported before tests are run, and are a hard error.
+* Running 0 tests (e.g. due to empty binary, or test spec not matching anything) returns non-0 exit code
+  * Flag `--allow-running-no-tests` overrides this behaviour.
+  * `NoTests` warning has been removed because it is fully subsumed by this change.
+* Catch2's compile-time configuration options (`CATCH_CONFIG_FOO`) can be set through CMake options of the same name
+  * They use the same semantics as C++ defines, including the `CATCH_CONFIG_NO_FOO` overrides,
+    * `-DCATCH_CONFIG_DEFAULT_REPORTER=compact` changes default reporter to "compact"
+    * `-DCATCH_CONFIG_NO_ANDROID_LOGWRITE=ON` forces android logwrite to off
+    * `-DCATCH_CONFIG_ANDROID_LOGWRITE=OFF` does nothing (the define will not exist)
+
+
+
+## 2.13.7
+
+### Fixes
+* Added missing `<iterator>` include in benchmarking. (#2231)
+* Fixed noexcept build with benchmarking enabled (#2235)
+* Fixed build for compilers with C++17 support but without C++17 library support (#2195)
+* JUnit only uses 3 decimal places when reporting durations (#2221)
+* `!mayfail` tagged tests are now marked as `skipped` in JUnit reporter output (#2116)
+
+
 ## 2.13.6
 
 ### Fixes
@@ -70,9 +339,6 @@
   * Including `windows.h` without `NOMINMAX` remains a really bad idea, don't do it
 
 ### Miscellaneous
-* `Catch2WithMain` target (static library) is no longer built by default (#2142)
-  * Building it by default was at best unnecessary overhead for people not using it, and at worst it caused trouble with install paths
-  * To have it built, set CMake option `CATCH_BUILD_STATIC_LIBRARY` to `ON`
 * The check whether Catch2 is being built as a subproject is now more reliable (#2202, #2204)
   * The problem was that if the variable name used internally was defined the project including Catch2 as subproject, it would not be properly overwritten for Catch2's CMake.
 
@@ -196,6 +462,7 @@
 * Added support for `^` (bitwise xor) to `CHECK` and `REQUIRE`
 
 
+
 ## 2.12.0
 
 ### Improvements
@@ -218,6 +485,7 @@
 * Improved detection of stdlib's support for `std::uncaught_exceptions` (#1911)
 
 
+
 ## 2.11.3
 
 ### Fixes
@@ -249,6 +517,7 @@
   * This bug has been present for the last ~2 years and nobody reported it
 
 
+
 ## 2.11.1
 
 ### Improvements
@@ -262,6 +531,7 @@
 * Some more cleanups in the benchmarking support
 
 
+
 ## 2.11.0
 
 ### Improvements
@@ -395,6 +665,7 @@
 ### Fixes
 * Fix benchmarking compilation failure in files without `CATCH_CONFIG_EXTERNAL_INTERFACES` (or implementation)
 
+
 ## 2.9.0
 
 ### Improvements
@@ -727,7 +998,7 @@ than `single_include/catch.hpp`.**
 * CLR objects (`T^`) can now be stringified (#1216)
   * This affects code compiled as C++/CLI
 * Added `PredicateMatcher`, a matcher that takes an arbitrary predicate function (#1236)
-  * See [documentation for details](https://github.com/catchorg/Catch2/blob/v2.x/docs/matchers.md)
+  * See [documentation for details](https://github.com/catchorg/Catch2/blob/devel/docs/matchers.md)
 
 ### Others
 * Modified CMake-installed pkg-config to allow `#include <catch.hpp>`(#1239)
@@ -755,7 +1026,7 @@ than `single_include/catch.hpp`.**
 * Added an option to warn (+ exit with error) when no tests were ran (#1158)
   * Use as `-w NoTests`
 * Added provisional support for Emscripten (#1114)
-* [Added a way to override the fallback stringifier](https://github.com/catchorg/Catch2/blob/v2.x/docs/configuration.md#fallback-stringifier) (#1024)
+* [Added a way to override the fallback stringifier](https://github.com/catchorg/Catch2/blob/devel/docs/configuration.md#fallback-stringifier) (#1024)
   * This allows project's own stringification machinery to be easily reused for Catch
 * `Catch::Session::run()` now accepts `char const * const *`, allowing it to accept array of string literals (#1031, #1178)
   * The embedded version of Clara was bumped to v1.1.3

+ 9 - 16
lib/Catch2/docs/release-process.md

@@ -1,7 +1,7 @@
 <a id="top"></a>
 # How to release
 
-When enough changes have accumulated, it is time to release new version of Catch. This document describes the process in doing so, that no steps are forgotten. Note that all referenced scripts can be found in the `scripts/` directory.
+When enough changes have accumulated, it is time to release new version of Catch. This document describes the process in doing so, that no steps are forgotten. Note that all referenced scripts can be found in the `tools/scripts/` directory.
 
 ## Necessary steps
 
@@ -40,14 +40,10 @@ After version number is incremented, single-include header is regenerated and re
 After pushing changes to GitHub, GitHub release *needs* to be created.
 Tag version and release title should be same as the new version,
 description should contain the release notes for the current release.
-Single header version of `catch.hpp` *needs* to be attached as a binary,
-as that is where the official download link links to. Preferably
-it should use linux line endings. All non-bundled reporters (Automake, TAP,
-TeamCity, SonarQube) should also be attached as binaries, as they might be
-dependent on a specific version of the single-include header.
+We also attach the two amalgamated files as "binaries".
 
-Since 2.5.0, the release tag and the "binaries" (headers) should be PGP
-signed.
+Since 2.5.0, the release tag and the "binaries" (amalgamated files) should
+be PGP signed.
 
 #### Signing a tag
 
@@ -57,17 +53,14 @@ is the version being released, e.g. `git tag -s v2.6.0`.
 Use the version name as the short message and the release notes as
 the body (long) message.
 
-#### Signing the headers
+#### Signing the amalgamated files
 
-This will create ASCII-armored signatures for the headers that are
-uploaded to the GitHub release:
+This will create ASCII-armored signatures for the two amalgamated files
+that are uploaded to the GitHub release:
 
 ```
-$ gpg2 --armor --output catch.hpp.asc --detach-sig catch.hpp
-$ gpg2 --armor --output catch_reporter_automake.hpp.asc --detach-sig catch_reporter_automake.hpp
-$ gpg2 --armor --output catch_reporter_teamcity.hpp.asc --detach-sig catch_reporter_teamcity.hpp
-$ gpg2 --armor --output catch_reporter_tap.hpp.asc --detach-sig catch_reporter_tap.hpp
-$ gpg2 --armor --output catch_reporter_sonarqube.hpp.asc --detach-sig catch_reporter_sonarqube.hpp
+gpg --armor --output extras/catch_amalgamated.hpp.asc --detach-sig extras/catch_amalgamated.hpp
+gpg --armor --output extras/catch_amalgamated.cpp.asc --detach-sig extras/catch_amalgamated.cpp
 ```
 
 _GPG does not support signing multiple files in single invocation._

+ 175 - 0
lib/Catch2/docs/reporter-events.md

@@ -0,0 +1,175 @@
+<a id="top"></a>
+# Reporter events
+
+**Contents**<br>
+[Test running events](#test-running-events)<br>
+[Benchmarking events](#benchmarking-events)<br>
+[Listings events](#listings-events)<br>
+[Miscellaneous events](#miscellaneous-events)<br>
+
+Reporter events are one of the customization points for user code. They
+are used by [reporters](reporters.md#top) to customize Catch2's output,
+and by [event listeners](event-listeners.md#top) to perform in-process
+actions under some conditions.
+
+There are currently 21 reporter events in Catch2, split between 4 distinct
+event groups:
+* test running events (10 events)
+* benchmarking (4 events)
+* listings (3 events)
+* miscellaneous (4 events)
+
+## Test running events
+
+Test running events are always paired so that for each `fooStarting` event,
+there is a `fooEnded` event. This means that the 10 test running events
+consist of 5 pairs of events:
+
+* `testRunStarting` and `testRunEnded`,
+* `testCaseStarting` and `testCaseEnded`,
+* `testCasePartialStarting` and `testCasePartialEnded`,
+* `sectionStarting` and `sectionEnded`,
+* `assertionStarting` and `assertionEnded`
+
+### `testRun` events
+
+```cpp
+void testRunStarting( TestRunInfo const& testRunInfo );
+void testRunEnded( TestRunStats const& testRunStats );
+```
+
+The `testRun` events bookend the entire test run. `testRunStarting` is
+emitted before the first test case is executed, and `testRunEnded` is
+emitted after all the test cases have been executed.
+
+### `testCase` events
+
+```cpp
+void testCaseStarting( TestCaseInfo const& testInfo );
+void testCaseEnded( TestCaseStats const& testCaseStats );
+```
+
+The `testCase` events bookend one _full_ run of a specific test case.
+Individual runs through a test case, e.g. due to `SECTION`s or `GENERATE`s,
+are handled by a different event.
+
+
+### `testCasePartial` events
+
+> Introduced in Catch2 3.0.1
+
+```cpp
+void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber );
+void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber );
+```
+
+`testCasePartial` events bookend one _partial_ run of a specific test case.
+This means that for any given test case, these events can be emitted
+multiple times, e.g. due to multiple leaf sections.
+
+In regards to nesting with `testCase` events, `testCasePartialStarting`
+will never be emitted before the corresponding `testCaseStarting`, and
+`testCasePartialEnded` will always be emitted before the corresponding
+`testCaseEnded`.
+
+
+### `section` events
+
+```cpp
+void sectionStarting( SectionInfo const& sectionInfo );
+void sectionEnded( SectionStats const& sectionStats );
+```
+
+`section` events are emitted only for active `SECTION`s, that is, sections
+that are entered. Sections that are skipped in this test case run-through
+do not cause events to be emitted.
+
+_Note that test cases always contain one implicit section. The event for
+this section is emitted after the corresponding `testCasePartialStarting`
+event._
+
+
+### `assertion` events
+
+```cpp
+void assertionStarting( AssertionInfo const& assertionInfo );
+void assertionEnded( AssertionStats const& assertionStats );
+```
+
+`assertionStarting` is called after the expression is captured, but before
+the assertion expression is evaluated. This might seem like a minor
+distinction, but what it means is that if you have assertion like
+`REQUIRE( a + b == c + d )`, then what happens is that `a + b` and `c + d`
+are evaluated before `assertionStarting` is emitted, while the `==` is
+evaluated after the event.
+
+
+## Benchmarking events
+
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
+
+```cpp
+void benchmarkPreparing( StringRef name ) override;
+void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+void benchmarkFailed( StringRef error ) override;
+```
+
+Due to the benchmark lifecycle being bit more complicated, the benchmarking
+events have their own category, even though they could be seen as parallel
+to the `assertion*` events. You should expect running a benchmark to
+generate at least 2 of the events above.
+
+To understand the explanation below, you should read the [benchmarking
+documentation](benchmarks.md#top) first.
+
+* `benchmarkPreparing` event is sent after the environmental probe
+finishes, but before the user code is first estimated.
+* `benchmarkStarting` event is sent after the user code is estimated,
+but has not been benchmarked yet.
+* `benchmarkEnded` event is sent after the user code has been benchmarked,
+and contains the benchmarking results.
+* `benchmarkFailed` event is sent if either the estimation or the
+benchmarking itself fails.
+
+
+## Listings events
+
+> Introduced in Catch2 3.0.1.
+
+Listings events are events that correspond to the test binary being
+invoked with `--list-foo` flag.
+
+There are currently 3 listing events, one for reporters, one for tests,
+and one for tags. Note that they are not exclusive to each other.
+
+```cpp
+void listReporters( std::vector<ReporterDescription> const& descriptions );
+void listTests( std::vector<TestCaseHandle> const& tests );
+void listTags( std::vector<TagInfo> const& tagInfos );
+```
+
+
+## Miscellaneous events
+
+```cpp
+void reportInvalidTestSpec( StringRef unmatchedSpec );
+void fatalErrorEncountered( StringRef error );
+void noMatchingTestCases( StringRef unmatchedSpec );
+```
+
+These are one-off events that do not neatly fit into other categories.
+
+`reportInvalidTestSpec` is sent for each [test specification command line
+argument](command-line.md#specifying-which-tests-to-run) that wasn't
+parsed into a valid spec.
+
+`fatalErrorEncountered` is sent when Catch2's POSIX signal handling
+or Windows SE handler is called into with a fatal signal/exception.
+
+`noMatchingTestCases` is sent for each user provided test specification
+that did not match any registered tests.
+
+---
+
+[Home](Readme.md#top)

+ 191 - 25
lib/Catch2/docs/reporters.md

@@ -1,46 +1,212 @@
 <a id="top"></a>
 # Reporters
 
-Catch has a modular reporting system and comes bundled with a handful of useful reporters built in.
-You can also write your own reporters.
+Reporters are a customization point for most of Catch2's output, e.g.
+formatting and writing out [assertions (whether passing or failing),
+sections, test cases, benchmarks, and so on](reporter-events.md#top).
+
+Catch2 comes with a bunch of reporters by default (currently 8), and
+you can also write your own reporter. Because multiple reporters can
+be active at the same time, your own reporters do not even have to handle
+all reporter event, just the ones you are interested in, e.g. benchmarks.
+
 
 ## Using different reporters
 
-The reporter to use can easily be controlled from the command line.
-To specify a reporter use [`-r` or `--reporter`](command-line.md#choosing-a-reporter-to-use), followed by the name of the reporter, e.g.:
+You can see which reporters are available by running the test binary
+with `--list-reporters`. You can then pick one of them with the [`-r`,
+`--reporter` option](command-line.md#choosing-a-reporter-to-use), followed
+by the name of the desired reporter, like so:
+
+```
+--reporter xml
+```
+
+You can also select multiple reporters to be used at the same time.
+In that case you should read the [section on using multiple
+reporters](#multiple-reporters) to avoid any surprises from doing so.
+
 
+<a id="multiple-reporters"></a>
+## Using multiple reporters
+
+> Support for having multiple parallel reporters was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 3.0.1
+
+Catch2 supports using multiple reporters at the same time while having
+them write into different destinations. The two main uses of this are
+
+* having both human-friendly and machine-parseable (e.g. in JUnit format)
+  output from one run of binary
+* having "partial" reporters that are highly specialized, e.g. having one
+  reporter that writes out benchmark results as markdown tables and does
+  nothing else, while also having standard testing output separately
+
+Specifying multiple reporter looks like this:
 ```
--r xml
+--reporter JUnit::out=result-junit.xml --reporter console::out=-::colour-mode=ansi
 ```
 
-If you don't specify a reporter then the console reporter is used by default.
-There are four reporters built in to the single include:
+This tells Catch2 to use two reporters, `JUnit` reporter that writes
+its machine-readable XML output to file `result-junit.xml`, and the
+`console` reporter that writes its user-friendly output to stdout and
+uses ANSI colour codes for colouring the output.
 
-* `console` writes as lines of text, formatted to a typical terminal width, with colours if a capable terminal is detected.
-* `compact` similar to `console` but optimised for minimal output - each entry on one line
-* `junit` writes xml that corresponds to Ant's [junitreport](http://help.catchsoftware.com/display/ET/JUnit+Format) target. Useful for build systems that understand Junit.
-Because of the way the junit format is structured the run must complete before anything is written. 
-* `xml` writes an xml format tailored to Catch. Unlike `junit` this is a streaming format so results are delivered progressively.
+Using multiple reporters (or one reporter and one-or-more [event
+listeners](event-listener.md#top)) can have surprisingly complex semantics
+when using customization points provided to reporters by Catch2, namely
+capturing stdout/stderr from test cases.
 
-There are a few additional reporters, for specific build systems, in the Catch repository (in `include\reporters`) which you can `#include` in your project if you would like to make use of them.
-Do this in one source file - the same one you have `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`.
+As long as at least one reporter (or listener) asks Catch2 to capture
+stdout/stderr, captured stdout and stderr will be available to all
+reporters and listeners.
 
-* `teamcity` writes the native, streaming, format that [TeamCity](https://www.jetbrains.com/teamcity/) understands. 
-Use this when building as part of a TeamCity build to see results as they happen ([code example](../examples/207-Rpt-TeamCityReporter.cpp)).
-* `tap` writes in the TAP ([Test Anything Protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol)) format.
-* `automake` writes in a format that correspond to [automake  .trs](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html) files
-* `sonarqube` writes the [SonarQube Generic Test Data](https://docs.sonarqube.org/latest/analysis/generic-test/) XML format.
+Because this might be surprising to the users, if at least one active
+_reporter_ is non-capturing, then Catch2 tries to roughly emulate
+non-capturing behaviour by printing out the captured stdout/stderr
+just before `testCasePartialEnded` event is sent out to the active
+reporters and listeners. This means that stdout/stderr is no longer
+printed out from tests as it is being written, but instead it is written
+out in batch after each runthrough of a test case is finished.
 
-You see what reporters are available from the command line by running with `--list-reporters`.
 
-By default all these reports are written to stdout, but can be redirected to a file with [`-o` or `--out`](command-line.md#sending-output-to-a-file)
 
 ## Writing your own reporter
 
-You can write your own custom reporter and register it with Catch.
-At time of writing the interface is subject to some changes so is not, yet, documented here.
-If you are determined you shouldn't have too much trouble working it out from the existing implementations -
-but do keep in mind upcoming changes (these will be minor, simplifying, changes such as not needing to forward calls to the base class).
+You can also write your own custom reporter and tell Catch2 to use it.
+When writing your reporter, you have two options:
+
+* Derive from `Catch::ReporterBase`. When doing this, you will have
+  to provide handling for all [reporter events](reporter-events.md#top).
+* Derive from one of the provided [utility reporter bases in
+  Catch2](#utility-reporter-bases).
+
+Generally we recommend doing the latter, as it is less work.
+
+Apart from overriding handling of the individual reporter events, reporters
+have access to some extra customization points, described below.
+
+
+### Utility reporter bases
+
+Catch2 currently provides two utility reporter bases:
+
+* `Catch::StreamingReporterBase`
+* `Catch::CumulativeReporterBase`
+
+`StreamingReporterBase` is useful for reporters that can format and write
+out the events as they come in. It provides (usually empty) implementation
+for all reporter events, and if you let it handle the relevant events,
+it also handles storing information about active test run and test case.
+
+`CumulativeReporterBase` is a base for reporters that need to see the whole
+test run, before they can start writing the output, such as the JUnit
+and SonarQube reporters. This post-facto approach requires the assertions
+to be stringified when it is finished, so that the assertion can be written
+out later. Because the stringification can be expensive, and not all
+cumulative reporters need the assertions, this base provides customization
+point to change whether the assertions are saved or not, separate for
+passing and failing assertions.
+
+
+_Generally we recommend that if you override a member function from either
+of the bases, you call into the base's implementation first. This is not
+necessarily in all cases, but it is safer and easier._
+
+
+Writing your own reporter then looks like this:
+
+```cpp
+#include <catch2/reporters/catch_reporter_streaming_base.hpp>
+#include <catch2/catch_test_case_info.hpp>
+#include <catch2/reporters/catch_reporter_registrars.hpp>
+
+#include <iostream>
+
+class PartialReporter : public Catch::StreamingReporterBase {
+public:
+    using StreamingReporterBase::StreamingReporterBase;
+
+    static std::string getDescription() {
+        return "Reporter for testing TestCasePartialStarting/Ended events";
+    }
+
+    void testCasePartialStarting(Catch::TestCaseInfo const& testInfo,
+                                 uint64_t partNumber) override {
+        std::cout << "TestCaseStartingPartial: " << testInfo.name << '#' << partNumber << '\n';
+    }
+
+    void testCasePartialEnded(Catch::TestCaseStats const& testCaseStats,
+                              uint64_t partNumber) override {
+        std::cout << "TestCasePartialEnded: " << testCaseStats.testInfo->name << '#' << partNumber << '\n';
+    }
+};
+
+
+CATCH_REGISTER_REPORTER("partial", PartialReporter)
+```
+
+This create a simple reporter that responds to `testCasePartial*` events,
+and calls itself "partial" reporter, so it can be invoked with
+`--reporter partial` command line flag.
+
+
+### `ReporterPreferences`
+
+Each reporter instance contains instance of `ReporterPreferences`, a type
+that holds flags for the behaviour of Catch2 when this reporter run.
+Currently there are two customization options:
+
+* `shouldRedirectStdOut` - whether the reporter wants to handle
+   writes to stdout/stderr from user code, or not. This is useful for
+   reporters that output machine-parseable output, e.g. the JUnit
+   reporter, or the XML reporter.
+* `shouldReportAllAssertions` - whether the reporter wants to handle
+  `assertionEnded` events for passing assertions as well as failing
+   assertions. Usually reporters do not report successful assertions
+   and don't need them for their output, but sometimes the desired output
+   format includes passing assertions even without the `-s` flag.
+
+
+### Per-reporter configuration
+
+> Per-reporter configuration was introduced in Catch2 3.0.1
+
+Catch2 supports some configuration to happen per reporter. The configuration
+options fall into one of two categories:
+
+* Catch2-recognized options
+* Reporter-specific options
+
+The former is a small set of universal options that Catch2 handles for
+the reporters, e.g. output file or console colour mode. The latter are
+options that the reporters have to handle themselves, but the keys and
+values can be arbitrary strings, as long as they don't contain `::`. This
+allows writing reporters that can be significantly customized at runtime.
+
+Reporter-specific options always have to be prefixed with "X" (large
+letter X).
+
+
+### Other expected functionality of a reporter
+
+When writing a custom reporter, there are few more things that you should
+keep in mind. These are not important for correctness, but they are
+important for the reporter to work _nicely_.
+
+* Catch2 provides a simple verbosity option for users. There are three
+  verbosity levels, "quiet", "normal", and "high", and if it makes sense
+  for reporter's output format, it should respond to these by changing
+  what, and how much, it writes out.
+
+* Catch2 operates with an rng-seed. Knowing what seed a test run had
+  is important if you want to replicate it, so your reporter should
+  report the rng-seed, if at all possible given the target output format.
+
+* Catch2 also operates with test filters, or test specs. If a filter
+  is present, you should also report the filter, if at all possible given
+  the target output format.
+
+
 
 ---
 

+ 0 - 106
lib/Catch2/docs/slow-compiles.md

@@ -1,106 +0,0 @@
-<a id="top"></a>
-# Why do my tests take so long to compile?
-
-**Contents**<br>
-[Short answer](#short-answer)<br>
-[Long answer](#long-answer)<br>
-[Practical example](#practical-example)<br>
-[Using the static library Catch2WithMain](#using-the-static-library-catch2withmain)<br>
-[Other possible solutions](#other-possible-solutions)<br>
-
-Several people have reported that test code written with Catch takes much longer to compile than they would expect. Why is that?
-
-Catch is implemented entirely in headers. There is a little overhead due to this - but not as much as you might think - and you can minimise it simply by organising your test code as follows:
-
-## Short answer
-Exactly one source file must ```#define``` either ```CATCH_CONFIG_MAIN``` or ```CATCH_CONFIG_RUNNER``` before ```#include```-ing Catch. In this file *do not write any test cases*! In most cases that means this file will just contain two lines (the ```#define``` and the ```#include```).
-
-## Long answer
-
-Usually C++ code is split between a header file, containing declarations and prototypes, and an implementation file (.cpp) containing the definition, or implementation, code. Each implementation file, along with all the headers that it includes (and which those headers include, etc), is expanded into a single entity called a translation unit - which is then passed to the compiler and compiled down to an object file.
-
-But functions and methods can also be written inline in header files. The downside to this is that these definitions will then be compiled in *every* translation unit that includes the header.
-
-Because Catch is implemented *entirely* in headers you might think that the whole of Catch must be compiled into every translation unit that uses it! Actually it's not quite as bad as that. Catch mitigates this situation by effectively maintaining the traditional separation between the implementation code and declarations. Internally the implementation code is protected by ```#ifdef```s and is conditionally compiled into only one translation unit. This translation unit is that one that ```#define```s ```CATCH_CONFIG_MAIN``` or ```CATCH_CONFIG_RUNNER```. Let's call this the main source file.
-
-As a result the main source file *does* compile the whole of Catch every time! So it makes sense to dedicate this file to *only* ```#define```-ing the identifier and ```#include```-ing Catch (and implementing the runner code, if you're doing that). Keep all your test cases in other files. This way you won't pay the recompilation cost for the whole of Catch.
-
-## Practical example
-Assume you have the `Factorial` function from the [tutorial](tutorial.md#top) in `factorial.cpp` (with forward declaration in `factorial.h`) and want to test it and keep the compile times down when adding new tests. Then you should have 2 files, `tests-main.cpp` and `tests-factorial.cpp`:
-
-```cpp
-// tests-main.cpp
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
-```
-
-```cpp
-// tests-factorial.cpp
-#include "catch.hpp"
-
-#include "factorial.h"
-
-TEST_CASE( "Factorials are computed", "[factorial]" ) {
-    REQUIRE( Factorial(1) == 1 );
-    REQUIRE( Factorial(2) == 2 );
-    REQUIRE( Factorial(3) == 6 );
-    REQUIRE( Factorial(10) == 3628800 );
-}
-```
-
-After compiling `tests-main.cpp` once, it is enough to link it with separately compiled `tests-factorial.cpp`. This means that adding more tests to `tests-factorial.cpp`, will not result in recompiling Catch's main and the resulting compilation times will decrease substantially.
-
-```
-$ g++ tests-main.cpp -c
-$ g++ factorial.cpp -c
-$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
-Passed 1 test case with 4 assertions.
-```
-
-Now, the next time we change the file `tests-factorial.cpp` (say we add `REQUIRE( Factorial(0) == 1)`), it is enough to recompile the tests instead of recompiling main as well:
-
-```
-$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
-tests-factorial.cpp:11: failed: Factorial(0) == 1 for: 0 == 1
-Failed 1 test case, failed 1 assertion.
-```
-
-
-## Using the static library Catch2WithMain
-
-Catch2 also provides a static library that implements the runner. Note
-that this support is experimental, due to interactions between Catch2 v2
-implementation and C++ linking limitations.
-
-As with the `Catch2` target, the `Catch2WithMain` CMake target can be used
-either from a subdirectory, or from installed build.
-
-
-### CMake
-```cmake
-add_executable(tests-factorial tests-factorial.cpp)
-
-target_link_libraries(tests-factorial Catch2::Catch2WithMain)
-```
-
-### bazel
-```python
-cc_test(
-    name = "hello_world_test",
-    srcs = [
-        "test/hello_world_test.cpp",
-    ],
-    deps = [
-        "lib_hello_world",
-        "@catch2//:catch2_with_main",
-    ],
-)
-```
-
-
-## Other possible solutions
-You can also opt to sacrifice some features in order to speed-up Catch's compilation times. For details see the [documentation on Catch's compile-time configuration](configuration.md#other-toggles).
-
----
-
-[Home](Readme.md#top)

+ 80 - 17
lib/Catch2/docs/test-cases-and-sections.md

@@ -15,9 +15,17 @@ Instead Catch provides a powerful mechanism for nesting test case sections withi
 Test cases and sections are very easy to use in practice:
 
 * **TEST_CASE(** _test name_ \[, _tags_ \] **)**
-* **SECTION(** _section name_ **)**
+* **SECTION(** _section name_, \[, _section description_ \] **)**
 
-_test name_ and _section name_ are free form, quoted, strings. The optional _tags_ argument is a quoted string containing one or more tags enclosed in square brackets. Tags are discussed below. Test names must be unique within the Catch executable.
+
+_test name_ and _section name_ are free form, quoted, strings.
+The optional _tags_ argument is a quoted string containing one or more
+tags enclosed in square brackets, and are discussed below.
+_section description_ can be used to provide long form description
+of a section while keeping the _section name_ short for use with the
+[`-c` command line parameter](command-line.md#specify-the-section-to-run).
+
+**Test names must be unique within the Catch executable.**
 
 For examples see the [Tutorial](tutorial.md#top)
 
@@ -36,13 +44,21 @@ The tag expression, ```"[widget]"``` selects A, B & D. ```"[gadget]"``` selects
 
 For more detail on command line selection see [the command line docs](command-line.md#specifying-which-tests-to-run)
 
-Tag names are not case sensitive and can contain any ASCII characters. This means that tags `[tag with spaces]` and `[I said "good day"]` are both allowed tags and can be filtered on. Escapes are not supported however and `[\]]` is not a valid tag.
+Tag names are not case sensitive and can contain any ASCII characters.
+This means that tags `[tag with spaces]` and `[I said "good day"]`
+are both allowed tags and can be filtered on. However, escapes are not
+supported however and `[\]]` is not a valid tag.
+
+The same tag can be specified multiple times for a single test case,
+but only one of the instances of identical tags will be kept. Which one
+is kept is functionally random.
+
 
 ### Special Tags
 
 All tag names beginning with non-alphanumeric characters are reserved by Catch. Catch defines a number of "special" tags, which have meaning to the test runner itself. These special tags all begin with a symbol character. Following is a list of currently defined special tags and their meanings.
 
-* `[!hide]` or `[.]` - causes test cases to be skipped from the default list (i.e. when no test cases have been explicitly selected through tag expressions or name wildcards). The hide tag is often combined with another, user, tag (for example `[.][integration]` - so all integration tests are excluded from the default run but can be run by passing `[integration]` on the command line). As a short-cut you can combine these by simply prefixing your user tag with a `.` - e.g. `[.integration]`. Because the hide tag has evolved to have several forms, all forms are added as tags if you use one of them.
+* `[.]` - causes test cases to be skipped from the default list (i.e. when no test cases have been explicitly selected through tag expressions or name wildcards). The hide tag is often combined with another, user, tag (for example `[.][integration]` - so all integration tests are excluded from the default run but can be run by passing `[integration]` on the command line). As a short-cut you can combine these by simply prefixing your user tag with a `.` - e.g. `[.integration]`.
 
 * `[!throws]` - lets Catch know that this test is likely to throw an exception even if successful. This causes the test to be excluded when running with `-e` or `--nothrow`.
 
@@ -56,7 +72,8 @@ All tag names beginning with non-alphanumeric characters are reserved by Catch.
 
 * `[@<alias>]` - tag aliases all begin with `@` (see below).
 
-* `[!benchmark]` - this test case is actually a benchmark. This is an experimental feature, and currently has no documentation. If you want to try it out, look at `projects/SelfTest/Benchmark.tests.cpp` for details.
+* `[!benchmark]` - this test case is actually a benchmark. Currently this only serves to hide the test case by default, to avoid the execution time costs.
+
 
 ## Tag aliases
 
@@ -82,15 +99,65 @@ This macro maps onto ```TEST_CASE``` and works in the same way, except that the
 * **WHEN(** _something_ **)**
 * **THEN(** _something_ **)**
 
-These macros map onto ```SECTION```s except that the section names are the _something_s prefixed by "given: ", "when: " or "then: " respectively.
+These macros map onto ```SECTION```s except that the section names are the _something_ texts prefixed by
+"given: ", "when: " or "then: " respectively. These macros also map onto the AAA or A<sup>3</sup> test pattern
+(standing either for [Assemble-Activate-Assert](http://wiki.c2.com/?AssembleActivateAssert) or
+[Arrange-Act-Assert](http://wiki.c2.com/?ArrangeActAssert)), and in this context, the macros provide both code
+documentation and reporting of these parts of a test case without the need for extra comments or code to do so.
+
+Semantically, a `GIVEN` clause may have multiple _independent_ `WHEN` clauses within it. This allows a test
+to have, e.g., one set of "given" objects and multiple subtests using those objects in various ways in each
+of the `WHEN` clauses without repeating the initialisation from the `GIVEN` clause. When there are _dependent_
+clauses -- such as a second `WHEN` clause that should only happen _after_ the previous `WHEN` clause has been
+executed and validated -- there are additional macros starting with `AND_`:
 
 * **AND_GIVEN(** _something_ **)**
 * **AND_WHEN(** _something_ **)**
 * **AND_THEN(** _something_ **)**
 
-Similar to ```GIVEN```, ```WHEN``` and ```THEN``` except that the prefixes start with "and ". These are used to chain ```GIVEN```s, ```WHEN```s and ```THEN```s together.
+These are used to chain ```GIVEN```s, ```WHEN```s and ```THEN```s together. The `AND_*` clause is placed
+_inside_ the clause on which it depends. There can be multiple _independent_ clauses that are all _dependent_
+on a single outer clause.
+```cpp
+SCENARIO( "vector can be sized and resized" ) {
+    GIVEN( "An empty vector" ) {
+        auto v = std::vector<std::string>{};
 
-> `AND_GIVEN` was [introduced](https://github.com/catchorg/Catch2/issues/1360) in Catch 2.4.0.
+        // Validate assumption of the GIVEN clause
+        THEN( "The size and capacity start at 0" ) {
+            REQUIRE( v.size() == 0 );
+            REQUIRE( v.capacity() == 0 );
+        }
+
+        // Validate one use case for the GIVEN object
+        WHEN( "push_back() is called" ) {
+            v.push_back("hullo");
+
+            THEN( "The size changes" ) {
+                REQUIRE( v.size() == 1 );
+                REQUIRE( v.capacity() >= 1 );
+            }
+        }
+    }
+}
+```
+
+This code will result in two runs through the scenario:
+```
+Scenario : vector can be sized and resized
+  Given  : An empty vector
+  Then   : The size and capacity start at 0
+
+Scenario : vector can be sized and resized
+  Given  : An empty vector
+  When   : push_back() is called
+  Then   : The size changes
+```
+
+See also [runnable example on godbolt](https://godbolt.org/z/eY5a64r99),
+with a more complicated (and failing) example.
+
+> `AND_GIVEN` was [introduced](https://github.com/catchorg/Catch2/issues/1360) in Catch2 2.4.0.
 
 When any of these macros are used the console reporter recognises them and formats the test case header such that the Givens, Whens and Thens are aligned to aid readability.
 
@@ -104,7 +171,7 @@ by types, in the form of `TEMPLATE_TEST_CASE`,
 
 * **TEMPLATE_TEST_CASE(** _test name_ , _tags_,  _type1_, _type2_, ..., _typen_ **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1437) in Catch 2.5.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1437) in Catch2 2.5.0.
 
 _test name_ and _tag_ are exactly the same as they are in `TEST_CASE`,
 with the difference that the tag string must be provided (however, it
@@ -156,7 +223,7 @@ TEMPLATE_TEST_CASE( "vectors can be sized and resized", "[vector][template]", in
 
 * **TEMPLATE_PRODUCT_TEST_CASE(** _test name_ , _tags_, (_template-type1_, _template-type2_, ..., _template-typen_), (_template-arg1_, _template-arg2_, ..., _template-argm_) **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch 2.6.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch2 2.6.0.
 
 _template-type1_ through _template-typen_ is list of template template
 types which should be combined with each of _template-arg1_ through
@@ -195,13 +262,9 @@ TEMPLATE_PRODUCT_TEST_CASE("Product with differing arities", "[template][product
 }
 ```
 
-_While there is an upper limit on the number of types you can specify
-in single `TEMPLATE_TEST_CASE` or `TEMPLATE_PRODUCT_TEST_CASE`, the limit
-is very high and should not be encountered in practice._
-
 * **TEMPLATE_LIST_TEST_CASE(** _test name_, _tags_, _type list_ **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1627) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1627) in Catch2 2.9.0.
 
 _type list_ is a generic list of types on which test case should be instantiated.
 List can be `std::tuple`, `boost::mpl::list`, `boost::mp11::mp_list` or anything with
@@ -221,7 +284,7 @@ TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside std
 
 ## Signature based parametrised test cases
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch2 2.8.0.
 
 In addition to [type parametrised test cases](#type-parametrised-test-cases) Catch2 also supports
 signature base parametrised test cases, in form of `TEMPLATE_TEST_CASE_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_SIG`.
@@ -243,7 +306,7 @@ Currently Catch2 support up to 11 template parameters in signature
 
 * **TEMPLATE_TEST_CASE_SIG(** _test name_ , _tags_,  _signature_, _type1_, _type2_, ..., _typen_ **)**
 
-Inside `TEMPLATE_TEST_CASE_SIG` test case you can use the names of template parameters as defined in _signature_. 
+Inside `TEMPLATE_TEST_CASE_SIG` test case you can use the names of template parameters as defined in _signature_.
 
 ```cpp
 TEMPLATE_TEST_CASE_SIG("TemplateTestSig: arrays can be created from NTTP arguments", "[vector][template][nttp]",

+ 28 - 9
lib/Catch2/docs/test-fixtures.md

@@ -3,7 +3,7 @@
 
 ## Defining test fixtures
 
-Although Catch allows you to group tests together as sections within a test case, it can still be convenient, sometimes, to group them using a more traditional test fixture. Catch fully supports this too. You define the test fixture as a simple structure:
+Although Catch allows you to group tests together as [sections within a test case](test-cases-and-sections.md), it can still be convenient, sometimes, to group them using a more traditional test fixture. Catch fully supports this too. You define the test fixture as a simple structure:
 
 ```c++
 class UniqueTestsFixture {
@@ -59,7 +59,10 @@ struct Template_Fixture {
     T m_a;
 };
 
-TEMPLATE_TEST_CASE_METHOD(Template_Fixture,"A TEMPLATE_TEST_CASE_METHOD based test run that succeeds", "[class][template]", int, float, double) {
+TEMPLATE_TEST_CASE_METHOD(Template_Fixture,
+                          "A TEMPLATE_TEST_CASE_METHOD based test run that succeeds",
+                          "[class][template]",
+                          int, float, double) {
     REQUIRE( Template_Fixture<TestType>::m_a == 1 );
 }
 
@@ -77,7 +80,11 @@ struct Foo_class {
     }
 };
 
-TEMPLATE_PRODUCT_TEST_CASE_METHOD(Template_Template_Fixture, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test succeeds", "[class][template]", (Foo_class, std::vector), int) {
+TEMPLATE_PRODUCT_TEST_CASE_METHOD(Template_Template_Fixture,
+                                  "A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test succeeds",
+                                  "[class][template]",
+                                  (Foo_class, std::vector),
+                                  int) {
     REQUIRE( Template_Template_Fixture<TestType>::m_a.size() == 0 );
 }
 ```
@@ -88,7 +95,7 @@ the limit is very high and should not be encountered in practice._
 
 ## Signature-based parametrised test fixtures
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch2 2.8.0.
 
 Catch2 also provides `TEMPLATE_TEST_CASE_METHOD_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG` to support
 fixtures using non-type template parameters. These test cases work similar to `TEMPLATE_TEST_CASE_METHOD` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
@@ -101,7 +108,12 @@ struct Nttp_Fixture{
     int value = V;
 };
 
-TEMPLATE_TEST_CASE_METHOD_SIG(Nttp_Fixture, "A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][nttp]",((int V), V), 1, 3, 6) {
+TEMPLATE_TEST_CASE_METHOD_SIG(
+    Nttp_Fixture,
+    "A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds",
+    "[class][template][nttp]",
+    ((int V), V),
+    1, 3, 6) {
     REQUIRE(Nttp_Fixture<V>::value > 0);
 }
 
@@ -117,8 +129,13 @@ struct Template_Foo_2 {
     size_t size() { return V; }
 };
 
-TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(Template_Fixture_2, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][product][nttp]", ((typename T, size_t S), T, S),(std::array, Template_Foo_2), ((int,2), (float,6)))
-{
+TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(
+    Template_Fixture_2,
+    "A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds",
+    "[class][template][product][nttp]",
+    ((typename T, size_t S), T, S),
+    (std::array, Template_Foo_2),
+    ((int,2), (float,6))) {
     REQUIRE(Template_Fixture_2<TestType>{}.m_a.size() >= 2);
 }
 ```
@@ -132,8 +149,10 @@ only difference is the source of types. This allows you to reuse the template ty
 Example:
 ```cpp
 using MyTypes = std::tuple<int, char, double>;
-TEMPLATE_LIST_TEST_CASE_METHOD(Template_Fixture, "Template test case method with test types specified inside std::tuple", "[class][template][list]", MyTypes)
-{
+TEMPLATE_LIST_TEST_CASE_METHOD(Template_Fixture,
+                               "Template test case method with test types specified inside std::tuple",
+                               "[class][template][list]",
+                               MyTypes) {
     REQUIRE( Template_Fixture<TestType>::m_a == 1 );
 }
 ```

+ 3 - 3
lib/Catch2/docs/tostring.md

@@ -64,14 +64,14 @@ namespace Catch {
 By default all exceptions deriving from `std::exception` will be translated to strings by calling the `what()` method. For exception types that do not derive from `std::exception` - or if `what()` does not return a suitable string - use `CATCH_TRANSLATE_EXCEPTION`. This defines a function that takes your exception type, by reference, and returns a string. It can appear anywhere in the code - it doesn't have to be in the same translation unit. For example:
 
 ```cpp
-CATCH_TRANSLATE_EXCEPTION( MyType& ex ) {
+CATCH_TRANSLATE_EXCEPTION( MyType const& ex ) {
     return ex.message();
 }
 ```
 
 ## Enums
 
-> Introduced in Catch 2.8.0.
+> Introduced in Catch2 2.8.0.
 
 Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected.
 If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type.
@@ -110,7 +110,7 @@ TEST_CASE() {
 
 ## Floating point precision
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1614) in Catch 2.8.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1614) in Catch2 2.8.0.
 
 Catch provides a built-in `StringMaker` specialization for both `float`
 and `double`. By default, it uses what we think is a reasonable precision,

+ 87 - 139
lib/Catch2/docs/tutorial.md

@@ -3,32 +3,21 @@
 
 **Contents**<br>
 [Getting Catch2](#getting-catch2)<br>
-[Where to put it?](#where-to-put-it)<br>
 [Writing tests](#writing-tests)<br>
 [Test cases and sections](#test-cases-and-sections)<br>
-[BDD-Style](#bdd-style)<br>
-[Scaling up](#scaling-up)<br>
-[Type parametrised test cases](#type-parametrised-test-cases)<br>
+[BDD style testing](#bdd-style-testing)<br>
+[Data and Type driven tests](#data-and-type-driven-tests)<br>
 [Next steps](#next-steps)<br>
 
-## Getting Catch2
-
-The simplest way to get Catch2 is to download the latest [single header version](https://raw.githubusercontent.com/catchorg/Catch2/v2.x/single_include/catch2/catch.hpp). The single header is generated by merging a set of individual headers but it is still just normal source code in a header file.
-
-Alternative ways of getting Catch2 include using your system package
-manager, or installing it using [its CMake package](cmake-integration.md#installing-catch2-from-git-repository).
-
-The full source for Catch2, including test projects, documentation, and other things, is hosted on GitHub. [http://catch-lib.net](http://catch-lib.net) will redirect you there.
-
-
-## Where to put it?
 
-Catch2 is header only. All you need to do is drop the file somewhere reachable from your project - either in some central location you can set your header search path to find, or directly into your project tree itself! This is a particularly good option for other Open-Source projects that want to use Catch for their test suite. See [this blog entry for more on that](https://levelofindirection.com/blog/unit-testing-in-cpp-and-objective-c-just-got-ridiculously-easier-still.html).
+## Getting Catch2
 
-The rest of this tutorial will assume that the Catch2 single-include header (or the include folder) is available unqualified - but you may need to prefix it with a folder name if necessary.
+Ideally you should be using Catch2 through its [CMake integration](cmake-integration.md#top).
+Catch2 also provides pkg-config files and two file (header + cpp)
+distribution, but this documentation will assume you are using CMake. If
+you are using the two file distribution instead, remember to replace
+the included header with `catch_amalgamated.hpp`.
 
-_If you have installed Catch2 from system package manager, or CMake
-package, you need to include the header as `#include <catch2/catch.hpp>`_
 
 ## Writing tests
 
@@ -40,11 +29,8 @@ unsigned int Factorial( unsigned int number ) {
 }
 ```
 
-To keep things simple we'll put everything in a single file (<a href="#scaling-up">see later for more on how to structure your test files</a>).
-
 ```c++
-#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
-#include "catch.hpp"
+#include <catch2/catch_test_macros.hpp>
 
 unsigned int Factorial( unsigned int number ) {
     return number <= 1 ? number : Factorial(number-1)*number;
@@ -60,13 +46,10 @@ TEST_CASE( "Factorials are computed", "[factorial]" ) {
 
 This will compile to a complete executable which responds to [command line arguments](command-line.md#top). If you just run it with no arguments it will execute all test cases (in this case there is just one), report any failures, report a summary of how many tests passed and failed and return the number of failed tests (useful for if you just want a yes/ no answer to: "did it work").
 
-If you run this as written it will pass. Everything is good. Right?
-Well, there is still a bug here. In fact the first version of this tutorial I posted here genuinely had the bug in! So it's not completely contrived (thanks to Daryle Walker (```@CTMacUser```) for pointing this out).
-
-What is the bug? Well what is the factorial of zero?
-[The factorial of zero is one](http://mathforum.org/library/drmath/view/57128.html) - which is just one of those things you have to know (and remember!).
-
-Let's add that to the test case:
+Anyway, as the tests above as written will pass, but there is a bug.
+The problem is that `Factorial(0)` should return 1 (due to [its
+definition](https://en.wikipedia.org/wiki/Factorial#Factorial_of_zero)).
+Let's add that as an assertion to the test case:
 
 ```c++
 TEST_CASE( "Factorials are computed", "[factorial]" ) {
@@ -78,7 +61,8 @@ TEST_CASE( "Factorials are computed", "[factorial]" ) {
 }
 ```
 
-Now we get a failure - something like:
+After another compile & run cycle, we will see a test failure. The output
+will look something like:
 
 ```
 Example.cpp:9: FAILED:
@@ -87,37 +71,51 @@ with expansion:
   0 == 1
 ```
 
-Note that we get the actual return value of Factorial(0) printed for us (0) - even though we used a natural expression with the == operator. That lets us immediately see what the problem is.
-
-Let's change the factorial function to:
+Note that the output contains both the original expression,
+`REQUIRE( Factorial(0) == 1 )` and the actual value returned by the call
+to the `Factorial` function: `0`.
 
+We can fix this bug by slightly modifying the `Factorial` function to:
 ```c++
 unsigned int Factorial( unsigned int number ) {
   return number > 1 ? Factorial(number-1)*number : 1;
 }
 ```
 
-Now all the tests pass.
-
-Of course there are still more issues to deal with. For example we'll hit problems when the return value starts to exceed the range of an unsigned int. With factorials that can happen quite quickly. You might want to add tests for such cases and decide how to handle them. We'll stop short of doing that here.
 
 ### What did we do here?
 
-Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take a moment to consider those before we move on.
+Although this was a simple test it's been enough to demonstrate a few
+things about how Catch2 is used. Let's take a moment to consider those
+before we move on.
 
-1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md#top). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md#top)).
-2. We introduce test cases with the ```TEST_CASE``` macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see <a href="#test-cases-and-sections">Test cases and Sections</a>). The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md#top) for more information on running tests.
-3. The name and tags arguments are just strings. We haven't had to declare a function or method - or explicitly register the test case anywhere. Behind the scenes a function with a generated name is defined for you, and automatically registered using static registry classes. By abstracting the function name away we can name our tests without the constraints of identifier names.
-4. We write our individual test assertions using the ```REQUIRE``` macro. Rather than a separate macro for each type of condition we express the condition naturally using C/C++ syntax. Behind the scenes a simple set of expression templates captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. As we'll see later there _are_ other assertion macros - but because of this technique the number of them is drastically reduced.
+* We introduce test cases with the `TEST_CASE` macro. This macro takes
+  one or two string arguments - a free form test name and, optionally,
+  one or more tags (for more see [Test cases and Sections](#test-cases-and-sections)).
+* The test automatically self-registers with the test runner, and user
+  does not have do anything more to ensure that it is picked up by the test
+  framework. _Note that you can run specific test, or set of tests,
+  through the [command line](command-line.md#top)._
+* The individual test assertions are written using the `REQUIRE` macro.
+  It accepts a boolean expression, and uses expression templates to
+  internally decompose it, so that it can be individually stringified
+  on test failure.
+
+On the last point, note that there are more testing macros available,
+because not all useful checks can be expressed as a simple boolean
+expression. As an example, checking that an expression throws an exception
+is done with the `REQUIRE_THROWS` macro. More on that later.
 
-<a id="test-cases-and-sections"></a>
-## Test cases and sections
 
-Most test frameworks have a class-based fixture mechanism. That is, test cases map to methods on a class and common setup and teardown can be performed in ```setup()``` and ```teardown()``` methods (or constructor/ destructor in languages, like C++, that support deterministic destruction).
+## Test cases and sections
 
-While Catch fully supports this way of working there are a few problems with the approach. In particular the way your code must be split up, and the blunt granularity of it, may cause problems. You can only have one setup/ teardown pair across a set of methods, but sometimes you want slightly different setup in each method, or you may even want several levels of setup (a concept which we will clarify later on in this tutorial). It was <a href="http://jamesnewkirk.typepad.com/posts/2007/09/why-you-should-.html">problems like these</a> that led James Newkirk, who led the team that built NUnit, to start again from scratch and <a href="http://jamesnewkirk.typepad.com/posts/2007/09/announcing-xuni.html">build xUnit</a>).
+Like most test frameworks, Catch2 supports a class-based fixture mechanism,
+where individual tests are methods on class and setup/teardown can be
+done in constructor/destructor of the type.
 
-Catch takes a different approach (to both NUnit and xUnit) that is a more natural fit for C++ and the C family of languages. This is best explained through an example ([code](../examples/100-Fix-Section.cpp)):
+However, their use in Catch2 is rare, because idiomatic Catch2 tests
+instead use _sections_ to share setup and teardown code between test code.
+This is best explained through an example ([code](../examples/100-Fix-Section.cpp)):
 
 ```c++
 TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
@@ -154,125 +152,75 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
 }
 ```
 
-For each ```SECTION``` the ```TEST_CASE``` is executed from the start - so as we enter each section we know that size is 5 and capacity is at least 5. We enforced those requirements with the ```REQUIRE```s at the top level so we can be confident in them.
-This works because the ```SECTION``` macro contains an if statement that calls back into Catch to see if the section should be executed. One leaf section is executed on each run through a ```TEST_CASE```. The other sections are skipped. Next time through the next section is executed, and so on until no new sections are encountered.
+For each `SECTION` the `TEST_CASE` is executed from the start. This means
+that each section is entered with a freshly constructed vector `v`, that
+we know has size 5 and capacity at least 5, because the two assertions
+are also checked before the section is entered. Each run through a test
+case will execute one, and only one, leaf section.
 
-So far so good - this is already an improvement on the setup/teardown approach because now we see our setup code inline and use the stack.
+Section can also be nested, in which case the parent section can be
+entered multiple times, once for each leaf section. Nested sections are
+most useful when you have multiple tests that share part of the set up.
+To continue on the vector example above, you could add a check that
+`std::vector::reserve` does not remove unused excess capacity, like this:
 
-The power of sections really shows, however, when we need to execute a sequence of checked operations. Continuing the vector example, we might want to verify that attempting to reserve a capacity smaller than the current capacity of the vector changes nothing. We can do that, naturally, like so:
-
-```c++
+```cpp
     SECTION( "reserving bigger changes capacity but not size" ) {
         v.reserve( 10 );
 
         REQUIRE( v.size() == 5 );
         REQUIRE( v.capacity() >= 10 );
-
-        SECTION( "reserving smaller again does not change capacity" ) {
+        SECTION( "reserving down unused capacity does not change capacity" ) {
             v.reserve( 7 );
-
+            REQUIRE( v.size() == 5 );
             REQUIRE( v.capacity() >= 10 );
         }
     }
 ```
 
-Sections can be nested to an arbitrary depth (limited only by your stack size). Each leaf section (i.e. a section that contains no nested sections) will be executed exactly once, on a separate path of execution from any other leaf section (so no leaf section can interfere with another). A failure in a parent section will prevent nested sections from running - but then that's the idea.
-
-## BDD-Style
-
-If you name your test cases and sections appropriately you can achieve a BDD-style specification structure. This became such a useful way of working that first class support has been added to Catch. Scenarios can be specified using ```SCENARIO```, ```GIVEN```, ```WHEN``` and ```THEN``` macros, which map on to ```TEST_CASE```s and ```SECTION```s, respectively. For more details see [Test cases and sections](test-cases-and-sections.md#top).
-
-The vector example can be adjusted to use these macros like so ([example code](../examples/120-Bdd-ScenarioGivenWhenThen.cpp)):
-
-```c++
-SCENARIO( "vectors can be sized and resized", "[vector]" ) {
-
-    GIVEN( "A vector with some items" ) {
-        std::vector<int> v( 5 );
-
-        REQUIRE( v.size() == 5 );
-        REQUIRE( v.capacity() >= 5 );
-
-        WHEN( "the size is increased" ) {
-            v.resize( 10 );
-
-            THEN( "the size and capacity change" ) {
-                REQUIRE( v.size() == 10 );
-                REQUIRE( v.capacity() >= 10 );
-            }
-        }
-        WHEN( "the size is reduced" ) {
-            v.resize( 0 );
-
-            THEN( "the size changes but not capacity" ) {
-                REQUIRE( v.size() == 0 );
-                REQUIRE( v.capacity() >= 5 );
-            }
-        }
-        WHEN( "more capacity is reserved" ) {
-            v.reserve( 10 );
-
-            THEN( "the capacity changes but not the size" ) {
-                REQUIRE( v.size() == 5 );
-                REQUIRE( v.capacity() >= 10 );
-            }
-        }
-        WHEN( "less capacity is reserved" ) {
-            v.reserve( 0 );
-
-            THEN( "neither size nor capacity are changed" ) {
-                REQUIRE( v.size() == 5 );
-                REQUIRE( v.capacity() >= 5 );
-            }
-        }
-    }
-}
-```
-
-Conveniently, these tests will be reported as follows when run:
-
-```
-Scenario: vectors can be sized and resized
-     Given: A vector with some items
-      When: more capacity is reserved
-      Then: the capacity changes but not the size
-```
-
-<a id="scaling-up"></a>
-## Scaling up
+Another way to look at sections is that they are a way to define a tree
+of paths through the test. Each section represents a node, and the final
+tree is walked in depth-first manner, with each path only visiting only
+one leaf node.
 
-To keep the tutorial simple we put all our code in a single file. This is fine to get started - and makes jumping into Catch even quicker and easier. As you write more real-world tests, though, this is not really the best approach.
+There is no practical limit on nesting sections, as long as your compiler
+can handle them, but keep in mind that overly nested sections can become
+unreadable. From experience, having section nest more than 3 levels is
+usually very hard to follow and not worth the removed duplication.
 
-The requirement is that the following block of code ([or equivalent](own-main.md#top)):
-
-```c++
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
-```
 
-appears in _exactly one_ source file. Use as many additional cpp files (or whatever you call your implementation files) as you need for your tests, partitioned however makes most sense for your way of working. Each additional file need only ```#include "catch.hpp"``` - do not repeat the ```#define```!
+## BDD style testing
 
-In fact it is usually a good idea to put the block with the ```#define``` [in its own source file](slow-compiles.md#top) (code example [main](../examples/020-TestCase-1.cpp), [tests](../examples/020-TestCase-2.cpp)).
+Catch2 also provides some basic support for BDD-style testing. There are
+macro aliases for `TEST_CASE` and `SECTIONS` that you can use so that
+the resulting tests read as BDD spec. `SCENARIO` acts as a `TEST_CASE`
+with "Scenario: " name prefix. Then there are `GIVEN`, `WHEN`, `THEN`
+(and their variants with `AND_` prefix), which act as a `SECTION`,
+similarly prefixed with the macro name.
 
-Do not write your tests in header files!
+For more details on the macros look at the [test cases and
+sections](test-cases-and-sections.md#top) part of the reference docs,
+or at the [vector example done with BDD macros](../examples/120-Bdd-ScenarioGivenWhenThen.cpp).
 
 
-## Type parametrised test cases
+## Data and Type driven tests
 
-Test cases in Catch2 can be also parametrised by type, via the
-`TEMPLATE_TEST_CASE` and `TEMPLATE_PRODUCT_TEST_CASE` macros,
-which behave in the same way the `TEST_CASE` macro, but are run for
-every type or type combination.
+Test cases in Catch2 can also be driven by types, input data, or both
+at the same time.
 
-For more details, see our documentation on [test cases and
-sections](test-cases-and-sections.md#type-parametrised-test-cases).
+For more details look into the Catch2 reference, either at the
+[type parametrized test cases](test-cases-and-sections.md#type-parametrised-test-cases),
+or [data generators](generators.md#top).
 
 
 ## Next steps
 
-This has been a brief introduction to get you up and running with Catch, and to point out some of the key differences between Catch and other frameworks you may already be familiar with. This will get you going quite far already and you are now in a position to dive in and write some tests.
+This page is a brief introduction to get you up and running with Catch2,
+and to show the basic features of Catch2. The features mentioned here
+can get you quite far, but there are many more. However, you can read
+about these as you go, in the ever-growing [reference section](Readme.md#top)
+of the documentation.
 
-Of course there is more to learn - most of which you should be able to page-fault in as you go. Please see the ever-growing [Reference section](Readme.md#top) for what's available.
 
 ---
 

+ 100 - 0
lib/Catch2/docs/usage-tips.md

@@ -0,0 +1,100 @@
+<a id="top"></a>
+# Best practices and other tips on using Catch2
+
+## Running tests
+
+Your tests should be run in a manner roughly equivalent with:
+
+```
+./tests --order rand --warn NoAssertions
+```
+
+Notice that all the tests are run in a large batch, their relative order
+is randomized, and that you ask Catch2 to fail test whose leaf-path
+does not contain an assertion.
+
+The reason I recommend running all your tests in the same process is that
+this exposes your tests to interference from their runs. This can be both
+positive interference, where the changes in global state from previous
+test allow later tests to pass, but also negative interference, where
+changes in global state from previous test causes later tests to fail.
+
+In my experience, interference, especially destructive interference,
+usually comes from errors in the code under test, rather than the tests
+themselves. This means that by allowing interference to happen, our tests
+can find these issues. Obviously, to shake out interference coming from
+different orderings of tests, the test order also need to be shuffled
+between runs.
+
+However, running all tests in a single batch eventually becomes impractical
+as they will take too long to run, and you will want to run your tests
+in parallel.
+
+
+<a id="parallel-tests"></a>
+## Running tests in parallel
+
+There are multiple ways of running tests in parallel, with various level
+of structure. If you are using CMake and CTest, then we provide a helper
+function [`catch_discover_tests`](cmake-integration.md#automatic-test-registration)
+that registers each Catch2 `TEST_CASE` as a single CTest test, which
+is then run in a separate process. This is an easy way to set up parallel
+tests if you are already using CMake & CTest to run your tests, but you
+will lose the advantage of running tests in batches.
+
+
+Catch2 also supports [splitting tests in a binary into multiple
+shards](command-line.md#test-sharding). This can be used by any test
+runner to run batches of tests in parallel. Do note that when selecting
+on the number of shards, you should have more shards than there are cores,
+to avoid issues with long-running tests getting accidentally grouped in
+the same shard, and causing long-tailed execution time.
+
+**Note that naively composing sharding and random ordering of tests will break.**
+
+Invoking Catch2 test executable like this
+
+```text
+./tests --order rand --shard-index 0 --shard-count 3
+./tests --order rand --shard-index 1 --shard-count 3
+./tests --order rand --shard-index 2 --shard-count 3
+```
+
+does not guarantee covering all tests inside the executable, because
+each invocation will have its own random seed, thus it will have its own
+random order of tests and thus the partitioning of tests into shards will
+be different as well.
+
+To do this properly, you need the individual shards to share the random
+seed, e.g.
+```text
+./tests --order rand --shard-index 0 --shard-count 3 --rng-seed 0xBEEF
+./tests --order rand --shard-index 1 --shard-count 3 --rng-seed 0xBEEF
+./tests --order rand --shard-index 2 --shard-count 3 --rng-seed 0xBEEF
+```
+
+Catch2 actually provides a helper to automatically register multiple shards
+as CTest tests, with shared random seed that changes each CTest invocation.
+For details look at the documentation of
+[`CatchShardTests.cmake` CMake script](cmake-integration.md#catchshardtestscmake).
+
+
+## Organizing tests into binaries
+
+Both overly large and overly small test binaries can cause issues. Overly
+large test binaries have to be recompiled and relinked often, and the
+link times are usually also long. Overly small test binaries in turn pay
+significant overhead from linking against Catch2 more often per compiled
+test case, and also make it hard/impossible to run tests in batches.
+
+Because there is no hard and fast rule for the right size of a test binary,
+I recommend having 1:1 correspondence between libraries in project and test
+binaries. (At least if it is possible, in some cases it is not.) Having
+a test binary for each library in project keeps related tests together,
+and makes tests easy to navigate by reflecting the project's organizational
+structure.
+
+
+---
+
+[Home](Readme.md#top)

+ 24 - 11
lib/Catch2/docs/why-catch.md

@@ -6,40 +6,53 @@ including (but not limited to),
 [Google Test](http://code.google.com/p/googletest/),
 [Boost.Test](http://www.boost.org/doc/libs/1_49_0/libs/test/doc/html/index.html),
 [CppUnit](http://sourceforge.net/apps/mediawiki/cppunit/index.php?title=Main_Page),
-[Cute](http://www.cute-test.com),
+[Cute](http://www.cute-test.com), and
 [many, many more](http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C.2B.2B).
 
-So what does Catch bring to the party that differentiates it from these? Apart from a Catchy name, of course.
+So what does Catch2 bring to the party that differentiates it from these? Apart from the catchy name, of course.
+
 
 ## Key Features
 
-* Quick and Really easy to get started. Just download catch.hpp, `#include` it and you're away.
-* No external dependencies. As long as you can compile C++11 and have a C++ standard library available.
+* Quick and easy to get started. Just download two files, add them into your project and you're away.
+* No external dependencies. As long as you can compile C++14 and have the C++ standard library available.
 * Write test cases as, self-registering, functions (or methods, if you prefer).
 * Divide test cases into sections, each of which is run in isolation (eliminates the need for fixtures).
 * Use BDD-style Given-When-Then sections as well as traditional unit test cases.
 * Only one core assertion macro for comparisons. Standard C/C++ operators are used for the comparison - yet the full expression is decomposed and lhs and rhs values are logged.
 * Tests are named using free-form strings - no more couching names in legal identifiers.
 
+
 ## Other core features
 
 * Tests can be tagged for easily running ad-hoc groups of tests.
-* Failures can (optionally) break into the debugger on Windows and Mac.
+* Failures can (optionally) break into the debugger on common platforms.
 * Output is through modular reporter objects. Basic textual and XML reporters are included. Custom reporters can easily be added.
 * JUnit xml output is supported for integration with third-party tools, such as CI servers.
 * A default main() function is provided, but you can supply your own for complete control (e.g. integration into your own test runner GUI).
 * A command line parser is provided and can still be used if you choose to provided your own main() function.
-* Catch can test itself.
 * Alternative assertion macro(s) report failures but don't abort the test case
-* Floating point tolerance comparisons are built in using an expressive Approx() syntax.
+* Good set of facilities for floating point comparisons (`Catch::Approx` and full set of matchers)
 * Internal and friendly macros are isolated so name clashes can be managed
-* Matchers
+* Data generators (data driven test support)
+* Hamcrest-style Matchers for testing complex properties
+* Microbenchmarking support
+
 
-## Who else is using Catch?
+## Who else is using Catch2?
 
-See the list of [open source projects using Catch](opensource-users.md#top).
+A whole lot of people. According to the 2021 JetBrains C++ ecosystem survey,
+about 11% of C++ programmers use Catch2 for unit testing, making it the
+second most popular unit testing framework.
+
+You can also take a look at the (incomplete) list of [open source projects](opensource-users.md#top)
+or the (very incomplete) list of [commercial users of Catch2](commercial-users.md#top)
+for some idea on who else also uses Catch2.
+
+---
 
-See the [tutorial](tutorial.md#top) to get more of a taste of using Catch in practice 
+See the [tutorial](tutorial.md#top) to get more of a taste of using
+Catch2 in practice.
 
 ---
 

+ 0 - 15
lib/Catch2/examples/000-CatchMain.cpp

@@ -1,15 +0,0 @@
-// 000-CatchMain.cpp
-
-// In a Catch project with multiple files, dedicate one file to compile the
-// source code of Catch itself and reuse the resulting object file for linking.
-
-// Let Catch provide main():
-#define CATCH_CONFIG_MAIN
-
-#include <catch2/catch.hpp>
-
-// That's it
-
-// Compile implementation of Catch for use with files that do contain tests:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 000-CatchMain.cpp
-// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -c 000-CatchMain.cpp

+ 4 - 7
lib/Catch2/examples/010-TestCase.cpp

@@ -1,11 +1,8 @@
 // 010-TestCase.cpp
+// And write tests in the same file:
+#include <catch2/catch_test_macros.hpp>
 
-// Let Catch provide main():
-#define CATCH_CONFIG_MAIN
-
-#include <catch2/catch.hpp>
-
-int Factorial( int number ) {
+static int Factorial( int number ) {
    return number <= 1 ? number : Factorial( number - 1 ) * number;  // fail
 // return number <= 1 ? 1      : Factorial( number - 1 ) * number;  // pass
 }
@@ -22,7 +19,7 @@ TEST_CASE( "Factorials of 1 and higher are computed (pass)", "[single-file]" ) {
 }
 
 // Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 010-TestCase 010-TestCase.cpp && 010-TestCase --success
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 010-TestCase 010-TestCase.cpp && 010-TestCase --success
 // - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 010-TestCase.cpp && 010-TestCase --success
 
 // Expected compact output (all assertions):

+ 3 - 9
lib/Catch2/examples/020-TestCase-1.cpp

@@ -1,12 +1,6 @@
 // 020-TestCase-1.cpp
 
-// In a Catch project with multiple files, dedicate one file to compile the
-// source code of Catch itself and reuse the resulting object file for linking.
-
-// Let Catch provide main():
-#define CATCH_CONFIG_MAIN
-
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
 
 TEST_CASE( "1: All test cases reside in other .cpp files (empty)", "[multi-file:1]" ) {
 }
@@ -16,8 +10,8 @@ TEST_CASE( "1: All test cases reside in other .cpp files (empty)", "[multi-file:
 // Here just to show there are two source files via option --list-tests.
 
 // Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 020-TestCase-1.cpp
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 020-TestCase TestCase-1.o 020-TestCase-2.cpp && 020-TestCase --success
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 020-TestCase-1.cpp
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 020-TestCase TestCase-1.o 020-TestCase-2.cpp && 020-TestCase --success
 //
 // - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -c 020-TestCase-1.cpp
 // - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -Fe020-TestCase.exe 020-TestCase-1.obj 020-TestCase-2.cpp && 020-TestCase --success

+ 2 - 2
lib/Catch2/examples/020-TestCase-2.cpp

@@ -2,9 +2,9 @@
 
 // main() provided by Catch in file 020-TestCase-1.cpp.
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
 
-int Factorial( int number ) {
+static int Factorial( int number ) {
    return number <= 1 ? number : Factorial( number - 1 ) * number;  // fail
 // return number <= 1 ? 1      : Factorial( number - 1 ) * number;  // pass
 }

+ 5 - 5
lib/Catch2/examples/030-Asn-Require-Check.cpp

@@ -8,11 +8,11 @@
 // - REQUIRE_FALSE() stops at first failure.
 // - CHECK_FALSE() continues after failure.
 
-// main() provided in 000-CatchMain.cpp
+// main() provided by linkage to Catch2WithMain
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
 
-std::string one() {
+static std::string one() {
     return "1";
 }
 
@@ -53,8 +53,8 @@ TEST_CASE( "Assert that something is false (continue after failure)", "[check-fa
 }
 
 // Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 030-Asn-Require-Check 030-Asn-Require-Check.cpp 000-CatchMain.o && 030-Asn-Require-Check --success
-// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 030-Asn-Require-Check.cpp 000-CatchMain.obj && 030-Asn-Require-Check --success
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 030-Asn-Require-Check 030-Asn-Require-Check.cpp && 030-Asn-Require-Check --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 030-Asn-Require-Check.cpp && 030-Asn-Require-Check --success
 
 // Expected compact output (all assertions):
 //

+ 5 - 4
lib/Catch2/examples/100-Fix-Section.cpp

@@ -4,9 +4,10 @@
 // - Sections (this file)
 // - Traditional class-based fixtures
 
-// main() provided in 000-CatchMain.cpp
+// main() provided by linkage to Catch2WithMain
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <vector>
 
 TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
 
@@ -44,8 +45,8 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
 }
 
 // Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 100-Fix-Section 100-Fix-Section.cpp 000-CatchMain.o && 100-Fix-Section --success
-// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 100-Fix-Section.cpp 000-CatchMain.obj && 100-Fix-Section --success
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 100-Fix-Section 100-Fix-Section.cpp && 100-Fix-Section --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 100-Fix-Section.cpp && 100-Fix-Section --success
 
 // Expected compact output (all assertions):
 //

+ 7 - 4
lib/Catch2/examples/110-Fix-ClassFixture.cpp

@@ -4,9 +4,9 @@
 // - Sections
 // - Traditional class-based fixtures (this file)
 
-// main() provided in 000-CatchMain.cpp
+// main() provided by linkage to Catch2WithMain
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
 
 class DBConnection
 {
@@ -52,8 +52,11 @@ TEST_CASE_METHOD( UniqueTestsFixture, "Create Employee/Normal", "[create]" ) {
 }
 
 // Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp 000-CatchMain.o && 110-Fix-ClassFixture --success
-// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 110-Fix-ClassFixture.cpp 000-CatchMain.obj && 110-Fix-ClassFixture --success
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp && 110-Fix-ClassFixture --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 110-Fix-ClassFixture.cpp && 110-Fix-ClassFixture --success
+//
+// Compile with pkg-config:
+// - g++ -std=c++14 -Wall $(pkg-config catch2-with-main --cflags)  -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp $(pkg-config catch2-with-main --libs)
 
 // Expected compact output (all assertions):
 //

+ 4 - 4
lib/Catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp

@@ -1,8 +1,8 @@
 // 120-Bdd-ScenarioGivenWhenThen.cpp
 
-// main() provided in 000-CatchMain.cpp
+// main() provided by linkage with Catch2WithMain
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
 
 SCENARIO( "vectors can be sized and resized", "[vector]" ) {
 
@@ -48,8 +48,8 @@ SCENARIO( "vectors can be sized and resized", "[vector]" ) {
 }
 
 // Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 120-Bdd-ScenarioGivenWhenThen 120-Bdd-ScenarioGivenWhenThen.cpp 000-CatchMain.o && 120-Bdd-ScenarioGivenWhenThen --success
-// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 120-Bdd-ScenarioGivenWhenThen.cpp 000-CatchMain.obj && 120-Bdd-ScenarioGivenWhenThen --success
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 120-Bdd-ScenarioGivenWhenThen 120-Bdd-ScenarioGivenWhenThen.cpp && 120-Bdd-ScenarioGivenWhenThen --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 120-Bdd-ScenarioGivenWhenThen.cpp && 120-Bdd-ScenarioGivenWhenThen --success
 
 // Expected compact output (all assertions):
 //

+ 0 - 27
lib/Catch2/examples/200-Rpt-CatchMain.cpp

@@ -1,27 +0,0 @@
-// 200-Rpt-CatchMain.cpp
-
-// In a Catch project with multiple files, dedicate one file to compile the
-// source code of Catch itself and reuse the resulting object file for linking.
-
-// Let Catch provide main():
-#define CATCH_CONFIG_MAIN
-
-#include <catch2/catch.hpp>
-
-#ifdef   CATCH_EXAMPLE_RPT_1
-#include CATCH_EXAMPLE_RPT_1
-#endif
-
-#ifdef   CATCH_EXAMPLE_RPT_2
-#include CATCH_EXAMPLE_RPT_2
-#endif
-
-#ifdef   CATCH_EXAMPLE_RPT_3
-#include CATCH_EXAMPLE_RPT_3
-#endif
-
-// That's it
-
-// Compile implementation of Catch for use with files that do contain tests:
-// - g++ -std=c++11 -Wall -I$(CATCH_ROOT) -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -o 200-Rpt-CatchMainTeamCity.o -c 200-Rpt-CatchMain.cpp
-// cl -EHsc -I%CATCH_ROOT% -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -Fo200-Rpt-CatchMainTeamCity.obj -c 200-Rpt-CatchMain.cpp

+ 0 - 171
lib/Catch2/examples/207-Rpt-TeamCityReporter.cpp

@@ -1,171 +0,0 @@
-// 207-Rpt-TeamCityReporter.cpp
-
-// Catch has built-in and external reporters:
-// Built-in:
-// - compact
-// - console
-// - junit
-// - xml
-// External:
-// - automake
-// - tap
-// - teamcity (this example)
-
-// main() and reporter code provided in 200-Rpt-CatchMain.cpp
-
-#include <catch2/catch.hpp>
-
-#ifdef _MSC_VER
-# pragma warning (disable : 4702) // Disable warning: unreachable code
-#endif
-
-TEST_CASE( "TeamCity passes unconditionally succeeding assertion", "[teamcity]" ) {
-
-    SUCCEED();
-}
-
-TEST_CASE( "TeamCity reports unconditionally failing assertion", "[teamcity]" ) {
-
-    FAIL();
-}
-
-TEST_CASE( "TeamCity reports failing check", "[teamcity]" ) {
-
-    REQUIRE( 3 == 7 );
-}
-
-TEST_CASE( "TeamCity reports failing check-false", "[teamcity]" ) {
-
-    REQUIRE_FALSE( 3 == 3 );
-}
-
-TEST_CASE( "TeamCity reports failing check-that", "[teamcity]" ) {
-
-    using namespace Catch;
-
-    REQUIRE_THAT( "hello", Contains( "world" ) );
-}
-
-TEST_CASE( "TeamCity reports unexpected exception", "[teamcity]" ) {
-
-    REQUIRE( (throw std::runtime_error("surprise!"), true) );
-}
-
-TEST_CASE( "TeamCity reports undesired exception", "[teamcity]" ) {
-
-    REQUIRE_NOTHROW( (throw std::runtime_error("surprise!"), true) );
-}
-
-TEST_CASE( "TeamCity reports missing expected exception", "[teamcity]" ) {
-
-    REQUIRE_THROWS( true );
-}
-
-TEST_CASE( "TeamCity reports missing specific expected exception", "[teamcity]" ) {
-
-    REQUIRE_THROWS_AS( throw std::bad_alloc(), std::runtime_error );
-}
-
-TEST_CASE( "TeamCity reports unexpected message in expected exception", "[teamcity]" ) {
-
-    using namespace Catch;
-
-    CHECK_THROWS_WITH( throw std::runtime_error("hello"), "world" );
-    CHECK_THROWS_WITH( throw std::runtime_error("hello"), Contains("world") );
-}
-
-struct MyException: public std::runtime_error
-{
-    MyException( char const * text )
-    : std::runtime_error( text ) {}
-    
-    ~MyException() override;
-};
-
-// prevent -Wweak-vtables:
-MyException::~MyException() = default;
-
-struct MyExceptionMatcher : Catch::MatcherBase< std::runtime_error >
-{
-    std::string m_text;
-
-    MyExceptionMatcher( char const * text )
-    : m_text( text )
-    {}
-
-    ~MyExceptionMatcher() override;
-    
-    bool match( std::runtime_error const & arg ) const override
-    { 
-        return m_text == arg.what() ; 
-    }
-    
-    std::string describe() const override 
-    { 
-        return "it's me";
-    }
-};
-
-// prevent -Wweak-vtables:
-MyExceptionMatcher::~MyExceptionMatcher() = default;
-
-TEST_CASE( "TeamCity failing check-throws-matches", "[teamcity]" ) {
-
-    CHECK_THROWS_MATCHES( throw MyException("hello"), MyException, MyExceptionMatcher("world") );
-}
-
-// [!throws] - lets Catch know that this test is likely to throw an exception even if successful. 
-// This causes the test to be excluded when running with -e or --nothrow.
-
-// No special effects for the reporter.
-
-TEST_CASE( "TeamCity throwing exception with tag [!throws]", "[teamcity][!throws]" ) {
-
-    REQUIRE_THROWS( throw std::runtime_error("unsurprisingly") );
-}
-
-// [!mayfail] - doesn't fail the test if any given assertion fails (but still reports it). This can be useful to flag a work-in-progress, or a known issue that you don't want to immediately fix but still want to track in your tests.
-
-TEST_CASE( "TeamCity failing assertion with tag [!mayfail]", "[teamcity][!mayfail] " ) {
-
-    REQUIRE( 3 == 7 );  // doesn't fail test case this time, reports: testIgnored
-    REQUIRE( 3 == 3 );
-}
-
-// [!shouldfail] - like [!mayfail] but fails the test if it passes. 
-// This can be useful if you want to be notified of accidental, or third-party, fixes.
-
-TEST_CASE( "TeamCity succeeding assertion with tag [!shouldfail]", "[teamcity][!shouldfail]" ) {
-
-    SUCCEED( "Marked [!shouldfail]" );
-}
-
-// Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_ROOT) -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -o 200-Rpt-CatchMainTeamCity.o -c 200-Rpt-CatchMain.cpp
-// - g++ -std=c++11 -Wall -I$(CATCH_ROOT) -o 207-Rpt-TeamCityReporter 207-Rpt-TeamCityReporter.cpp 200-Rpt-CatchMainTeamCity.o && 207-Rpt-TeamCityReporter --list-reporters
-//
-// - cl -EHsc -I%CATCH_ROOT% -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -Fo200-Rpt-CatchMainTeamCity.obj -c 200-Rpt-CatchMain.cpp
-// - cl -EHsc -I%CATCH_ROOT% 207-Rpt-TeamCityReporter.cpp 200-Rpt-CatchMainTeamCity.o && 207-Rpt-TeamCityReporter --list-reporters
-
-// Compilation output (--list-reporters):
-// Available reporters:
-//   compact:   Reports test results on a single line, suitable for IDEs
-//   console:   Reports test results as plain lines of text
-//   junit:     Reports test results in an XML format that looks like Ant's
-//                junitreport target
-//   teamcity:  Reports test results as TeamCity service messages
-//   xml:       Reports test results as an XML document
-
-// Expected output (abbreviated and broken into shorter lines):
-//
-// prompt> 207-Rpt-TeamCityReporter.exe --reporter teamcity
-// ##teamcity[testSuiteStarted name='207-Rpt-TeamCityReporter.exe']
-// ##teamcity[testStarted name='TeamCity passes unconditionally succeeding assertion']
-// ##teamcity[testFinished name='TeamCity passes unconditionally succeeding assertion' duration='1']
-// ##teamcity[testStarted name='TeamCity reports unconditionally failing assertion']
-// ##teamcity[testFailed name='TeamCity reports unconditionally failing assertion' /
-// message='.../examples/207-Rpt-TeamCityReporter.cpp:23|n/
-// ...............................................................................|n|n/
-// .../examples/207-Rpt-TeamCityReporter.cpp:25|nexplicit failure']
-// ##teamcity[testFinished name='TeamCity reports unconditionally failing assertion' duration='3']
-// ...

+ 51 - 44
lib/Catch2/examples/210-Evt-EventListeners.cpp

@@ -5,22 +5,26 @@
 // 2. My listener and registration
 // 3. Test cases
 
-// main() provided in 000-CatchMain.cpp
-
-// Let Catch provide the required interfaces:
-#define CATCH_CONFIG_EXTERNAL_INTERFACES
-
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/reporters/catch_reporter_event_listener.hpp>
+#include <catch2/reporters/catch_reporter_registrars.hpp>
+#include <catch2/catch_test_case_info.hpp>
 #include <iostream>
 
 // -----------------------------------------------------------------------
 // 1. Printing of listener data:
 //
 
+
+namespace {
 std::string ws(int const level) {
     return std::string( 2 * level, ' ' );
 }
 
+std::ostream& operator<<(std::ostream& out, Catch::Tag t) {
+    return out << "original: " << t.original;
+}
+
 template< typename T >
 std::ostream& operator<<( std::ostream& os, std::vector<T> const& v ) {
     os << "{ ";
@@ -28,7 +32,6 @@ std::ostream& operator<<( std::ostream& os, std::vector<T> const& v ) {
         os << x << ", ";
     return os << "}";
 }
-
 // struct SourceLineInfo {
 //     char const* file;
 //     std::size_t line;
@@ -119,32 +122,36 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
     os << ws(level+1) << "- aborting: " << info.aborting << "\n";
 }
 
-// struct TestCaseInfo {
-//     enum SpecialProperties{
-//         None = 0,
-//         IsHidden = 1 << 1,
-//         ShouldFail = 1 << 2,
-//         MayFail = 1 << 3,
-//         Throws = 1 << 4,
-//         NonPortable = 1 << 5,
-//         Benchmark = 1 << 6
-//     };
+//    struct Tag {
+//        StringRef original, lowerCased;
+//    };
 //
-//     bool isHidden() const;
-//     bool throws() const;
-//     bool okToFail() const;
-//     bool expectedToFail() const;
 //
-//     std::string tagsAsString() const;
+//    enum class TestCaseProperties : uint8_t {
+//        None = 0,
+//        IsHidden = 1 << 1,
+//        ShouldFail = 1 << 2,
+//        MayFail = 1 << 3,
+//        Throws = 1 << 4,
+//        NonPortable = 1 << 5,
+//        Benchmark = 1 << 6
+//    };
 //
-//     std::string name;
-//     std::string className;
-//     std::string description;
-//     std::vector<std::string> tags;
-//     std::vector<std::string> lcaseTags;
-//     SourceLineInfo lineInfo;
-//     SpecialProperties properties;
-// };
+//
+//    struct TestCaseInfo : NonCopyable {
+//
+//        bool isHidden() const;
+//        bool throws() const;
+//        bool okToFail() const;
+//        bool expectedToFail() const;
+//
+//
+//        std::string name;
+//        std::string className;
+//        std::vector<Tag> tags;
+//        SourceLineInfo lineInfo;
+//        TestCaseProperties properties = TestCaseProperties::None;
+//    };
 
 void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseInfo const& info ) {
     os << ws(level  ) << title << ":\n"
@@ -155,11 +162,9 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
        << ws(level+1) << "- tagsAsString(): '"  << info.tagsAsString() << "'\n"
        << ws(level+1) << "- name: '"            << info.name << "'\n"
        << ws(level+1) << "- className: '"       << info.className << "'\n"
-       << ws(level+1) << "- description: '"     << info.description << "'\n"
-       << ws(level+1) << "- tags: "             << info.tags << "\n"
-       << ws(level+1) << "- lcaseTags: "        << info.lcaseTags << "\n";
+       << ws(level+1) << "- tags: "             << info.tags << "\n";
     print( os, level+1 , "- lineInfo", info.lineInfo );
-    os << ws(level+1) << "- properties (flags): 0x" << std::hex << info.properties << std::dec << "\n";
+    os << ws(level+1) << "- properties (flags): 0x" << std::hex << static_cast<uint32_t>(info.properties) << std::dec << "\n";
 }
 
 // struct TestCaseStats {
@@ -172,7 +177,7 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
 
 void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseStats const& info ) {
     os << ws(level  ) << title << ":\n";
-    print( os, level+1 , "- testInfo", info.testInfo );
+    print( os, level+1 , "- testInfo", *info.testInfo );
     print( os, level+1 , "- totals"  , info.totals   );
     os << ws(level+1) << "- stdOut: "   << info.stdOut << "\n"
        << ws(level+1) << "- stdErr: "   << info.stdErr << "\n"
@@ -273,8 +278,8 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
     print( os, level+1 , "- getSourceInfo(): ", info.getSourceInfo() );
     os << ws(level+1) << "- getTestMacroName(): '"  << info.getTestMacroName() << "'\n";
 
-//    print( os, level+1 , "- *** m_info (AssertionInfo)", info.m_info );
-//    print( os, level+1 , "- *** m_resultData (AssertionResultData)", info.m_resultData );
+    print( os, level+1 , "- *** m_info (AssertionInfo)", info.m_info );
+    print( os, level+1 , "- *** m_resultData (AssertionResultData)", info.m_resultData );
 }
 
 // struct AssertionStats {
@@ -297,12 +302,13 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
 char const * dashed_line =
     "--------------------------------------------------------------------------";
 
-struct MyListener : Catch::TestEventListenerBase {
 
-    using TestEventListenerBase::TestEventListenerBase; // inherit constructor
+struct MyListener : Catch::EventListenerBase {
+
+    using EventListenerBase::EventListenerBase; // inherit constructor
 
     // Get rid of Wweak-tables
-    ~MyListener();
+    ~MyListener() override;
 
     // The whole test run starting
     void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
@@ -360,13 +366,14 @@ struct MyListener : Catch::TestEventListenerBase {
         print( std::cout, 1, "- assertionInfo", assertionInfo );
     }
 
-    bool assertionEnded( Catch::AssertionStats const& assertionStats ) override {
+    void assertionEnded( Catch::AssertionStats const& assertionStats ) override {
         std::cout << "\nEvent: assertionEnded:\n";
         print( std::cout, 1, "- assertionStats", assertionStats );
-        return true;
     }
 };
 
+} // end anonymous namespace
+
 CATCH_REGISTER_LISTENER( MyListener )
 
 // Get rid of Wweak-tables
@@ -413,8 +420,8 @@ TEST_CASE_METHOD( Fixture, "3: Testcase with class-based fixture", "[tag-C][tag-
 }
 
 // Compile & run:
-// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 210-Evt-EventListeners 210-Evt-EventListeners.cpp 000-CatchMain.o && 210-Evt-EventListeners --success
-// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 210-Evt-EventListeners.cpp 000-CatchMain.obj && 210-Evt-EventListeners --success
+// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 210-Evt-EventListeners 210-Evt-EventListeners.cpp && 210-Evt-EventListeners --success
+// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 210-Evt-EventListeners.cpp && 210-Evt-EventListeners --success
 
 // Expected compact output (all assertions):
 //

+ 3 - 4
lib/Catch2/examples/231-Cfg-OutputStreams.cpp

@@ -5,11 +5,10 @@
 // semantic, because it buffers the output. For most uses however,
 // there is no important difference between having `std::cerr` buffered
 // or unbuffered.
+#include <catch2/catch_test_macros.hpp>
 
-#define CATCH_CONFIG_NOSTDOUT
-#define CATCH_CONFIG_MAIN
-#include <catch2/catch.hpp>
-
+#include <sstream>
+#include <cstdio>
 
 class out_buff : public std::stringbuf {
     std::FILE* m_stream;

+ 13 - 3
lib/Catch2/examples/300-Gen-OwnGenerator.cpp

@@ -4,10 +4,14 @@
 // Specifically we will implement a random number generator for integers
 // It will have infinite capacity and settable lower/upper bound
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
 
 #include <random>
 
+namespace {
+
 // This class shows how to implement a simple generator for Catch tests
 class RandomIntGenerator : public Catch::Generators::IGenerator<int> {
     std::minstd_rand m_rand;
@@ -38,9 +42,15 @@ int const& RandomIntGenerator::get() const {
 // Notice that it returns an instance of GeneratorWrapper<int>, which
 // is a value-wrapper around std::unique_ptr<IGenerator<int>>.
 Catch::Generators::GeneratorWrapper<int> random(int low, int high) {
-    return Catch::Generators::GeneratorWrapper<int>(std::unique_ptr<Catch::Generators::IGenerator<int>>(new RandomIntGenerator(low, high)));
+    return Catch::Generators::GeneratorWrapper<int>(
+        new RandomIntGenerator(low, high)
+        // Another possibility:
+        // Catch::Detail::make_unique<RandomIntGenerator>(low, high)
+    );
 }
 
+} // end anonymous namespaces
+
 // The two sections in this test case are equivalent, but the first one
 // is much more readable/nicer to use
 TEST_CASE("Generating random ints", "[example][generator]") {
@@ -50,7 +60,7 @@ TEST_CASE("Generating random ints", "[example][generator]") {
         REQUIRE(i <= 100);
     }
     SECTION("Creating the random generator directly") {
-        auto i = GENERATE(take(100, GeneratorWrapper<int>(std::unique_ptr<IGenerator<int>>(new RandomIntGenerator(-100, 100)))));
+        auto i = GENERATE(take(100, GeneratorWrapper<int>(Catch::Detail::make_unique<RandomIntGenerator>(-100, 100))));
         REQUIRE(i >= -100);
         REQUIRE(i <= 100);
     }

+ 9 - 7
lib/Catch2/examples/301-Gen-MapTypeConversion.cpp

@@ -5,11 +5,14 @@
 // that converts the strings using stoi, so the returned type is actually
 // an int.
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
 
 #include <string>
 #include <sstream>
 
+namespace {
+
 // Returns a line from a stream. You could have it e.g. read lines from
 // a file, but to avoid problems with paths in examples, we will use
 // a fixed stringstream.
@@ -20,12 +23,12 @@ public:
     LineGenerator() {
         m_stream.str("1\n2\n3\n4\n");
         if (!next()) {
-            throw Catch::GeneratorException("Couldn't read a single line");
+            Catch::Generators::Detail::throw_generator_exception("Couldn't read a single line");
         }
     }
 
     std::string const& get() const override;
-    
+
     bool next() override {
         return !!std::getline(m_stream, m_line);
     }
@@ -40,18 +43,17 @@ std::string const& LineGenerator::get() const {
 // is a value-wrapper around std::unique_ptr<IGenerator<std::string>>.
 Catch::Generators::GeneratorWrapper<std::string> lines(std::string /* ignored for example */) {
     return Catch::Generators::GeneratorWrapper<std::string>(
-        std::unique_ptr<Catch::Generators::IGenerator<std::string>>(
-            new LineGenerator()
-        )
+        new LineGenerator()
     );
 }
 
+} // end anonymous namespace
 
 
 TEST_CASE("filter can convert types inside the generator expression", "[example][generator]") {
     auto num = GENERATE(map<int>([](std::string const& line) { return std::stoi(line); },
                                  lines("fake-file")));
-                                 
+
     REQUIRE(num > 0);
 }
 

+ 4 - 3
lib/Catch2/examples/302-Gen-Table.cpp

@@ -2,7 +2,8 @@
 // Shows how to use table to run a test many times with different inputs. Lifted from examples on
 // issue #850.
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators.hpp>
 #include <string>
 
 struct TestSubject {
@@ -43,11 +44,11 @@ TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][genera
 
 /* Possible simplifications where less legacy toolchain support is needed:
  *
- * - With libstdc++6 or newer, the make_tuple() calls can be omitted
+ * - With libstdc++6 or newer, the make_tuple() calls can be ommitted
  * (technically C++17 but does not require -std in GCC/Clang). See
  *   https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
  *
- * - In C++17 mode std::tie() and the preceeding variable delcarations can be
+ * - In C++17 mode std::tie() and the preceding variable delcarations can be
  * replaced by structured bindings: auto [test_input, expected] = GENERATE(
  * table<std::string, size_t>({ ...
  */

+ 3 - 1
lib/Catch2/examples/310-Gen-VariablesInGenerators.cpp

@@ -6,7 +6,9 @@
 // _WILL_ outlive the variables -- thus they should be either captured
 // by value directly, or copied by the generators during construction.
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
 
 TEST_CASE("Generate random doubles across different ranges",
           "[generator][example][advanced]") {

+ 3 - 1
lib/Catch2/examples/311-Gen-CustomCapture.cpp

@@ -9,7 +9,9 @@
 // per-variable custom capture list, this example shows how to achieve
 // that.
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/generators/catch_generators_adapters.hpp>
+#include <catch2/generators/catch_generators_random.hpp>
 
 TEST_CASE("Generate random doubles across different ranges",
           "[generator][example][advanced]") {

+ 31 - 126
lib/Catch2/examples/CMakeLists.txt

@@ -1,44 +1,30 @@
-#
-# Build examples.
-#
-# Requires CATCH_BUILD_EXAMPLES to be defined 'true', see ../CMakeLists.txt.
-#
+cmake_minimum_required( VERSION 3.10 )
 
-cmake_minimum_required( VERSION 3.0 )
-
-project( CatchExamples CXX )
+project( Catch2Examples LANGUAGES CXX )
 
 message( STATUS "Examples included" )
 
-# define folders used:
-
-set( EXAMPLES_DIR ${CATCH_DIR}/examples )
-set( HEADER_DIR   ${CATCH_DIR}/single_include )
-set( REPORTER_HEADER_DIR ${CATCH_DIR}/include/reporters )
-
-# single-file sources:
 
-set( SOURCES_SINGLE_FILE
-    010-TestCase.cpp
-    231-Cfg-OutputStreams.cpp
+# Some one-offs first:
+# 1) Tests and main in one file
+add_executable( 010-TestCase
+  010-TestCase.cpp
 )
 
-# multiple-file modules:
-
-set( SOURCES_020
-    020-TestCase-1.cpp
-    020-TestCase-2.cpp
+# 2) Tests and main across two files
+add_executable( 020-MultiFile
+  020-TestCase-1.cpp
+  020-TestCase-2.cpp
 )
 
-# main for idiomatic test sources:
-
-set( SOURCES_IDIOMATIC_MAIN
-    000-CatchMain.cpp
+add_executable(231-Cfg_OutputStreams
+    231-Cfg-OutputStreams.cpp
 )
+target_link_libraries(231-Cfg_OutputStreams Catch2_buildall_interface)
+target_compile_definitions(231-Cfg_OutputStreams PUBLIC CATCH_CONFIG_NOSTDOUT)
 
-# sources to combine with 000-CatchMain.cpp:
-
-set( SOURCES_IDIOMATIC_TESTS
+# These examples use the standard separate compilation
+set( SOURCES_IDIOMATIC_EXAMPLES
     030-Asn-Require-Check.cpp
     100-Fix-Section.cpp
     110-Fix-ClassFixture.cpp
@@ -51,108 +37,27 @@ set( SOURCES_IDIOMATIC_TESTS
     311-Gen-CustomCapture.cpp
 )
 
-# main-s for reporter-specific test sources:
-
-set( SOURCES_REPORTERS_MAIN
-    200-Rpt-CatchMain.cpp
-)
-
-string( REPLACE ".cpp" "" BASENAMES_REPORTERS_MAIN 200-Rpt-CatchMain.cpp )
-
-set( NAMES_REPORTERS TeamCity )
-
-foreach( reporter ${NAMES_REPORTERS} )
-    list( APPEND SOURCES_SPECIFIC_REPORTERS_MAIN ${BASENAMES_REPORTERS_MAIN}${reporter}.cpp )
-endforeach()
-
-# sources to combine with 200-Rpt-CatchMain{Reporter}.cpp:
-
-set( SOURCES_REPORTERS_TESTS
-    207-Rpt-TeamCityReporter.cpp
-)
-
-# check if all sources are listed, warn if not:
+string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_EXAMPLES "${SOURCES_IDIOMATIC_EXAMPLES}" )
+set( TARGETS_IDIOMATIC_EXAMPLES ${BASENAMES_IDIOMATIC_EXAMPLES} )
 
-set( SOURCES_ALL
-    ${SOURCES_020}
-    ${SOURCES_SINGLE_FILE}
-    ${SOURCES_IDIOMATIC_MAIN}
-    ${SOURCES_IDIOMATIC_TESTS}
-    ${SOURCES_REPORTERS_MAIN}
-    ${SOURCES_REPORTERS_TESTS}
-)
 
-foreach( name ${SOURCES_ALL} )
-    list( APPEND SOURCES_ALL_PATH ${EXAMPLES_DIR}/${name} )
+foreach( name ${TARGETS_IDIOMATIC_EXAMPLES} )
+    add_executable( ${name}
+      ${EXAMPLES_DIR}/${name}.cpp )
 endforeach()
 
-CheckFileList( SOURCES_ALL_PATH ${EXAMPLES_DIR} )
-
-# create target names:
-
-string( REPLACE ".cpp" "" BASENAMES_SINGLE_FILE     "${SOURCES_SINGLE_FILE}" )
-string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_TESTS "${SOURCES_IDIOMATIC_TESTS}" )
-string( REPLACE ".cpp" "" BASENAMES_REPORTERS_TESTS "${SOURCES_REPORTERS_TESTS}" )
-string( REPLACE ".cpp" "" BASENAMES_REPORTERS_MAIN  "${SOURCES_REPORTERS_MAIN}" )
-
-set( TARGETS_SINGLE_FILE     ${BASENAMES_SINGLE_FILE} )
-set( TARGETS_IDIOMATIC_TESTS ${BASENAMES_IDIOMATIC_TESTS} )
-set( TARGETS_REPORTERS_TESTS ${BASENAMES_REPORTERS_TESTS} )
-set( TARGETS_REPORTERS_MAIN  ${BASENAMES_REPORTERS_MAIN} )
-
-set( TARGETS_ALL
-    ${TARGETS_SINGLE_FILE}
-    020-TestCase
-    ${TARGETS_IDIOMATIC_TESTS} CatchMain
-    ${TARGETS_REPORTERS_TESTS} CatchMainTeamCity
+set(ALL_EXAMPLE_TARGETS
+  ${TARGETS_IDIOMATIC_EXAMPLES}
+  010-TestCase
+  020-MultiFile
 )
 
-# define program targets:
-
-add_library( CatchMain         OBJECT ${EXAMPLES_DIR}/${SOURCES_IDIOMATIC_MAIN} ${HEADER_DIR}/catch2/catch.hpp )
-#add_library( CatchMainAutomake OBJECT ${EXAMPLES_DIR}/200-Rpt-CatchMain.cpp ${HEADER_DIR}/catch2/catch.hpp )
-#add_library( CatchMainTap      OBJECT ${EXAMPLES_DIR}/200-Rpt-CatchMain.cpp ${HEADER_DIR}/catch2/catch.hpp )
-add_library( CatchMainTeamCity OBJECT ${EXAMPLES_DIR}/200-Rpt-CatchMain.cpp ${HEADER_DIR}/catch2/catch.hpp )
-
-#target_compile_definitions( CatchMainAutomake PRIVATE CATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_automake.hpp\" )
-#target_compile_definitions( CatchMainTap      PRIVATE CATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_tap.hpp\" )
-target_compile_definitions( CatchMainTeamCity PRIVATE CATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" )
-
-foreach( name ${TARGETS_SINGLE_FILE} )
-    add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp ${HEADER_DIR}/catch2/catch.hpp )
-endforeach()
-
-foreach( name ${TARGETS_IDIOMATIC_TESTS} )
-    add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp $<TARGET_OBJECTS:CatchMain> ${HEADER_DIR}/catch2/catch.hpp )
-endforeach()
-
-add_executable( 020-TestCase ${EXAMPLES_DIR}/020-TestCase-1.cpp ${EXAMPLES_DIR}/020-TestCase-2.cpp ${HEADER_DIR}/catch2/catch.hpp )
-
-#add_executable( 207-Rpt-AutomakeReporter ${EXAMPLES_DIR}/207-Rpt-AutomakeReporter.cpp $<TARGET_OBJECTS:CatchMainAutomake> ${HEADER_DIR}/catch2/catch.hpp )
-#add_executable( 207-Rpt-TapReporter      ${EXAMPLES_DIR}/207-Rpt-TapReporter.cpp      $<TARGET_OBJECTS:CatchMainTap>      ${HEADER_DIR}/catch2/catch.hpp )
-add_executable( 207-Rpt-TeamCityReporter ${EXAMPLES_DIR}/207-Rpt-TeamCityReporter.cpp $<TARGET_OBJECTS:CatchMainTeamCity> ${HEADER_DIR}/catch2/catch.hpp )
-
-#foreach( name ${TARGETS_REPORTERS_TESTS} )
-#    add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp $<TARGET_OBJECTS:CatchMain> ${HEADER_DIR}/catch2/catch.hpp )
-#endforeach()
-
-foreach( name ${TARGETS_ALL} )
-    target_include_directories( ${name} PRIVATE ${HEADER_DIR} ${CATCH_DIR} )
-
-    set_property(TARGET ${name} PROPERTY CXX_STANDARD 11)
+foreach( name ${ALL_EXAMPLE_TARGETS} )
+    target_link_libraries( ${name} Catch2 Catch2WithMain )
+    set_property(TARGET ${name} PROPERTY CXX_STANDARD 14)
     set_property(TARGET ${name} PROPERTY CXX_EXTENSIONS OFF)
-
-    # Add desired warnings
-    if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
-        target_compile_options( ${name}  PRIVATE -Wall -Wextra -Wunreachable-code )
-    endif()
-    # Clang specific warning go here
-    if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
-        # Actually keep these
-        target_compile_options( ${name}  PRIVATE -Wweak-vtables -Wexit-time-destructors -Wglobal-constructors -Wmissing-noreturn )
-    endif()
-    if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
-        target_compile_options( ${name}  PRIVATE /W4 /w44265 /WX )
-    endif()
 endforeach()
 
+
+list(APPEND CATCH_WARNING_TARGETS ${ALL_EXAMPLE_TARGETS})
+set(CATCH_WARNING_TARGETS ${CATCH_WARNING_TARGETS} PARENT_SCOPE)

+ 15 - 1
lib/Catch2/contrib/Catch.cmake

@@ -116,6 +116,13 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
     ``--out dir/<test_name>suffix``. This can be used to add a file extension to
     the output e.g. ".xml".
 
+  ``DL_PATHS path...``
+    Specifies paths that need to be set for the dynamic linker to find shared
+    libraries/DLLs when running the test executable (PATH/LD_LIBRARY_PATH respectively).
+    These paths will both be set when retrieving the list of test cases from the
+    test executable and when the tests are executed themselves. This requires
+    cmake/ctest >= 3.22.
+
 #]=======================================================================]
 
 #------------------------------------------------------------------------------
@@ -124,7 +131,7 @@ function(catch_discover_tests TARGET)
     ""
     ""
     "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX"
-    "TEST_SPEC;EXTRA_ARGS;PROPERTIES"
+    "TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS"
     ${ARGN}
   )
 
@@ -135,6 +142,12 @@ function(catch_discover_tests TARGET)
     set(_TEST_LIST ${TARGET}_TESTS)
   endif()
 
+  if (_DL_PATHS)
+    if(${CMAKE_VERSION} VERSION_LESS "3.22.0")
+        message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22")
+    endif()
+  endif()
+
   ## Generate a unique name based on the extra arguments
   string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}")
   string(SUBSTRING ${args_hash} 0 7 args_hash)
@@ -164,6 +177,7 @@ function(catch_discover_tests TARGET)
             -D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
             -D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
             -D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
+            -D "TEST_DL_PATHS=${_DL_PATHS}"
             -D "CTEST_FILE=${ctest_tests_file}"
             -P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
     VERBATIM

+ 33 - 12
lib/Catch2/contrib/CatchAddTests.cmake

@@ -10,10 +10,19 @@ set(reporter ${TEST_REPORTER})
 set(output_dir ${TEST_OUTPUT_DIR})
 set(output_prefix ${TEST_OUTPUT_PREFIX})
 set(output_suffix ${TEST_OUTPUT_SUFFIX})
+set(dl_paths ${TEST_DL_PATHS})
 set(script)
 set(suite)
 set(tests)
 
+if(WIN32)
+  set(dl_paths_variable_name PATH)
+elseif(APPLE)
+  set(dl_paths_variable_name DYLD_LIBRARY_PATH)
+else()
+  set(dl_paths_variable_name LD_LIBRARY_PATH)
+endif()
+
 function(add_command NAME)
   set(_args "")
   # use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
@@ -35,18 +44,19 @@ if(NOT EXISTS "${TEST_EXECUTABLE}")
     "Specified test executable '${TEST_EXECUTABLE}' does not exist"
   )
 endif()
+
+if(dl_paths)
+  cmake_path(CONVERT "${dl_paths}" TO_NATIVE_PATH_LIST paths)
+  set(ENV{${dl_paths_variable_name}} "${paths}")
+endif()
+
 execute_process(
-  COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
+  COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet
   OUTPUT_VARIABLE output
   RESULT_VARIABLE result
   WORKING_DIRECTORY "${TEST_WORKING_DIR}"
 )
-# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
-if(${result} EQUAL 0)
-  message(WARNING
-    "Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
-  )
-elseif(${result} LESS 0)
+if(NOT ${result} EQUAL 0)
   message(FATAL_ERROR
     "Error running test executable '${TEST_EXECUTABLE}':\n"
     "  Result: ${result}\n"
@@ -63,11 +73,7 @@ execute_process(
   RESULT_VARIABLE reporters_result
   WORKING_DIRECTORY "${TEST_WORKING_DIR}"
 )
-if(${reporters_result} EQUAL 0)
-  message(WARNING
-    "Test executable '${TEST_EXECUTABLE}' contains no reporters!\n"
-  )
-elseif(${reporters_result} LESS 0)
+if(NOT ${reporters_result} EQUAL 0)
   message(FATAL_ERROR
     "Error running test executable '${TEST_EXECUTABLE}':\n"
     "  Result: ${reporters_result}\n"
@@ -94,6 +100,13 @@ if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
   endif()
 endif()
 
+if(dl_paths)
+  foreach(path ${dl_paths})
+    cmake_path(NATIVE_PATH path native_path)
+    list(APPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}")
+  endforeach()
+endif()
+
 # Parse output
 foreach(line ${output})
   set(test ${line})
@@ -124,6 +137,14 @@ foreach(line ${output})
     WORKING_DIRECTORY "${TEST_WORKING_DIR}"
     ${properties}
   )
+
+   if(environment_modifications)
+     add_command(set_tests_properties
+       "${prefix}${test}${suffix}"
+       PROPERTIES
+       ENVIRONMENT_MODIFICATION "${environment_modifications}")
+   endif()
+
   list(APPEND tests "${prefix}${test}${suffix}")
 endforeach()
 

+ 66 - 0
lib/Catch2/extras/CatchShardTests.cmake

@@ -0,0 +1,66 @@
+
+#              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
+
+# Supported optional args:
+#  * SHARD_COUNT - number of shards to split target's tests into
+#  * REPORTER    - reporter spec to use for tests
+#  * TEST_SPEC   - test spec used for filtering tests
+function(catch_add_sharded_tests TARGET)
+  if (${CMAKE_VERSION} VERSION_LESS "3.10.0")
+    message(FATAL_ERROR "add_sharded_catch_tests only supports CMake versions 3.10.0 and up")
+  endif()
+
+  cmake_parse_arguments(
+    ""
+    ""
+    "SHARD_COUNT;REPORTER;TEST_SPEC"
+    ""
+    ${ARGN}
+  )
+  
+  if (NOT DEFINED _SHARD_COUNT)
+    set(_SHARD_COUNT 2)
+  endif()
+
+  # Generate a unique name based on the extra arguments
+  string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX} ${_SHARD_COUNT}")
+  string(SUBSTRING ${args_hash} 0 7 args_hash)
+
+  set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-sharded-tests-include-${args_hash}.cmake")
+  set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-sharded-tests-impl-${args_hash}.cmake")
+
+  file(WRITE "${ctest_include_file}"
+    "if(EXISTS \"${ctest_tests_file}\")\n"
+    "  include(\"${ctest_tests_file}\")\n"
+    "else()\n"
+    "  add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
+    "endif()\n"
+  )
+
+  set_property(DIRECTORY
+    APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
+  )
+
+  set(shard_impl_script_file "${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake")
+
+  add_custom_command(
+    TARGET ${TARGET} POST_BUILD
+    BYPRODUCTS "${ctest_tests_file}"
+    COMMAND "${CMAKE_COMMAND}"
+            -D "TARGET_NAME=${TARGET}"
+            -D "TEST_BINARY=$<TARGET_FILE:${TARGET}>"
+            -D "CTEST_FILE=${ctest_tests_file}"
+            -D "SHARD_COUNT=${_SHARD_COUNT}"
+            -D "REPORTER_SPEC=${_REPORTER}"
+            -D "TEST_SPEC=${_TEST_SPEC}"
+            -P "${shard_impl_script_file}"
+    VERBATIM
+  )
+
+
+endfunction()

+ 52 - 0
lib/Catch2/extras/CatchShardTestsImpl.cmake

@@ -0,0 +1,52 @@
+
+#              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
+
+# Indirection for CatchShardTests that allows us to delay the script
+# file generation until build time.
+
+# Expected args:
+#  * TEST_BINARY - full path to the test binary to run sharded
+#  * CTEST_FILE  - full path to ctest script file to write to
+#  * TARGET_NAME - name of the target to shard (used for test names)
+#  * SHARD_COUNT - number of shards to split the binary into
+# Optional args:
+#  * REPORTER_SPEC - reporter specs to be passed down to the binary
+#  * TEST_SPEC     - test spec to pass down to the test binary
+
+if(NOT EXISTS "${TEST_BINARY}")
+  message(FATAL_ERROR
+    "Specified test binary '${TEST_BINARY}' does not exist"
+  )
+endif()
+
+set(other_args "")
+if (TEST_SPEC)
+  set(other_args "${other_args} ${TEST_SPEC}")
+endif()
+if (REPORTER_SPEC)
+  set(other_args "${other_args} --reporter ${REPORTER_SPEC}")
+endif()
+
+# foreach RANGE in cmake is inclusive of the end, so we have to adjust it
+math(EXPR adjusted_shard_count "${SHARD_COUNT} - 1")
+
+file(WRITE "${CTEST_FILE}"
+  "string(RANDOM LENGTH 8 ALPHABET \"0123456789abcdef\" rng_seed)\n"
+  "\n"
+  "foreach(shard_idx RANGE ${adjusted_shard_count})\n"
+  "  add_test(${TARGET_NAME}-shard-" [[${shard_idx}]] "/${adjusted_shard_count}\n"
+  "    ${TEST_BINARY}"
+  " --shard-index " [[${shard_idx}]]
+  " --shard-count ${SHARD_COUNT}"
+  " --rng-seed " [[0x${rng_seed}]]
+  " --order rand"
+  "${other_args}"
+  "\n"
+  "  )\n"
+  "endforeach()\n"
+)

lib/Catch2/contrib/ParseAndAddCatchTests.cmake → lib/Catch2/extras/ParseAndAddCatchTests.cmake


+ 10395 - 0
lib/Catch2/extras/catch_amalgamated.cpp

@@ -0,0 +1,10395 @@
+//              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
+
+//  Catch v3.1.1
+//  Generated: 2022-10-17 18:47:22.400176
+//  ----------------------------------------------------------
+//  This file is an amalgamation of multiple different files.
+//  You probably shouldn't edit it directly.
+//  ----------------------------------------------------------
+
+#include "catch_amalgamated.hpp"
+
+
+#ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
+#define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
+
+
+#if defined(CATCH_PLATFORM_WINDOWS)
+
+// We might end up with the define made globally through the compiler,
+// and we don't want to trigger warnings for this
+#if !defined(NOMINMAX)
+#  define NOMINMAX
+#endif
+#if !defined(WIN32_LEAN_AND_MEAN)
+#  define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+
+#endif // defined(CATCH_PLATFORM_WINDOWS)
+
+#endif // CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
+
+
+
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            ChronometerConcept::~ChronometerConcept() = default;
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+
+
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            BenchmarkFunction::callable::~callable() = default;
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+
+
+#include <exception>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            struct optimized_away_error : std::exception {
+                const char* what() const noexcept override;
+            };
+
+            const char* optimized_away_error::what() const noexcept {
+                return "could not measure benchmark, maybe it was optimized away";
+            }
+
+            void throw_optimized_away_error() {
+                Catch::throw_exception(optimized_away_error{});
+            }
+
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+
+// Adapted from donated nonius code.
+
+
+
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+#include <random>
+
+
+#if defined(CATCH_CONFIG_USE_ASYNC)
+#include <future>
+#endif
+
+namespace {
+
+using Catch::Benchmark::Detail::sample;
+
+     template <typename URng, typename Estimator>
+     sample resample(URng& rng, unsigned int resamples, std::vector<double>::iterator first, std::vector<double>::iterator last, Estimator& estimator) {
+         auto n = static_cast<size_t>(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[static_cast<std::ptrdiff_t>(dist(rng))]; });
+             return estimator(resampled.begin(), resampled.end());
+         });
+         std::sort(out.begin(), out.end());
+         return out;
+     }
+
+
+    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 {
+
+#if defined( __GNUC__ ) || defined( __clang__ )
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+            bool directCompare( double lhs, double rhs ) { return lhs == rhs; }
+#if defined( __GNUC__ ) || defined( __clang__ )
+#    pragma GCC diagnostic pop
+#endif
+
+            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 ( directCompare( 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 static_cast<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, unsigned 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
+
+
+
+#include <cmath>
+#include <limits>
+
+namespace {
+
+// 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);
+}
+
+}
+
+namespace Catch {
+
+    Approx::Approx ( double value )
+    :   m_epsilon( std::numeric_limits<float>::epsilon()*100. ),
+        m_margin( 0.0 ),
+        m_scale( 0.0 ),
+        m_value( value )
+    {}
+
+    Approx Approx::custom() {
+        return Approx( 0 );
+    }
+
+    Approx Approx::operator-() const {
+        auto temp(*this);
+        temp.m_value = -temp.m_value;
+        return temp;
+    }
+
+
+    std::string Approx::toString() const {
+        ReusableStringStream rss;
+        rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
+        return rss.str();
+    }
+
+    bool Approx::equalityComparisonImpl(const double other) const {
+        // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
+        // Thanks to Richard Harris for his help refining the scaled margin value
+        return marginComparison(m_value, other, m_margin)
+            || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
+    }
+
+    void Approx::setMargin(double newMargin) {
+        CATCH_ENFORCE(newMargin >= 0,
+            "Invalid Approx::margin: " << newMargin << '.'
+            << " Approx::Margin has to be non-negative.");
+        m_margin = newMargin;
+    }
+
+    void Approx::setEpsilon(double newEpsilon) {
+        CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
+            "Invalid Approx::epsilon: " << newEpsilon << '.'
+            << " Approx::epsilon has to be in [0, 1]");
+        m_epsilon = newEpsilon;
+    }
+
+namespace literals {
+    Approx operator "" _a(long double val) {
+        return Approx(val);
+    }
+    Approx operator "" _a(unsigned long long val) {
+        return Approx(val);
+    }
+} // end namespace literals
+
+std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) {
+    return value.toString();
+}
+
+} // end namespace Catch
+
+
+
+namespace Catch {
+
+    AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression):
+        lazyExpression(_lazyExpression),
+        resultType(_resultType) {}
+
+    std::string AssertionResultData::reconstructExpression() const {
+
+        if( reconstructedExpression.empty() ) {
+            if( lazyExpression ) {
+                ReusableStringStream rss;
+                rss << lazyExpression;
+                reconstructedExpression = rss.str();
+            }
+        }
+        return reconstructedExpression;
+    }
+
+    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+    :   m_info( info ),
+        m_resultData( data )
+    {}
+
+    // Result was a success
+    bool AssertionResult::succeeded() const {
+        return Catch::isOk( m_resultData.resultType );
+    }
+
+    // Result was a success, or failure is suppressed
+    bool AssertionResult::isOk() const {
+        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+    }
+
+    ResultWas::OfType AssertionResult::getResultType() const {
+        return m_resultData.resultType;
+    }
+
+    bool AssertionResult::hasExpression() const {
+        return !m_info.capturedExpression.empty();
+    }
+
+    bool AssertionResult::hasMessage() const {
+        return !m_resultData.message.empty();
+    }
+
+    std::string AssertionResult::getExpression() const {
+        // 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.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;
+            expr += "( ";
+            expr += m_info.capturedExpression;
+            expr += " )";
+        }
+        return expr;
+    }
+
+    bool AssertionResult::hasExpandedExpression() const {
+        return hasExpression() && getExpandedExpression() != getExpression();
+    }
+
+    std::string AssertionResult::getExpandedExpression() const {
+        std::string expr = m_resultData.reconstructExpression();
+        return expr.empty()
+                ? getExpression()
+                : expr;
+    }
+
+    StringRef AssertionResult::getMessage() const {
+        return m_resultData.message;
+    }
+    SourceLineInfo AssertionResult::getSourceInfo() const {
+        return m_info.lineInfo;
+    }
+
+    StringRef AssertionResult::getTestMacroName() const {
+        return m_info.macroName;
+    }
+
+} // end namespace Catch
+
+
+
+namespace {
+    bool provideBazelReporterOutput() {
+#if defined(CATCH_CONFIG_BAZEL_SUPPORT)
+        return true;
+#elif defined(CATCH_PLATFORM_WINDOWS_UWP)
+        // UWP does not support environment variables
+        return false;
+#else
+
+#    if defined( _MSC_VER )
+        // On Windows getenv throws a warning as there is no input validation,
+        // since the switch is hardcoded, this should not be an issue.
+#        pragma warning( push )
+#        pragma warning( disable : 4996 )
+#    endif
+
+        return std::getenv( "BAZEL_TEST" ) != nullptr;
+
+#    if defined( _MSC_VER )
+#        pragma warning( pop )
+#    endif
+#endif
+    }
+}
+
+namespace Catch {
+
+    bool operator==( ProcessedReporterSpec const& lhs,
+                     ProcessedReporterSpec const& rhs ) {
+        return lhs.name == rhs.name &&
+               lhs.outputFilename == rhs.outputFilename &&
+               lhs.colourMode == rhs.colourMode &&
+               lhs.customOptions == rhs.customOptions;
+    }
+
+    Config::Config( ConfigData const& data ):
+        m_data( data ) {
+        // We need to trim filter specs to avoid trouble with superfluous
+        // whitespace (esp. important for bdd macros, as those are manually
+        // aligned with whitespace).
+
+        for (auto& elem : m_data.testsOrTags) {
+            elem = trim(elem);
+        }
+        for (auto& elem : m_data.sectionsToRun) {
+            elem = trim(elem);
+        }
+
+
+        TestSpecParser parser(ITagAliasRegistry::get());
+        if (!m_data.testsOrTags.empty()) {
+            m_hasTestFilters = true;
+            for (auto const& testOrTags : m_data.testsOrTags) {
+                parser.parse(testOrTags);
+            }
+        }
+        m_testSpec = parser.testSpec();
+
+
+        // Insert the default reporter if user hasn't asked for a specfic one
+        if ( m_data.reporterSpecifications.empty() ) {
+            m_data.reporterSpecifications.push_back( {
+#if defined( CATCH_CONFIG_DEFAULT_REPORTER )
+                CATCH_CONFIG_DEFAULT_REPORTER,
+#else
+                "console",
+#endif
+                {}, {}, {}
+            } );
+        }
+
+#if !defined(CATCH_PLATFORM_WINDOWS_UWP)
+    if(provideBazelReporterOutput()){
+            // Register a JUnit reporter for Bazel. Bazel sets an environment
+            // variable with the path to XML output. If this file is written to
+            // during test, Bazel will not generate a default XML output.
+            // This allows the XML output file to contain higher level of detail
+            // than what is possible otherwise.
+#    if defined( _MSC_VER )
+            // On Windows getenv throws a warning as there is no input validation,
+            // since the key is hardcoded, this should not be an issue.
+#           pragma warning( push )
+#           pragma warning( disable : 4996 )
+#    endif
+            const auto bazelOutputFilePtr = std::getenv( "XML_OUTPUT_FILE" );
+#    if defined( _MSC_VER )
+#        pragma warning( pop )
+#    endif
+            if ( bazelOutputFilePtr != nullptr ) {
+                m_data.reporterSpecifications.push_back(
+                    { "junit", std::string( bazelOutputFilePtr ), {}, {} } );
+            }
+    }
+#endif
+
+        // We now fixup the reporter specs to handle default output spec,
+        // default colour spec, etc
+        bool defaultOutputUsed = false;
+        for ( auto const& reporterSpec : m_data.reporterSpecifications ) {
+            // We do the default-output check separately, while always
+            // using the default output below to make the code simpler
+            // and avoid superfluous copies.
+            if ( reporterSpec.outputFile().none() ) {
+                CATCH_ENFORCE( !defaultOutputUsed,
+                               "Internal error: cannot use default output for "
+                               "multiple reporters" );
+                defaultOutputUsed = true;
+            }
+
+            m_processedReporterSpecs.push_back( ProcessedReporterSpec{
+                reporterSpec.name(),
+                reporterSpec.outputFile() ? *reporterSpec.outputFile()
+                                          : data.defaultOutputFilename,
+                reporterSpec.colourMode().valueOr( data.defaultColourMode ),
+                reporterSpec.customOptions() } );
+        }
+    }
+
+    Config::~Config() = default;
+
+
+    bool Config::listTests() const          { return m_data.listTests; }
+    bool Config::listTags() const           { return m_data.listTags; }
+    bool Config::listReporters() const      { return m_data.listReporters; }
+    bool Config::listListeners() const      { return m_data.listListeners; }
+
+    std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
+    std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
+
+    std::vector<ReporterSpec> const& Config::getReporterSpecs() const {
+        return m_data.reporterSpecifications;
+    }
+
+    std::vector<ProcessedReporterSpec> const&
+    Config::getProcessedReporterSpecs() const {
+        return m_processedReporterSpecs;
+    }
+
+    TestSpec const& Config::testSpec() const { return m_testSpec; }
+    bool Config::hasTestFilters() const { return m_hasTestFilters; }
+
+    bool Config::showHelp() const { return m_data.showHelp; }
+
+    // IConfig interface
+    bool Config::allowThrows() const                   { return !m_data.noThrow; }
+    StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
+    bool Config::includeSuccessfulResults() const      { return m_data.showSuccessfulTests; }
+    bool Config::warnAboutMissingAssertions() const {
+        return !!( m_data.warnings & WarnAbout::NoAssertions );
+    }
+    bool Config::warnAboutUnmatchedTestSpecs() const {
+        return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec );
+    }
+    bool Config::zeroTestsCountAsSuccess() const       { return m_data.allowZeroTests; }
+    ShowDurations Config::showDurations() const        { return m_data.showDurations; }
+    double Config::minDuration() const                 { return m_data.minDuration; }
+    TestRunOrder Config::runOrder() const              { return m_data.runOrder; }
+    uint32_t Config::rngSeed() const                   { return m_data.rngSeed; }
+    unsigned int Config::shardCount() const            { return m_data.shardCount; }
+    unsigned int Config::shardIndex() const            { return m_data.shardIndex; }
+    ColourMode Config::defaultColourMode() const       { return m_data.defaultColourMode; }
+    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::skipBenchmarks() const                           { return m_data.skipBenchmarks; }
+    bool Config::benchmarkNoAnalysis() const                      { return m_data.benchmarkNoAnalysis; }
+    unsigned 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); }
+
+} // end namespace Catch
+
+
+
+
+
+namespace Catch {
+    std::uint32_t getSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+}
+
+
+
+#include <cassert>
+#include <stack>
+
+namespace Catch {
+
+    ////////////////////////////////////////////////////////////////////////////
+
+
+    ScopedMessage::ScopedMessage( MessageBuilder const& builder ):
+        m_info( builder.m_info ) {
+        m_info.message = builder.m_stream.str();
+        getResultCapture().pushScopedMessage( m_info );
+    }
+
+    ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
+        m_info( CATCH_MOVE( old.m_info ) ) {
+        old.m_moved = true;
+    }
+
+    ScopedMessage::~ScopedMessage() {
+        if ( !uncaught_exceptions() && !m_moved ){
+            getResultCapture().popScopedMessage(m_info);
+        }
+    }
+
+
+    Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
+        auto trimmed = [&] (size_t start, size_t end) {
+            while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
+                ++start;
+            }
+            while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) {
+                --end;
+            }
+            return names.substr(start, end - start + 1);
+        };
+        auto skipq = [&] (size_t start, char quote) {
+            for (auto i = start + 1; i < names.size() ; ++i) {
+                if (names[i] == quote)
+                    return i;
+                if (names[i] == '\\')
+                    ++i;
+            }
+            CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
+        };
+
+        size_t start = 0;
+        std::stack<char> openings;
+        for (size_t pos = 0; pos < names.size(); ++pos) {
+            char c = names[pos];
+            switch (c) {
+            case '[':
+            case '{':
+            case '(':
+            // It is basically impossible to disambiguate between
+            // comparison and start of template args in this context
+//            case '<':
+                openings.push(c);
+                break;
+            case ']':
+            case '}':
+            case ')':
+//           case '>':
+                openings.pop();
+                break;
+            case '"':
+            case '\'':
+                pos = skipq(pos, c);
+                break;
+            case ',':
+                if (start != pos && openings.empty()) {
+                    m_messages.emplace_back(macroName, lineInfo, resultType);
+                    m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
+                    m_messages.back().message += " := ";
+                    start = pos;
+                }
+            }
+        }
+        assert(openings.empty() && "Mismatched openings");
+        m_messages.emplace_back(macroName, lineInfo, resultType);
+        m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
+        m_messages.back().message += " := ";
+    }
+    Capturer::~Capturer() {
+        if ( !uncaught_exceptions() ){
+            assert( m_captured == m_messages.size() );
+            for( size_t i = 0; i < m_captured; ++i  )
+                m_resultCapture.popScopedMessage( m_messages[i] );
+        }
+    }
+
+    void Capturer::captureValue( size_t index, std::string const& value ) {
+        assert( index < m_messages.size() );
+        m_messages[index].message += value;
+        m_resultCapture.pushScopedMessage( m_messages[index] );
+        m_captured++;
+    }
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+
+    namespace {
+
+        class RegistryHub : public IRegistryHub,
+                            public IMutableRegistryHub,
+                            private Detail::NonCopyable {
+
+        public: // IRegistryHub
+            RegistryHub() = default;
+            IReporterRegistry const& getReporterRegistry() const override {
+                return m_reporterRegistry;
+            }
+            ITestCaseRegistry const& getTestCaseRegistry() const override {
+                return m_testCaseRegistry;
+            }
+            IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
+                return m_exceptionTranslatorRegistry;
+            }
+            ITagAliasRegistry const& getTagAliasRegistry() const override {
+                return m_tagAliasRegistry;
+            }
+            StartupExceptionRegistry const& getStartupExceptionRegistry() const override {
+                return m_exceptionRegistry;
+            }
+
+        public: // IMutableRegistryHub
+            void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override {
+                m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) );
+            }
+            void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) override {
+                m_reporterRegistry.registerListener( CATCH_MOVE(factory) );
+            }
+            void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker ) override {
+                m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) );
+            }
+            void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) override {
+                m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) );
+            }
+            void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
+                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:
+            TestRegistry m_testCaseRegistry;
+            ReporterRegistry m_reporterRegistry;
+            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+            TagAliasRegistry m_tagAliasRegistry;
+            StartupExceptionRegistry m_exceptionRegistry;
+            Detail::EnumValuesRegistry m_enumValuesRegistry;
+        };
+    }
+
+    using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>;
+
+    IRegistryHub const& getRegistryHub() {
+        return RegistryHubSingleton::get();
+    }
+    IMutableRegistryHub& getMutableRegistryHub() {
+        return RegistryHubSingleton::getMutable();
+    }
+    void cleanUp() {
+        cleanupSingletons();
+        cleanUpContext();
+    }
+    std::string translateActiveException() {
+        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+    }
+
+
+} // end namespace Catch
+
+
+
+#include <algorithm>
+#include <cassert>
+#include <iomanip>
+#include <set>
+
+namespace Catch {
+
+    namespace {
+        const int MaxExitCode = 255;
+
+        IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) {
+            auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config));
+            CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\'');
+
+            return reporter;
+        }
+
+        IEventListenerPtr prepareReporters(Config const* config) {
+            if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()
+                    && config->getProcessedReporterSpecs().size() == 1) {
+                auto const& spec = config->getProcessedReporterSpecs()[0];
+                return createReporter(
+                    spec.name,
+                    ReporterConfig( config,
+                                    makeStream( spec.outputFilename ),
+                                    spec.colourMode,
+                                    spec.customOptions ) );
+            }
+
+            auto multi = Detail::make_unique<MultiReporter>(config);
+
+            auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
+            for (auto const& listener : listeners) {
+                multi->addListener(listener->create(config));
+            }
+
+            for ( auto const& reporterSpec : config->getProcessedReporterSpecs() ) {
+                multi->addReporter( createReporter(
+                    reporterSpec.name,
+                    ReporterConfig( config,
+                                    makeStream( reporterSpec.outputFilename ),
+                                    reporterSpec.colourMode,
+                                    reporterSpec.customOptions ) ) );
+            }
+
+            return multi;
+        }
+
+        class TestGroup {
+        public:
+            explicit TestGroup(IEventListenerPtr&& reporter, Config const* config):
+                m_reporter(reporter.get()),
+                m_config{config},
+                m_context{config, CATCH_MOVE(reporter)} {
+
+                assert( m_config->testSpec().getInvalidSpecs().empty() &&
+                        "Invalid test specs should be handled before running tests" );
+
+                auto const& allTestCases = getAllTestCasesSorted(*m_config);
+                auto const& testSpec = m_config->testSpec();
+                if ( !testSpec.hasFilters() ) {
+                    for ( auto const& test : allTestCases ) {
+                        if ( !test.getTestCaseInfo().isHidden() ) {
+                            m_tests.emplace( &test );
+                        }
+                    }
+                } else {
+                    m_matches =
+                        testSpec.matchesByFilter( allTestCases, *m_config );
+                    for ( auto const& match : m_matches ) {
+                        m_tests.insert( match.tests.begin(),
+                                        match.tests.end() );
+                    }
+                }
+
+                m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex());
+            }
+
+            Totals execute() {
+                Totals totals;
+                for (auto const& testCase : m_tests) {
+                    if (!m_context.aborting())
+                        totals += m_context.runTest(*testCase);
+                    else
+                        m_reporter->skipTest(testCase->getTestCaseInfo());
+                }
+
+                for (auto const& match : m_matches) {
+                    if (match.tests.empty()) {
+                        m_unmatchedTestSpecs = true;
+                        m_reporter->noMatchingTestCases( match.name );
+                    }
+                }
+
+                return totals;
+            }
+
+            bool hadUnmatchedTestSpecs() const {
+                return m_unmatchedTestSpecs;
+            }
+
+
+        private:
+            IEventListener* m_reporter;
+            Config const* m_config;
+            RunContext m_context;
+            std::set<TestCaseHandle const*> m_tests;
+            TestSpec::Matches m_matches;
+            bool m_unmatchedTestSpecs = false;
+        };
+
+        void applyFilenamesAsTags() {
+            for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) {
+                testInfo->addFilenameTag();
+            }
+        }
+
+    } // anon namespace
+
+    Session::Session() {
+        static bool alreadyInstantiated = false;
+        if( alreadyInstantiated ) {
+            CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
+            CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
+        }
+
+        // There cannot be exceptions at startup in no-exception mode.
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
+        if ( !exceptions.empty() ) {
+            config();
+            getCurrentMutableContext().setConfig(m_config.get());
+
+            m_startupExceptions = true;
+            auto errStream = makeStream( "%stderr" );
+            auto colourImpl = makeColourImpl(
+                ColourMode::PlatformDefault, errStream.get() );
+            auto guard = colourImpl->guardColour( Colour::Red );
+            errStream->stream() << "Errors occurred during startup!" << '\n';
+            // iterate over all exceptions and notify user
+            for ( const auto& ex_ptr : exceptions ) {
+                try {
+                    std::rethrow_exception(ex_ptr);
+                } catch ( std::exception const& ex ) {
+                    errStream->stream() << TextFlow::Column( ex.what() ).indent(2) << '\n';
+                }
+            }
+        }
+#endif
+
+        alreadyInstantiated = true;
+        m_cli = makeCommandLineParser( m_configData );
+    }
+    Session::~Session() {
+        Catch::cleanUp();
+    }
+
+    void Session::showHelp() const {
+        Catch::cout()
+                << "\nCatch2 v" << libraryVersion() << '\n'
+                << m_cli << '\n'
+                << "For more detailed usage please see the project docs\n\n" << std::flush;
+    }
+    void Session::libIdentify() {
+        Catch::cout()
+                << 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: " << "Catch2\n"
+                << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush;
+    }
+
+    int Session::applyCommandLine( int argc, char const * const * argv ) {
+        if( m_startupExceptions )
+            return 1;
+
+        auto result = m_cli.parse( Clara::Args( argc, argv ) );
+
+        if( !result ) {
+            config();
+            getCurrentMutableContext().setConfig(m_config.get());
+            auto errStream = makeStream( "%stderr" );
+            auto colour = makeColourImpl( ColourMode::PlatformDefault, errStream.get() );
+
+            errStream->stream()
+                << colour->guardColour( Colour::Red )
+                << "\nError(s) in input:\n"
+                << TextFlow::Column( result.errorMessage() ).indent( 2 )
+                << "\n\n";
+            errStream->stream() << "Run with -? for usage\n\n" << std::flush;
+            return MaxExitCode;
+        }
+
+        if( m_configData.showHelp )
+            showHelp();
+        if( m_configData.libIdentify )
+            libIdentify();
+
+        m_config.reset();
+        return 0;
+    }
+
+#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, nullptr, 0, nullptr, nullptr );
+
+            utf8Argv[ i ] = new char[ bufSize ];
+
+            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
+        }
+
+        int returnCode = applyCommandLine( argc, utf8Argv );
+
+        for ( int i = 0; i < argc; ++i )
+            delete [] utf8Argv[ i ];
+
+        delete [] utf8Argv;
+
+        return returnCode;
+    }
+#endif
+
+    void Session::useConfigData( ConfigData const& configData ) {
+        m_configData = configData;
+        m_config.reset();
+    }
+
+    int Session::run() {
+        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
+            Catch::cout() << "...waiting for enter/ return before starting\n" << std::flush;
+            static_cast<void>(std::getchar());
+        }
+        int exitCode = runInternal();
+        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
+            Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
+            static_cast<void>(std::getchar());
+        }
+        return exitCode;
+    }
+
+    Clara::Parser const& Session::cli() const {
+        return m_cli;
+    }
+    void Session::cli( Clara::Parser const& newParser ) {
+        m_cli = newParser;
+    }
+    ConfigData& Session::configData() {
+        return m_configData;
+    }
+    Config& Session::config() {
+        if( !m_config )
+            m_config = Detail::make_unique<Config>( m_configData );
+        return *m_config;
+    }
+
+    int Session::runInternal() {
+        if( m_startupExceptions )
+            return 1;
+
+        if (m_configData.showHelp || m_configData.libIdentify) {
+            return 0;
+        }
+
+        if ( m_configData.shardIndex >= m_configData.shardCount ) {
+            Catch::cerr() << "The shard count (" << m_configData.shardCount
+                          << ") must be greater than the shard index ("
+                          << m_configData.shardIndex << ")\n"
+                          << std::flush;
+            return 1;
+        }
+
+        CATCH_TRY {
+            config(); // Force config to be constructed
+
+            seedRng( *m_config );
+
+            if (m_configData.filenamesAsTags) {
+                applyFilenamesAsTags();
+            }
+
+            // Set up global config instance before we start calling into other functions
+            getCurrentMutableContext().setConfig(m_config.get());
+
+            // Create reporter(s) so we can route listings through them
+            auto reporter = prepareReporters(m_config.get());
+
+            auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs();
+            if ( !invalidSpecs.empty() ) {
+                for ( auto const& spec : invalidSpecs ) {
+                    reporter->reportInvalidTestSpec( spec );
+                }
+                return 1;
+            }
+
+
+            // Handle list request
+            if (list(*reporter, *m_config)) {
+                return 0;
+            }
+
+            TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
+            auto const totals = tests.execute();
+
+            if ( tests.hadUnmatchedTestSpecs()
+                && m_config->warnAboutUnmatchedTestSpecs() ) {
+                return 3;
+            }
+
+            if ( totals.testCases.total() == 0
+                && !m_config->zeroTestsCountAsSuccess() ) {
+                return 2;
+            }
+
+            // Note that on unices only the lower 8 bits are usually used, clamping
+            // the return value to 255 prevents false negative when some multiple
+            // of 256 tests has failed
+            return (std::min) (MaxExitCode, static_cast<int>(totals.assertions.failed));
+        }
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        catch( std::exception& ex ) {
+            Catch::cerr() << ex.what() << '\n' << std::flush;
+            return MaxExitCode;
+        }
+#endif
+    }
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+
+    RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) {
+        CATCH_TRY {
+            getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo);
+        } CATCH_CATCH_ALL {
+            // Do not throw when constructing global objects, instead register the exception to be processed later
+            getMutableRegistryHub().registerStartupException();
+        }
+    }
+
+}
+
+
+
+#include <cassert>
+#include <cctype>
+#include <algorithm>
+
+namespace Catch {
+
+    namespace {
+        using TCP_underlying_type = uint8_t;
+        static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type),
+                      "The size of the TestCaseProperties is different from the assumed size");
+
+        TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) {
+            return static_cast<TestCaseProperties>(
+                static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs)
+            );
+        }
+
+        TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) {
+            lhs = static_cast<TestCaseProperties>(
+                static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs)
+            );
+            return lhs;
+        }
+
+        TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) {
+            return static_cast<TestCaseProperties>(
+                static_cast<TCP_underlying_type>(lhs) & static_cast<TCP_underlying_type>(rhs)
+            );
+        }
+
+        bool applies(TestCaseProperties tcp) {
+            static_assert(static_cast<TCP_underlying_type>(TestCaseProperties::None) == 0,
+                          "TestCaseProperties::None must be equal to 0");
+            return tcp != TestCaseProperties::None;
+        }
+
+        TestCaseProperties parseSpecialTag( StringRef tag ) {
+            if( !tag.empty() && tag[0] == '.' )
+                return TestCaseProperties::IsHidden;
+            else if( tag == "!throws"_sr )
+                return TestCaseProperties::Throws;
+            else if( tag == "!shouldfail"_sr )
+                return TestCaseProperties::ShouldFail;
+            else if( tag == "!mayfail"_sr )
+                return TestCaseProperties::MayFail;
+            else if( tag == "!nonportable"_sr )
+                return TestCaseProperties::NonPortable;
+            else if( tag == "!benchmark"_sr )
+                return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden;
+            else
+                return TestCaseProperties::None;
+        }
+        bool isReservedTag( StringRef tag ) {
+            return parseSpecialTag( tag ) == TestCaseProperties::None
+                && tag.size() > 0
+                && !std::isalnum( static_cast<unsigned char>(tag[0]) );
+        }
+        void enforceNotReservedTag( StringRef tag, SourceLineInfo const& _lineInfo ) {
+            CATCH_ENFORCE( !isReservedTag(tag),
+                          "Tag name: [" << tag << "] is not allowed.\n"
+                          << "Tag names starting with non alphanumeric characters are reserved\n"
+                          << _lineInfo );
+        }
+
+        std::string makeDefaultName() {
+            static size_t counter = 0;
+            return "Anonymous test case " + std::to_string(++counter);
+        }
+
+        StringRef extractFilenamePart(StringRef filename) {
+            size_t lastDot = filename.size();
+            while (lastDot > 0 && filename[lastDot - 1] != '.') {
+                --lastDot;
+            }
+            --lastDot;
+
+            size_t nameStart = lastDot;
+            while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') {
+                --nameStart;
+            }
+
+            return filename.substr(nameStart, lastDot - nameStart);
+        }
+
+        // Returns the upper bound on size of extra tags ([#file]+[.])
+        size_t sizeOfExtraTags(StringRef filepath) {
+            // [.] is 3, [#] is another 3
+            const size_t extras = 3 + 3;
+            return extractFilenamePart(filepath).size() + extras;
+        }
+    } // end unnamed namespace
+
+    bool operator<(  Tag const& lhs, Tag const& rhs ) {
+        Detail::CaseInsensitiveLess cmp;
+        return cmp( lhs.original, rhs.original );
+    }
+    bool operator==( Tag const& lhs, Tag const& rhs ) {
+        Detail::CaseInsensitiveEqualTo cmp;
+        return cmp( lhs.original, rhs.original );
+    }
+
+    Detail::unique_ptr<TestCaseInfo>
+        makeTestCaseInfo(StringRef _className,
+                         NameAndTags const& nameAndTags,
+                         SourceLineInfo const& _lineInfo ) {
+        return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo);
+    }
+
+    TestCaseInfo::TestCaseInfo(StringRef _className,
+                               NameAndTags const& _nameAndTags,
+                               SourceLineInfo const& _lineInfo):
+        name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ),
+        className( _className ),
+        lineInfo( _lineInfo )
+    {
+        StringRef originalTags = _nameAndTags.tags;
+        // We need to reserve enough space to store all of the tags
+        // (including optional hidden tag and filename tag)
+        auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file);
+        backingTags.reserve(requiredSize);
+
+        // We cannot copy the tags directly, as we need to normalize
+        // some tags, so that [.foo] is copied as [.][foo].
+        size_t tagStart = 0;
+        size_t tagEnd = 0;
+        bool inTag = false;
+        for (size_t idx = 0; idx < originalTags.size(); ++idx) {
+            auto c = originalTags[idx];
+            if (c == '[') {
+                assert(!inTag);
+                inTag = true;
+                tagStart = idx;
+            }
+            if (c == ']') {
+                assert(inTag);
+                inTag = false;
+                tagEnd = idx;
+                assert(tagStart < tagEnd);
+
+                // We need to check the tag for special meanings, copy
+                // it over to backing storage and actually reference the
+                // backing storage in the saved tags
+                StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
+                CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed");
+                enforceNotReservedTag(tagStr, lineInfo);
+                properties |= parseSpecialTag(tagStr);
+                // When copying a tag to the backing storage, we need to
+                // check if it is a merged hide tag, such as [.foo], and
+                // if it is, we need to handle it as if it was [foo].
+                if (tagStr.size() > 1 && tagStr[0] == '.') {
+                    tagStr = tagStr.substr(1, tagStr.size() - 1);
+                }
+                // We skip over dealing with the [.] tag, as we will add
+                // it later unconditionally and then sort and unique all
+                // the tags.
+                internalAppendTag(tagStr);
+            }
+            (void)inTag; // Silence "set-but-unused" warning in release mode.
+        }
+        // Add [.] if relevant
+        if (isHidden()) {
+            internalAppendTag("."_sr);
+        }
+
+        // Sort and prepare tags
+        std::sort(begin(tags), end(tags));
+        tags.erase(std::unique(begin(tags), end(tags)),
+                   end(tags));
+    }
+
+    bool TestCaseInfo::isHidden() const {
+        return applies( properties & TestCaseProperties::IsHidden );
+    }
+    bool TestCaseInfo::throws() const {
+        return applies( properties & TestCaseProperties::Throws );
+    }
+    bool TestCaseInfo::okToFail() const {
+        return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) );
+    }
+    bool TestCaseInfo::expectedToFail() const {
+        return applies( properties & (TestCaseProperties::ShouldFail) );
+    }
+
+    void TestCaseInfo::addFilenameTag() {
+        std::string combined("#");
+        combined += extractFilenamePart(lineInfo.file);
+        internalAppendTag(combined);
+    }
+
+    std::string TestCaseInfo::tagsAsString() const {
+        std::string ret;
+        // '[' and ']' per tag
+        std::size_t full_size = 2 * tags.size();
+        for (const auto& tag : tags) {
+            full_size += tag.original.size();
+        }
+        ret.reserve(full_size);
+        for (const auto& tag : tags) {
+            ret.push_back('[');
+            ret += tag.original;
+            ret.push_back(']');
+        }
+
+        return ret;
+    }
+
+    void TestCaseInfo::internalAppendTag(StringRef tagStr) {
+        backingTags += '[';
+        const auto backingStart = backingTags.size();
+        backingTags += tagStr;
+        const auto backingEnd = backingTags.size();
+        backingTags += ']';
+        tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart));
+    }
+
+    bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) {
+        // We want to avoid redoing the string comparisons multiple times,
+        // so we store the result of a three-way comparison before using
+        // it in the actual comparison logic.
+        const auto cmpName = lhs.name.compare( rhs.name );
+        if ( cmpName != 0 ) {
+            return cmpName < 0;
+        }
+        const auto cmpClassName = lhs.className.compare( rhs.className );
+        if ( cmpClassName != 0 ) {
+            return cmpClassName < 0;
+        }
+        return lhs.tags < rhs.tags;
+    }
+
+    TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const {
+        return *m_info;
+    }
+
+} // end namespace Catch
+
+
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    TestSpec::Pattern::Pattern( std::string const& name )
+    : m_name( name )
+    {}
+
+    TestSpec::Pattern::~Pattern() = default;
+
+    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( testCase.name );
+    }
+
+
+    TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
+    : Pattern( filterString )
+    , m_tag( tag )
+    {}
+
+    bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
+        return std::find( begin( testCase.tags ),
+                          end( testCase.tags ),
+                          Tag( m_tag ) ) != end( testCase.tags );
+    }
+
+    bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
+        bool should_use = !testCase.isHidden();
+        for (auto const& pattern : m_required) {
+            should_use = true;
+            if (!pattern->matches(testCase)) {
+                return false;
+            }
+        }
+        for (auto const& pattern : m_forbidden) {
+            if (pattern->matches(testCase)) {
+                return false;
+            }
+        }
+        return should_use;
+    }
+
+    std::string TestSpec::Filter::name() const {
+        std::string name;
+        for (auto const& p : m_required) {
+            name += p->name();
+        }
+        for (auto const& p : m_forbidden) {
+            name += p->name();
+        }
+        return name;
+    }
+
+
+    bool TestSpec::hasFilters() const {
+        return !m_filters.empty();
+    }
+
+    bool TestSpec::matches( TestCaseInfo const& testCase ) const {
+        return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
+    }
+
+    TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> 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<TestCaseHandle const*> currentMatches;
+            for( auto const& test : testCases )
+                if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) )
+                    currentMatches.emplace_back( &test );
+            return FilterMatch{ filter.name(), currentMatches };
+        } );
+        return matches;
+    }
+
+    const TestSpec::vectorStrings& TestSpec::getInvalidSpecs() const {
+        return m_invalidSpecs;
+    }
+
+}
+
+
+
+#include <chrono>
+
+namespace Catch {
+
+    namespace {
+        static auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
+            return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
+        }
+    } // end unnamed namespace
+
+    void Timer::start() {
+       m_nanoseconds = getCurrentNanosecondsSinceEpoch();
+    }
+    auto Timer::getElapsedNanoseconds() const -> uint64_t {
+        return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
+    }
+    auto Timer::getElapsedMicroseconds() const -> uint64_t {
+        return getElapsedNanoseconds()/1000;
+    }
+    auto Timer::getElapsedMilliseconds() const -> unsigned int {
+        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+    }
+    auto Timer::getElapsedSeconds() const -> double {
+        return getElapsedMicroseconds()/1000000.0;
+    }
+
+
+} // namespace Catch
+
+
+
+
+#include <cmath>
+#include <iomanip>
+
+namespace Catch {
+
+namespace Detail {
+
+    namespace {
+        const int hexThreshold = 255;
+
+        struct Endianness {
+            enum Arch { Big, Little };
+
+            static Arch which() {
+                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;
+            }
+        };
+
+        template<typename T>
+        std::string fpToString(T value, int precision) {
+            if (Catch::isnan(value)) {
+                return "nan";
+            }
+
+            ReusableStringStream rss;
+            rss << std::setprecision(precision)
+                << std::fixed
+                << value;
+            std::string d = rss.str();
+            std::size_t i = d.find_last_not_of('0');
+            if (i != std::string::npos && i != d.size() - 1) {
+                if (d[i] == '.')
+                    i++;
+                d = d.substr(0, i + 1);
+            }
+            return d;
+        }
+    } // end unnamed namespace
+
+    std::string convertIntoString(StringRef string, bool escape_invisibles) {
+        std::string ret;
+        // This is enough for the "don't escape invisibles" case, and a good
+        // lower bound on the "escape invisibles" case.
+        ret.reserve(string.size() + 2);
+
+        if (!escape_invisibles) {
+            ret += '"';
+            ret += string;
+            ret += '"';
+            return ret;
+        }
+
+        ret += '"';
+        for (char c : string) {
+            switch (c) {
+            case '\r':
+                ret.append("\\r");
+                break;
+            case '\n':
+                ret.append("\\n");
+                break;
+            case '\t':
+                ret.append("\\t");
+                break;
+            case '\f':
+                ret.append("\\f");
+                break;
+            default:
+                ret.push_back(c);
+                break;
+            }
+        }
+        ret += '"';
+
+        return ret;
+    }
+
+    std::string convertIntoString(StringRef string) {
+        return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles());
+    }
+
+    std::string rawMemoryToString( const void *object, std::size_t size ) {
+        // Reverse order for little endian architectures
+        int i = 0, end = static_cast<int>( size ), inc = 1;
+        if( Endianness::which() == Endianness::Little ) {
+            i = end-1;
+            end = inc = -1;
+        }
+
+        unsigned char const *bytes = static_cast<unsigned char const *>(object);
+        ReusableStringStream rss;
+        rss << "0x" << std::setfill('0') << std::hex;
+        for( ; i != end; i += inc )
+             rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
+       return rss.str();
+    }
+} // end Detail namespace
+
+
+
+//// ======================================================= ////
+//
+//   Out-of-line defs for full specialization of StringMaker
+//
+//// ======================================================= ////
+
+std::string StringMaker<std::string>::convert(const std::string& str) {
+    return Detail::convertIntoString( str );
+}
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::string_view>::convert(std::string_view str) {
+    return Detail::convertIntoString( StringRef( str.data(), str.size() ) );
+}
+#endif
+
+std::string StringMaker<char const*>::convert(char const* str) {
+    if (str) {
+        return Detail::convertIntoString( str );
+    } else {
+        return{ "{null string}" };
+    }
+}
+std::string StringMaker<char*>::convert(char* str) {
+    if (str) {
+        return Detail::convertIntoString( str );
+    } else {
+        return{ "{null string}" };
+    }
+}
+
+#ifdef CATCH_CONFIG_WCHAR
+std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
+    std::string s;
+    s.reserve(wstr.size());
+    for (auto c : wstr) {
+        s += (c <= 0xff) ? static_cast<char>(c) : '?';
+    }
+    return ::Catch::Detail::stringify(s);
+}
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) {
+    return StringMaker<std::wstring>::convert(std::wstring(str));
+}
+# endif
+
+std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::wstring{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::wstring{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+#endif
+
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+#include <cstddef>
+std::string StringMaker<std::byte>::convert(std::byte value) {
+    return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
+}
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+
+std::string StringMaker<int>::convert(int value) {
+    return ::Catch::Detail::stringify(static_cast<long long>(value));
+}
+std::string StringMaker<long>::convert(long value) {
+    return ::Catch::Detail::stringify(static_cast<long long>(value));
+}
+std::string StringMaker<long long>::convert(long long value) {
+    ReusableStringStream rss;
+    rss << value;
+    if (value > Detail::hexThreshold) {
+        rss << " (0x" << std::hex << value << ')';
+    }
+    return rss.str();
+}
+
+std::string StringMaker<unsigned int>::convert(unsigned int value) {
+    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long>::convert(unsigned long value) {
+    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long long>::convert(unsigned long long value) {
+    ReusableStringStream rss;
+    rss << value;
+    if (value > Detail::hexThreshold) {
+        rss << " (0x" << std::hex << value << ')';
+    }
+    return rss.str();
+}
+
+std::string StringMaker<signed char>::convert(signed char value) {
+    if (value == '\r') {
+        return "'\\r'";
+    } else if (value == '\f') {
+        return "'\\f'";
+    } else if (value == '\n') {
+        return "'\\n'";
+    } else if (value == '\t') {
+        return "'\\t'";
+    } else if ('\0' <= value && value < ' ') {
+        return ::Catch::Detail::stringify(static_cast<unsigned int>(value));
+    } else {
+        char chstr[] = "' '";
+        chstr[1] = value;
+        return chstr;
+    }
+}
+std::string StringMaker<char>::convert(char c) {
+    return ::Catch::Detail::stringify(static_cast<signed char>(c));
+}
+std::string StringMaker<unsigned char>::convert(unsigned char c) {
+    return ::Catch::Detail::stringify(static_cast<char>(c));
+}
+
+int StringMaker<float>::precision = 5;
+
+std::string StringMaker<float>::convert(float value) {
+    return Detail::fpToString(value, precision) + 'f';
+}
+
+int StringMaker<double>::precision = 10;
+
+std::string StringMaker<double>::convert(double value) {
+    return Detail::fpToString(value, precision);
+}
+
+} // end namespace Catch
+
+
+
+namespace Catch {
+
+    Counts Counts::operator - ( Counts const& other ) const {
+        Counts diff;
+        diff.passed = passed - other.passed;
+        diff.failed = failed - other.failed;
+        diff.failedButOk = failedButOk - other.failedButOk;
+        return diff;
+    }
+
+    Counts& Counts::operator += ( Counts const& other ) {
+        passed += other.passed;
+        failed += other.failed;
+        failedButOk += other.failedButOk;
+        return *this;
+    }
+
+    std::uint64_t Counts::total() const {
+        return passed + failed + failedButOk;
+    }
+    bool Counts::allPassed() const {
+        return failed == 0 && failedButOk == 0;
+    }
+    bool Counts::allOk() const {
+        return failed == 0;
+    }
+
+    Totals Totals::operator - ( Totals const& other ) const {
+        Totals diff;
+        diff.assertions = assertions - other.assertions;
+        diff.testCases = testCases - other.testCases;
+        return diff;
+    }
+
+    Totals& Totals::operator += ( Totals const& other ) {
+        assertions += other.assertions;
+        testCases += other.testCases;
+        return *this;
+    }
+
+    Totals Totals::delta( Totals const& prevTotals ) const {
+        Totals diff = *this - prevTotals;
+        if( diff.assertions.failed > 0 )
+            ++diff.testCases.failed;
+        else if( diff.assertions.failedButOk > 0 )
+            ++diff.testCases.failedButOk;
+        else
+            ++diff.testCases.passed;
+        return diff;
+    }
+
+}
+
+
+#include <ostream>
+
+namespace Catch {
+
+    Version::Version
+        (   unsigned int _majorVersion,
+            unsigned int _minorVersion,
+            unsigned int _patchNumber,
+            char const * const _branchName,
+            unsigned int _buildNumber )
+    :   majorVersion( _majorVersion ),
+        minorVersion( _minorVersion ),
+        patchNumber( _patchNumber ),
+        branchName( _branchName ),
+        buildNumber( _buildNumber )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, Version const& version ) {
+        os  << version.majorVersion << '.'
+            << version.minorVersion << '.'
+            << version.patchNumber;
+        // branchName is never null -> 0th char is \0 if it is empty
+        if (version.branchName[0]) {
+            os << '-' << version.branchName
+               << '.' << version.buildNumber;
+        }
+        return os;
+    }
+
+    Version const& libraryVersion() {
+        static Version version( 3, 1, 1, "", 0 );
+        return version;
+    }
+
+}
+
+
+
+
+namespace Catch {
+
+    const char* GeneratorException::what() const noexcept {
+        return m_msg;
+    }
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+
+    IGeneratorTracker::~IGeneratorTracker() = default;
+
+namespace Generators {
+
+namespace Detail {
+
+    [[noreturn]]
+    void throw_generator_exception(char const* msg) {
+        Catch::throw_exception(GeneratorException{ msg });
+    }
+} // end namespace Detail
+
+    GeneratorUntypedBase::~GeneratorUntypedBase() = default;
+
+    auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+        return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+    }
+
+} // namespace Generators
+} // namespace Catch
+
+
+
+
+
+std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); }
+
+
+
+
+namespace Catch {
+    IResultCapture::~IResultCapture() = default;
+}
+
+
+
+
+namespace Catch {
+    IConfig::~IConfig() = default;
+}
+
+
+
+
+namespace Catch {
+    IExceptionTranslator::~IExceptionTranslator() = default;
+    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default;
+}
+
+
+
+#include <string>
+
+namespace Catch {
+    namespace Generators {
+
+        bool GeneratorUntypedBase::countedNext() {
+            auto ret = next();
+            if ( ret ) {
+                m_stringReprCache.clear();
+                ++m_currentElementIndex;
+            }
+            return ret;
+        }
+
+        StringRef GeneratorUntypedBase::currentElementAsString() const {
+            if ( m_stringReprCache.empty() ) {
+                m_stringReprCache = stringifyImpl();
+            }
+            return m_stringReprCache;
+        }
+
+    } // namespace Generators
+} // namespace Catch
+
+
+
+
+namespace Catch {
+    IRegistryHub::~IRegistryHub() = default;
+    IMutableRegistryHub::~IMutableRegistryHub() = default;
+}
+
+
+
+#include <algorithm>
+#include <cassert>
+#include <iomanip>
+
+namespace Catch {
+
+    ReporterConfig::ReporterConfig(
+        IConfig const* _fullConfig,
+        Detail::unique_ptr<IStream> _stream,
+        ColourMode colourMode,
+        std::map<std::string, std::string> customOptions ):
+        m_stream( CATCH_MOVE(_stream) ),
+        m_fullConfig( _fullConfig ),
+        m_colourMode( colourMode ),
+        m_customOptions( CATCH_MOVE( customOptions ) ) {}
+
+    Detail::unique_ptr<IStream> ReporterConfig::takeStream() && {
+        assert( m_stream );
+        return CATCH_MOVE( m_stream );
+    }
+    IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; }
+    ColourMode ReporterConfig::colourMode() const { return m_colourMode; }
+
+    std::map<std::string, std::string> const&
+    ReporterConfig::customOptions() const {
+        return m_customOptions;
+    }
+
+    ReporterConfig::~ReporterConfig() = default;
+
+    AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
+                                    std::vector<MessageInfo> const& _infoMessages,
+                                    Totals const& _totals )
+    :   assertionResult( _assertionResult ),
+        infoMessages( _infoMessages ),
+        totals( _totals )
+    {
+        assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
+
+        if( assertionResult.hasMessage() ) {
+            // Copy message into messages list.
+            // !TBD This should have been done earlier, somewhere
+            MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+            builder << assertionResult.getMessage();
+            builder.m_info.message = builder.m_stream.str();
+
+            infoMessages.push_back( builder.m_info );
+        }
+    }
+
+    SectionStats::SectionStats(  SectionInfo const& _sectionInfo,
+                                 Counts const& _assertions,
+                                 double _durationInSeconds,
+                                 bool _missingAssertions )
+    :   sectionInfo( _sectionInfo ),
+        assertions( _assertions ),
+        durationInSeconds( _durationInSeconds ),
+        missingAssertions( _missingAssertions )
+    {}
+
+
+    TestCaseStats::TestCaseStats(  TestCaseInfo const& _testInfo,
+                                   Totals const& _totals,
+                                   std::string const& _stdOut,
+                                   std::string const& _stdErr,
+                                   bool _aborting )
+    : testInfo( &_testInfo ),
+        totals( _totals ),
+        stdOut( _stdOut ),
+        stdErr( _stdErr ),
+        aborting( _aborting )
+    {}
+
+
+    TestRunStats::TestRunStats(   TestRunInfo const& _runInfo,
+                    Totals const& _totals,
+                    bool _aborting )
+    :   runInfo( _runInfo ),
+        totals( _totals ),
+        aborting( _aborting )
+    {}
+
+    IEventListener::~IEventListener() = default;
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+    IReporterFactory::~IReporterFactory() = default;
+    EventListenerFactory::~EventListenerFactory() = default;
+}
+
+
+
+
+namespace Catch {
+    IReporterRegistry::~IReporterRegistry() = default;
+}
+
+
+
+
+namespace Catch {
+    ITestInvoker::~ITestInvoker() = default;
+    ITestCaseRegistry::~ITestCaseRegistry() = default;
+}
+
+
+
+namespace Catch {
+
+    AssertionHandler::AssertionHandler
+        (   StringRef macroName,
+            SourceLineInfo const& lineInfo,
+            StringRef capturedExpression,
+            ResultDisposition::Flags resultDisposition )
+    :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
+        m_resultCapture( getResultCapture() )
+    {}
+
+    void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
+        m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
+    }
+    void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) {
+        m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
+    }
+
+    auto AssertionHandler::allowThrows() const -> bool {
+        return getCurrentContext().getConfig()->allowThrows();
+    }
+
+    void AssertionHandler::complete() {
+        setCompleted();
+        if( m_reaction.shouldDebugBreak ) {
+
+            // If you find your debugger stopping you here then go one level up on the
+            // call-stack for the code that caused it (typically a failed assertion)
+
+            // (To go back to the test and change execution, jump over the throw, next)
+            CATCH_BREAK_INTO_DEBUGGER();
+        }
+        if (m_reaction.shouldThrow) {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+            throw Catch::TestFailureException();
+#else
+            CATCH_ERROR( "Test failure requires aborting test!" );
+#endif
+        }
+    }
+    void AssertionHandler::setCompleted() {
+        m_completed = true;
+    }
+
+    void AssertionHandler::handleUnexpectedInflightException() {
+        m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
+    }
+
+    void AssertionHandler::handleExceptionThrownAsExpected() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+    void AssertionHandler::handleExceptionNotThrownAsExpected() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+
+    void AssertionHandler::handleUnexpectedExceptionNotThrown() {
+        m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
+    }
+
+    void AssertionHandler::handleThrowingCallSkipped() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+
+    // This is the overload that takes a string and infers the Equals matcher from it
+    // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString  ) {
+        handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );
+    }
+
+} // namespace Catch
+
+
+
+
+#include <algorithm>
+
+namespace Catch {
+    namespace Detail {
+
+        bool CaseInsensitiveLess::operator()( StringRef lhs,
+                                              StringRef rhs ) const {
+            return std::lexicographical_compare(
+                lhs.begin(), lhs.end(),
+                rhs.begin(), rhs.end(),
+                []( char l, char r ) { return toLower( l ) < toLower( r ); } );
+        }
+
+        bool
+        CaseInsensitiveEqualTo::operator()( StringRef lhs,
+                                            StringRef rhs ) const {
+            return std::equal(
+                lhs.begin(), lhs.end(),
+                rhs.begin(), rhs.end(),
+                []( char l, char r ) { return toLower( l ) == toLower( r ); } );
+        }
+
+    } // namespace Detail
+} // namespace Catch
+
+
+
+
+#include <algorithm>
+#include <ostream>
+
+namespace {
+    bool isOptPrefix( char c ) {
+        return c == '-'
+#ifdef CATCH_PLATFORM_WINDOWS
+               || c == '/'
+#endif
+            ;
+    }
+
+    std::string normaliseOpt( std::string const& optName ) {
+#ifdef CATCH_PLATFORM_WINDOWS
+        if ( optName[0] == '/' )
+            return "-" + optName.substr( 1 );
+        else
+#endif
+            return optName;
+    }
+
+} // namespace
+
+namespace Catch {
+    namespace Clara {
+        namespace Detail {
+
+            void TokenStream::loadBuffer() {
+                m_tokenBuffer.clear();
+
+                // Skip any empty strings
+                while ( it != itEnd && it->empty() ) {
+                    ++it;
+                }
+
+                if ( it != itEnd ) {
+                    auto const& next = *it;
+                    if ( isOptPrefix( next[0] ) ) {
+                        auto delimiterPos = next.find_first_of( " :=" );
+                        if ( delimiterPos != std::string::npos ) {
+                            m_tokenBuffer.push_back(
+                                { TokenType::Option,
+                                  next.substr( 0, delimiterPos ) } );
+                            m_tokenBuffer.push_back(
+                                { TokenType::Argument,
+                                  next.substr( delimiterPos + 1 ) } );
+                        } else {
+                            if ( next[1] != '-' && next.size() > 2 ) {
+                                std::string opt = "- ";
+                                for ( size_t i = 1; i < next.size(); ++i ) {
+                                    opt[1] = next[i];
+                                    m_tokenBuffer.push_back(
+                                        { TokenType::Option, opt } );
+                                }
+                            } else {
+                                m_tokenBuffer.push_back(
+                                    { TokenType::Option, next } );
+                            }
+                        }
+                    } else {
+                        m_tokenBuffer.push_back(
+                            { TokenType::Argument, next } );
+                    }
+                }
+            }
+
+            TokenStream::TokenStream( Args const& args ):
+                TokenStream( args.m_args.begin(), args.m_args.end() ) {}
+
+            TokenStream::TokenStream( Iterator it_, Iterator itEnd_ ):
+                it( it_ ), itEnd( itEnd_ ) {
+                loadBuffer();
+            }
+
+            TokenStream& TokenStream::operator++() {
+                if ( m_tokenBuffer.size() >= 2 ) {
+                    m_tokenBuffer.erase( m_tokenBuffer.begin() );
+                } else {
+                    if ( it != itEnd )
+                        ++it;
+                    loadBuffer();
+                }
+                return *this;
+            }
+
+            ParserResult convertInto( std::string const& source,
+                                      std::string& target ) {
+                target = source;
+                return ParserResult::ok( ParseResultType::Matched );
+            }
+
+            ParserResult convertInto( std::string const& source,
+                                      bool& target ) {
+                std::string srcLC = toLower( source );
+
+                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" ) {
+                    target = false;
+                } else {
+                    return ParserResult::runtimeError(
+                        "Expected a boolean value but did not recognise: '" +
+                        source + '\'' );
+                }
+                return ParserResult::ok( ParseResultType::Matched );
+            }
+
+            size_t ParserBase::cardinality() const { return 1; }
+
+            InternalParseResult ParserBase::parse( Args const& args ) const {
+                return parse( args.exeName(), TokenStream( args ) );
+            }
+
+            ParseState::ParseState( ParseResultType type,
+                                    TokenStream const& remainingTokens ):
+                m_type( type ), m_remainingTokens( remainingTokens ) {}
+
+            ParserResult BoundFlagRef::setFlag( bool flag ) {
+                m_ref = flag;
+                return ParserResult::ok( ParseResultType::Matched );
+            }
+
+            ResultBase::~ResultBase() = default;
+
+            bool BoundRef::isContainer() const { return false; }
+
+            bool BoundRef::isFlag() const { return false; }
+
+            bool BoundFlagRefBase::isFlag() const { return true; }
+
+} // namespace Detail
+
+        Detail::InternalParseResult Arg::parse(std::string const&,
+                                               Detail::TokenStream const& tokens) const {
+            auto validationResult = validate();
+            if (!validationResult)
+                return Detail::InternalParseResult(validationResult);
+
+            auto remainingTokens = tokens;
+            auto const& token = *remainingTokens;
+            if (token.type != Detail::TokenType::Argument)
+                return Detail::InternalParseResult::ok(Detail::ParseState(
+                    ParseResultType::NoMatch, remainingTokens));
+
+            assert(!m_ref->isFlag());
+            auto valueRef =
+                static_cast<Detail::BoundValueRefBase*>(m_ref.get());
+
+            auto result = valueRef->setValue(remainingTokens->token);
+            if (!result)
+                return Detail::InternalParseResult(result);
+            else
+                return Detail::InternalParseResult::ok(Detail::ParseState(
+                    ParseResultType::Matched, ++remainingTokens));
+        }
+
+        Opt::Opt(bool& ref) :
+            ParserRefImpl(std::make_shared<Detail::BoundFlagRef>(ref)) {}
+
+        std::vector<Detail::HelpColumns> Opt::getHelpColumns() const {
+            std::ostringstream oss;
+            bool first = true;
+            for (auto const& opt : m_optNames) {
+                if (first)
+                    first = false;
+                else
+                    oss << ", ";
+                oss << opt;
+            }
+            if (!m_hint.empty())
+                oss << " <" << m_hint << '>';
+            return { { oss.str(), m_description } };
+        }
+
+        bool Opt::isMatch(std::string const& optToken) const {
+            auto normalisedToken = normaliseOpt(optToken);
+            for (auto const& name : m_optNames) {
+                if (normaliseOpt(name) == normalisedToken)
+                    return true;
+            }
+            return false;
+        }
+
+        Detail::InternalParseResult Opt::parse(std::string const&,
+                                       Detail::TokenStream const& tokens) const {
+            auto validationResult = validate();
+            if (!validationResult)
+                return Detail::InternalParseResult(validationResult);
+
+            auto remainingTokens = tokens;
+            if (remainingTokens &&
+                remainingTokens->type == Detail::TokenType::Option) {
+                auto const& token = *remainingTokens;
+                if (isMatch(token.token)) {
+                    if (m_ref->isFlag()) {
+                        auto flagRef =
+                            static_cast<Detail::BoundFlagRefBase*>(
+                                m_ref.get());
+                        auto result = flagRef->setFlag(true);
+                        if (!result)
+                            return Detail::InternalParseResult(result);
+                        if (result.value() ==
+                            ParseResultType::ShortCircuitAll)
+                            return Detail::InternalParseResult::ok(Detail::ParseState(
+                                result.value(), remainingTokens));
+                    } else {
+                        auto valueRef =
+                            static_cast<Detail::BoundValueRefBase*>(
+                                m_ref.get());
+                        ++remainingTokens;
+                        if (!remainingTokens)
+                            return Detail::InternalParseResult::runtimeError(
+                                "Expected argument following " +
+                                token.token);
+                        auto const& argToken = *remainingTokens;
+                        if (argToken.type != Detail::TokenType::Argument)
+                            return Detail::InternalParseResult::runtimeError(
+                                "Expected argument following " +
+                                token.token);
+                        const auto result = valueRef->setValue(argToken.token);
+                        if (!result)
+                            return Detail::InternalParseResult(result);
+                        if (result.value() ==
+                            ParseResultType::ShortCircuitAll)
+                            return Detail::InternalParseResult::ok(Detail::ParseState(
+                                result.value(), remainingTokens));
+                    }
+                    return Detail::InternalParseResult::ok(Detail::ParseState(
+                        ParseResultType::Matched, ++remainingTokens));
+                }
+            }
+            return Detail::InternalParseResult::ok(
+                Detail::ParseState(ParseResultType::NoMatch, remainingTokens));
+        }
+
+        Detail::Result Opt::validate() const {
+            if (m_optNames.empty())
+                return Detail::Result::logicError("No options supplied to Opt");
+            for (auto const& name : m_optNames) {
+                if (name.empty())
+                    return Detail::Result::logicError(
+                        "Option name cannot be empty");
+#ifdef CATCH_PLATFORM_WINDOWS
+                if (name[0] != '-' && name[0] != '/')
+                    return Detail::Result::logicError(
+                        "Option name must begin with '-' or '/'");
+#else
+                if (name[0] != '-')
+                    return Detail::Result::logicError(
+                        "Option name must begin with '-'");
+#endif
+            }
+            return ParserRefImpl::validate();
+        }
+
+        ExeName::ExeName() :
+            m_name(std::make_shared<std::string>("<executable>")) {}
+
+        ExeName::ExeName(std::string& ref) : ExeName() {
+            m_ref = std::make_shared<Detail::BoundValueRef<std::string>>(ref);
+        }
+
+        Detail::InternalParseResult
+            ExeName::parse(std::string const&,
+                           Detail::TokenStream const& tokens) const {
+            return Detail::InternalParseResult::ok(
+                Detail::ParseState(ParseResultType::NoMatch, tokens));
+        }
+
+        ParserResult ExeName::set(std::string const& newName) {
+            auto lastSlash = newName.find_last_of("\\/");
+            auto filename = (lastSlash == std::string::npos)
+                ? newName
+                : newName.substr(lastSlash + 1);
+
+            *m_name = filename;
+            if (m_ref)
+                return m_ref->setValue(filename);
+            else
+                return ParserResult::ok(ParseResultType::Matched);
+        }
+
+
+
+
+        Parser& Parser::operator|=( Parser const& other ) {
+            m_options.insert( m_options.end(),
+                              other.m_options.begin(),
+                              other.m_options.end() );
+            m_args.insert(
+                m_args.end(), other.m_args.begin(), other.m_args.end() );
+            return *this;
+        }
+
+        std::vector<Detail::HelpColumns> Parser::getHelpColumns() const {
+            std::vector<Detail::HelpColumns> cols;
+            for ( auto const& o : m_options ) {
+                auto childCols = o.getHelpColumns();
+                cols.insert( cols.end(), childCols.begin(), childCols.end() );
+            }
+            return cols;
+        }
+
+        void Parser::writeToStream( std::ostream& os ) const {
+            if ( !m_exeName.name().empty() ) {
+                os << "usage:\n"
+                   << "  " << m_exeName.name() << ' ';
+                bool required = true, first = true;
+                for ( auto const& arg : m_args ) {
+                    if ( first )
+                        first = false;
+                    else
+                        os << ' ';
+                    if ( arg.isOptional() && required ) {
+                        os << '[';
+                        required = false;
+                    }
+                    os << '<' << arg.hint() << '>';
+                    if ( arg.cardinality() == 0 )
+                        os << " ... ";
+                }
+                if ( !required )
+                    os << ']';
+                if ( !m_options.empty() )
+                    os << " options";
+                os << "\n\nwhere options are:\n";
+            }
+
+            auto rows = getHelpColumns();
+            size_t consoleWidth = CATCH_CONFIG_CONSOLE_WIDTH;
+            size_t optWidth = 0;
+            for ( auto const& cols : rows )
+                optWidth = ( std::max )( optWidth, cols.left.size() + 2 );
+
+            optWidth = ( std::min )( optWidth, consoleWidth / 2 );
+
+            for ( auto const& cols : rows ) {
+                auto row = TextFlow::Column( cols.left )
+                               .width( optWidth )
+                               .indent( 2 ) +
+                           TextFlow::Spacer( 4 ) +
+                           TextFlow::Column( cols.right )
+                               .width( consoleWidth - 7 - optWidth );
+                os << row << '\n';
+            }
+        }
+
+        Detail::Result Parser::validate() const {
+            for ( auto const& opt : m_options ) {
+                auto result = opt.validate();
+                if ( !result )
+                    return result;
+            }
+            for ( auto const& arg : m_args ) {
+                auto result = arg.validate();
+                if ( !result )
+                    return result;
+            }
+            return Detail::Result::ok();
+        }
+
+        Detail::InternalParseResult
+        Parser::parse( std::string const& exeName,
+                       Detail::TokenStream const& tokens ) const {
+
+            struct ParserInfo {
+                ParserBase const* parser = nullptr;
+                size_t count = 0;
+            };
+            std::vector<ParserInfo> parseInfos;
+            parseInfos.reserve( m_options.size() + m_args.size() );
+            for ( auto const& opt : m_options ) {
+                parseInfos.push_back( { &opt, 0 } );
+            }
+            for ( auto const& arg : m_args ) {
+                parseInfos.push_back( { &arg, 0 } );
+            }
+
+            m_exeName.set( exeName );
+
+            auto result = Detail::InternalParseResult::ok(
+                Detail::ParseState( ParseResultType::NoMatch, tokens ) );
+            while ( result.value().remainingTokens() ) {
+                bool tokenParsed = false;
+
+                for ( auto& parseInfo : parseInfos ) {
+                    if ( parseInfo.parser->cardinality() == 0 ||
+                         parseInfo.count < parseInfo.parser->cardinality() ) {
+                        result = parseInfo.parser->parse(
+                            exeName, result.value().remainingTokens() );
+                        if ( !result )
+                            return result;
+                        if ( result.value().type() !=
+                             ParseResultType::NoMatch ) {
+                            tokenParsed = true;
+                            ++parseInfo.count;
+                            break;
+                        }
+                    }
+                }
+
+                if ( result.value().type() == ParseResultType::ShortCircuitAll )
+                    return result;
+                if ( !tokenParsed )
+                    return Detail::InternalParseResult::runtimeError(
+                        "Unrecognised token: " +
+                        result.value().remainingTokens()->token );
+            }
+            // !TBD Check missing required options
+            return result;
+        }
+
+        Args::Args(int argc, char const* const* argv) :
+            m_exeName(argv[0]), m_args(argv + 1, argv + argc) {}
+
+        Args::Args(std::initializer_list<std::string> args) :
+            m_exeName(*args.begin()),
+            m_args(args.begin() + 1, args.end()) {}
+
+
+        Help::Help( bool& showHelpFlag ):
+            Opt( [&]( bool flag ) {
+                showHelpFlag = flag;
+                return ParserResult::ok( ParseResultType::ShortCircuitAll );
+            } ) {
+            static_cast<Opt&> ( *this )(
+                "display usage information" )["-?"]["-h"]["--help"]
+                .optional();
+        }
+
+    } // namespace Clara
+} // namespace Catch
+
+
+
+
+#include <fstream>
+#include <string>
+
+namespace Catch {
+
+    Clara::Parser makeCommandLineParser( ConfigData& config ) {
+
+        using namespace Clara;
+
+        auto const setWarning = [&]( std::string const& warning ) {
+            if ( warning == "NoAssertions" ) {
+                config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::NoAssertions);
+                return ParserResult::ok( ParseResultType::Matched );
+            } else if ( warning == "UnmatchedTestSpec" ) {
+                config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::UnmatchedTestSpec);
+                return ParserResult::ok( ParseResultType::Matched );
+            }
+
+            return ParserResult ::runtimeError(
+                "Unrecognised warning option: '" + warning + '\'' );
+        };
+        auto const loadTestNamesFromFile = [&]( std::string const& filename ) {
+                std::ifstream f( filename.c_str() );
+                if( !f.is_open() )
+                    return ParserResult::runtimeError( "Unable to load input file: '" + filename + '\'' );
+
+                std::string line;
+                while( std::getline( f, line ) ) {
+                    line = trim(line);
+                    if( !line.empty() && !startsWith( line, '#' ) ) {
+                        if( !startsWith( line, '"' ) )
+                            line = '"' + 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 ) {
+                if( startsWith( "declared", order ) )
+                    config.runOrder = TestRunOrder::Declared;
+                else if( startsWith( "lexical", order ) )
+                    config.runOrder = TestRunOrder::LexicographicallySorted;
+                else if( startsWith( "random", order ) )
+                    config.runOrder = TestRunOrder::Randomized;
+                else
+                    return ParserResult::runtimeError( "Unrecognised ordering: '" + order + '\'' );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setRngSeed = [&]( std::string const& seed ) {
+                if( seed == "time" ) {
+                    config.rngSeed = generateRandomSeed(GenerateFrom::Time);
+                    return ParserResult::ok(ParseResultType::Matched);
+                } else if (seed == "random-device") {
+                    config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice);
+                    return ParserResult::ok(ParseResultType::Matched);
+                }
+
+                CATCH_TRY {
+                    std::size_t parsedTo = 0;
+                    unsigned long parsedSeed = std::stoul(seed, &parsedTo, 0);
+                    if (parsedTo != seed.size()) {
+                        return ParserResult::runtimeError("Could not parse '" + seed + "' as seed");
+                    }
+
+                    // TODO: Ideally we could parse unsigned int directly,
+                    //       but the stdlib doesn't provide helper for that
+                    //       type. After this is refactored to use fixed size
+                    //       type, we should check the parsed value is in range
+                    //       of the underlying type.
+                    config.rngSeed = static_cast<unsigned int>(parsedSeed);
+                    return ParserResult::ok(ParseResultType::Matched);
+                } CATCH_CATCH_ANON(std::exception const&) {
+                    return ParserResult::runtimeError("Could not parse '" + seed + "' as seed");
+                }
+            };
+        auto const setDefaultColourMode = [&]( std::string const& colourMode ) {
+            Optional<ColourMode> maybeMode = Catch::Detail::stringToColourMode(toLower( colourMode ));
+            if ( !maybeMode ) {
+                return ParserResult::runtimeError(
+                    "colour mode must be one of: default, ansi, win32, "
+                    "or none. '" +
+                    colourMode + "' is not recognised" );
+            }
+            auto mode = *maybeMode;
+            if ( !isColourImplAvailable( mode ) ) {
+                return ParserResult::runtimeError(
+                    "colour mode '" + colourMode +
+                    "' is not supported in this binary" );
+            }
+            config.defaultColourMode = mode;
+            return ParserResult::ok( ParseResultType::Matched );
+        };
+        auto const setWaitForKeypress = [&]( std::string const& keypress ) {
+                auto keypressLc = toLower( keypress );
+                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: never, start, exit or both. '" + keypress + "' not recognised" );
+            return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setVerbosity = [&]( std::string const& verbosity ) {
+            auto lcVerbosity = toLower( verbosity );
+            if( lcVerbosity == "quiet" )
+                config.verbosity = Verbosity::Quiet;
+            else if( lcVerbosity == "normal" )
+                config.verbosity = Verbosity::Normal;
+            else if( lcVerbosity == "high" )
+                config.verbosity = Verbosity::High;
+            else
+                return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + '\'' );
+            return ParserResult::ok( ParseResultType::Matched );
+        };
+        auto const setReporter = [&]( std::string const& userReporterSpec ) {
+            if ( userReporterSpec.empty() ) {
+                return ParserResult::runtimeError( "Received empty reporter spec." );
+            }
+
+            Optional<ReporterSpec> parsed =
+                parseReporterSpec( userReporterSpec );
+            if ( !parsed ) {
+                return ParserResult::runtimeError(
+                    "Could not parse reporter spec '" + userReporterSpec +
+                    "'" );
+            }
+
+            auto const& reporterSpec = *parsed;
+
+            IReporterRegistry::FactoryMap const& factories =
+                getRegistryHub().getReporterRegistry().getFactories();
+            auto result = factories.find( reporterSpec.name() );
+
+            if ( result == factories.end() ) {
+                return ParserResult::runtimeError(
+                    "Unrecognized reporter, '" + reporterSpec.name() +
+                    "'. Check available with --list-reporters" );
+            }
+
+
+            const bool hadOutputFile = reporterSpec.outputFile().some();
+            config.reporterSpecifications.push_back( CATCH_MOVE( *parsed ) );
+            // It would be enough to check this only once at the very end, but
+            // there is  not a place where we could call this check, so do it
+            // every time it could fail. For valid inputs, this is still called
+            // at most once.
+            if (!hadOutputFile) {
+                int n_reporters_without_file = 0;
+                for (auto const& spec : config.reporterSpecifications) {
+                    if (spec.outputFile().none()) {
+                        n_reporters_without_file++;
+                    }
+                }
+                if (n_reporters_without_file > 1) {
+                    return ParserResult::runtimeError( "Only one reporter may have unspecified output file." );
+                }
+            }
+
+            return ParserResult::ok( ParseResultType::Matched );
+        };
+        auto const setShardCount = [&]( std::string const& shardCount ) {
+            CATCH_TRY{
+                std::size_t parsedTo = 0;
+                int64_t parsedCount = std::stoll(shardCount, &parsedTo, 0);
+                if (parsedTo != shardCount.size()) {
+                    return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count");
+                }
+                if (parsedCount <= 0) {
+                    return ParserResult::runtimeError("Shard count must be a positive number");
+                }
+
+                config.shardCount = static_cast<unsigned int>(parsedCount);
+                return ParserResult::ok(ParseResultType::Matched);
+            } CATCH_CATCH_ANON(std::exception const&) {
+                return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count");
+            }
+        };
+
+        auto const setShardIndex = [&](std::string const& shardIndex) {
+            CATCH_TRY{
+                std::size_t parsedTo = 0;
+                int64_t parsedIndex = std::stoll(shardIndex, &parsedTo, 0);
+                if (parsedTo != shardIndex.size()) {
+                    return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index");
+                }
+                if (parsedIndex < 0) {
+                    return ParserResult::runtimeError("Shard index must be a non-negative number");
+                }
+
+                config.shardIndex = static_cast<unsigned int>(parsedIndex);
+                return ParserResult::ok(ParseResultType::Matched);
+            } CATCH_CATCH_ANON(std::exception const&) {
+                return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index");
+            }
+        };
+
+
+        auto cli
+            = ExeName( config.processName )
+            | Help( config.showHelp )
+            | Opt( config.showSuccessfulTests )
+                ["-s"]["--success"]
+                ( "include successful tests in output" )
+            | Opt( config.shouldDebugBreak )
+                ["-b"]["--break"]
+                ( "break into debugger on failure" )
+            | Opt( config.noThrow )
+                ["-e"]["--nothrow"]
+                ( "skip exception tests" )
+            | Opt( config.showInvisibles )
+                ["-i"]["--invisibles"]
+                ( "show invisibles (tabs, newlines)" )
+            | Opt( config.defaultOutputFilename, "filename" )
+                ["-o"]["--out"]
+                ( "default output filename" )
+            | Opt( accept_many, setReporter, "name[::key=value]*" )
+                ["-r"]["--reporter"]
+                ( "reporter to use (defaults to console)" )
+            | Opt( config.name, "name" )
+                ["-n"]["--name"]
+                ( "suite name" )
+            | Opt( [&]( bool ){ config.abortAfter = 1; } )
+                ["-a"]["--abort"]
+                ( "abort at first failure" )
+            | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
+                ["-x"]["--abortx"]
+                ( "abort after x failures" )
+            | Opt( accept_many, setWarning, "warning name" )
+                ["-w"]["--warn"]
+                ( "enable warnings" )
+            | 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" )
+            | Opt( config.filenamesAsTags )
+                ["-#"]["--filenames-as-tags"]
+                ( "adds a tag for the filename" )
+            | Opt( config.sectionsToRun, "section name" )
+                ["-c"]["--section"]
+                ( "specify section to run" )
+            | Opt( setVerbosity, "quiet|normal|high" )
+                ["-v"]["--verbosity"]
+                ( "set output verbosity" )
+            | Opt( config.listTests )
+                ["--list-tests"]
+                ( "list all/matching test cases" )
+            | Opt( config.listTags )
+                ["--list-tags"]
+                ( "list all/matching tags" )
+            | Opt( config.listReporters )
+                ["--list-reporters"]
+                ( "list all available reporters" )
+            | Opt( config.listListeners )
+                ["--list-listeners"]
+                ( "list all listeners" )
+            | Opt( setTestOrder, "decl|lex|rand" )
+                ["--order"]
+                ( "test case order (defaults to decl)" )
+            | Opt( setRngSeed, "'time'|'random-device'|number" )
+                ["--rng-seed"]
+                ( "set a specific seed for random numbers" )
+            | Opt( setDefaultColourMode, "ansi|win32|none|default" )
+                ["--colour-mode"]
+                ( "what color mode should be used as default" )
+            | Opt( config.libIdentify )
+                ["--libidentify"]
+                ( "report name and version according to libidentify standard" )
+            | Opt( setWaitForKeypress, "never|start|exit|both" )
+                ["--wait-for-keypress"]
+                ( "waits for a keypress before exiting" )
+            | Opt( config.skipBenchmarks)
+                ["--skip-benchmarks"]
+                ( "disable running 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)" )
+            | Opt( setShardCount, "shard count" )
+                ["--shard-count"]
+                ( "split the tests to execute into this many groups" )
+            | Opt( setShardIndex, "shard index" )
+                ["--shard-index"]
+                ( "index of the group of tests to execute (see --shard-count)" ) |
+            Opt( config.allowZeroTests )
+                ["--allow-running-no-tests"]
+                ( "Treat 'No tests run' as a success" )
+            | Arg( config.testsOrTags, "test name|pattern|tags" )
+                ( "which test or tests to use" );
+
+        return cli;
+    }
+
+} // end namespace Catch
+
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+
+
+#include <cassert>
+#include <ostream>
+#include <utility>
+
+namespace Catch {
+
+    ColourImpl::~ColourImpl() = default;
+
+    ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) {
+        return ColourGuard(colourCode, this );
+    }
+
+    void ColourImpl::ColourGuard::engageImpl( std::ostream& stream ) {
+        assert( &stream == &m_colourImpl->m_stream->stream() &&
+                "Engaging colour guard for different stream than used by the "
+                "parent colour implementation" );
+        static_cast<void>( stream );
+
+        m_engaged = true;
+        m_colourImpl->use( m_code );
+    }
+
+    ColourImpl::ColourGuard::ColourGuard( Colour::Code code,
+                                          ColourImpl const* colour ):
+        m_colourImpl( colour ), m_code( code ) {
+    }
+    ColourImpl::ColourGuard::ColourGuard( ColourGuard&& rhs ) noexcept:
+        m_colourImpl( rhs.m_colourImpl ),
+        m_code( rhs.m_code ),
+        m_engaged( rhs.m_engaged ) {
+        rhs.m_engaged = false;
+    }
+    ColourImpl::ColourGuard&
+    ColourImpl::ColourGuard::operator=( ColourGuard&& rhs ) noexcept {
+        using std::swap;
+        swap( m_colourImpl, rhs.m_colourImpl );
+        swap( m_code, rhs.m_code );
+        swap( m_engaged, rhs.m_engaged );
+
+        return *this;
+    }
+    ColourImpl::ColourGuard::~ColourGuard() {
+        if ( m_engaged ) {
+            m_colourImpl->use( Colour::None );
+        }
+    }
+
+    ColourImpl::ColourGuard&
+    ColourImpl::ColourGuard::engage( std::ostream& stream ) & {
+        engageImpl( stream );
+        return *this;
+    }
+
+    ColourImpl::ColourGuard&&
+    ColourImpl::ColourGuard::engage( std::ostream& stream ) && {
+        engageImpl( stream );
+        return CATCH_MOVE(*this);
+    }
+
+    namespace {
+        //! A do-nothing implementation of colour, used as fallback for unknown
+        //! platforms, and when the user asks to deactivate all colours.
+        class NoColourImpl : public ColourImpl {
+        public:
+            NoColourImpl( IStream* stream ): ColourImpl( stream ) {}
+
+        private:
+            void use( Colour::Code ) const override {}
+        };
+    } // namespace
+
+
+} // namespace Catch
+
+
+#if defined ( CATCH_CONFIG_COLOUR_WIN32 ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+    class Win32ColourImpl : public ColourImpl {
+    public:
+        Win32ColourImpl(IStream* stream):
+            ColourImpl(stream) {
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ),
+                                        &csbiInfo );
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+        }
+
+        static bool useImplementationForStream(IStream const& stream) {
+            // Win32 text colour APIs can only be used on console streams
+            // We cannot check that the output hasn't been redirected,
+            // so we just check that the original stream is console stream.
+            return stream.isConsole();
+        }
+
+    private:
+        void use( Colour::Code _colourCode ) const override {
+            switch( _colourCode ) {
+                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
+                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
+                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
+                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
+                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+                case Colour::Grey:      return setTextAttribute( 0 );
+
+                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
+                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::BrightYellow:  return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
+
+                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+
+                default:
+                    CATCH_ERROR( "Unknown colour requested" );
+            }
+        }
+
+        void setTextAttribute( WORD _textAttribute ) const {
+            SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ),
+                                     _textAttribute |
+                                         originalBackgroundAttributes );
+        }
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
+    };
+
+} // end anon namespace
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+
+#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC )
+#    define CATCH_INTERNAL_HAS_ISATTY
+#    include <unistd.h>
+#endif
+
+namespace Catch {
+namespace {
+
+    class ANSIColourImpl : public ColourImpl {
+    public:
+        ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {}
+
+        static bool useImplementationForStream(IStream const& stream) {
+            // This is kinda messy due to trying to support a bunch of
+            // different platforms at once.
+            // The basic idea is that if we are asked to do autodetection (as
+            // opposed to being told to use posixy colours outright), then we
+            // only want to use the colours if we are writing to console.
+            // However, console might be redirected, so we make an attempt at
+            // checking for that on platforms where we know how to do that.
+            bool useColour = stream.isConsole();
+#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \
+    !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) )
+            ErrnoGuard _; // for isatty
+            useColour = useColour && isatty( STDOUT_FILENO );
+#    endif
+#    if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE )
+            useColour = useColour && !isDebuggerActive();
+#    endif
+
+            return useColour;
+        }
+
+    private:
+        void use( Colour::Code _colourCode ) const override {
+            auto setColour = [&out =
+                                  m_stream->stream()]( char const* escapeCode ) {
+                // The escape sequence must be flushed to console, otherwise
+                // if stdin and stderr are intermixed, we'd get accidentally
+                // coloured output.
+                out << '\033' << escapeCode << std::flush;
+            };
+            switch( _colourCode ) {
+                case Colour::None:
+                case Colour::White:     return setColour( "[0m" );
+                case Colour::Red:       return setColour( "[0;31m" );
+                case Colour::Green:     return setColour( "[0;32m" );
+                case Colour::Blue:      return setColour( "[0;34m" );
+                case Colour::Cyan:      return setColour( "[0;36m" );
+                case Colour::Yellow:    return setColour( "[0;33m" );
+                case Colour::Grey:      return setColour( "[1;30m" );
+
+                case Colour::LightGrey:     return setColour( "[0;37m" );
+                case Colour::BrightRed:     return setColour( "[1;31m" );
+                case Colour::BrightGreen:   return setColour( "[1;32m" );
+                case Colour::BrightWhite:   return setColour( "[1;37m" );
+                case Colour::BrightYellow:  return setColour( "[1;33m" );
+
+                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+                default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
+            }
+        }
+    };
+
+} // end anon namespace
+} // end namespace Catch
+
+namespace Catch {
+
+    Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode implSelection,
+                                                   IStream* stream ) {
+#if defined( CATCH_CONFIG_COLOUR_WIN32 )
+        if ( implSelection == ColourMode::Win32 ) {
+            return Detail::make_unique<Win32ColourImpl>( stream );
+        }
+#endif
+        if ( implSelection == ColourMode::ANSI ) {
+            return Detail::make_unique<ANSIColourImpl>( stream );
+        }
+        if ( implSelection == ColourMode::None ) {
+            return Detail::make_unique<NoColourImpl>( stream );
+        }
+
+        if ( implSelection == ColourMode::PlatformDefault) {
+#if defined( CATCH_CONFIG_COLOUR_WIN32 )
+            if ( Win32ColourImpl::useImplementationForStream( *stream ) ) {
+                return Detail::make_unique<Win32ColourImpl>( stream );
+            }
+#endif
+            if ( ANSIColourImpl::useImplementationForStream( *stream ) ) {
+                return Detail::make_unique<ANSIColourImpl>( stream );
+            }
+            return Detail::make_unique<NoColourImpl>( stream );
+        }
+
+        CATCH_ERROR( "Could not create colour impl for selection " << static_cast<int>(implSelection) );
+    }
+
+    bool isColourImplAvailable( ColourMode colourSelection ) {
+        switch ( colourSelection ) {
+#if defined( CATCH_CONFIG_COLOUR_WIN32 )
+        case ColourMode::Win32:
+#endif
+        case ColourMode::ANSI:
+        case ColourMode::None:
+        case ColourMode::PlatformDefault:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+
+} // end namespace Catch
+
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+
+
+
+
+namespace Catch {
+
+    class Context : public IMutableContext, private Detail::NonCopyable {
+
+    public: // IContext
+        IResultCapture* getResultCapture() override {
+            return m_resultCapture;
+        }
+
+        IConfig const* getConfig() const override {
+            return m_config;
+        }
+
+        ~Context() override;
+
+    public: // IMutableContext
+        void setResultCapture( IResultCapture* resultCapture ) override {
+            m_resultCapture = resultCapture;
+        }
+        void setConfig( IConfig const* config ) override {
+            m_config = config;
+        }
+
+        friend IMutableContext& getCurrentMutableContext();
+
+    private:
+        IConfig const* m_config = nullptr;
+        IResultCapture* m_resultCapture = nullptr;
+    };
+
+    IMutableContext *IMutableContext::currentContext = nullptr;
+
+    void IMutableContext::createContext()
+    {
+        currentContext = new Context();
+    }
+
+    void cleanUpContext() {
+        delete IMutableContext::currentContext;
+        IMutableContext::currentContext = nullptr;
+    }
+    IContext::~IContext() = default;
+    IMutableContext::~IMutableContext() = default;
+    Context::~Context() = default;
+
+
+    SimplePcg32& sharedRng() {
+        static SimplePcg32 s_rng;
+        return s_rng;
+    }
+
+}
+
+
+
+
+
+#include <ostream>
+
+#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+#include <android/log.h>
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
+        }
+    }
+
+#elif defined(CATCH_PLATFORM_WINDOWS)
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            ::OutputDebugStringA( text.c_str() );
+        }
+    }
+
+#else
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            // !TBD: Need a version for Mac/ XCode and other IDEs
+            Catch::cout() << text;
+        }
+    }
+
+#endif // Platform
+
+
+
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+
+#  include <cassert>
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <cstddef>
+#  include <ostream>
+
+#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:
+        // 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;
+
+            // Initialize the flags so that, if sysctl fails for some bizarre
+            // reason, we get a predictable result.
+
+            info.kp_proc.p_flag = 0;
+
+            // Initialize mib, which tells sysctl the info we want, in this case
+            // we're looking for information about a specific process ID.
+
+            mib[0] = CTL_KERN;
+            mib[1] = KERN_PROC;
+            mib[2] = KERN_PROC_PID;
+            mib[3] = getpid();
+
+            // Call sysctl.
+
+            size = sizeof(info);
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) {
+                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush;
+                return false;
+            }
+
+            // We're being debugged if the P_TRACED flag is set.
+
+            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)
+    #include <fstream>
+    #include <string>
+
+    namespace Catch{
+        // The standard POSIX way of detecting a debugger is to attempt to
+        // ptrace() the process, but this needs to be done from a child and not
+        // this process itself to still allow attaching to this process later
+        // if wanted, so is rather heavy. Under Linux we have the PID of the
+        // "debugger" (which doesn't need to be gdb, of course, it could also
+        // be strace, for example) in /proc/$PID/status, so just get it from
+        // there instead.
+        bool isDebuggerActive(){
+            // Libstdc++ has a bug, where std::ifstream sets errno to 0
+            // This way our users can properly assert over errno values
+            ErrnoGuard guard;
+            std::ifstream in("/proc/self/status");
+            for( std::string line; std::getline(in, line); ) {
+                static const int PREFIX_LEN = 11;
+                if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+                    // We're traced if the PID is not 0 and no other PID starts
+                    // with 0 digit, so it's enough to check for just a single
+                    // character.
+                    return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+                }
+            }
+
+            return false;
+        }
+    } // namespace Catch
+#elif defined(_MSC_VER)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#else
+    namespace Catch {
+       bool isDebuggerActive() { return false; }
+    }
+#endif // Platform
+
+
+
+
+namespace Catch {
+
+    ITransientExpression::~ITransientExpression() = default;
+
+    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) {
+        if( lhs.size() + rhs.size() < 40 &&
+                lhs.find('\n') == std::string::npos &&
+                rhs.find('\n') == std::string::npos )
+            os << lhs << ' ' << op << ' ' << rhs;
+        else
+            os << lhs << '\n' << op << '\n' << rhs;
+    }
+}
+
+
+
+#include <stdexcept>
+
+
+namespace Catch {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
+    [[noreturn]]
+    void throw_exception(std::exception const& e) {
+        Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n"
+                      << "The message was: " << e.what() << '\n';
+        std::terminate();
+    }
+#endif
+
+    [[noreturn]]
+    void throw_logic_error(std::string const& msg) {
+        throw_exception(std::logic_error(msg));
+    }
+
+    [[noreturn]]
+    void throw_domain_error(std::string const& msg) {
+        throw_exception(std::domain_error(msg));
+    }
+
+    [[noreturn]]
+    void throw_runtime_error(std::string const& msg) {
+        throw_exception(std::runtime_error(msg));
+    }
+
+
+
+} // namespace Catch;
+
+
+
+#include <cassert>
+
+namespace Catch {
+
+    IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
+
+    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;
+        }
+
+        Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
+            auto enumInfo = Catch::Detail::make_unique<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
+
+
+
+
+
+#include <cerrno>
+
+namespace Catch {
+        ErrnoGuard::ErrnoGuard():m_oldErrno(errno){}
+        ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; }
+}
+
+
+
+namespace Catch {
+
+    ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
+    }
+
+    void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) {
+        m_translators.push_back( CATCH_MOVE( translator ) );
+    }
+
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    std::string ExceptionTranslatorRegistry::translateActiveException() const {
+        // Compiling a mixed mode project with MSVC means that CLR
+        // exceptions will be caught in (...) as well. However, these do
+        // do not fill-in std::current_exception and thus lead to crash
+        // when attempting rethrow.
+        // /EHa switch also causes structured exceptions to be caught
+        // here, but they fill-in current_exception properly, so
+        // at worst the output should be a little weird, instead of
+        // causing a crash.
+        if ( std::current_exception() == nullptr ) {
+            return "Non C++ exception. Possibly a CLR exception.";
+        }
+
+        // First we try user-registered translators. If none of them can
+        // handle the exception, it will be rethrown handled by our defaults.
+        try {
+            return tryTranslators();
+        }
+        // To avoid having to handle TFE explicitly everywhere, we just
+        // rethrow it so that it goes back up the caller.
+        catch( TestFailureException& ) {
+            std::rethrow_exception(std::current_exception());
+        }
+        catch( std::exception const& ex ) {
+            return ex.what();
+        }
+        catch( std::string const& msg ) {
+            return msg;
+        }
+        catch( const char* msg ) {
+            return msg;
+        }
+        catch(...) {
+            return "Unknown exception";
+        }
+    }
+
+    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!");
+    }
+
+    std::string ExceptionTranslatorRegistry::tryTranslators() const {
+        CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+    }
+#endif
+
+
+}
+
+
+
+/** \file
+ * This file provides platform specific implementations of FatalConditionHandler
+ *
+ * This means that there is a lot of conditional compilation, and platform
+ * specific code. Currently, Catch2 supports a dummy handler (if no
+ * handler is desired), and 2 platform specific handlers:
+ *  * Windows' SEH
+ *  * POSIX signals
+ *
+ * Consequently, various pieces of code below are compiled if either of
+ * the platform specific handlers is enabled, or if none of them are
+ * enabled. It is assumed that both cannot be enabled at the same time,
+ * and doing so should cause a compilation error.
+ *
+ * If another platform specific handler is added, the compile guards
+ * below will need to be updated taking these assumptions into account.
+ */
+
+
+
+#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() noexcept {}
+    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 {
+    //! Signals fatal error message to the run context
+    void reportFatal( char const * const message ) {
+        Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
+    }
+
+    //! Minimal size Catch2 needs for its own fatal error handling.
+    //! Picked empirically, 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 LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
+        for (auto const& def : signalDefs) {
+            if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
+                reportFatal(def.name);
+            }
+        }
+        // If its not an exception we care about, pass it along.
+        // This stops us from eating debugger breaks etc.
+        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 LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
+
+
+    // For MSVC, we reserve part of the stack memory for handling
+    // memory overflow structured exception.
+    FatalConditionHandler::FatalConditionHandler() {
+        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 a the top level exception filter.
+        previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter);
+    }
+
+    void FatalConditionHandler::disengage_platform() noexcept {
+        if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) {
+            Catch::cerr()
+                << "Unexpected SEH unhandled exception filter on disengage."
+                << " The filter was restored, but might be rolled back unexpectedly.";
+        }
+        previousTopLevelExceptionFilter = nullptr;
+    }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_WINDOWS_SEH
+
+#if defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+#include <signal.h>
+
+namespace Catch {
+
+    struct SignalDefs {
+        int id;
+        const char* name;
+    };
+
+    static SignalDefs signalDefs[] = {
+        { SIGINT,  "SIGINT - Terminal interrupt signal" },
+        { SIGILL,  "SIGILL - Illegal instruction signal" },
+        { SIGFPE,  "SIGFPE - Floating point error signal" },
+        { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+        { SIGTERM, "SIGTERM - Termination request signal" },
+        { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+    };
+
+// 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() noexcept {
+        // 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) {
+                name = def.name;
+                break;
+            }
+        }
+        // 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() {
+        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 = altStackSize;
+        sigStack.ss_flags = 0;
+        sigaltstack(&sigStack, &oldSigStack);
+        struct sigaction sa = { };
+
+        sa.sa_handler = handleSignal;
+        sa.sa_flags = SA_ONSTACK;
+        for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+            sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+        }
+    }
+
+#if defined(__GNUC__)
+#    pragma GCC diagnostic pop
+#endif
+
+
+    void FatalConditionHandler::disengage_platform() noexcept {
+        restorePreviousSignalHandlers();
+    }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_POSIX_SIGNALS
+
+
+
+
+#include <cstring>
+
+namespace Catch {
+    namespace Detail {
+
+        uint32_t convertToBits(float f) {
+            static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated");
+            uint32_t i;
+            std::memcpy(&i, &f, sizeof(f));
+            return i;
+        }
+
+        uint64_t convertToBits(double d) {
+            static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated");
+            uint64_t i;
+            std::memcpy(&i, &d, sizeof(d));
+            return i;
+        }
+
+    } // end namespace Detail
+} // end namespace Catch
+
+
+
+
+
+#include <cstdio>
+#include <fstream>
+#include <sstream>
+#include <vector>
+
+namespace Catch {
+
+    Catch::IStream::~IStream() = default;
+
+namespace Detail {
+    namespace {
+        template<typename WriterF, std::size_t bufferSize=256>
+        class StreamBufImpl : public std::streambuf {
+            char data[bufferSize];
+            WriterF m_writer;
+
+        public:
+            StreamBufImpl() {
+                setp( data, data + sizeof(data) );
+            }
+
+            ~StreamBufImpl() noexcept override {
+                StreamBufImpl::sync();
+            }
+
+        private:
+            int overflow( int c ) override {
+                sync();
+
+                if( c != EOF ) {
+                    if( pbase() == epptr() )
+                        m_writer( std::string( 1, static_cast<char>( c ) ) );
+                    else
+                        sputc( static_cast<char>( c ) );
+                }
+                return 0;
+            }
+
+            int sync() override {
+                if( pbase() != pptr() ) {
+                    m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+                    setp( pbase(), epptr() );
+                }
+                return 0;
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        struct OutputDebugWriter {
+
+            void operator()( std::string const& str ) {
+                if ( !str.empty() ) {
+                    writeToDebugConsole( str );
+                }
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class FileStream : public IStream {
+            std::ofstream m_ofs;
+        public:
+            FileStream( std::string const& filename ) {
+                m_ofs.open( filename.c_str() );
+                CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' );
+                m_ofs << std::unitbuf;
+            }
+            ~FileStream() override = default;
+        public: // IStream
+            std::ostream& stream() override {
+                return m_ofs;
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class CoutStream : public IStream {
+            std::ostream m_os;
+        public:
+            // Store the streambuf from cout up-front because
+            // cout may get redirected when running tests
+            CoutStream() : m_os( Catch::cout().rdbuf() ) {}
+            ~CoutStream() override = default;
+
+        public: // IStream
+            std::ostream& stream() override { return m_os; }
+            bool isConsole() const override { return true; }
+        };
+
+        class CerrStream : public IStream {
+            std::ostream m_os;
+
+        public:
+            // Store the streambuf from cerr up-front because
+            // cout may get redirected when running tests
+            CerrStream(): m_os( Catch::cerr().rdbuf() ) {}
+            ~CerrStream() override = default;
+
+        public: // IStream
+            std::ostream& stream() override { return m_os; }
+            bool isConsole() const override { return true; }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class DebugOutStream : public IStream {
+            Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
+            std::ostream m_os;
+        public:
+            DebugOutStream()
+            :   m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ),
+                m_os( m_streamBuf.get() )
+            {}
+
+            ~DebugOutStream() override = default;
+
+        public: // IStream
+            std::ostream& stream() override { return m_os; }
+        };
+
+    } // unnamed namespace
+} // namespace Detail
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream> {
+        if ( filename.empty() || filename == "-" ) {
+            return Detail::make_unique<Detail::CoutStream>();
+        }
+        if( filename[0] == '%' ) {
+            if ( filename == "%debug" ) {
+                return Detail::make_unique<Detail::DebugOutStream>();
+            } else if ( filename == "%stderr" ) {
+                return Detail::make_unique<Detail::CerrStream>();
+            } else if ( filename == "%stdout" ) {
+                return Detail::make_unique<Detail::CoutStream>();
+            } else {
+                CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' );
+            }
+        }
+        return Detail::make_unique<Detail::FileStream>( filename );
+    }
+
+}
+
+
+
+
+namespace Catch {
+
+    auto operator << (std::ostream& os, LazyExpression const& lazyExpr) -> std::ostream& {
+        if (lazyExpr.m_isNegated)
+            os << '!';
+
+        if (lazyExpr) {
+            if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression())
+                os << '(' << *lazyExpr.m_transientExpression << ')';
+            else
+                os << *lazyExpr.m_transientExpression;
+        } else {
+            os << "{** error - unchecked empty expression requested **}";
+        }
+        return os;
+    }
+
+} // namespace Catch
+
+
+
+
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+
+namespace Catch {
+
+    LeakDetector::LeakDetector() {
+        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+        flag |= _CRTDBG_LEAK_CHECK_DF;
+        flag |= _CRTDBG_ALLOC_MEM_DF;
+        _CrtSetDbgFlag(flag);
+        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+        // Change this to leaking allocation's number to break there
+        _CrtSetBreakAlloc(-1);
+    }
+}
+
+#else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv
+
+    Catch::LeakDetector::LeakDetector() {}
+
+#endif // CATCH_CONFIG_WINDOWS_CRTDBG
+
+Catch::LeakDetector::~LeakDetector() {
+    Catch::cleanUp();
+}
+
+
+
+
+
+namespace Catch {
+    namespace {
+
+        void listTests(IEventListener& reporter, IConfig const& config) {
+            auto const& testSpec = config.testSpec();
+            auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config);
+            reporter.listTests(matchedTestCases);
+        }
+
+        void listTags(IEventListener& reporter, IConfig const& config) {
+            auto const& testSpec = config.testSpec();
+            std::vector<TestCaseHandle> matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config);
+
+            std::map<StringRef, TagInfo, Detail::CaseInsensitiveLess> tagCounts;
+            for (auto const& testCase : matchedTestCases) {
+                for (auto const& tagName : testCase.getTestCaseInfo().tags) {
+                    auto it = tagCounts.find(tagName.original);
+                    if (it == tagCounts.end())
+                        it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first;
+                    it->second.add(tagName.original);
+                }
+            }
+
+            std::vector<TagInfo> infos; infos.reserve(tagCounts.size());
+            for (auto& tagc : tagCounts) {
+                infos.push_back(CATCH_MOVE(tagc.second));
+            }
+
+            reporter.listTags(infos);
+        }
+
+        void listReporters(IEventListener& reporter) {
+            std::vector<ReporterDescription> descriptions;
+
+            IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+            descriptions.reserve(factories.size());
+            for (auto const& fac : factories) {
+                descriptions.push_back({ fac.first, fac.second->getDescription() });
+            }
+
+            reporter.listReporters(descriptions);
+        }
+
+        void listListeners(IEventListener& reporter) {
+            std::vector<ListenerDescription> descriptions;
+
+            auto const& factories =
+                getRegistryHub().getReporterRegistry().getListeners();
+            descriptions.reserve( factories.size() );
+            for ( auto const& fac : factories ) {
+                descriptions.push_back( { fac->getName(), fac->getDescription() } );
+            }
+
+            reporter.listListeners( descriptions );
+        }
+
+    } // end anonymous namespace
+
+    void TagInfo::add( StringRef spelling ) {
+        ++count;
+        spellings.insert( spelling );
+    }
+
+    std::string TagInfo::all() const {
+        // 2 per tag for brackets '[' and ']'
+        size_t size =  spellings.size() * 2;
+        for (auto const& spelling : spellings) {
+            size += spelling.size();
+        }
+
+        std::string out; out.reserve(size);
+        for (auto const& spelling : spellings) {
+            out += '[';
+            out += spelling;
+            out += ']';
+        }
+        return out;
+    }
+
+    bool list( IEventListener& reporter, Config const& config ) {
+        bool listed = false;
+        if (config.listTests()) {
+            listed = true;
+            listTests(reporter, config);
+        }
+        if (config.listTags()) {
+            listed = true;
+            listTags(reporter, config);
+        }
+        if (config.listReporters()) {
+            listed = true;
+            listReporters(reporter);
+        }
+        if ( config.listListeners() ) {
+            listed = true;
+            listListeners( reporter );
+        }
+        return listed;
+    }
+
+} // end namespace Catch
+
+
+
+namespace Catch {
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+    static LeakDetector leakDetector;
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+}
+
+// Allow users of amalgamated .cpp file to remove our main and provide their own.
+#if !defined(CATCH_AMALGAMATED_CUSTOM_MAIN)
+
+#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 __cdecl wmain (int argc, wchar_t * argv[], wchar_t * []) {
+#else
+// Standard C/C++ main entry point
+int main (int argc, char * argv[]) {
+#endif
+
+    // We want to force the linker not to discard the global variable
+    // and its constructor, as it (optionally) registers leak detector
+    (void)&Catch::leakDetector;
+
+    return Catch::Session().run( argc, argv );
+}
+
+#endif // !defined(CATCH_AMALGAMATED_CUSTOM_MAIN
+
+
+
+
+namespace Catch {
+
+    MessageInfo::MessageInfo(   StringRef _macroName,
+                                SourceLineInfo const& _lineInfo,
+                                ResultWas::OfType _type )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        type( _type ),
+        sequence( ++globalCount )
+    {}
+
+    // This may need protecting if threading support is added
+    unsigned int MessageInfo::globalCount = 0;
+
+} // end namespace Catch
+
+
+
+#include <cstdio>
+#include <cstring>
+#include <sstream>
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+    #if defined(_MSC_VER)
+    #include <io.h>      //_dup and _dup2
+    #define dup _dup
+    #define dup2 _dup2
+    #define fileno _fileno
+    #else
+    #include <unistd.h>  // dup and dup2
+    #endif
+#endif
+
+
+namespace Catch {
+
+    RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
+    :   m_originalStream( originalStream ),
+        m_redirectionStream( redirectionStream ),
+        m_prevBuf( m_originalStream.rdbuf() )
+    {
+        m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
+    }
+
+    RedirectedStream::~RedirectedStream() {
+        m_originalStream.rdbuf( m_prevBuf );
+    }
+
+    RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
+    auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
+
+    RedirectedStdErr::RedirectedStdErr()
+    :   m_cerr( Catch::cerr(), m_rss.get() ),
+        m_clog( Catch::clog(), m_rss.get() )
+    {}
+    auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
+
+    RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
+    :   m_redirectedCout(redirectedCout),
+        m_redirectedCerr(redirectedCerr)
+    {}
+
+    RedirectedStreams::~RedirectedStreams() {
+        m_redirectedCout += m_redirectedStdOut.str();
+        m_redirectedCerr += m_redirectedStdErr.str();
+    }
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+
+#if defined(_MSC_VER)
+    TempFile::TempFile() {
+        if (tmpnam_s(m_buffer)) {
+            CATCH_RUNTIME_ERROR("Could not get a temp filename");
+        }
+        if (fopen_s(&m_file, m_buffer, "w+")) {
+            char buffer[100];
+            if (strerror_s(buffer, errno)) {
+                CATCH_RUNTIME_ERROR("Could not translate errno to a string");
+            }
+            CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
+        }
+    }
+#else
+    TempFile::TempFile() {
+        m_file = std::tmpfile();
+        if (!m_file) {
+            CATCH_RUNTIME_ERROR("Could not create a temp file.");
+        }
+    }
+
+#endif
+
+    TempFile::~TempFile() {
+         // TBD: What to do about errors here?
+         std::fclose(m_file);
+         // We manually create the file on Windows only, on Linux
+         // it will be autodeleted
+#if defined(_MSC_VER)
+         std::remove(m_buffer);
+#endif
+    }
+
+
+    FILE* TempFile::getFile() {
+        return m_file;
+    }
+
+    std::string TempFile::getContents() {
+        std::stringstream sstr;
+        char buffer[100] = {};
+        std::rewind(m_file);
+        while (std::fgets(buffer, sizeof(buffer), m_file)) {
+            sstr << buffer;
+        }
+        return sstr.str();
+    }
+
+    OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
+        m_originalStdout(dup(1)),
+        m_originalStderr(dup(2)),
+        m_stdoutDest(stdout_dest),
+        m_stderrDest(stderr_dest) {
+        dup2(fileno(m_stdoutFile.getFile()), 1);
+        dup2(fileno(m_stderrFile.getFile()), 2);
+    }
+
+    OutputRedirect::~OutputRedirect() {
+        Catch::cout() << std::flush;
+        fflush(stdout);
+        // Since we support overriding these streams, we flush cerr
+        // even though std::cerr is unbuffered
+        Catch::cerr() << std::flush;
+        Catch::clog() << std::flush;
+        fflush(stderr);
+
+        dup2(m_originalStdout, 1);
+        dup2(m_originalStderr, 2);
+
+        m_stdoutDest += m_stdoutFile.getContents();
+        m_stderrDest += m_stderrFile.getContents();
+    }
+
+#endif // CATCH_CONFIG_NEW_CAPTURE
+
+} // namespace Catch
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+    #if defined(_MSC_VER)
+    #undef dup
+    #undef dup2
+    #undef fileno
+    #endif
+#endif
+
+
+
+
+#include <cmath>
+
+namespace Catch {
+
+#if !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+    bool isnan(float f) {
+        return std::isnan(f);
+    }
+    bool isnan(double d) {
+        return std::isnan(d);
+    }
+#else
+    // For now we only use this for embarcadero
+    bool isnan(float f) {
+        return std::_isnan(f);
+    }
+    bool isnan(double d) {
+        return std::_isnan(d);
+    }
+#endif
+
+} // end namespace Catch
+
+
+
+namespace Catch {
+
+namespace {
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4146) // we negate uint32 during the rotate
+#endif
+        // Safe rotr implementation thanks to John Regehr
+        uint32_t rotate_right(uint32_t val, uint32_t count) {
+            const uint32_t mask = 31;
+            count &= mask;
+            return (val >> count) | (val << (-count & mask));
+        }
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+}
+
+
+    SimplePcg32::SimplePcg32(result_type seed_) {
+        seed(seed_);
+    }
+
+
+    void SimplePcg32::seed(result_type seed_) {
+        m_state = 0;
+        (*this)();
+        m_state += seed_;
+        (*this)();
+    }
+
+    void SimplePcg32::discard(uint64_t skip) {
+        // We could implement this to run in O(log n) steps, but this
+        // should suffice for our use case.
+        for (uint64_t s = 0; s < skip; ++s) {
+            static_cast<void>((*this)());
+        }
+    }
+
+    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;
+    }
+}
+
+
+
+
+
+#include <ctime>
+#include <random>
+
+namespace Catch {
+
+    std::uint32_t generateRandomSeed( GenerateFrom from ) {
+        switch ( from ) {
+        case GenerateFrom::Time:
+            return static_cast<std::uint32_t>( std::time( nullptr ) );
+
+        case GenerateFrom::Default:
+        case GenerateFrom::RandomDevice:
+            // In theory, a platform could have random_device that returns just
+            // 16 bits. That is still some randomness, so we don't care too much
+            return static_cast<std::uint32_t>( std::random_device{}() );
+
+        default:
+            CATCH_ERROR("Unknown generation method");
+        }
+    }
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+
+    ReporterRegistry::ReporterRegistry() {
+        // Because it is impossible to move out of initializer list,
+        // we have to add the elements manually
+        m_factories["Automake"] = Detail::make_unique<ReporterFactory<AutomakeReporter>>();
+        m_factories["compact"] = Detail::make_unique<ReporterFactory<CompactReporter>>();
+        m_factories["console"] = Detail::make_unique<ReporterFactory<ConsoleReporter>>();
+        m_factories["JUnit"] = Detail::make_unique<ReporterFactory<JunitReporter>>();
+        m_factories["SonarQube"] = Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
+        m_factories["TAP"] = Detail::make_unique<ReporterFactory<TAPReporter>>();
+        m_factories["TeamCity"] = Detail::make_unique<ReporterFactory<TeamCityReporter>>();
+        m_factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>();
+    }
+
+    ReporterRegistry::~ReporterRegistry() = default;
+
+
+    IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const {
+        auto it =  m_factories.find( name );
+        if( it == m_factories.end() )
+            return nullptr;
+        return it->second->create( CATCH_MOVE(config) );
+    }
+
+    void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) {
+        CATCH_ENFORCE( name.find( "::" ) == name.npos,
+                       "'::' is not allowed in reporter name: '" + name + '\'' );
+        auto ret = m_factories.emplace(name, CATCH_MOVE(factory));
+        CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" );
+    }
+    void ReporterRegistry::registerListener(
+        Detail::unique_ptr<EventListenerFactory> factory ) {
+        m_listeners.push_back( CATCH_MOVE(factory) );
+    }
+
+    IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
+        return m_factories;
+    }
+    IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
+        return m_listeners;
+    }
+
+}
+
+
+
+
+
+#include <algorithm>
+
+namespace Catch {
+
+    namespace {
+        struct kvPair {
+            StringRef key, value;
+        };
+
+        kvPair splitKVPair(StringRef kvString) {
+            auto splitPos = static_cast<size_t>( std::distance(
+                kvString.begin(),
+                std::find( kvString.begin(), kvString.end(), '=' ) ) );
+
+            return { kvString.substr( 0, splitPos ),
+                     kvString.substr( splitPos + 1, kvString.size() ) };
+        }
+    }
+
+    namespace Detail {
+        std::vector<std::string> splitReporterSpec( StringRef reporterSpec ) {
+            static constexpr auto separator = "::";
+            static constexpr size_t separatorSize = 2;
+
+            size_t separatorPos = 0;
+            auto findNextSeparator = [&reporterSpec]( size_t startPos ) {
+                static_assert(
+                    separatorSize == 2,
+                    "The code below currently assumes 2 char separator" );
+
+                auto currentPos = startPos;
+                do {
+                    while ( currentPos < reporterSpec.size() &&
+                            reporterSpec[currentPos] != separator[0] ) {
+                        ++currentPos;
+                    }
+                    if ( currentPos + 1 < reporterSpec.size() &&
+                         reporterSpec[currentPos + 1] == separator[1] ) {
+                        return currentPos;
+                    }
+                    ++currentPos;
+                } while ( currentPos < reporterSpec.size() );
+
+                return static_cast<size_t>( -1 );
+            };
+
+            std::vector<std::string> parts;
+
+            while ( separatorPos < reporterSpec.size() ) {
+                const auto nextSeparator = findNextSeparator( separatorPos );
+                parts.push_back( static_cast<std::string>( reporterSpec.substr(
+                    separatorPos, nextSeparator - separatorPos ) ) );
+
+                if ( nextSeparator == static_cast<size_t>( -1 ) ) {
+                    break;
+                }
+                separatorPos = nextSeparator + separatorSize;
+            }
+
+            // Handle a separator at the end.
+            // This is not a valid spec, but we want to do validation in a
+            // centralized place
+            if ( separatorPos == reporterSpec.size() ) {
+                parts.emplace_back();
+            }
+
+            return parts;
+        }
+
+        Optional<ColourMode> stringToColourMode( StringRef colourMode ) {
+            if ( colourMode == "default" ) {
+                return ColourMode::PlatformDefault;
+            } else if ( colourMode == "ansi" ) {
+                return ColourMode::ANSI;
+            } else if ( colourMode == "win32" ) {
+                return ColourMode::Win32;
+            } else if ( colourMode == "none" ) {
+                return ColourMode::None;
+            } else {
+                return {};
+            }
+        }
+    } // namespace Detail
+
+
+    bool operator==( ReporterSpec const& lhs, ReporterSpec const& rhs ) {
+        return lhs.m_name == rhs.m_name &&
+               lhs.m_outputFileName == rhs.m_outputFileName &&
+               lhs.m_colourMode == rhs.m_colourMode &&
+               lhs.m_customOptions == rhs.m_customOptions;
+    }
+
+    Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec ) {
+        auto parts = Detail::splitReporterSpec( reporterSpec );
+
+        assert( parts.size() > 0 && "Split should never return empty vector" );
+
+        std::map<std::string, std::string> kvPairs;
+        Optional<std::string> outputFileName;
+        Optional<ColourMode> colourMode;
+
+        // First part is always reporter name, so we skip it
+        for ( size_t i = 1; i < parts.size(); ++i ) {
+            auto kv = splitKVPair( parts[i] );
+            auto key = kv.key, value = kv.value;
+
+            if ( key.empty() || value.empty() ) {
+                return {};
+            } else if ( key[0] == 'X' ) {
+                // This is a reporter-specific option, we don't check these
+                // apart from basic sanity checks
+                if ( key.size() == 1 ) {
+                    return {};
+                }
+
+                auto ret = kvPairs.emplace( std::string(kv.key), std::string(kv.value) );
+                if ( !ret.second ) {
+                    // Duplicated key. We might want to handle this differently,
+                    // e.g. by overwriting the existing value?
+                    return {};
+                }
+            } else if ( key == "out" ) {
+                // Duplicated key
+                if ( outputFileName ) {
+                    return {};
+                }
+                outputFileName = static_cast<std::string>( value );
+            } else if ( key == "colour-mode" ) {
+                // Duplicated key
+                if ( colourMode ) {
+                    return {};
+                }
+                colourMode = Detail::stringToColourMode( value );
+                // Parsing failed
+                if ( !colourMode ) {
+                    return {};
+                }
+            } else {
+                // Unrecognized option
+                return {};
+            }
+        }
+
+        return ReporterSpec{ CATCH_MOVE( parts[0] ),
+                             CATCH_MOVE( outputFileName ),
+                             CATCH_MOVE( colourMode ),
+                             CATCH_MOVE( kvPairs ) };
+    }
+
+ReporterSpec::ReporterSpec(
+        std::string name,
+        Optional<std::string> outputFileName,
+        Optional<ColourMode> colourMode,
+        std::map<std::string, std::string> customOptions ):
+        m_name( CATCH_MOVE( name ) ),
+        m_outputFileName( CATCH_MOVE( outputFileName ) ),
+        m_colourMode( CATCH_MOVE( colourMode ) ),
+        m_customOptions( CATCH_MOVE( customOptions ) ) {}
+
+} // namespace Catch
+
+
+
+namespace Catch {
+
+    bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+    }
+
+    bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+    bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+
+
+#include <cstdio>
+#include <sstream>
+#include <vector>
+
+namespace Catch {
+
+    // This class encapsulates the idea of a pool of ostringstreams that can be reused.
+    struct StringStreams {
+        std::vector<Detail::unique_ptr<std::ostringstream>> m_streams;
+        std::vector<std::size_t> m_unused;
+        std::ostringstream m_referenceStream; // Used for copy state/ flags from
+
+        auto add() -> std::size_t {
+            if( m_unused.empty() ) {
+                m_streams.push_back( Detail::make_unique<std::ostringstream>() );
+                return m_streams.size()-1;
+            }
+            else {
+                auto index = m_unused.back();
+                m_unused.pop_back();
+                return index;
+            }
+        }
+
+        void release( std::size_t index ) {
+            m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
+            m_unused.push_back(index);
+        }
+    };
+
+    ReusableStringStream::ReusableStringStream()
+    :   m_index( Singleton<StringStreams>::getMutable().add() ),
+        m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
+    {}
+
+    ReusableStringStream::~ReusableStringStream() {
+        static_cast<std::ostringstream*>( m_oss )->str("");
+        m_oss->clear();
+        Singleton<StringStreams>::getMutable().release( m_index );
+    }
+
+    std::string ReusableStringStream::str() const {
+        return static_cast<std::ostringstream*>( m_oss )->str();
+    }
+
+    void ReusableStringStream::str( std::string const& str ) {
+        static_cast<std::ostringstream*>( m_oss )->str( str );
+    }
+
+
+}
+
+
+
+
+#include <cassert>
+#include <algorithm>
+
+namespace Catch {
+
+    namespace Generators {
+        struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
+            GeneratorBasePtr m_generator;
+
+            GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+            :   TrackerBase( nameAndLocation, ctx, parent )
+            {}
+            ~GeneratorTracker() override;
+
+            static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
+                GeneratorTracker* tracker;
+
+                ITracker& currentTracker = ctx.currentTracker();
+                // Under specific circumstances, the generator we want
+                // to acquire is also the current tracker. If this is
+                // the case, we have to avoid looking through current
+                // tracker's children, and instead return the current
+                // tracker.
+                // A case where this check is important is e.g.
+                //     for (int i = 0; i < 5; ++i) {
+                //         int n = GENERATE(1, 2);
+                //     }
+                //
+                // without it, the code above creates 5 nested generators.
+                if ( currentTracker.nameAndLocation() == nameAndLocation ) {
+                    auto thisTracker =
+                        currentTracker.parent()->findChild( nameAndLocation );
+                    assert( thisTracker );
+                    assert( thisTracker->isGeneratorTracker() );
+                    tracker = static_cast<GeneratorTracker*>( thisTracker );
+                } else if ( ITracker* childTracker =
+                                currentTracker.findChild( nameAndLocation ) ) {
+                    assert( childTracker );
+                    assert( childTracker->isGeneratorTracker() );
+                    tracker = static_cast<GeneratorTracker*>( childTracker );
+                } else {
+                    auto newTracker =
+                        Catch::Detail::make_unique<GeneratorTracker>(
+                            nameAndLocation, ctx, &currentTracker );
+                    tracker = newTracker.get();
+                    currentTracker.addChild( CATCH_MOVE(newTracker) );
+                }
+
+                if( !tracker->isComplete() ) {
+                    tracker->open();
+                }
+
+                return *tracker;
+            }
+
+            // TrackerBase interface
+            bool isGeneratorTracker() const override { return true; }
+            auto hasGenerator() const -> bool override {
+                return !!m_generator;
+            }
+            void close() override {
+                TrackerBase::close();
+                // If a generator has a child (it is followed by a section)
+                // and none of its children have started, then we must wait
+                // until later to start consuming its values.
+                // This catches cases where `GENERATE` is placed between two
+                // `SECTION`s.
+                // **The check for m_children.empty cannot be removed**.
+                // doing so would break `GENERATE` _not_ followed by `SECTION`s.
+                const bool should_wait_for_child = [&]() {
+                    // No children -> nobody to wait for
+                    if ( m_children.empty() ) {
+                        return false;
+                    }
+                    // If at least one child started executing, don't wait
+                    if ( std::find_if(
+                             m_children.begin(),
+                             m_children.end(),
+                             []( TestCaseTracking::ITrackerPtr const& 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
+                    ITracker* 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 const&>( *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 const&>( *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->countedNext() ) ) {
+                    m_children.clear();
+                    m_runState = Executing;
+                }
+            }
+
+            // IGeneratorTracker interface
+            auto getGenerator() const -> GeneratorBasePtr const& override {
+                return m_generator;
+            }
+            void setGenerator( GeneratorBasePtr&& generator ) override {
+                m_generator = CATCH_MOVE( generator );
+            }
+        };
+        GeneratorTracker::~GeneratorTracker() = default;
+    }
+
+    RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
+    :   m_runInfo(_config->name()),
+        m_context(getCurrentMutableContext()),
+        m_config(_config),
+        m_reporter(CATCH_MOVE(reporter)),
+        m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
+        m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
+    {
+        m_context.setResultCapture(this);
+        m_reporter->testRunStarting(m_runInfo);
+    }
+
+    RunContext::~RunContext() {
+        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
+    }
+
+    Totals RunContext::runTest(TestCaseHandle const& testCase) {
+        const Totals prevTotals = m_totals;
+
+        std::string redirectedCout;
+        std::string redirectedCerr;
+
+        auto const& testInfo = testCase.getTestCaseInfo();
+
+        m_reporter->testCaseStarting(testInfo);
+
+        m_activeTestCase = &testCase;
+
+
+        ITracker& rootTracker = m_trackerContext.startRun();
+        assert(rootTracker.isSectionTracker());
+        static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
+
+        // We intentionally only seed the internal RNG once per test case,
+        // before it is first invoked. The reason for that is a complex
+        // interplay of generator/section implementation details and the
+        // Random*Generator types.
+        //
+        // The issue boils down to us needing to seed the Random*Generators
+        // with different seed each, so that they return different sequences
+        // of random numbers. We do this by giving them a number from the
+        // shared RNG instance as their seed.
+        //
+        // However, this runs into an issue if the reseeding happens each
+        // time the test case is entered (as opposed to first time only),
+        // because multiple generators could get the same seed, e.g. in
+        // ```cpp
+        // TEST_CASE() {
+        //     auto i = GENERATE(take(10, random(0, 100));
+        //     SECTION("A") {
+        //         auto j = GENERATE(take(10, random(0, 100));
+        //     }
+        //     SECTION("B") {
+        //         auto k = GENERATE(take(10, random(0, 100));
+        //     }
+        // }
+        // ```
+        // `i` and `j` would properly return values from different sequences,
+        // but `i` and `k` would return the same sequence, because their seed
+        // would be the same.
+        // (The reason their seeds would be the same is that the generator
+        //  for k would be initialized when the test case is entered the second
+        //  time, after the shared RNG instance was reset to the same value
+        //  it had when the generator for i was initialized.)
+        seedRng( *m_config );
+
+        uint64_t testRuns = 0;
+        do {
+            m_trackerContext.startCycle();
+            m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
+
+            m_reporter->testCasePartialStarting(testInfo, testRuns);
+
+            const auto beforeRunTotals = m_totals;
+            std::string oneRunCout, oneRunCerr;
+            runCurrentTest(oneRunCout, oneRunCerr);
+            redirectedCout += oneRunCout;
+            redirectedCerr += oneRunCerr;
+
+            const auto singleRunTotals = m_totals.delta(beforeRunTotals);
+            auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting());
+
+            m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
+            ++testRuns;
+        } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
+
+        Totals deltaTotals = m_totals.delta(prevTotals);
+        if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) {
+            deltaTotals.assertions.failed++;
+            deltaTotals.testCases.passed--;
+            deltaTotals.testCases.failed++;
+        }
+        m_totals.testCases += deltaTotals.testCases;
+        m_reporter->testCaseEnded(TestCaseStats(testInfo,
+                                  deltaTotals,
+                                  redirectedCout,
+                                  redirectedCerr,
+                                  aborting()));
+
+        m_activeTestCase = nullptr;
+        m_testCaseTracker = nullptr;
+
+        return deltaTotals;
+    }
+
+
+    void RunContext::assertionEnded(AssertionResult const & result) {
+        if (result.getResultType() == ResultWas::Ok) {
+            m_totals.assertions.passed++;
+            m_lastAssertionPassed = true;
+        } else if (!result.succeeded()) {
+            m_lastAssertionPassed = false;
+            if (result.isOk()) {
+            }
+            else if( m_activeTestCase->getTestCaseInfo().okToFail() )
+                m_totals.assertions.failedButOk++;
+            else
+                m_totals.assertions.failed++;
+        }
+        else {
+            m_lastAssertionPassed = true;
+        }
+
+        m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
+
+        if (result.getResultType() != ResultWas::Warning)
+            m_messageScopes.clear();
+
+        // Reset working state
+        resetAssertionInfo();
+        m_lastResult = result;
+    }
+    void RunContext::resetAssertionInfo() {
+        m_lastAssertionInfo.macroName = StringRef();
+        m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
+    }
+
+    bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
+        ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
+        if (!sectionTracker.isOpen())
+            return false;
+        m_activeSections.push_back(&sectionTracker);
+
+        m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+        m_reporter->sectionStarting(sectionInfo);
+
+        assertions = m_totals.assertions;
+
+        return true;
+    }
+    auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+        using namespace Generators;
+        GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
+                                                              TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
+        m_lastAssertionInfo.lineInfo = lineInfo;
+        return tracker;
+    }
+
+    bool RunContext::testForMissingAssertions(Counts& assertions) {
+        if (assertions.total() != 0)
+            return false;
+        if (!m_config->warnAboutMissingAssertions())
+            return false;
+        if (m_trackerContext.currentTracker().hasChildren())
+            return false;
+        m_totals.assertions.failed++;
+        assertions.failed++;
+        return true;
+    }
+
+    void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
+        Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+        bool missingAssertions = testForMissingAssertions(assertions);
+
+        if (!m_activeSections.empty()) {
+            m_activeSections.back()->close();
+            m_activeSections.pop_back();
+        }
+
+        m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
+        m_messages.clear();
+        m_messageScopes.clear();
+    }
+
+    void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
+        if (m_unfinishedSections.empty())
+            m_activeSections.back()->fail();
+        else
+            m_activeSections.back()->close();
+        m_activeSections.pop_back();
+
+        m_unfinishedSections.push_back(endInfo);
+    }
+
+    void RunContext::benchmarkPreparing( StringRef name ) {
+        m_reporter->benchmarkPreparing(name);
+    }
+    void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
+        m_reporter->benchmarkStarting( info );
+    }
+    void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
+        m_reporter->benchmarkEnded( stats );
+    }
+    void RunContext::benchmarkFailed( StringRef error ) {
+        m_reporter->benchmarkFailed( error );
+    }
+
+    void RunContext::pushScopedMessage(MessageInfo const & message) {
+        m_messages.push_back(message);
+    }
+
+    void RunContext::popScopedMessage(MessageInfo const & message) {
+        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
+            : std::string();
+    }
+
+    const AssertionResult * RunContext::getLastResult() const {
+        return &(*m_lastResult);
+    }
+
+    void RunContext::exceptionEarlyReported() {
+        m_shouldReportUnexpected = false;
+    }
+
+    void RunContext::handleFatalErrorCondition( StringRef message ) {
+        // First notify reporter that bad things happened
+        m_reporter->fatalErrorEncountered(message);
+
+        // 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 = static_cast<std::string>(message);
+        AssertionResult result(m_lastAssertionInfo, tempResult);
+
+        assertionEnded(result);
+
+        handleUnfinishedSections();
+
+        // Recreate section for test case (as we will lose the one that was in scope)
+        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+
+        Counts assertions;
+        assertions.failed = 1;
+        SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
+        m_reporter->sectionEnded(testCaseSectionStats);
+
+        auto const& testInfo = m_activeTestCase->getTestCaseInfo();
+
+        Totals deltaTotals;
+        deltaTotals.testCases.failed = 1;
+        deltaTotals.assertions.failed = 1;
+        m_reporter->testCaseEnded(TestCaseStats(testInfo,
+                                  deltaTotals,
+                                  std::string(),
+                                  std::string(),
+                                  false));
+        m_totals.testCases.failed++;
+        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
+    }
+
+    bool RunContext::lastAssertionPassed() {
+         return m_lastAssertionPassed;
+    }
+
+    void RunContext::assertionPassed() {
+        m_lastAssertionPassed = true;
+        ++m_totals.assertions.passed;
+        resetAssertionInfo();
+        m_messageScopes.clear();
+    }
+
+    bool RunContext::aborting() const {
+        return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
+    }
+
+    void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
+        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+        m_reporter->sectionStarting(testCaseSection);
+        Counts prevAssertions = m_totals.assertions;
+        double duration = 0;
+        m_shouldReportUnexpected = true;
+        m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
+
+        Timer timer;
+        CATCH_TRY {
+            if (m_reporter->getPreferences().shouldRedirectStdOut) {
+#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+                RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
+
+                timer.start();
+                invokeActiveTestCase();
+#else
+                OutputRedirect r(redirectedCout, redirectedCerr);
+                timer.start();
+                invokeActiveTestCase();
+#endif
+            } else {
+                timer.start();
+                invokeActiveTestCase();
+            }
+            duration = timer.getElapsedSeconds();
+        } CATCH_CATCH_ANON (TestFailureException&) {
+            // This just means the test was aborted due to failure
+        } CATCH_CATCH_ALL {
+            // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+            // are reported without translation at the point of origin.
+            if( m_shouldReportUnexpected ) {
+                AssertionReaction dummyReaction;
+                handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
+            }
+        }
+        Counts assertions = m_totals.assertions - prevAssertions;
+        bool missingAssertions = testForMissingAssertions(assertions);
+
+        m_testCaseTracker->close();
+        handleUnfinishedSections();
+        m_messages.clear();
+        m_messageScopes.clear();
+
+        SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
+        m_reporter->sectionEnded(testCaseSectionStats);
+    }
+
+    void RunContext::invokeActiveTestCase() {
+        // We need to engage a handler for signals/structured exceptions
+        // before running the tests themselves, or the binary can crash
+        // without failed test being reported.
+        FatalConditionHandlerGuard _(&m_fatalConditionhandler);
+        // We keep having issue where some compilers warn about an unused
+        // variable, even though the type has non-trivial constructor and
+        // destructor. This is annoying and ugly, but it makes them stfu.
+        (void)_;
+
+        m_activeTestCase->invoke();
+    }
+
+    void RunContext::handleUnfinishedSections() {
+        // If sections ended prematurely due to an exception we stored their
+        // infos here so we can tear them down outside the unwind process.
+        for (auto it = m_unfinishedSections.rbegin(),
+             itEnd = m_unfinishedSections.rend();
+             it != itEnd;
+             ++it)
+            sectionEnded(*it);
+        m_unfinishedSections.clear();
+    }
+
+    void RunContext::handleExpr(
+        AssertionInfo const& info,
+        ITransientExpression const& expr,
+        AssertionReaction& reaction
+    ) {
+        m_reporter->assertionStarting( info );
+
+        bool negated = isFalseTest( info.resultDisposition );
+        bool result = expr.getResult() != negated;
+
+        if( result ) {
+            if (!m_includeSuccessfulResults) {
+                assertionPassed();
+            }
+            else {
+                reportExpr(info, ResultWas::Ok, &expr, negated);
+            }
+        }
+        else {
+            reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
+            populateReaction( reaction );
+        }
+    }
+    void RunContext::reportExpr(
+            AssertionInfo const &info,
+            ResultWas::OfType resultType,
+            ITransientExpression const *expr,
+            bool negated ) {
+
+        m_lastAssertionInfo = info;
+        AssertionResultData data( resultType, LazyExpression( negated ) );
+
+        AssertionResult assertionResult{ info, data };
+        assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
+
+        assertionEnded( assertionResult );
+    }
+
+    void RunContext::handleMessage(
+            AssertionInfo const& info,
+            ResultWas::OfType resultType,
+            StringRef message,
+            AssertionReaction& reaction
+    ) {
+        m_reporter->assertionStarting( info );
+
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( resultType, LazyExpression( false ) );
+        data.message = static_cast<std::string>(message);
+        AssertionResult assertionResult{ m_lastAssertionInfo, data };
+        assertionEnded( assertionResult );
+        if( !assertionResult.isOk() )
+            populateReaction( reaction );
+    }
+    void RunContext::handleUnexpectedExceptionNotThrown(
+            AssertionInfo const& info,
+            AssertionReaction& reaction
+    ) {
+        handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
+    }
+
+    void RunContext::handleUnexpectedInflightException(
+            AssertionInfo const& info,
+            std::string const& message,
+            AssertionReaction& reaction
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+        data.message = message;
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+        populateReaction( reaction );
+    }
+
+    void RunContext::populateReaction( AssertionReaction& reaction ) {
+        reaction.shouldDebugBreak = m_config->shouldDebugBreak();
+        reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
+    }
+
+    void RunContext::handleIncomplete(
+            AssertionInfo const& info
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+        data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+    }
+    void RunContext::handleNonExpr(
+            AssertionInfo const &info,
+            ResultWas::OfType resultType,
+            AssertionReaction &reaction
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( resultType, LazyExpression( false ) );
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+
+        if( !assertionResult.isOk() )
+            populateReaction( reaction );
+    }
+
+
+    IResultCapture& getResultCapture() {
+        if (auto* capture = getCurrentContext().getResultCapture())
+            return *capture;
+        else
+            CATCH_INTERNAL_ERROR("No result capture instance");
+    }
+
+    void seedRng(IConfig const& config) {
+        sharedRng().seed(config.rngSeed());
+    }
+
+    unsigned int rngSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+
+}
+
+
+
+namespace Catch {
+
+    Section::Section( SectionInfo&& info ):
+        m_info( CATCH_MOVE( info ) ),
+        m_sectionIncluded(
+            getResultCapture().sectionStarted( m_info, m_assertions ) ) {
+        // Non-"included" sections will not use the timing information
+        // anyway, so don't bother with the potential syscall.
+        if (m_sectionIncluded) {
+            m_timer.start();
+        }
+    }
+
+    Section::~Section() {
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() };
+            if( uncaught_exceptions() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
+    }
+
+    // This indicates whether the section should be executed or not
+    Section::operator bool() const {
+        return m_sectionIncluded;
+    }
+
+
+} // end namespace Catch
+
+
+
+#include <vector>
+
+namespace Catch {
+
+    namespace {
+        static auto getSingletons() -> std::vector<ISingleton*>*& {
+            static std::vector<ISingleton*>* g_singletons = nullptr;
+            if( !g_singletons )
+                g_singletons = new std::vector<ISingleton*>();
+            return g_singletons;
+        }
+    }
+
+    ISingleton::~ISingleton() = default;
+
+    void addSingleton(ISingleton* singleton ) {
+        getSingletons()->push_back( singleton );
+    }
+    void cleanupSingletons() {
+        auto& singletons = getSingletons();
+        for( auto singleton : *singletons )
+            delete singleton;
+        delete singletons;
+        singletons = nullptr;
+    }
+
+} // namespace Catch
+
+
+
+#include <cstring>
+#include <ostream>
+
+namespace Catch {
+
+    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
+        return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+    }
+    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept {
+        // We can assume that the same file will usually have the same pointer.
+        // Thus, if the pointers are the same, there is no point in calling the strcmp
+        return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));
+    }
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+        os << info.file << '(' << info.line << ')';
+#else
+        os << info.file << ':' << info.line;
+#endif
+        return os;
+    }
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
+        CATCH_TRY {
+            m_exceptions.push_back(exception);
+        } CATCH_CATCH_ALL {
+            // If we run out of memory during start-up there's really not a lot more we can do about it
+            std::terminate();
+        }
+    }
+
+    std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept {
+        return m_exceptions;
+    }
+#endif
+
+} // end namespace Catch
+
+
+
+
+
+#include <iostream>
+
+namespace Catch {
+
+// If you #define this you must implement these functions
+#if !defined( CATCH_CONFIG_NOSTDOUT )
+    std::ostream& cout() { return std::cout; }
+    std::ostream& cerr() { return std::cerr; }
+    std::ostream& clog() { return std::clog; }
+#endif
+
+} // namespace Catch
+
+
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cctype>
+#include <vector>
+
+namespace Catch {
+
+    bool startsWith( std::string const& s, std::string const& prefix ) {
+        return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+    }
+    bool startsWith( StringRef s, char prefix ) {
+        return !s.empty() && s[0] == prefix;
+    }
+    bool endsWith( std::string const& s, std::string const& suffix ) {
+        return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+    }
+    bool endsWith( std::string const& s, char suffix ) {
+        return !s.empty() && s[s.size()-1] == suffix;
+    }
+    bool contains( std::string const& s, std::string const& infix ) {
+        return s.find( infix ) != std::string::npos;
+    }
+    void toLowerInPlace( std::string& s ) {
+        std::transform( s.begin(), s.end(), s.begin(), []( char c ) {
+            return toLower( c );
+        } );
+    }
+    std::string toLower( std::string const& s ) {
+        std::string lc = s;
+        toLowerInPlace( lc );
+        return lc;
+    }
+    char toLower(char c) {
+        return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
+    }
+
+    std::string trim( std::string const& str ) {
+        static char const* whitespaceChars = "\n\r\t ";
+        std::string::size_type start = str.find_first_not_of( whitespaceChars );
+        std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+        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 );
+        while( i != std::string::npos ) {
+            replaced = true;
+            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+            if( i < str.size()-withThis.size() )
+                i = str.find( replaceThis, i+withThis.size() );
+            else
+                i = std::string::npos;
+        }
+        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;
+    }
+
+    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+        os << pluraliser.m_count << ' ' << pluraliser.m_label;
+        if( pluraliser.m_count != 1 )
+            os << 's';
+        return os;
+    }
+
+}
+
+
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cstdint>
+
+namespace Catch {
+    StringRef::StringRef( char const* rawChars ) noexcept
+    : StringRef( rawChars, std::strlen(rawChars) )
+    {}
+
+    auto StringRef::operator == ( StringRef other ) const noexcept -> bool {
+        return m_size == other.m_size
+            && (std::memcmp( m_start, other.m_start, m_size ) == 0);
+    }
+
+    bool StringRef::operator<(StringRef rhs) const noexcept {
+        if (m_size < rhs.m_size) {
+            return strncmp(m_start, rhs.m_start, m_size) <= 0;
+        }
+        return strncmp(m_start, rhs.m_start, rhs.m_size) < 0;
+    }
+
+    int StringRef::compare( StringRef rhs ) const {
+        auto cmpResult =
+            strncmp( m_start, rhs.m_start, std::min( m_size, rhs.m_size ) );
+
+        // This means that strncmp found a difference before the strings
+        // ended, and we can return it directly
+        if ( cmpResult != 0 ) {
+            return cmpResult;
+        }
+
+        // If strings are equal up to length, then their comparison results on
+        // their size
+        if ( m_size < rhs.m_size ) {
+            return -1;
+        } else if ( m_size > rhs.m_size ) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    auto operator << ( std::ostream& os, StringRef str ) -> std::ostream& {
+        return os.write(str.data(), static_cast<std::streamsize>(str.size()));
+    }
+
+    std::string operator+(StringRef lhs, StringRef rhs) {
+        std::string ret;
+        ret.reserve(lhs.size() + rhs.size());
+        ret += lhs;
+        ret += rhs;
+        return ret;
+    }
+
+    auto operator+=( std::string& lhs, StringRef rhs ) -> std::string& {
+        lhs.append(rhs.data(), rhs.size());
+        return lhs;
+    }
+
+} // namespace Catch
+
+
+
+namespace Catch {
+
+    TagAliasRegistry::~TagAliasRegistry() {}
+
+    TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
+        auto it = m_registry.find( alias );
+        if( it != m_registry.end() )
+            return &(it->second);
+        else
+            return nullptr;
+    }
+
+    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+        std::string expandedTestSpec = unexpandedTestSpec;
+        for( auto const& registryKvp : m_registry ) {
+            std::size_t pos = expandedTestSpec.find( registryKvp.first );
+            if( pos != std::string::npos ) {
+                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
+                                    registryKvp.second.tag +
+                                    expandedTestSpec.substr( pos + registryKvp.first.size() );
+            }
+        }
+        return expandedTestSpec;
+    }
+
+    void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+        CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'),
+                      "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo );
+
+        CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second,
+                      "error: tag alias, '" << alias << "' already registered.\n"
+                      << "\tFirst seen at: " << find(alias)->lineInfo << "\n"
+                      << "\tRedefined at: " << lineInfo );
+    }
+
+    ITagAliasRegistry::~ITagAliasRegistry() = default;
+
+    ITagAliasRegistry const& ITagAliasRegistry::get() {
+        return getRegistryHub().getTagAliasRegistry();
+    }
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+    TestCaseInfoHasher::TestCaseInfoHasher( hash_t seed ): m_seed( seed ) {}
+
+    uint32_t TestCaseInfoHasher::operator()( TestCaseInfo const& t ) const {
+        // FNV-1a hash algorithm that is designed for uniqueness:
+        const hash_t prime = 1099511628211u;
+        hash_t hash = 14695981039346656037u;
+        for ( const char c : t.name ) {
+            hash ^= c;
+            hash *= prime;
+        }
+        for ( const char c : t.className ) {
+            hash ^= c;
+            hash *= prime;
+        }
+        for ( const Tag& tag : t.tags ) {
+            for ( const char c : tag.original ) {
+                hash ^= c;
+                hash *= prime;
+            }
+        }
+        hash ^= m_seed;
+        hash *= prime;
+        const uint32_t low{ static_cast<uint32_t>( hash ) };
+        const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) };
+        return low * high;
+    }
+} // namespace Catch
+
+
+
+
+#include <algorithm>
+#include <set>
+
+namespace Catch {
+
+    std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) {
+        switch (config.runOrder()) {
+        case TestRunOrder::Declared:
+            return unsortedTestCases;
+
+        case TestRunOrder::LexicographicallySorted: {
+            std::vector<TestCaseHandle> sorted = unsortedTestCases;
+            std::sort(
+                sorted.begin(),
+                sorted.end(),
+                []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) {
+                    return lhs.getTestCaseInfo() < rhs.getTestCaseInfo();
+                }
+            );
+            return sorted;
+        }
+        case TestRunOrder::Randomized: {
+            seedRng(config);
+            using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>;
+
+            TestCaseInfoHasher h{ config.rngSeed() };
+            std::vector<TestWithHash> indexed_tests;
+            indexed_tests.reserve(unsortedTestCases.size());
+
+            for (auto const& handle : unsortedTestCases) {
+                indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle);
+            }
+
+            std::sort( indexed_tests.begin(),
+                       indexed_tests.end(),
+                       []( TestWithHash const& lhs, TestWithHash const& rhs ) {
+                           if ( lhs.first == rhs.first ) {
+                               return lhs.second.getTestCaseInfo() <
+                                      rhs.second.getTestCaseInfo();
+                           }
+                           return lhs.first < rhs.first;
+                       } );
+
+            std::vector<TestCaseHandle> randomized;
+            randomized.reserve(indexed_tests.size());
+
+            for (auto const& indexed : indexed_tests) {
+                randomized.push_back(indexed.second);
+            }
+
+            return randomized;
+        }
+        }
+
+        CATCH_INTERNAL_ERROR("Unknown test order value!");
+    }
+
+    bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ) {
+        return !testCase.getTestCaseInfo().throws() || config.allowThrows();
+    }
+
+    bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config );
+    }
+
+    void
+    enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& tests ) {
+        auto testInfoCmp = []( TestCaseInfo const* lhs,
+                               TestCaseInfo const* rhs ) {
+            return *lhs < *rhs;
+        };
+        std::set<TestCaseInfo const*, decltype(testInfoCmp)> seenTests(testInfoCmp);
+        for ( auto const& test : tests ) {
+            const auto infoPtr = &test.getTestCaseInfo();
+            const auto prev = seenTests.insert( infoPtr );
+            CATCH_ENFORCE(
+                prev.second,
+                "error: test case \"" << infoPtr->name << "\", with tags \""
+                    << infoPtr->tagsAsString() << "\" already defined.\n"
+                    << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n"
+                    << "\tRedefined at " << infoPtr->lineInfo );
+        }
+    }
+
+    std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+        std::vector<TestCaseHandle> filtered;
+        filtered.reserve( testCases.size() );
+        for (auto const& testCase : testCases) {
+            if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) ||
+                (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
+                filtered.push_back(testCase);
+            }
+        }
+        return createShard(filtered, config.shardCount(), config.shardIndex());
+    }
+    std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+    }
+
+    void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
+        m_handles.emplace_back(testInfo.get(), testInvoker.get());
+        m_viewed_test_infos.push_back(testInfo.get());
+        m_owned_test_infos.push_back(CATCH_MOVE(testInfo));
+        m_invokers.push_back(CATCH_MOVE(testInvoker));
+    }
+
+    std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const {
+        return m_viewed_test_infos;
+    }
+
+    std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const {
+        return m_handles;
+    }
+    std::vector<TestCaseHandle> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const {
+        if( m_sortedFunctions.empty() )
+            enforceNoDuplicateTestCases( m_handles );
+
+        if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+            m_sortedFunctions = sortTests( config, m_handles );
+            m_currentSortOrder = config.runOrder();
+        }
+        return m_sortedFunctions;
+    }
+
+
+
+    ///////////////////////////////////////////////////////////////////////////
+    void TestInvokerAsFunction::invoke() const {
+        m_testAsFunction();
+    }
+
+} // end namespace Catch
+
+
+
+
+#include <algorithm>
+#include <cassert>
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+namespace Catch {
+namespace TestCaseTracking {
+
+    NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+    :   name( _name ),
+        location( _location )
+    {}
+
+
+    ITracker::~ITracker() = default;
+
+    void ITracker::markAsNeedingAnotherRun() {
+        m_runState = NeedsAnotherRun;
+    }
+
+    void ITracker::addChild( ITrackerPtr&& child ) {
+        m_children.push_back( CATCH_MOVE(child) );
+    }
+
+    ITracker* ITracker::findChild( NameAndLocation const& nameAndLocation ) {
+        auto it = std::find_if(
+            m_children.begin(),
+            m_children.end(),
+            [&nameAndLocation]( ITrackerPtr const& tracker ) {
+                return tracker->nameAndLocation().location ==
+                           nameAndLocation.location &&
+                       tracker->nameAndLocation().name == nameAndLocation.name;
+            } );
+        return ( it != m_children.end() ) ? it->get() : nullptr;
+    }
+
+    bool ITracker::isSectionTracker() const { return false; }
+    bool ITracker::isGeneratorTracker() const { return false; }
+
+    bool ITracker::isSuccessfullyCompleted() const {
+        return m_runState == CompletedSuccessfully;
+    }
+
+    bool ITracker::isOpen() const {
+        return m_runState != NotStarted && !isComplete();
+    }
+
+    bool ITracker::hasStarted() const { return m_runState != NotStarted; }
+
+    void ITracker::openChild() {
+        if (m_runState != ExecutingChildren) {
+            m_runState = ExecutingChildren;
+            if (m_parent) {
+                m_parent->openChild();
+            }
+        }
+    }
+
+    ITracker& TrackerContext::startRun() {
+        using namespace std::string_literals;
+        m_rootTracker = Catch::Detail::make_unique<SectionTracker>(
+            NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ),
+            *this,
+            nullptr );
+        m_currentTracker = nullptr;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
+
+    void TrackerContext::endRun() {
+        m_rootTracker.reset();
+        m_currentTracker = nullptr;
+        m_runState = NotStarted;
+    }
+
+    void TrackerContext::startCycle() {
+        m_currentTracker = m_rootTracker.get();
+        m_runState = Executing;
+    }
+    void TrackerContext::completeCycle() {
+        m_runState = CompletedCycle;
+    }
+
+    bool TrackerContext::completedCycle() const {
+        return m_runState == CompletedCycle;
+    }
+    ITracker& TrackerContext::currentTracker() {
+        return *m_currentTracker;
+    }
+    void TrackerContext::setCurrentTracker( ITracker* tracker ) {
+        m_currentTracker = tracker;
+    }
+
+
+    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
+        ITracker(nameAndLocation, parent),
+        m_ctx( ctx )
+    {}
+
+    bool TrackerBase::isComplete() const {
+        return m_runState == CompletedSuccessfully || m_runState == Failed;
+    }
+
+    void TrackerBase::open() {
+        m_runState = Executing;
+        moveToThis();
+        if( m_parent )
+            m_parent->openChild();
+    }
+
+    void TrackerBase::close() {
+
+        // Close any still open children (e.g. generators)
+        while( &m_ctx.currentTracker() != this )
+            m_ctx.currentTracker().close();
+
+        switch( m_runState ) {
+            case NeedsAnotherRun:
+                break;
+
+            case Executing:
+                m_runState = CompletedSuccessfully;
+                break;
+            case ExecutingChildren:
+                if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
+                    m_runState = CompletedSuccessfully;
+                break;
+
+            case NotStarted:
+            case CompletedSuccessfully:
+            case Failed:
+                CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
+
+            default:
+                CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
+        }
+        moveToParent();
+        m_ctx.completeCycle();
+    }
+    void TrackerBase::fail() {
+        m_runState = Failed;
+        if( m_parent )
+            m_parent->markAsNeedingAnotherRun();
+        moveToParent();
+        m_ctx.completeCycle();
+    }
+
+    void TrackerBase::moveToParent() {
+        assert( m_parent );
+        m_ctx.setCurrentTracker( m_parent );
+    }
+    void TrackerBase::moveToThis() {
+        m_ctx.setCurrentTracker( this );
+    }
+
+    SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+    :   TrackerBase( nameAndLocation, ctx, parent ),
+        m_trimmed_name(trim(nameAndLocation.name))
+    {
+        if( parent ) {
+            while( !parent->isSectionTracker() )
+                parent = parent->parent();
+
+            SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+            addNextFilters( parentSection.m_filters );
+        }
+    }
+
+    bool SectionTracker::isComplete() const {
+        bool complete = true;
+
+        if (m_filters.empty()
+            || m_filters[0].empty()
+            || 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 ) {
+        SectionTracker* section;
+
+        ITracker& currentTracker = ctx.currentTracker();
+        if ( ITracker* childTracker =
+                 currentTracker.findChild( nameAndLocation ) ) {
+            assert( childTracker );
+            assert( childTracker->isSectionTracker() );
+            section = static_cast<SectionTracker*>( childTracker );
+        } else {
+            auto newSection = Catch::Detail::make_unique<SectionTracker>(
+                nameAndLocation, ctx, &currentTracker );
+            section = newSection.get();
+            currentTracker.addChild( CATCH_MOVE( newSection ) );
+        }
+        if( !ctx.completedCycle() )
+            section->tryOpen();
+        return *section;
+    }
+
+    void SectionTracker::tryOpen() {
+        if( !isComplete() )
+            open();
+    }
+
+    void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
+        if( !filters.empty() ) {
+            m_filters.reserve( m_filters.size() + filters.size() + 2 );
+            m_filters.emplace_back(StringRef{}); // Root - should never be consulted
+            m_filters.emplace_back(StringRef{}); // Test Case - not a section filter
+            m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+        }
+    }
+    void SectionTracker::addNextFilters( std::vector<StringRef> const& filters ) {
+        if( filters.size() > 1 )
+            m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
+    }
+
+    std::vector<StringRef> const& SectionTracker::getFilters() const {
+        return m_filters;
+    }
+
+    StringRef SectionTracker::trimmedName() const {
+        return m_trimmed_name;
+    }
+
+} // namespace TestCaseTracking
+
+} // namespace Catch
+
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+
+
+
+#include <algorithm>
+#include <iterator>
+
+namespace Catch {
+
+    namespace {
+        StringRef extractClassName( StringRef classOrMethodName ) {
+            if ( !startsWith( classOrMethodName, '&' ) ) {
+                return classOrMethodName;
+            }
+
+            // Remove the leading '&' to avoid having to special case it later
+            const auto methodName =
+                classOrMethodName.substr( 1, classOrMethodName.size() );
+
+            auto reverseStart = std::make_reverse_iterator( methodName.end() );
+            auto reverseEnd = std::make_reverse_iterator( methodName.begin() );
+
+            // We make a simplifying assumption that ":" is only present
+            // in the input as part of "::" from C++ typenames (this is
+            // relatively safe assumption because the input is generated
+            // as stringification of type through preprocessor).
+            auto lastColons = std::find( reverseStart, reverseEnd, ':' ) + 1;
+            auto secondLastColons =
+                std::find( lastColons + 1, reverseEnd, ':' );
+
+            auto const startIdx = reverseEnd - secondLastColons;
+            auto const classNameSize = secondLastColons - lastColons - 1;
+
+            return methodName.substr(
+                static_cast<std::size_t>( startIdx ),
+                static_cast<std::size_t>( classNameSize ) );
+        }
+    } // namespace
+
+    Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) {
+        return Detail::make_unique<TestInvokerAsFunction>( testAsFunction );
+    }
+
+    AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept {
+        CATCH_TRY {
+            getMutableRegistryHub()
+                    .registerTest(
+                        makeTestCaseInfo(
+                            extractClassName( classOrMethod ),
+                            nameAndTags,
+                            lineInfo),
+                        CATCH_MOVE(invoker)
+                    );
+        } CATCH_CATCH_ALL {
+            // Do not throw when constructing global objects, instead register the exception to be processed later
+            getMutableRegistryHub().registerStartupException();
+        }
+    }
+}
+
+
+
+
+
+namespace Catch {
+
+    TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+    TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
+        m_mode = None;
+        m_exclusion = false;
+        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 )
+          //if visitChar fails
+           if( !visitChar( m_arg[m_pos] ) ){
+               m_testSpec.m_invalidSpecs.push_back(arg);
+               break;
+           }
+        endMode();
+        return *this;
+    }
+    TestSpec TestSpecParser::testSpec() {
+        addFilter();
+        return CATCH_MOVE(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();
+        }
+
+        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 );
+        }
+    }
+    bool TestSpecParser::processOtherChar( char c ) {
+        if( !isControlChar( c ) )
+            return false;
+        m_substring += c;
+        endMode();
+        return true;
+    }
+    void TestSpecParser::startNewMode( Mode mode ) {
+        m_mode = mode;
+    }
+    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() {
+        saveLastMode();
+        m_mode = EscapedName;
+        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 == ']';
+        }
+    }
+
+    void TestSpecParser::addFilter() {
+        if( !m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty() ) {
+            m_testSpec.m_filters.push_back( CATCH_MOVE(m_currentFilter) );
+            m_currentFilter = TestSpec::Filter();
+        }
+    }
+
+    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()) {
+            if (m_exclusion) {
+                m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::NamePattern>(token, m_substring));
+            } else {
+                m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::NamePattern>(token, m_substring));
+            }
+        }
+        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());
+                if (m_exclusion) {
+                    m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
+                    m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
+                } else {
+                    m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
+                    m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
+                }
+            }
+            if (m_exclusion) {
+                m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
+            } else {
+                m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
+            }
+        }
+        m_substring.clear();
+        m_exclusion = false;
+        m_mode = None;
+    }
+
+    TestSpec parseTestSpec( std::string const& arg ) {
+        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+    }
+
+} // namespace Catch
+
+
+
+#include <algorithm>
+#include <cstring>
+#include <ostream>
+
+namespace {
+    bool isWhitespace( char c ) {
+        return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+    }
+
+    bool isBreakableBefore( char c ) {
+        static const char chars[] = "[({<|";
+        return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr;
+    }
+
+    bool isBreakableAfter( char c ) {
+        static const char chars[] = "])}>.,:;*+-=&/\\";
+        return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr;
+    }
+
+    bool isBoundary( std::string const& line, size_t at ) {
+        assert( at > 0 );
+        assert( at <= line.size() );
+
+        return at == line.size() ||
+               ( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) ||
+               isBreakableBefore( line[at] ) ||
+               isBreakableAfter( line[at - 1] );
+    }
+
+} // namespace
+
+namespace Catch {
+    namespace TextFlow {
+
+        void Column::const_iterator::calcLength() {
+            m_addHyphen = false;
+            m_parsedTo = m_lineStart;
+
+            std::string const& current_line = m_column.m_string;
+            if ( current_line[m_lineStart] == '\n' ) {
+                ++m_parsedTo;
+            }
+
+            const auto maxLineLength = m_column.m_width - indentSize();
+            const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength);
+            while ( m_parsedTo < maxParseTo &&
+                    current_line[m_parsedTo] != '\n' ) {
+                ++m_parsedTo;
+            }
+
+            // If we encountered a newline before the column is filled,
+            // then we linebreak at the newline and consider this line
+            // finished.
+            if ( m_parsedTo < m_lineStart + maxLineLength ) {
+                m_lineLength = m_parsedTo - m_lineStart;
+            } else {
+                // Look for a natural linebreak boundary in the column
+                // (We look from the end, so that the first found boundary is
+                // the right one)
+                size_t newLineLength = maxLineLength;
+                while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) {
+                    --newLineLength;
+                }
+                while ( newLineLength > 0 &&
+                        isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) {
+                    --newLineLength;
+                }
+
+                // If we found one, then that is where we linebreak
+                if ( newLineLength > 0 ) {
+                    m_lineLength = newLineLength;
+                } else {
+                    // Otherwise we have to split text with a hyphen
+                    m_addHyphen = true;
+                    m_lineLength = maxLineLength - 1;
+                }
+            }
+        }
+
+        size_t Column::const_iterator::indentSize() const {
+            auto initial =
+                m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos;
+            return initial == std::string::npos ? m_column.m_indent : initial;
+        }
+
+        std::string
+        Column::const_iterator::addIndentAndSuffix( size_t position,
+                                              size_t length ) const {
+            std::string ret;
+            const auto desired_indent = indentSize();
+            ret.reserve( desired_indent + length + m_addHyphen );
+            ret.append( desired_indent, ' ' );
+            ret.append( m_column.m_string, position, length );
+            if ( m_addHyphen ) {
+                ret.push_back( '-' );
+            }
+
+            return ret;
+        }
+
+        Column::const_iterator::const_iterator( Column const& column ): m_column( column ) {
+            assert( m_column.m_width > m_column.m_indent );
+            assert( m_column.m_initialIndent == std::string::npos ||
+                    m_column.m_width > m_column.m_initialIndent );
+            calcLength();
+            if ( m_lineLength == 0 ) {
+                m_lineStart = m_column.m_string.size();
+            }
+        }
+
+        std::string Column::const_iterator::operator*() const {
+            assert( m_lineStart <= m_parsedTo );
+            return addIndentAndSuffix( m_lineStart, m_lineLength );
+        }
+
+        Column::const_iterator& Column::const_iterator::operator++() {
+            m_lineStart += m_lineLength;
+            std::string const& current_line = m_column.m_string;
+            if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) {
+                m_lineStart += 1;
+            } else {
+                while ( m_lineStart < current_line.size() &&
+                        isWhitespace( current_line[m_lineStart] ) ) {
+                    ++m_lineStart;
+                }
+            }
+
+            if ( m_lineStart != current_line.size() ) {
+                calcLength();
+            }
+            return *this;
+        }
+
+        Column::const_iterator Column::const_iterator::operator++( int ) {
+            const_iterator prev( *this );
+            operator++();
+            return prev;
+        }
+
+        std::ostream& operator<<( std::ostream& os, Column const& col ) {
+            bool first = true;
+            for ( auto line : col ) {
+                if ( first ) {
+                    first = false;
+                } else {
+                    os << '\n';
+                }
+                os << line;
+            }
+            return os;
+        }
+
+        Column Spacer( size_t spaceWidth ) {
+            Column ret{ "" };
+            ret.width( spaceWidth );
+            return ret;
+        }
+
+        Columns::iterator::iterator( Columns const& columns, EndTag ):
+            m_columns( columns.m_columns ), m_activeIterators( 0 ) {
+
+            m_iterators.reserve( m_columns.size() );
+            for ( auto const& col : m_columns ) {
+                m_iterators.push_back( col.end() );
+            }
+        }
+
+        Columns::iterator::iterator( Columns const& columns ):
+            m_columns( columns.m_columns ),
+            m_activeIterators( m_columns.size() ) {
+
+            m_iterators.reserve( m_columns.size() );
+            for ( auto const& col : m_columns ) {
+                m_iterators.push_back( col.begin() );
+            }
+        }
+
+        std::string Columns::iterator::operator*() const {
+            std::string row, padding;
+
+            for ( size_t i = 0; i < m_columns.size(); ++i ) {
+                const auto width = m_columns[i].width();
+                if ( m_iterators[i] != m_columns[i].end() ) {
+                    std::string col = *m_iterators[i];
+                    row += padding;
+                    row += col;
+
+                    padding.clear();
+                    if ( col.size() < width ) {
+                        padding.append( width - col.size(), ' ' );
+                    }
+                } else {
+                    padding.append( width, ' ' );
+                }
+            }
+            return row;
+        }
+
+        Columns::iterator& Columns::iterator::operator++() {
+            for ( size_t i = 0; i < m_columns.size(); ++i ) {
+                if ( m_iterators[i] != m_columns[i].end() ) {
+                    ++m_iterators[i];
+                }
+            }
+            return *this;
+        }
+
+        Columns::iterator Columns::iterator::operator++( int ) {
+            iterator prev( *this );
+            operator++();
+            return prev;
+        }
+
+        std::ostream& operator<<( std::ostream& os, Columns const& cols ) {
+            bool first = true;
+            for ( auto line : cols ) {
+                if ( first ) {
+                    first = false;
+                } else {
+                    os << '\n';
+                }
+                os << line;
+            }
+            return os;
+        }
+
+        Columns Column::operator+( Column const& other ) {
+            Columns cols;
+            cols += *this;
+            cols += other;
+            return cols;
+        }
+
+        Columns& Columns::operator+=( Column const& col ) {
+            m_columns.push_back( col );
+            return *this;
+        }
+
+        Columns Columns::operator+( Column const& col ) {
+            Columns combined = *this;
+            combined += col;
+            return combined;
+        }
+
+    } // namespace TextFlow
+} // namespace Catch
+
+
+
+
+#include <exception>
+
+namespace Catch {
+    bool uncaught_exceptions() {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        return false;
+#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+        return std::uncaught_exceptions() > 0;
+#else
+        return std::uncaught_exception();
+#endif
+  }
+} // end namespace Catch
+
+
+
+namespace Catch {
+
+    WildcardPattern::WildcardPattern( std::string const& pattern,
+                                      CaseSensitive caseSensitivity )
+    :   m_caseSensitivity( caseSensitivity ),
+        m_pattern( normaliseString( pattern ) )
+    {
+        if( startsWith( m_pattern, '*' ) ) {
+            m_pattern = m_pattern.substr( 1 );
+            m_wildcard = WildcardAtStart;
+        }
+        if( endsWith( m_pattern, '*' ) ) {
+            m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+            m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+        }
+    }
+
+    bool WildcardPattern::matches( std::string const& str ) const {
+        switch( m_wildcard ) {
+            case NoWildcard:
+                return m_pattern == normaliseString( str );
+            case WildcardAtStart:
+                return endsWith( normaliseString( str ), m_pattern );
+            case WildcardAtEnd:
+                return startsWith( normaliseString( str ), m_pattern );
+            case WildcardAtBothEnds:
+                return contains( normaliseString( str ), m_pattern );
+            default:
+                CATCH_INTERNAL_ERROR( "Unknown enum" );
+        }
+    }
+
+    std::string WildcardPattern::normaliseString( std::string const& str ) const {
+        return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
+    }
+}
+
+
+// Note: swapping these two includes around causes MSVC to error out
+//       while in /permissive- mode. No, I don't know why.
+//       Tested on VS 2019, 18.{3, 4}.x
+
+#include <iomanip>
+#include <type_traits>
+
+namespace Catch {
+
+namespace {
+
+    size_t trailingBytes(unsigned char c) {
+        if ((c & 0xE0) == 0xC0) {
+            return 2;
+        }
+        if ((c & 0xF0) == 0xE0) {
+            return 3;
+        }
+        if ((c & 0xF8) == 0xF0) {
+            return 4;
+        }
+        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+    }
+
+    uint32_t headerValue(unsigned char c) {
+        if ((c & 0xE0) == 0xC0) {
+            return c & 0x1F;
+        }
+        if ((c & 0xF0) == 0xE0) {
+            return c & 0x0F;
+        }
+        if ((c & 0xF8) == 0xF0) {
+            return c & 0x07;
+        }
+        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+    }
+
+    void 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_t<XmlFormatting>>(fmt & XmlFormatting::Newline));
+    }
+
+    bool shouldIndent(XmlFormatting fmt) {
+        return !!(static_cast<std::underlying_type_t<XmlFormatting>>(fmt & XmlFormatting::Indent));
+    }
+
+} // anonymous namespace
+
+    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
+        return static_cast<XmlFormatting>(
+            static_cast<std::underlying_type_t<XmlFormatting>>(lhs) |
+            static_cast<std::underlying_type_t<XmlFormatting>>(rhs)
+        );
+    }
+
+    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
+        return static_cast<XmlFormatting>(
+            static_cast<std::underlying_type_t<XmlFormatting>>(lhs) &
+            static_cast<std::underlying_type_t<XmlFormatting>>(rhs)
+        );
+    }
+
+
+    XmlEncode::XmlEncode( StringRef str, ForWhat forWhat )
+    :   m_str( str ),
+        m_forWhat( forWhat )
+    {}
+
+    void XmlEncode::encodeTo( std::ostream& os ) const {
+        // Apostrophe escaping not necessary if we always use " to write attributes
+        // (see: http://www.w3.org/TR/xml/#syntax)
+
+        for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+            unsigned char c = static_cast<unsigned char>(m_str[idx]);
+            switch (c) {
+            case '<':   os << "&lt;"; break;
+            case '&':   os << "&amp;"; break;
+
+            case '>':
+                // See: http://www.w3.org/TR/xml/#syntax
+                if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
+                    os << "&gt;";
+                else
+                    os << c;
+                break;
+
+            case '\"':
+                if (m_forWhat == ForAttributes)
+                    os << "&quot;";
+                else
+                    os << c;
+                break;
+
+            default:
+                // Check for control characters and invalid utf-8
+
+                // Escape control characters in standard ascii
+                // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+                if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                // Plain ASCII: Write it to stream
+                if (c < 0x7F) {
+                    os << c;
+                    break;
+                }
+
+                // UTF-8 territory
+                // Check if the encoding is valid and if it is not, hex escape bytes.
+                // Important: We do not check the exact decoded values for validity, only the encoding format
+                // First check that this bytes is a valid lead byte:
+                // This means that it is not encoded as 1111 1XXX
+                // Or as 10XX XXXX
+                if (c <  0xC0 ||
+                    c >= 0xF8) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                auto encBytes = trailingBytes(c);
+                // Are there enough bytes left to avoid accessing out-of-bounds memory?
+                if (idx + encBytes - 1 >= m_str.size()) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+                // The header is valid, check data
+                // The next encBytes bytes must together be a valid utf-8
+                // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
+                bool valid = true;
+                uint32_t value = headerValue(c);
+                for (std::size_t n = 1; n < encBytes; ++n) {
+                    unsigned char nc = static_cast<unsigned char>(m_str[idx + n]);
+                    valid &= ((nc & 0xC0) == 0x80);
+                    value = (value << 6) | (nc & 0x3F);
+                }
+
+                if (
+                    // Wrong bit pattern of following bytes
+                    (!valid) ||
+                    // Overlong encodings
+                    (value < 0x80) ||
+                    (0x80 <= value && value < 0x800   && encBytes > 2) ||
+                    (0x800 < value && value < 0x10000 && encBytes > 3) ||
+                    // Encoded value out of range
+                    (value >= 0x110000)
+                    ) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                // If we got here, this is in fact a valid(ish) utf-8 sequence
+                for (std::size_t n = 0; n < encBytes; ++n) {
+                    os << m_str[idx + n];
+                }
+                idx += encBytes - 1;
+                break;
+            }
+        }
+    }
+
+    std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+        xmlEncode.encodeTo( os );
+        return os;
+    }
+
+    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_fmt(other.m_fmt)
+    {
+        other.m_writer = nullptr;
+        other.m_fmt = XmlFormatting::None;
+    }
+    XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
+        if ( m_writer ) {
+            m_writer->endElement();
+        }
+        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(m_fmt);
+        }
+    }
+
+    XmlWriter::ScopedElement&
+    XmlWriter::ScopedElement::writeText( StringRef text, XmlFormatting fmt ) {
+        m_writer->writeText( text, fmt );
+        return *this;
+    }
+
+    XmlWriter::ScopedElement&
+    XmlWriter::ScopedElement::writeAttribute( StringRef name,
+                                              StringRef attribute ) {
+        m_writer->writeAttribute( name, attribute );
+        return *this;
+    }
+
+
+    XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
+    {
+        writeDeclaration();
+    }
+
+    XmlWriter::~XmlWriter() {
+        while (!m_tags.empty()) {
+            endElement();
+        }
+        newlineIfNecessary();
+    }
+
+    XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
+        ensureTagClosed();
+        newlineIfNecessary();
+        if (shouldIndent(fmt)) {
+            m_os << m_indent;
+            m_indent += "  ";
+        }
+        m_os << '<' << name;
+        m_tags.push_back( name );
+        m_tagIsOpen = true;
+        applyFormatting(fmt);
+        return *this;
+    }
+
+    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
+        ScopedElement scoped( this, fmt );
+        startElement( name, fmt );
+        return scoped;
+    }
+
+    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() << '>';
+        }
+        m_os << std::flush;
+        applyFormatting(fmt);
+        m_tags.pop_back();
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeAttribute( StringRef name,
+                                          StringRef attribute ) {
+        if( !name.empty() && !attribute.empty() )
+            m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeAttribute( StringRef name, bool attribute ) {
+        writeAttribute(name, (attribute ? "true"_sr : "false"_sr));
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeAttribute( StringRef name,
+                                          char const* attribute ) {
+        writeAttribute( name, StringRef( attribute ) );
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeText( StringRef text, XmlFormatting fmt ) {
+        CATCH_ENFORCE(!m_tags.empty(), "Cannot write text as top level element");
+        if( !text.empty() ){
+            bool tagWasOpen = m_tagIsOpen;
+            ensureTagClosed();
+            if (tagWasOpen && shouldIndent(fmt)) {
+                m_os << m_indent;
+            }
+            m_os << XmlEncode( text, XmlEncode::ForTextNodes );
+            applyFormatting(fmt);
+        }
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeComment( StringRef text, XmlFormatting fmt ) {
+        ensureTagClosed();
+        if (shouldIndent(fmt)) {
+            m_os << m_indent;
+        }
+        m_os << "<!-- " << text << " -->";
+        applyFormatting(fmt);
+        return *this;
+    }
+
+    void XmlWriter::writeStylesheetRef( StringRef url ) {
+        m_os << R"(<?xml-stylesheet type="text/xsl" href=")" << url << R"("?>)" << '\n';
+    }
+
+    void XmlWriter::ensureTagClosed() {
+        if( m_tagIsOpen ) {
+            m_os << '>' << std::flush;
+            newlineIfNecessary();
+            m_tagIsOpen = false;
+        }
+    }
+
+    void XmlWriter::applyFormatting(XmlFormatting fmt) {
+        m_needsNewline = shouldNewline(fmt);
+    }
+
+    void XmlWriter::writeDeclaration() {
+        m_os << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
+    }
+
+    void XmlWriter::newlineIfNecessary() {
+        if( m_needsNewline ) {
+            m_os << '\n' << std::flush;
+            m_needsNewline = false;
+        }
+    }
+}
+
+
+
+
+
+namespace Catch {
+namespace Matchers {
+
+    std::string MatcherUntypedBase::toString() const {
+        if (m_cachedToString.empty()) {
+            m_cachedToString = describe();
+        }
+        return m_cachedToString;
+    }
+
+    MatcherUntypedBase::~MatcherUntypedBase() = default;
+
+} // namespace Matchers
+} // namespace Catch
+
+
+
+
+namespace Catch {
+namespace Matchers {
+
+    std::string IsEmptyMatcher::describe() const {
+        return "is empty";
+    }
+
+    std::string HasSizeMatcher::describe() const {
+        ReusableStringStream sstr;
+        sstr << "has size == " << m_target_size;
+        return sstr.str();
+    }
+
+    IsEmptyMatcher IsEmpty() {
+        return {};
+    }
+
+    HasSizeMatcher SizeIs(std::size_t sz) {
+        return HasSizeMatcher{ sz };
+    }
+
+} // end namespace Matchers
+} // end namespace Catch
+
+
+
+namespace Catch {
+namespace Matchers {
+
+bool ExceptionMessageMatcher::match(std::exception const& ex) const {
+    return ex.what() == m_message;
+}
+
+std::string ExceptionMessageMatcher::describe() const {
+    return "exception message matches \"" + m_message + '"';
+}
+
+ExceptionMessageMatcher Message(std::string const& message) {
+    return ExceptionMessageMatcher(message);
+}
+
+} // namespace Matchers
+} // namespace Catch
+
+
+
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <cstdint>
+#include <sstream>
+#include <iomanip>
+#include <limits>
+
+
+namespace Catch {
+namespace {
+
+    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;
+        }
+
+        // This should also handle positive and negative zeros, infinities
+        const auto ulpDist = ulpDistance(lhs, rhs);
+
+        return ulpDist <= maxUlpDiff;
+    }
+
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+
+    float nextafter(float x, float y) {
+        return ::nextafterf(x, y);
+    }
+
+    double nextafter(double x, double y) {
+        return ::nextafter(x, y);
+    }
+
+#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
+
+template <typename FP>
+FP step(FP start, FP direction, uint64_t steps) {
+    for (uint64_t i = 0; i < steps; ++i) {
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+        start = Catch::nextafter(start, direction);
+#else
+        start = std::nextafter(start, direction);
+#endif
+    }
+    return start;
+}
+
+// Performs equivalent check of std::fabs(lhs - rhs) <= margin
+// But without the subtraction to allow for INFINITY in comparison
+bool marginComparison(double lhs, double rhs, double margin) {
+    return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+}
+
+template <typename FloatingPoint>
+void write(std::ostream& out, FloatingPoint num) {
+    out << std::scientific
+        << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
+        << num;
+}
+
+} // end anonymous namespace
+
+namespace Matchers {
+namespace Detail {
+
+    enum class FloatingPointKind : uint8_t {
+        Float,
+        Double
+    };
+
+} // end namespace Detail
+
+
+    WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
+        :m_target{ target }, m_margin{ margin } {
+        CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
+            << " Margin has to be non-negative.");
+    }
+
+    // Performs equivalent check of std::fabs(lhs - rhs) <= margin
+    // But without the subtraction to allow for INFINITY in comparison
+    bool WithinAbsMatcher::match(double const& matchee) const {
+        return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
+    }
+
+    std::string WithinAbsMatcher::describe() const {
+        return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
+    }
+
+
+    WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType)
+        :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
+        CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double
+                   || m_ulps < (std::numeric_limits<uint32_t>::max)(),
+            "Provided ULP is impossibly large for a float comparison.");
+        CATCH_ENFORCE( std::numeric_limits<double>::is_iec559,
+                       "WithinUlp matcher only supports platforms with "
+                       "IEEE-754 compatible floating point representation" );
+    }
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+// Clang <3.5 reports on the default branch in the switch below
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+
+    bool WithinUlpsMatcher::match(double const& matchee) const {
+        switch (m_type) {
+        case Detail::FloatingPointKind::Float:
+            return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
+        case Detail::FloatingPointKind::Double:
+            return almostEqualUlps<double>(matchee, m_target, m_ulps);
+        default:
+            CATCH_INTERNAL_ERROR( "Unknown Detail::FloatingPointKind value" );
+        }
+    }
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+    std::string WithinUlpsMatcher::describe() const {
+        std::stringstream ret;
+
+        ret << "is within " << m_ulps << " ULPs of ";
+
+        if (m_type == Detail::FloatingPointKind::Float) {
+            write(ret, static_cast<float>(m_target));
+            ret << 'f';
+        } else {
+            write(ret, m_target);
+        }
+
+        ret << " ([";
+        if (m_type == Detail::FloatingPointKind::Double) {
+            write( ret,
+                   step( m_target,
+                         -std::numeric_limits<double>::infinity(),
+                         m_ulps ) );
+            ret << ", ";
+            write( ret,
+                   step( m_target,
+                         std::numeric_limits<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 ),
+                         -std::numeric_limits<float>::infinity(),
+                         m_ulps ) );
+            ret << ", ";
+            write( ret,
+                   step( static_cast<float>( m_target ),
+                         std::numeric_limits<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();
+    }
+
+
+WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
+    return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Double);
+}
+
+WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
+    return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Float);
+}
+
+WithinAbsMatcher WithinAbs(double target, double margin) {
+    return WithinAbsMatcher(target, margin);
+}
+
+WithinRelMatcher WithinRel(double target, double eps) {
+    return WithinRelMatcher(target, eps);
+}
+
+WithinRelMatcher WithinRel(double target) {
+    return WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
+}
+
+WithinRelMatcher WithinRel(float target, float eps) {
+    return WithinRelMatcher(target, eps);
+}
+
+WithinRelMatcher WithinRel(float target) {
+    return WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
+}
+
+
+} // namespace Matchers
+} // namespace Catch
+
+
+
+
+std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) {
+    if (desc.empty()) {
+        return "matches undescribed predicate";
+    } else {
+        return "matches predicate: \"" + desc + '"';
+    }
+}
+
+
+
+namespace Catch {
+    namespace Matchers {
+        std::string AllTrueMatcher::describe() const { return "contains only true"; }
+
+        AllTrueMatcher AllTrue() { return AllTrueMatcher{}; }
+
+        std::string NoneTrueMatcher::describe() const { return "contains no true"; }
+
+        NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; }
+
+        std::string AnyTrueMatcher::describe() const { return "contains at least one true"; }
+
+        AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; }
+    } // namespace Matchers
+} // namespace Catch
+
+
+
+#include <regex>
+
+namespace Catch {
+namespace Matchers {
+
+    CasedString::CasedString( std::string const& str, CaseSensitive caseSensitivity )
+    :   m_caseSensitivity( caseSensitivity ),
+        m_str( adjustString( str ) )
+    {}
+    std::string CasedString::adjustString( std::string const& str ) const {
+        return m_caseSensitivity == CaseSensitive::No
+               ? toLower( str )
+               : str;
+    }
+    StringRef CasedString::caseSensitivitySuffix() const {
+        return m_caseSensitivity == CaseSensitive::Yes
+                   ? StringRef()
+                   : " (case insensitive)"_sr;
+    }
+
+
+    StringMatcherBase::StringMatcherBase( StringRef operation, CasedString const& comparator )
+    : m_comparator( comparator ),
+      m_operation( operation ) {
+    }
+
+    std::string StringMatcherBase::describe() const {
+        std::string description;
+        description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+                                    m_comparator.caseSensitivitySuffix().size());
+        description += m_operation;
+        description += ": \"";
+        description += m_comparator.m_str;
+        description += '"';
+        description += m_comparator.caseSensitivitySuffix();
+        return description;
+    }
+
+    StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals"_sr, comparator ) {}
+
+    bool StringEqualsMatcher::match( std::string const& source ) const {
+        return m_comparator.adjustString( source ) == m_comparator.m_str;
+    }
+
+
+    StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains"_sr, comparator ) {}
+
+    bool StringContainsMatcher::match( std::string const& source ) const {
+        return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+    }
+
+
+    StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with"_sr, comparator ) {}
+
+    bool StartsWithMatcher::match( std::string const& source ) const {
+        return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+    }
+
+
+    EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with"_sr, comparator ) {}
+
+    bool EndsWithMatcher::match( std::string const& source ) const {
+        return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+    }
+
+
+
+    RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {}
+
+    bool RegexMatcher::match(std::string const& matchee) const {
+        auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
+        if (m_caseSensitivity == CaseSensitive::No) {
+            flags |= std::regex::icase;
+        }
+        auto reg = std::regex(m_regex, flags);
+        return std::regex_match(matchee, reg);
+    }
+
+    std::string RegexMatcher::describe() const {
+        return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Yes)? " case sensitively" : " case insensitively");
+    }
+
+
+    StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity ) {
+        return StringEqualsMatcher( CasedString( str, caseSensitivity) );
+    }
+    StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity ) {
+        return StringContainsMatcher( CasedString( str, caseSensitivity) );
+    }
+    EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity ) {
+        return EndsWithMatcher( CasedString( str, caseSensitivity) );
+    }
+    StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity ) {
+        return StartsWithMatcher( CasedString( str, caseSensitivity) );
+    }
+
+    RegexMatcher Matches(std::string const& regex, CaseSensitive caseSensitivity) {
+        return RegexMatcher(regex, caseSensitivity);
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+
+
+namespace Catch {
+namespace Matchers {
+    MatcherGenericBase::~MatcherGenericBase() = default;
+
+    namespace Detail {
+
+        std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end) {
+            std::string description;
+            std::size_t combined_size = 4;
+            for ( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) {
+                combined_size += desc->size();
+            }
+            combined_size += static_cast<size_t>(descriptions_end - descriptions_begin - 1) * combine.size();
+
+            description.reserve(combined_size);
+
+            description += "( ";
+            bool first = true;
+            for( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) {
+                if( first )
+                    first = false;
+                else
+                    description += combine;
+                description += *desc;
+            }
+            description += " )";
+            return description;
+        }
+
+    } // namespace Detail
+} // namespace Matchers
+} // namespace Catch
+
+
+
+
+namespace Catch {
+
+    // This is the general overload that takes a any string matcher
+    // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
+    // the Equals matcher (so the header does not mention matchers)
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString  ) {
+        std::string exceptionMessage = Catch::translateActiveException();
+        MatchExpr<std::string, StringMatcher const&> expr( CATCH_MOVE(exceptionMessage), matcher, matcherString );
+        handler.handleExpr( expr );
+    }
+
+} // namespace Catch
+
+
+
+#include <ostream>
+
+namespace Catch {
+
+    AutomakeReporter::~AutomakeReporter() {}
+
+    void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
+        // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR.
+        m_stream << ":test-result: ";
+        if (_testCaseStats.totals.assertions.allPassed()) {
+            m_stream << "PASS";
+        } else if (_testCaseStats.totals.assertions.allOk()) {
+            m_stream << "XFAIL";
+        } else {
+            m_stream << "FAIL";
+        }
+        m_stream << ' ' << _testCaseStats.testInfo->name << '\n';
+        StreamingReporterBase::testCaseEnded(_testCaseStats);
+    }
+
+    void AutomakeReporter::skipTest(TestCaseInfo const& testInfo) {
+        m_stream << ":test-result: SKIP " << testInfo.name << '\n';
+    }
+
+} // end namespace Catch
+
+
+
+
+
+
+namespace Catch {
+    ReporterBase::ReporterBase( ReporterConfig&& config ):
+        IEventListener( config.fullConfig() ),
+        m_wrapped_stream( CATCH_MOVE(config).takeStream() ),
+        m_stream( m_wrapped_stream->stream() ),
+        m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.get() ) ),
+        m_customOptions( config.customOptions() )
+    {}
+
+    ReporterBase::~ReporterBase() = default;
+
+    void ReporterBase::listReporters(
+        std::vector<ReporterDescription> const& descriptions ) {
+        defaultListReporters(m_stream, descriptions, m_config->verbosity());
+    }
+
+    void ReporterBase::listListeners(
+        std::vector<ListenerDescription> const& descriptions ) {
+        defaultListListeners( m_stream, descriptions );
+    }
+
+    void ReporterBase::listTests(std::vector<TestCaseHandle> const& tests) {
+        defaultListTests(m_stream,
+                         m_colour.get(),
+                         tests,
+                         m_config->hasTestFilters(),
+                         m_config->verbosity());
+    }
+
+    void ReporterBase::listTags(std::vector<TagInfo> const& tags) {
+        defaultListTags( m_stream, tags, m_config->hasTestFilters() );
+    }
+
+} // namespace Catch
+
+
+
+
+#include <ostream>
+
+namespace {
+
+    constexpr Catch::StringRef bothOrAll( std::uint64_t count ) {
+        switch (count) {
+        case 1:
+            return Catch::StringRef{};
+        case 2:
+            return "both "_catch_sr;
+        default:
+            return "all "_catch_sr;
+        }
+    }
+
+} // anon namespace
+
+
+namespace Catch {
+namespace {
+
+    // Colour::LightGrey
+    static constexpr Colour::Code compactDimColour = Colour::FileName;
+
+#ifdef CATCH_PLATFORM_MAC
+    static constexpr Catch::StringRef compactFailedString = "FAILED"_sr;
+    static constexpr Catch::StringRef compactPassedString = "PASSED"_sr;
+#else
+    static constexpr Catch::StringRef compactFailedString = "failed"_sr;
+    static constexpr Catch::StringRef compactPassedString = "passed"_sr;
+#endif
+
+// Colour, message variants:
+// - white: No tests ran.
+// -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
+// - white: Passed [both/all] N test cases (no assertions).
+// -   red: Failed N tests cases, failed M assertions.
+// - green: Passed [both/all] N tests cases with M assertions.
+void printTotals(std::ostream& out, const Totals& totals, ColourImpl* colourImpl) {
+    if (totals.testCases.total() == 0) {
+        out << "No tests ran.";
+    } else if (totals.testCases.failed == totals.testCases.total()) {
+        auto guard = colourImpl->guardColour( Colour::ResultError ).engage( out );
+        const StringRef qualify_assertions_failed =
+            totals.assertions.failed == totals.assertions.total() ?
+            bothOrAll(totals.assertions.failed) : StringRef{};
+        out <<
+            "Failed " << bothOrAll(totals.testCases.failed)
+            << pluralise(totals.testCases.failed, "test case"_sr) << ", "
+            "failed " << qualify_assertions_failed <<
+            pluralise(totals.assertions.failed, "assertion"_sr) << '.';
+    } else if (totals.assertions.total() == 0) {
+        out <<
+            "Passed " << bothOrAll(totals.testCases.total())
+            << pluralise(totals.testCases.total(), "test case"_sr)
+            << " (no assertions).";
+    } else if (totals.assertions.failed) {
+        out << colourImpl->guardColour( Colour::ResultError ) <<
+            "Failed " << pluralise(totals.testCases.failed, "test case"_sr) << ", "
+            "failed " << pluralise(totals.assertions.failed, "assertion"_sr) << '.';
+    } else {
+        out << colourImpl->guardColour( Colour::ResultSuccess ) <<
+            "Passed " << bothOrAll(totals.testCases.passed)
+            << pluralise(totals.testCases.passed, "test case"_sr) <<
+            " with " << pluralise(totals.assertions.passed, "assertion"_sr) << '.';
+    }
+}
+
+// Implementation of CompactReporter formatting
+class AssertionPrinter {
+public:
+    AssertionPrinter& operator= (AssertionPrinter const&) = delete;
+    AssertionPrinter(AssertionPrinter const&) = delete;
+    AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages, ColourImpl* colourImpl_)
+        : stream(_stream)
+        , result(_stats.assertionResult)
+        , messages(_stats.infoMessages)
+        , itMessage(_stats.infoMessages.begin())
+        , printInfoMessages(_printInfoMessages)
+        , colourImpl(colourImpl_)
+    {}
+
+    void print() {
+        printSourceInfo();
+
+        itMessage = messages.begin();
+
+        switch (result.getResultType()) {
+        case ResultWas::Ok:
+            printResultType(Colour::ResultSuccess, compactPassedString);
+            printOriginalExpression();
+            printReconstructedExpression();
+            if (!result.hasExpression())
+                printRemainingMessages(Colour::None);
+            else
+                printRemainingMessages();
+            break;
+        case ResultWas::ExpressionFailed:
+            if (result.isOk())
+                printResultType(Colour::ResultSuccess, compactFailedString + " - but was ok"_sr);
+            else
+                printResultType(Colour::Error, compactFailedString);
+            printOriginalExpression();
+            printReconstructedExpression();
+            printRemainingMessages();
+            break;
+        case ResultWas::ThrewException:
+            printResultType(Colour::Error, compactFailedString);
+            printIssue("unexpected exception with message:");
+            printMessage();
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::FatalErrorCondition:
+            printResultType(Colour::Error, compactFailedString);
+            printIssue("fatal error condition with message:");
+            printMessage();
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::DidntThrowException:
+            printResultType(Colour::Error, compactFailedString);
+            printIssue("expected exception, got none");
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::Info:
+            printResultType(Colour::None, "info"_sr);
+            printMessage();
+            printRemainingMessages();
+            break;
+        case ResultWas::Warning:
+            printResultType(Colour::None, "warning"_sr);
+            printMessage();
+            printRemainingMessages();
+            break;
+        case ResultWas::ExplicitFailure:
+            printResultType(Colour::Error, compactFailedString);
+            printIssue("explicitly");
+            printRemainingMessages(Colour::None);
+            break;
+            // These cases are here to prevent compiler warnings
+        case ResultWas::Unknown:
+        case ResultWas::FailureBit:
+        case ResultWas::Exception:
+            printResultType(Colour::Error, "** internal error **");
+            break;
+        }
+    }
+
+private:
+    void printSourceInfo() const {
+        stream << colourImpl->guardColour( Colour::FileName )
+               << result.getSourceInfo() << ':';
+    }
+
+    void printResultType(Colour::Code colour, StringRef passOrFail) const {
+        if (!passOrFail.empty()) {
+            stream << colourImpl->guardColour(colour) << ' ' << passOrFail;
+            stream << ':';
+        }
+    }
+
+    void printIssue(char const* issue) const {
+        stream << ' ' << issue;
+    }
+
+    void printExpressionWas() {
+        if (result.hasExpression()) {
+            stream << ';';
+            {
+                stream << colourImpl->guardColour(compactDimColour) << " expression was:";
+            }
+            printOriginalExpression();
+        }
+    }
+
+    void printOriginalExpression() const {
+        if (result.hasExpression()) {
+            stream << ' ' << result.getExpression();
+        }
+    }
+
+    void printReconstructedExpression() const {
+        if (result.hasExpandedExpression()) {
+            stream << colourImpl->guardColour(compactDimColour) << " for: ";
+            stream << result.getExpandedExpression();
+        }
+    }
+
+    void printMessage() {
+        if (itMessage != messages.end()) {
+            stream << " '" << itMessage->message << '\'';
+            ++itMessage;
+        }
+    }
+
+    void printRemainingMessages(Colour::Code colour = compactDimColour) {
+        if (itMessage == messages.end())
+            return;
+
+        const auto itEnd = messages.cend();
+        const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+
+        stream << colourImpl->guardColour( colour ) << " with "
+               << pluralise( N, "message"_sr ) << ':';
+
+        while (itMessage != itEnd) {
+            // If this assertion is a warning ignore any INFO messages
+            if (printInfoMessages || itMessage->type != ResultWas::Info) {
+                printMessage();
+                if (itMessage != itEnd) {
+                    stream << colourImpl->guardColour(compactDimColour) << " and";
+                }
+                continue;
+            }
+            ++itMessage;
+        }
+    }
+
+private:
+    std::ostream& stream;
+    AssertionResult const& result;
+    std::vector<MessageInfo> messages;
+    std::vector<MessageInfo>::const_iterator itMessage;
+    bool printInfoMessages;
+    ColourImpl* colourImpl;
+};
+
+} // anon namespace
+
+        std::string CompactReporter::getDescription() {
+            return "Reports test results on a single line, suitable for IDEs";
+        }
+
+        void CompactReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+            m_stream << "No test cases matched '" << unmatchedSpec << "'\n";
+        }
+
+        void CompactReporter::testRunStarting( TestRunInfo const& ) {
+            if ( m_config->testSpec().hasFilters() ) {
+                m_stream << m_colour->guardColour( Colour::BrightYellow )
+                         << "Filters: "
+                         << serializeFilters( m_config->getTestsOrTags() )
+                         << '\n';
+            }
+            m_stream << "RNG seed: " << getSeed() << '\n';
+        }
+
+        void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return;
+                printInfoMessages = false;
+            }
+
+            AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages, m_colour.get() );
+            printer.print();
+
+            m_stream << '\n' << std::flush;
+        }
+
+        void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
+            double dur = _sectionStats.durationInSeconds;
+            if ( shouldShowDuration( *m_config, dur ) ) {
+                m_stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush;
+            }
+        }
+
+        void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotals( m_stream, _testRunStats.totals, m_colour.get() );
+            m_stream << "\n\n" << std::flush;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+        CompactReporter::~CompactReporter() {}
+
+} // end namespace Catch
+
+
+
+
+#include <cstdio>
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+ // Note that 4062 (not all labels are handled and default is missing) is enabled
+#endif
+
+#if defined(__clang__)
+#  pragma clang diagnostic push
+// For simplicity, benchmarking-only helpers are always enabled
+#  pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+
+
+namespace Catch {
+
+namespace {
+
+// Formatter impl for ConsoleReporter
+class ConsoleAssertionPrinter {
+public:
+    ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
+    ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
+    ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, ColourImpl* colourImpl_, bool _printInfoMessages)
+        : stream(_stream),
+        stats(_stats),
+        result(_stats.assertionResult),
+        colour(Colour::None),
+        message(result.getMessage()),
+        messages(_stats.infoMessages),
+        colourImpl(colourImpl_),
+        printInfoMessages(_printInfoMessages) {
+        switch (result.getResultType()) {
+        case ResultWas::Ok:
+            colour = Colour::Success;
+            passOrFail = "PASSED"_sr;
+            //if( result.hasMessage() )
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "with messages";
+            break;
+        case ResultWas::ExpressionFailed:
+            if (result.isOk()) {
+                colour = Colour::Success;
+                passOrFail = "FAILED - but was ok"_sr;
+            } else {
+                colour = Colour::Error;
+                passOrFail = "FAILED"_sr;
+            }
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "with messages";
+            break;
+        case ResultWas::ThrewException:
+            colour = Colour::Error;
+            passOrFail = "FAILED"_sr;
+            messageLabel = "due to unexpected exception with ";
+            if (_stats.infoMessages.size() == 1)
+                messageLabel += "message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel += "messages";
+            break;
+        case ResultWas::FatalErrorCondition:
+            colour = Colour::Error;
+            passOrFail = "FAILED"_sr;
+            messageLabel = "due to a fatal error condition";
+            break;
+        case ResultWas::DidntThrowException:
+            colour = Colour::Error;
+            passOrFail = "FAILED"_sr;
+            messageLabel = "because no exception was thrown where one was expected";
+            break;
+        case ResultWas::Info:
+            messageLabel = "info";
+            break;
+        case ResultWas::Warning:
+            messageLabel = "warning";
+            break;
+        case ResultWas::ExplicitFailure:
+            passOrFail = "FAILED"_sr;
+            colour = Colour::Error;
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "explicitly with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "explicitly with messages";
+            break;
+            // These cases are here to prevent compiler warnings
+        case ResultWas::Unknown:
+        case ResultWas::FailureBit:
+        case ResultWas::Exception:
+            passOrFail = "** internal error **"_sr;
+            colour = Colour::Error;
+            break;
+        }
+    }
+
+    void print() const {
+        printSourceInfo();
+        if (stats.totals.assertions.total() > 0) {
+            printResultType();
+            printOriginalExpression();
+            printReconstructedExpression();
+        } else {
+            stream << '\n';
+        }
+        printMessage();
+    }
+
+private:
+    void printResultType() const {
+        if (!passOrFail.empty()) {
+            stream << colourImpl->guardColour(colour) << passOrFail << ":\n";
+        }
+    }
+    void printOriginalExpression() const {
+        if (result.hasExpression()) {
+            stream << colourImpl->guardColour( Colour::OriginalExpression )
+                   << "  " << result.getExpressionInMacro() << '\n';
+        }
+    }
+    void printReconstructedExpression() const {
+        if (result.hasExpandedExpression()) {
+            stream << "with expansion:\n";
+            stream << colourImpl->guardColour( Colour::ReconstructedExpression )
+                   << TextFlow::Column( result.getExpandedExpression() )
+                          .indent( 2 )
+                   << '\n';
+        }
+    }
+    void printMessage() const {
+        if (!messageLabel.empty())
+            stream << messageLabel << ':' << '\n';
+        for (auto const& msg : messages) {
+            // If this assertion is a warning ignore any INFO messages
+            if (printInfoMessages || msg.type != ResultWas::Info)
+                stream << TextFlow::Column(msg.message).indent(2) << '\n';
+        }
+    }
+    void printSourceInfo() const {
+        stream << colourImpl->guardColour( Colour::FileName )
+               << result.getSourceInfo() << ": ";
+    }
+
+    std::ostream& stream;
+    AssertionStats const& stats;
+    AssertionResult const& result;
+    Colour::Code colour;
+    StringRef passOrFail;
+    std::string messageLabel;
+    std::string message;
+    std::vector<MessageInfo> messages;
+    ColourImpl* colourImpl;
+    bool printInfoMessages;
+};
+
+std::size_t makeRatio( std::uint64_t number, std::uint64_t total ) {
+    const auto ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
+    return (ratio == 0 && number > 0) ? 1 : static_cast<std::size_t>(ratio);
+}
+
+std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+    if (i > j && i > k)
+        return i;
+    else if (j > k)
+        return j;
+    else
+        return k;
+}
+
+enum class Justification { Left, Right };
+
+struct ColumnInfo {
+    std::string name;
+    std::size_t width;
+    Justification justification;
+};
+struct ColumnBreak {};
+struct RowBreak {};
+
+class Duration {
+    enum class Unit {
+        Auto,
+        Nanoseconds,
+        Microseconds,
+        Milliseconds,
+        Seconds,
+        Minutes
+    };
+    static const uint64_t s_nanosecondsInAMicrosecond = 1000;
+    static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
+    static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
+    static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
+
+    double m_inNanoseconds;
+    Unit m_units;
+
+public:
+    explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
+        : m_inNanoseconds(inNanoseconds),
+        m_units(units) {
+        if (m_units == Unit::Auto) {
+            if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
+                m_units = Unit::Nanoseconds;
+            else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
+                m_units = Unit::Microseconds;
+            else if (m_inNanoseconds < s_nanosecondsInASecond)
+                m_units = Unit::Milliseconds;
+            else if (m_inNanoseconds < s_nanosecondsInAMinute)
+                m_units = Unit::Seconds;
+            else
+                m_units = Unit::Minutes;
+        }
+
+    }
+
+    auto value() const -> double {
+        switch (m_units) {
+        case Unit::Microseconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond);
+        case Unit::Milliseconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond);
+        case Unit::Seconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond);
+        case Unit::Minutes:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
+        default:
+            return m_inNanoseconds;
+        }
+    }
+    StringRef unitsAsString() const {
+        switch (m_units) {
+        case Unit::Nanoseconds:
+            return "ns"_sr;
+        case Unit::Microseconds:
+            return "us"_sr;
+        case Unit::Milliseconds:
+            return "ms"_sr;
+        case Unit::Seconds:
+            return "s"_sr;
+        case Unit::Minutes:
+            return "m"_sr;
+        default:
+            return "** internal error **"_sr;
+        }
+
+    }
+    friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
+        return os << duration.value() << ' ' << duration.unitsAsString();
+    }
+};
+} // end anon namespace
+
+class TablePrinter {
+    std::ostream& m_os;
+    std::vector<ColumnInfo> m_columnInfos;
+    ReusableStringStream m_oss;
+    int m_currentColumn = -1;
+    bool m_isOpen = false;
+
+public:
+    TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
+    :   m_os( os ),
+        m_columnInfos( CATCH_MOVE( columnInfos ) ) {}
+
+    auto columnInfos() const -> std::vector<ColumnInfo> const& {
+        return m_columnInfos;
+    }
+
+    void open() {
+        if (!m_isOpen) {
+            m_isOpen = true;
+            *this << RowBreak();
+
+			TextFlow::Columns headerCols;
+			auto spacer = TextFlow::Spacer(2);
+			for (auto const& info : m_columnInfos) {
+                assert(info.width > 2);
+				headerCols += TextFlow::Column(info.name).width(info.width - 2);
+				headerCols += spacer;
+			}
+			m_os << headerCols << '\n';
+
+            m_os << lineOfChars('-') << '\n';
+        }
+    }
+    void close() {
+        if (m_isOpen) {
+            *this << RowBreak();
+            m_os << '\n' << std::flush;
+            m_isOpen = false;
+        }
+    }
+
+    template<typename T>
+    friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
+        tp.m_oss << value;
+        return tp;
+    }
+
+    friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
+        auto colStr = tp.m_oss.str();
+        const auto strSize = colStr.size();
+        tp.m_oss.str("");
+        tp.open();
+        if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
+            tp.m_currentColumn = -1;
+            tp.m_os << '\n';
+        }
+        tp.m_currentColumn++;
+
+        auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
+        auto padding = (strSize + 1 < colInfo.width)
+            ? std::string(colInfo.width - (strSize + 1), ' ')
+            : std::string();
+        if (colInfo.justification == Justification::Left)
+            tp.m_os << colStr << padding << ' ';
+        else
+            tp.m_os << padding << colStr << ' ';
+        return tp;
+    }
+
+    friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
+        if (tp.m_currentColumn > 0) {
+            tp.m_os << '\n';
+            tp.m_currentColumn = -1;
+        }
+        return tp;
+    }
+};
+
+ConsoleReporter::ConsoleReporter(ReporterConfig&& config):
+    StreamingReporterBase( CATCH_MOVE( config ) ),
+    m_tablePrinter(Detail::make_unique<TablePrinter>(m_stream,
+        [&config]() -> std::vector<ColumnInfo> {
+        if (config.fullConfig()->benchmarkNoAnalysis())
+        {
+            return{
+                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left },
+                { "     samples", 14, Justification::Right },
+                { "  iterations", 14, Justification::Right },
+                { "        mean", 14, Justification::Right }
+            };
+        }
+        else
+        {
+            return{
+                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left },
+                { "samples      mean       std dev", 14, Justification::Right },
+                { "iterations   low mean   low std dev", 14, Justification::Right },
+                { "estimated    high mean  high std dev", 14, Justification::Right }
+            };
+        }
+    }())) {}
+ConsoleReporter::~ConsoleReporter() = default;
+
+std::string ConsoleReporter::getDescription() {
+    return "Reports test results as plain lines of text";
+}
+
+void ConsoleReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+    m_stream << "No test cases matched '" << unmatchedSpec << "'\n";
+}
+
+void ConsoleReporter::reportInvalidTestSpec( StringRef arg ) {
+    m_stream << "Invalid Filter: " << arg << '\n';
+}
+
+void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
+
+void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+    AssertionResult const& result = _assertionStats.assertionResult;
+
+    bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+    // Drop out if result was successful but we're not printing them.
+    if (!includeResults && result.getResultType() != ResultWas::Warning)
+        return;
+
+    lazyPrint();
+
+    ConsoleAssertionPrinter printer(m_stream, _assertionStats, m_colour.get(), includeResults);
+    printer.print();
+    m_stream << '\n' << std::flush;
+}
+
+void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
+    m_tablePrinter->close();
+    m_headerPrinted = false;
+    StreamingReporterBase::sectionStarting(_sectionInfo);
+}
+void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
+    m_tablePrinter->close();
+    if (_sectionStats.missingAssertions) {
+        lazyPrint();
+        auto guard =
+            m_colour->guardColour( Colour::ResultError ).engage( m_stream );
+        if (m_sectionStack.size() > 1)
+            m_stream << "\nNo assertions in section";
+        else
+            m_stream << "\nNo assertions in test case";
+        m_stream << " '" << _sectionStats.sectionInfo.name << "'\n\n" << std::flush;
+    }
+    double dur = _sectionStats.durationInSeconds;
+    if (shouldShowDuration(*m_config, dur)) {
+        m_stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush;
+    }
+    if (m_headerPrinted) {
+        m_headerPrinted = false;
+    }
+    StreamingReporterBase::sectionEnded(_sectionStats);
+}
+
+void ConsoleReporter::benchmarkPreparing( StringRef name ) {
+	lazyPrintWithoutClosingBenchmarkTable();
+
+	auto nameCol = TextFlow::Column( static_cast<std::string>( name ) )
+                       .width( m_tablePrinter->columnInfos()[0].width - 2 );
+
+	bool firstLine = true;
+	for (auto line : nameCol) {
+		if (!firstLine)
+			(*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
+		else
+			firstLine = false;
+
+		(*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::benchmarkFailed( StringRef error ) {
+    auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream );
+    (*m_tablePrinter)
+        << "Benchmark failed (" << error << ')'
+        << ColumnBreak() << RowBreak();
+}
+
+void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
+    m_tablePrinter->close();
+    StreamingReporterBase::testCaseEnded(_testCaseStats);
+    m_headerPrinted = false;
+}
+void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
+    printTotalsDivider(_testRunStats.totals);
+    printTotals(_testRunStats.totals);
+    m_stream << '\n' << std::flush;
+    StreamingReporterBase::testRunEnded(_testRunStats);
+}
+void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
+    StreamingReporterBase::testRunStarting(_testInfo);
+    if ( m_config->testSpec().hasFilters() ) {
+        m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: "
+                 << serializeFilters( m_config->getTestsOrTags() ) << '\n';
+    }
+    m_stream << "Randomness seeded to: " << getSeed() << '\n';
+}
+
+void ConsoleReporter::lazyPrint() {
+
+    m_tablePrinter->close();
+    lazyPrintWithoutClosingBenchmarkTable();
+}
+
+void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
+
+    if ( !m_testRunInfoPrinted ) {
+        lazyPrintRunInfo();
+    }
+    if (!m_headerPrinted) {
+        printTestCaseAndSectionHeader();
+        m_headerPrinted = true;
+    }
+}
+void ConsoleReporter::lazyPrintRunInfo() {
+    m_stream << '\n'
+             << lineOfChars( '~' ) << '\n'
+             << m_colour->guardColour( Colour::SecondaryText )
+             << currentTestRunInfo.name << " is a Catch2 v" << libraryVersion()
+             << " host application.\n"
+             << "Run with -? for options\n\n";
+
+    m_testRunInfoPrinted = true;
+}
+void ConsoleReporter::printTestCaseAndSectionHeader() {
+    assert(!m_sectionStack.empty());
+    printOpenHeader(currentTestCaseInfo->name);
+
+    if (m_sectionStack.size() > 1) {
+        auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream );
+
+        auto
+            it = m_sectionStack.begin() + 1, // Skip first section (test case)
+            itEnd = m_sectionStack.end();
+        for (; it != itEnd; ++it)
+            printHeaderString(it->name, 2);
+    }
+
+    SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+
+    m_stream << lineOfChars( '-' ) << '\n'
+             << m_colour->guardColour( Colour::FileName ) << lineInfo << '\n'
+             << lineOfChars( '.' ) << "\n\n"
+             << std::flush;
+}
+
+void ConsoleReporter::printClosedHeader(std::string const& _name) {
+    printOpenHeader(_name);
+    m_stream << lineOfChars('.') << '\n';
+}
+void ConsoleReporter::printOpenHeader(std::string const& _name) {
+    m_stream << lineOfChars('-') << '\n';
+    {
+        auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream );
+        printHeaderString(_name);
+    }
+}
+
+void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
+    // We want to get a bit fancy with line breaking here, so that subsequent
+    // lines start after ":" if one is present, e.g.
+    // ```
+    // blablabla: Fancy
+    //            linebreaking
+    // ```
+    // but we also want to avoid problems with overly long indentation causing
+    // the text to take up too many lines, e.g.
+    // ```
+    // blablabla: F
+    //            a
+    //            n
+    //            c
+    //            y
+    //            .
+    //            .
+    //            .
+    // ```
+    // So we limit the prefix indentation check to first quarter of the possible
+    // width
+    std::size_t idx = _string.find( ": " );
+    if ( idx != std::string::npos && idx < CATCH_CONFIG_CONSOLE_WIDTH / 4 ) {
+        idx += 2;
+    } else {
+        idx = 0;
+    }
+    m_stream << TextFlow::Column( _string )
+                  .indent( indent + idx )
+                  .initialIndent( indent )
+           << '\n';
+}
+
+struct SummaryColumn {
+
+    SummaryColumn( std::string _label, Colour::Code _colour )
+    :   label( CATCH_MOVE( _label ) ),
+        colour( _colour ) {}
+    SummaryColumn addRow( std::uint64_t count ) {
+        ReusableStringStream rss;
+        rss << count;
+        std::string row = rss.str();
+        for (auto& oldRow : rows) {
+            while (oldRow.size() < row.size())
+                oldRow = ' ' + oldRow;
+            while (oldRow.size() > row.size())
+                row = ' ' + row;
+        }
+        rows.push_back(row);
+        return *this;
+    }
+
+    std::string label;
+    Colour::Code colour;
+    std::vector<std::string> rows;
+
+};
+
+void ConsoleReporter::printTotals( Totals const& totals ) {
+    if (totals.testCases.total() == 0) {
+        m_stream << m_colour->guardColour( Colour::Warning )
+                 << "No tests ran\n";
+    } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
+        m_stream << m_colour->guardColour( Colour::ResultSuccess )
+                 << "All tests passed";
+        m_stream << " ("
+            << pluralise(totals.assertions.passed, "assertion"_sr) << " in "
+            << pluralise(totals.testCases.passed, "test case"_sr) << ')'
+            << '\n';
+    } else {
+
+        std::vector<SummaryColumn> columns;
+        columns.push_back(SummaryColumn("", Colour::None)
+                          .addRow(totals.testCases.total())
+                          .addRow(totals.assertions.total()));
+        columns.push_back(SummaryColumn("passed", Colour::Success)
+                          .addRow(totals.testCases.passed)
+                          .addRow(totals.assertions.passed));
+        columns.push_back(SummaryColumn("failed", Colour::ResultError)
+                          .addRow(totals.testCases.failed)
+                          .addRow(totals.assertions.failed));
+        columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure)
+                          .addRow(totals.testCases.failedButOk)
+                          .addRow(totals.assertions.failedButOk));
+
+        printSummaryRow("test cases"_sr, columns, 0);
+        printSummaryRow("assertions"_sr, columns, 1);
+    }
+}
+void ConsoleReporter::printSummaryRow(StringRef label, std::vector<SummaryColumn> const& cols, std::size_t row) {
+    for (auto col : cols) {
+        std::string const& value = col.rows[row];
+        if (col.label.empty()) {
+            m_stream << label << ": ";
+            if ( value != "0" ) {
+                m_stream << value;
+            } else {
+                m_stream << m_colour->guardColour( Colour::Warning )
+                         << "- none -";
+            }
+        } else if (value != "0") {
+            m_stream << m_colour->guardColour( Colour::LightGrey ) << " | "
+                     << m_colour->guardColour( col.colour ) << value << ' '
+                     << col.label;
+        }
+    }
+    m_stream << '\n';
+}
+
+void ConsoleReporter::printTotalsDivider(Totals const& totals) {
+    if (totals.testCases.total() > 0) {
+        std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
+        std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
+        std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
+        while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
+            findMax(failedRatio, failedButOkRatio, passedRatio)++;
+        while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
+            findMax(failedRatio, failedButOkRatio, passedRatio)--;
+
+        m_stream << m_colour->guardColour( Colour::Error )
+                 << std::string( failedRatio, '=' )
+                 << m_colour->guardColour( Colour::ResultExpectedFailure )
+                 << std::string( failedButOkRatio, '=' );
+        if ( totals.testCases.allPassed() ) {
+            m_stream << m_colour->guardColour( Colour::ResultSuccess )
+                     << std::string( passedRatio, '=' );
+        } else {
+            m_stream << m_colour->guardColour( Colour::Success )
+                     << std::string( passedRatio, '=' );
+        }
+    } else {
+        m_stream << m_colour->guardColour( Colour::Warning )
+                 << std::string( CATCH_CONFIG_CONSOLE_WIDTH - 1, '=' );
+    }
+    m_stream << '\n';
+}
+void ConsoleReporter::printSummaryDivider() {
+    m_stream << lineOfChars('-') << '\n';
+}
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__)
+#  pragma clang diagnostic pop
+#endif
+
+
+
+
+#include <algorithm>
+#include <cassert>
+
+namespace Catch {
+    namespace {
+        struct BySectionInfo {
+            BySectionInfo( SectionInfo const& other ): m_other( other ) {}
+            BySectionInfo( BySectionInfo const& other ):
+                m_other( other.m_other ) {}
+            bool operator()(
+                Detail::unique_ptr<CumulativeReporterBase::SectionNode> const&
+                    node ) const {
+                return (
+                    ( node->stats.sectionInfo.name == m_other.name ) &&
+                    ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) );
+            }
+            void operator=( BySectionInfo const& ) = delete;
+
+        private:
+            SectionInfo const& m_other;
+        };
+
+    } // namespace
+
+    namespace Detail {
+        AssertionOrBenchmarkResult::AssertionOrBenchmarkResult(
+            AssertionStats const& assertion ):
+            m_assertion( assertion ) {}
+
+        AssertionOrBenchmarkResult::AssertionOrBenchmarkResult(
+            BenchmarkStats<> const& benchmark ):
+            m_benchmark( benchmark ) {}
+
+        bool AssertionOrBenchmarkResult::isAssertion() const {
+            return m_assertion.some();
+        }
+        bool AssertionOrBenchmarkResult::isBenchmark() const {
+            return m_benchmark.some();
+        }
+
+        AssertionStats const& AssertionOrBenchmarkResult::asAssertion() const {
+            assert(m_assertion.some());
+
+            return *m_assertion;
+        }
+        BenchmarkStats<> const& AssertionOrBenchmarkResult::asBenchmark() const {
+            assert(m_benchmark.some());
+
+            return *m_benchmark;
+        }
+
+    }
+
+    CumulativeReporterBase::~CumulativeReporterBase() = default;
+
+    void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
+        m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats);
+    }
+
+    void
+    CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) {
+        SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+        SectionNode* node;
+        if ( m_sectionStack.empty() ) {
+            if ( !m_rootSection ) {
+                m_rootSection =
+                    Detail::make_unique<SectionNode>( incompleteStats );
+            }
+            node = m_rootSection.get();
+        } else {
+            SectionNode& parentNode = *m_sectionStack.back();
+            auto it = std::find_if( parentNode.childSections.begin(),
+                                    parentNode.childSections.end(),
+                                    BySectionInfo( sectionInfo ) );
+            if ( it == parentNode.childSections.end() ) {
+                auto newNode =
+                    Detail::make_unique<SectionNode>( incompleteStats );
+                node = newNode.get();
+                parentNode.childSections.push_back( CATCH_MOVE( newNode ) );
+            } else {
+                node = it->get();
+            }
+        }
+
+        m_deepestSection = node;
+        m_sectionStack.push_back( node );
+    }
+
+    void CumulativeReporterBase::assertionEnded(
+        AssertionStats const& assertionStats ) {
+        assert( !m_sectionStack.empty() );
+        // AssertionResult holds a pointer to a temporary DecomposedExpression,
+        // which getExpandedExpression() calls to build the expression string.
+        // Our section stack copy of the assertionResult will likely outlive the
+        // temporary, so it must be expanded or discarded now to avoid calling
+        // a destroyed object later.
+        if ( m_shouldStoreFailedAssertions &&
+             !assertionStats.assertionResult.isOk() ) {
+            static_cast<void>(
+                assertionStats.assertionResult.getExpandedExpression() );
+        }
+        if ( m_shouldStoreSuccesfulAssertions &&
+             assertionStats.assertionResult.isOk() ) {
+            static_cast<void>(
+                assertionStats.assertionResult.getExpandedExpression() );
+        }
+        SectionNode& sectionNode = *m_sectionStack.back();
+        sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats );
+    }
+
+    void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) {
+        assert( !m_sectionStack.empty() );
+        SectionNode& node = *m_sectionStack.back();
+        node.stats = sectionStats;
+        m_sectionStack.pop_back();
+    }
+
+    void CumulativeReporterBase::testCaseEnded(
+        TestCaseStats const& testCaseStats ) {
+        auto node = Detail::make_unique<TestCaseNode>( testCaseStats );
+        assert( m_sectionStack.size() == 0 );
+        node->children.push_back( CATCH_MOVE(m_rootSection) );
+        m_testCases.push_back( CATCH_MOVE(node) );
+
+        assert( m_deepestSection );
+        m_deepestSection->stdOut = testCaseStats.stdOut;
+        m_deepestSection->stdErr = testCaseStats.stdErr;
+    }
+
+
+    void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) {
+        assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run");
+        m_testRun = Detail::make_unique<TestRunNode>( testRunStats );
+        m_testRun->children.swap( m_testCases );
+        testRunEndedCumulative();
+    }
+
+    bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const {
+        return std::any_of(
+            assertionsAndBenchmarks.begin(),
+            assertionsAndBenchmarks.end(),
+            []( Detail::AssertionOrBenchmarkResult const& res ) {
+                return res.isAssertion();
+            } );
+    }
+
+} // end namespace Catch
+
+
+
+
+namespace Catch {
+
+    void EventListenerBase::fatalErrorEncountered( StringRef ) {}
+
+    void EventListenerBase::benchmarkPreparing( StringRef ) {}
+    void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {}
+    void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {}
+    void EventListenerBase::benchmarkFailed( StringRef ) {}
+
+    void EventListenerBase::assertionStarting( AssertionInfo const& ) {}
+
+    void EventListenerBase::assertionEnded( AssertionStats const& ) {}
+    void EventListenerBase::listReporters(
+        std::vector<ReporterDescription> const& ) {}
+    void EventListenerBase::listListeners(
+        std::vector<ListenerDescription> const& ) {}
+    void EventListenerBase::listTests( std::vector<TestCaseHandle> const& ) {}
+    void EventListenerBase::listTags( std::vector<TagInfo> const& ) {}
+    void EventListenerBase::noMatchingTestCases( StringRef ) {}
+    void EventListenerBase::reportInvalidTestSpec( StringRef ) {}
+    void EventListenerBase::testRunStarting( TestRunInfo const& ) {}
+    void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {}
+    void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {}
+    void EventListenerBase::sectionStarting( SectionInfo const& ) {}
+    void EventListenerBase::sectionEnded( SectionStats const& ) {}
+    void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {}
+    void EventListenerBase::testCaseEnded( TestCaseStats const& ) {}
+    void EventListenerBase::testRunEnded( TestRunStats const& ) {}
+    void EventListenerBase::skipTest( TestCaseInfo const& ) {}
+} // namespace Catch
+
+
+
+
+#include <algorithm>
+#include <cfloat>
+#include <cstdio>
+#include <ostream>
+#include <iomanip>
+
+namespace Catch {
+
+    namespace {
+        void listTestNamesOnly(std::ostream& out,
+                               std::vector<TestCaseHandle> const& tests) {
+            for (auto const& test : tests) {
+                auto const& testCaseInfo = test.getTestCaseInfo();
+
+                if (startsWith(testCaseInfo.name, '#')) {
+                    out << '"' << testCaseInfo.name << '"';
+                } else {
+                    out << testCaseInfo.name;
+                }
+
+                out << '\n';
+            }
+            out << std::flush;
+        }
+    } // end unnamed namespace
+
+
+    // Because formatting using c++ streams is stateful, drop down to C is
+    // required Alternatively we could use stringstream, but its performance
+    // is... not good.
+    std::string getFormattedDuration( double duration ) {
+        // Max exponent + 1 is required to represent the whole part
+        // + 1 for decimal point
+        // + 3 for the 3 decimal places
+        // + 1 for null terminator
+        const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+        char buffer[maxDoubleSize];
+
+        // Save previous errno, to prevent sprintf from overwriting it
+        ErrnoGuard guard;
+#ifdef _MSC_VER
+        size_t printedLength = static_cast<size_t>(
+            sprintf_s( buffer, "%.3f", duration ) );
+#else
+        size_t printedLength = static_cast<size_t>(
+            std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) );
+#endif
+        return std::string( buffer, printedLength );
+    }
+
+    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& filters ) {
+        // We add a ' ' separator between each filter
+        size_t serialized_size = filters.size() - 1;
+        for (auto const& filter : filters) {
+            serialized_size += filter.size();
+        }
+
+        std::string serialized;
+        serialized.reserve(serialized_size);
+        bool first = true;
+
+        for (auto const& filter : filters) {
+            if (!first) {
+                serialized.push_back(' ');
+            }
+            first = false;
+            serialized.append(filter);
+        }
+
+        return serialized;
+    }
+
+    std::ostream& operator<<( std::ostream& out, lineOfChars value ) {
+        for ( size_t idx = 0; idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++idx ) {
+            out.put( value.c );
+        }
+        return out;
+    }
+
+    void
+    defaultListReporters( std::ostream& out,
+                          std::vector<ReporterDescription> const& descriptions,
+                          Verbosity verbosity ) {
+        out << "Available reporters:\n";
+        const auto maxNameLen =
+            std::max_element( descriptions.begin(),
+                              descriptions.end(),
+                              []( ReporterDescription const& lhs,
+                                  ReporterDescription const& rhs ) {
+                                  return lhs.name.size() < rhs.name.size();
+                              } )
+                ->name.size();
+
+        for ( auto const& desc : descriptions ) {
+            if ( verbosity == Verbosity::Quiet ) {
+                out << TextFlow::Column( desc.name )
+                           .indent( 2 )
+                           .width( 5 + maxNameLen )
+                    << '\n';
+            } else {
+                out << TextFlow::Column( desc.name + ':' )
+                               .indent( 2 )
+                               .width( 5 + maxNameLen ) +
+                           TextFlow::Column( desc.description )
+                               .initialIndent( 0 )
+                               .indent( 2 )
+                               .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 )
+                    << '\n';
+            }
+        }
+        out << '\n' << std::flush;
+    }
+
+    void defaultListListeners( std::ostream& out,
+                               std::vector<ListenerDescription> const& descriptions ) {
+        out << "Registered listeners:\n";
+
+        if(descriptions.empty()) {
+            return;
+        }
+
+        const auto maxNameLen =
+            std::max_element( descriptions.begin(),
+                              descriptions.end(),
+                              []( ListenerDescription const& lhs,
+                                  ListenerDescription const& rhs ) {
+                                  return lhs.name.size() < rhs.name.size();
+                              } )
+                ->name.size();
+
+        for ( auto const& desc : descriptions ) {
+            out << TextFlow::Column( static_cast<std::string>( desc.name ) +
+                                     ':' )
+                           .indent( 2 )
+                           .width( maxNameLen + 5 ) +
+                       TextFlow::Column( desc.description )
+                           .initialIndent( 0 )
+                           .indent( 2 )
+                           .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 )
+                << '\n';
+        }
+
+        out << '\n' << std::flush;
+    }
+
+    void defaultListTags( std::ostream& out,
+                          std::vector<TagInfo> const& tags,
+                          bool isFiltered ) {
+        if ( isFiltered ) {
+            out << "Tags for matching test cases:\n";
+        } else {
+            out << "All available tags:\n";
+        }
+
+        for ( auto const& tagCount : tags ) {
+            ReusableStringStream rss;
+            rss << "  " << std::setw( 2 ) << tagCount.count << "  ";
+            auto str = rss.str();
+            auto wrapper = TextFlow::Column( tagCount.all() )
+                               .initialIndent( 0 )
+                               .indent( str.size() )
+                               .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 );
+            out << str << wrapper << '\n';
+        }
+        out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush;
+    }
+
+    void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector<TestCaseHandle> const& tests, bool isFiltered, Verbosity verbosity) {
+        // We special case this to provide the equivalent of old
+        // `--list-test-names-only`, which could then be used by the
+        // `--input-file` option.
+        if (verbosity == Verbosity::Quiet) {
+            listTestNamesOnly(out, tests);
+            return;
+        }
+
+        if (isFiltered) {
+            out << "Matching test cases:\n";
+        } else {
+            out << "All available test cases:\n";
+        }
+
+        for (auto const& test : tests) {
+            auto const& testCaseInfo = test.getTestCaseInfo();
+            Colour::Code colour = testCaseInfo.isHidden()
+                ? Colour::SecondaryText
+                : Colour::None;
+            auto colourGuard = streamColour->guardColour( colour ).engage( out );
+
+            out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n';
+            if (verbosity >= Verbosity::High) {
+                out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n';
+            }
+            if (!testCaseInfo.tags.empty() &&
+                verbosity > Verbosity::Quiet) {
+                out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n';
+            }
+        }
+
+        if (isFiltered) {
+            out << pluralise(tests.size(), "matching test case"_sr);
+        } else {
+            out << pluralise(tests.size(), "test case"_sr);
+        }
+        out << "\n\n" << std::flush;
+    }
+
+} // namespace Catch
+
+
+
+
+#include <cassert>
+#include <ctime>
+#include <algorithm>
+#include <iomanip>
+
+namespace Catch {
+
+    namespace {
+        std::string getCurrentTimestamp() {
+            time_t rawtime;
+            std::time(&rawtime);
+
+            std::tm timeInfo = {};
+#if defined (_MSC_VER) || defined (__MINGW32__)
+            gmtime_s(&timeInfo, &rawtime);
+#else
+            gmtime_r(&rawtime, &timeInfo);
+#endif
+
+            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+            char timeStamp[timeStampSize];
+            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+
+            return std::string(timeStamp, timeStampSize - 1);
+        }
+
+        std::string fileNameTag(std::vector<Tag> const& tags) {
+            auto it = std::find_if(begin(tags),
+                                   end(tags),
+                                   [] (Tag const& tag) {
+                                       return tag.original.size() > 0
+                                           && tag.original[0] == '#'; });
+            if (it != tags.end()) {
+                return static_cast<std::string>(
+                    it->original.substr(1, it->original.size() - 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();
+        }
+
+        static void normalizeNamespaceMarkers(std::string& str) {
+            std::size_t pos = str.find( "::" );
+            while ( pos != str.npos ) {
+                str.replace( pos, 2, "." );
+                pos += 1;
+                pos = str.find( "::", pos );
+            }
+        }
+
+    } // anonymous namespace
+
+    JunitReporter::JunitReporter( ReporterConfig&& _config )
+        :   CumulativeReporterBase( CATCH_MOVE(_config) ),
+            xml( m_stream )
+        {
+            m_preferences.shouldRedirectStdOut = true;
+            m_preferences.shouldReportAllAssertions = true;
+            m_shouldStoreSuccesfulAssertions = false;
+        }
+
+    std::string JunitReporter::getDescription() {
+        return "Reports test results in an XML format that looks like Ant's junitreport target";
+    }
+
+    void JunitReporter::testRunStarting( TestRunInfo const& runInfo )  {
+        CumulativeReporterBase::testRunStarting( runInfo );
+        xml.startElement( "testsuites" );
+        suiteTimer.start();
+        stdOutForSuite.clear();
+        stdErrForSuite.clear();
+        unexpectedExceptions = 0;
+    }
+
+    void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
+        m_okToFail = testCaseInfo.okToFail();
+    }
+
+    void JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
+        if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+            unexpectedExceptions++;
+        CumulativeReporterBase::assertionEnded( assertionStats );
+    }
+
+    void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        stdOutForSuite += testCaseStats.stdOut;
+        stdErrForSuite += testCaseStats.stdErr;
+        CumulativeReporterBase::testCaseEnded( testCaseStats );
+    }
+
+    void JunitReporter::testRunEndedCumulative() {
+        const auto suiteTime = suiteTimer.getElapsedSeconds();
+        writeRun( *m_testRun, suiteTime );
+        xml.endElement();
+    }
+
+    void JunitReporter::writeRun( TestRunNode const& testRunNode, double suiteTime ) {
+        XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+
+        TestRunStats const& stats = testRunNode.value;
+        xml.writeAttribute( "name"_sr, stats.runInfo.name );
+        xml.writeAttribute( "errors"_sr, unexpectedExceptions );
+        xml.writeAttribute( "failures"_sr, stats.totals.assertions.failed-unexpectedExceptions );
+        xml.writeAttribute( "tests"_sr, stats.totals.assertions.total() );
+        xml.writeAttribute( "hostname"_sr, "tbd"_sr ); // !TBD
+        if( m_config->showDurations() == ShowDurations::Never )
+            xml.writeAttribute( "time"_sr, ""_sr );
+        else
+            xml.writeAttribute( "time"_sr, formatDuration( suiteTime ) );
+        xml.writeAttribute( "timestamp"_sr, getCurrentTimestamp() );
+
+        // Write properties
+        {
+            auto properties = xml.scopedElement("properties");
+            xml.scopedElement("property")
+                .writeAttribute("name"_sr, "random-seed"_sr)
+                .writeAttribute("value"_sr, m_config->rngSeed());
+            if (m_config->hasTestFilters()) {
+                xml.scopedElement("property")
+                    .writeAttribute("name"_sr, "filters"_sr)
+                    .writeAttribute("value"_sr, serializeFilters(m_config->getTestsOrTags()));
+            }
+        }
+
+        // Write test cases
+        for( auto const& child : testRunNode.children )
+            writeTestCase( *child );
+
+        xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
+        xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
+    }
+
+    void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
+        TestCaseStats const& stats = testCaseNode.value;
+
+        // All test cases have exactly one section - which represents the
+        // test case itself. That section may have 0-n nested sections
+        assert( testCaseNode.children.size() == 1 );
+        SectionNode const& rootSection = *testCaseNode.children.front();
+
+        std::string className =
+            static_cast<std::string>( stats.testInfo->className );
+
+        if( className.empty() ) {
+            className = fileNameTag(stats.testInfo->tags);
+            if ( className.empty() ) {
+                className = "global";
+            }
+        }
+
+        if ( !m_config->name().empty() )
+            className = static_cast<std::string>(m_config->name()) + '.' + className;
+
+        normalizeNamespaceMarkers(className);
+
+        writeSection( className, "", rootSection, stats.testInfo->okToFail() );
+    }
+
+    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;
+
+        if( sectionNode.hasAnyAssertions()
+           || !sectionNode.stdOut.empty()
+           || !sectionNode.stdErr.empty() ) {
+            XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+            if( className.empty() ) {
+                xml.writeAttribute( "classname"_sr, name );
+                xml.writeAttribute( "name"_sr, "root"_sr );
+            }
+            else {
+                xml.writeAttribute( "classname"_sr, className );
+                xml.writeAttribute( "name"_sr, name );
+            }
+            xml.writeAttribute( "time"_sr, 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"_sr, "run"_sr );
+
+            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 ), XmlFormatting::Newline );
+            if( !sectionNode.stdErr.empty() )
+                xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
+        }
+        for( auto const& childNode : sectionNode.childSections )
+            if( className.empty() )
+                writeSection( name, "", *childNode, testOkToFail );
+            else
+                writeSection( className, name, *childNode, testOkToFail );
+    }
+
+    void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
+        for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) {
+            if (assertionOrBenchmark.isAssertion()) {
+                writeAssertion(assertionOrBenchmark.asAssertion());
+            }
+        }
+    }
+
+    void JunitReporter::writeAssertion( AssertionStats const& stats ) {
+        AssertionResult const& result = stats.assertionResult;
+        if( !result.isOk() ) {
+            std::string elementName;
+            switch( result.getResultType() ) {
+                case ResultWas::ThrewException:
+                case ResultWas::FatalErrorCondition:
+                    elementName = "error";
+                    break;
+                case ResultWas::ExplicitFailure:
+                case ResultWas::ExpressionFailed:
+                case ResultWas::DidntThrowException:
+                    elementName = "failure";
+                    break;
+
+                // We should never see these here:
+                case ResultWas::Info:
+                case ResultWas::Warning:
+                case ResultWas::Ok:
+                case ResultWas::Unknown:
+                case ResultWas::FailureBit:
+                case ResultWas::Exception:
+                    elementName = "internalError";
+                    break;
+            }
+
+            XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+            xml.writeAttribute( "message"_sr, result.getExpression() );
+            xml.writeAttribute( "type"_sr, 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 << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n';
+                }
+            } else {
+                rss << '\n';
+            }
+
+            if( !result.getMessage().empty() )
+                rss << result.getMessage() << '\n';
+            for( auto const& msg : stats.infoMessages )
+                if( msg.type == ResultWas::Info )
+                    rss << msg.message << '\n';
+
+            rss << "at " << result.getSourceInfo();
+            xml.writeText( rss.str(), XmlFormatting::Newline );
+        }
+    }
+
+} // end namespace Catch
+
+
+
+
+#include <ostream>
+
+namespace Catch {
+    void MultiReporter::updatePreferences(IEventListener const& reporterish) {
+        m_preferences.shouldRedirectStdOut |=
+            reporterish.getPreferences().shouldRedirectStdOut;
+        m_preferences.shouldReportAllAssertions |=
+            reporterish.getPreferences().shouldReportAllAssertions;
+    }
+
+    void MultiReporter::addListener( IEventListenerPtr&& listener ) {
+        updatePreferences(*listener);
+        m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener) );
+        ++m_insertedListeners;
+    }
+
+    void MultiReporter::addReporter( IEventListenerPtr&& reporter ) {
+        updatePreferences(*reporter);
+
+        // We will need to output the captured stdout if there are reporters
+        // that do not want it captured.
+        // We do not consider listeners, because it is generally assumed that
+        // listeners are output-transparent, even though they can ask for stdout
+        // capture to do something with it.
+        m_haveNoncapturingReporters |= !reporter->getPreferences().shouldRedirectStdOut;
+
+        // Reporters can always be placed to the back without breaking the
+        // reporting order
+        m_reporterLikes.push_back( CATCH_MOVE( reporter ) );
+    }
+
+    void MultiReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->noMatchingTestCases( unmatchedSpec );
+        }
+    }
+
+    void MultiReporter::fatalErrorEncountered( StringRef error ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->fatalErrorEncountered( error );
+        }
+    }
+
+    void MultiReporter::reportInvalidTestSpec( StringRef arg ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->reportInvalidTestSpec( arg );
+        }
+    }
+
+    void MultiReporter::benchmarkPreparing( StringRef name ) {
+        for (auto& reporterish : m_reporterLikes) {
+            reporterish->benchmarkPreparing(name);
+        }
+    }
+    void MultiReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->benchmarkStarting( benchmarkInfo );
+        }
+    }
+    void MultiReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->benchmarkEnded( benchmarkStats );
+        }
+    }
+
+    void MultiReporter::benchmarkFailed( StringRef error ) {
+        for (auto& reporterish : m_reporterLikes) {
+            reporterish->benchmarkFailed(error);
+        }
+    }
+
+    void MultiReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->testRunStarting( testRunInfo );
+        }
+    }
+
+    void MultiReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->testCaseStarting( testInfo );
+        }
+    }
+
+    void
+    MultiReporter::testCasePartialStarting( TestCaseInfo const& testInfo,
+                                                uint64_t partNumber ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->testCasePartialStarting( testInfo, partNumber );
+        }
+    }
+
+    void MultiReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->sectionStarting( sectionInfo );
+        }
+    }
+
+    void MultiReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->assertionStarting( assertionInfo );
+        }
+    }
+
+    // The return value indicates if the messages buffer should be cleared:
+    void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) {
+        const bool reportByDefault =
+            assertionStats.assertionResult.getResultType() != ResultWas::Ok ||
+            m_config->includeSuccessfulResults();
+
+        for ( auto & reporterish : m_reporterLikes ) {
+            if ( reportByDefault ||
+                 reporterish->getPreferences().shouldReportAllAssertions ) {
+                    reporterish->assertionEnded( assertionStats );
+            }
+        }
+    }
+
+    void MultiReporter::sectionEnded( SectionStats const& sectionStats ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->sectionEnded( sectionStats );
+        }
+    }
+
+    void MultiReporter::testCasePartialEnded( TestCaseStats const& testStats,
+                                                  uint64_t partNumber ) {
+        if ( m_preferences.shouldRedirectStdOut &&
+             m_haveNoncapturingReporters ) {
+            if ( !testStats.stdOut.empty() ) {
+                Catch::cout() << testStats.stdOut << std::flush;
+            }
+            if ( !testStats.stdErr.empty() ) {
+                Catch::cerr() << testStats.stdErr << std::flush;
+            }
+        }
+
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->testCasePartialEnded( testStats, partNumber );
+        }
+    }
+
+    void MultiReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->testCaseEnded( testCaseStats );
+        }
+    }
+
+    void MultiReporter::testRunEnded( TestRunStats const& testRunStats ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->testRunEnded( testRunStats );
+        }
+    }
+
+
+    void MultiReporter::skipTest( TestCaseInfo const& testInfo ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->skipTest( testInfo );
+        }
+    }
+
+    void MultiReporter::listReporters(std::vector<ReporterDescription> const& descriptions) {
+        for (auto& reporterish : m_reporterLikes) {
+            reporterish->listReporters(descriptions);
+        }
+    }
+
+    void MultiReporter::listListeners(
+        std::vector<ListenerDescription> const& descriptions ) {
+        for ( auto& reporterish : m_reporterLikes ) {
+            reporterish->listListeners( descriptions );
+        }
+    }
+
+    void MultiReporter::listTests(std::vector<TestCaseHandle> const& tests) {
+        for (auto& reporterish : m_reporterLikes) {
+            reporterish->listTests(tests);
+        }
+    }
+
+    void MultiReporter::listTags(std::vector<TagInfo> const& tags) {
+        for (auto& reporterish : m_reporterLikes) {
+            reporterish->listTags(tags);
+        }
+    }
+
+} // end namespace Catch
+
+
+
+
+
+namespace Catch {
+    namespace Detail {
+
+        void registerReporterImpl( std::string const& name,
+                                   IReporterFactoryPtr reporterPtr ) {
+            CATCH_TRY {
+                getMutableRegistryHub().registerReporter(
+                    name, CATCH_MOVE( reporterPtr ) );
+            }
+            CATCH_CATCH_ALL {
+                // Do not throw when constructing global objects, instead
+                // register the exception to be processed later
+                getMutableRegistryHub().registerStartupException();
+            }
+        }
+
+    } // namespace Detail
+} // namespace Catch
+
+
+
+
+#include <map>
+
+namespace Catch {
+
+    namespace {
+        std::string createRngSeedString(uint32_t seed) {
+            ReusableStringStream sstr;
+            sstr << "rng-seed=" << seed;
+            return sstr.str();
+        }
+    }
+
+    void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) {
+        CumulativeReporterBase::testRunStarting(testRunInfo);
+
+        xml.writeComment( createRngSeedString( m_config->rngSeed() ) );
+        xml.startElement("testExecutions");
+        xml.writeAttribute("version"_sr, '1');
+    }
+
+    void SonarQubeReporter::writeRun( TestRunNode const& runNode ) {
+        std::map<std::string, std::vector<TestCaseNode const*>> testsPerFile;
+
+        for ( auto const& child : runNode.children ) {
+            testsPerFile[child->value.testInfo->lineInfo.file].push_back(
+                child.get() );
+        }
+
+        for ( auto const& kv : testsPerFile ) {
+            writeTestFile( kv.first, kv.second );
+        }
+    }
+
+    void SonarQubeReporter::writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes) {
+        XmlWriter::ScopedElement e = xml.scopedElement("file");
+        xml.writeAttribute("path"_sr, filename);
+
+        for (auto const& child : testCaseNodes)
+            writeTestCase(*child);
+    }
+
+    void SonarQubeReporter::writeTestCase(TestCaseNode const& testCaseNode) {
+        // All test cases have exactly one section - which represents the
+        // test case itself. That section may have 0-n nested sections
+        assert(testCaseNode.children.size() == 1);
+        SectionNode const& rootSection = *testCaseNode.children.front();
+        writeSection("", rootSection, testCaseNode.value.testInfo->okToFail());
+    }
+
+    void SonarQubeReporter::writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
+        std::string name = trim(sectionNode.stats.sectionInfo.name);
+        if (!rootName.empty())
+            name = rootName + '/' + name;
+
+        if ( sectionNode.hasAnyAssertions()
+            || !sectionNode.stdOut.empty()
+            ||  !sectionNode.stdErr.empty() ) {
+            XmlWriter::ScopedElement e = xml.scopedElement("testCase");
+            xml.writeAttribute("name"_sr, name);
+            xml.writeAttribute("duration"_sr, static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
+
+            writeAssertions(sectionNode, okToFail);
+        }
+
+        for (auto const& childNode : sectionNode.childSections)
+            writeSection(name, *childNode, okToFail);
+    }
+
+    void SonarQubeReporter::writeAssertions(SectionNode const& sectionNode, bool okToFail) {
+        for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) {
+            if (assertionOrBenchmark.isAssertion()) {
+                writeAssertion(assertionOrBenchmark.asAssertion(), okToFail);
+            }
+        }
+    }
+
+    void SonarQubeReporter::writeAssertion(AssertionStats const& stats, bool okToFail) {
+        AssertionResult const& result = stats.assertionResult;
+        if (!result.isOk()) {
+            std::string elementName;
+            if (okToFail) {
+                elementName = "skipped";
+            } else {
+                switch (result.getResultType()) {
+                case ResultWas::ThrewException:
+                case ResultWas::FatalErrorCondition:
+                    elementName = "error";
+                    break;
+                case ResultWas::ExplicitFailure:
+                    elementName = "failure";
+                    break;
+                case ResultWas::ExpressionFailed:
+                    elementName = "failure";
+                    break;
+                case ResultWas::DidntThrowException:
+                    elementName = "failure";
+                    break;
+
+                    // We should never see these here:
+                case ResultWas::Info:
+                case ResultWas::Warning:
+                case ResultWas::Ok:
+                case ResultWas::Unknown:
+                case ResultWas::FailureBit:
+                case ResultWas::Exception:
+                    elementName = "internalError";
+                    break;
+                }
+            }
+
+            XmlWriter::ScopedElement e = xml.scopedElement(elementName);
+
+            ReusableStringStream messageRss;
+            messageRss << result.getTestMacroName() << '(' << result.getExpression() << ')';
+            xml.writeAttribute("message"_sr, messageRss.str());
+
+            ReusableStringStream textRss;
+            if (stats.totals.assertions.total() > 0) {
+                textRss << "FAILED:\n";
+                if (result.hasExpression()) {
+                    textRss << '\t' << result.getExpressionInMacro() << '\n';
+                }
+                if (result.hasExpandedExpression()) {
+                    textRss << "with expansion:\n\t" << result.getExpandedExpression() << '\n';
+                }
+            }
+
+            if (!result.getMessage().empty())
+                textRss << result.getMessage() << '\n';
+
+            for (auto const& msg : stats.infoMessages)
+                if (msg.type == ResultWas::Info)
+                    textRss << msg.message << '\n';
+
+            textRss << "at " << result.getSourceInfo();
+            xml.writeText(textRss.str(), XmlFormatting::Newline);
+        }
+    }
+
+} // end namespace Catch
+
+
+
+namespace Catch {
+
+    StreamingReporterBase::~StreamingReporterBase() = default;
+
+    void
+    StreamingReporterBase::testRunStarting( TestRunInfo const& _testRunInfo ) {
+        currentTestRunInfo = _testRunInfo;
+    }
+
+    void StreamingReporterBase::testRunEnded( TestRunStats const& ) {
+        currentTestCaseInfo = nullptr;
+    }
+
+} // end namespace Catch
+
+
+
+#include <algorithm>
+#include <iterator>
+#include <ostream>
+
+namespace Catch {
+
+    namespace {
+        // Yes, this has to be outside the class and namespaced by naming.
+        // Making older compiler happy is hard.
+        static constexpr StringRef tapFailedString = "not ok"_sr;
+        static constexpr StringRef tapPassedString = "ok"_sr;
+        static constexpr Colour::Code tapDimColour = Colour::FileName;
+
+        class TapAssertionPrinter {
+        public:
+            TapAssertionPrinter& operator= (TapAssertionPrinter const&) = delete;
+            TapAssertionPrinter(TapAssertionPrinter const&) = delete;
+            TapAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter, ColourImpl* colour_)
+                : stream(_stream)
+                , result(_stats.assertionResult)
+                , messages(_stats.infoMessages)
+                , itMessage(_stats.infoMessages.begin())
+                , printInfoMessages(true)
+                , counter(_counter)
+                , colourImpl( colour_ ) {}
+
+            void print() {
+                itMessage = messages.begin();
+
+                switch (result.getResultType()) {
+                case ResultWas::Ok:
+                    printResultType(tapPassedString);
+                    printOriginalExpression();
+                    printReconstructedExpression();
+                    if (!result.hasExpression())
+                        printRemainingMessages(Colour::None);
+                    else
+                        printRemainingMessages();
+                    break;
+                case ResultWas::ExpressionFailed:
+                    if (result.isOk()) {
+                        printResultType(tapPassedString);
+                    } else {
+                        printResultType(tapFailedString);
+                    }
+                    printOriginalExpression();
+                    printReconstructedExpression();
+                    if (result.isOk()) {
+                        printIssue(" # TODO");
+                    }
+                    printRemainingMessages();
+                    break;
+                case ResultWas::ThrewException:
+                    printResultType(tapFailedString);
+                    printIssue("unexpected exception with message:"_sr);
+                    printMessage();
+                    printExpressionWas();
+                    printRemainingMessages();
+                    break;
+                case ResultWas::FatalErrorCondition:
+                    printResultType(tapFailedString);
+                    printIssue("fatal error condition with message:"_sr);
+                    printMessage();
+                    printExpressionWas();
+                    printRemainingMessages();
+                    break;
+                case ResultWas::DidntThrowException:
+                    printResultType(tapFailedString);
+                    printIssue("expected exception, got none"_sr);
+                    printExpressionWas();
+                    printRemainingMessages();
+                    break;
+                case ResultWas::Info:
+                    printResultType("info"_sr);
+                    printMessage();
+                    printRemainingMessages();
+                    break;
+                case ResultWas::Warning:
+                    printResultType("warning"_sr);
+                    printMessage();
+                    printRemainingMessages();
+                    break;
+                case ResultWas::ExplicitFailure:
+                    printResultType(tapFailedString);
+                    printIssue("explicitly"_sr);
+                    printRemainingMessages(Colour::None);
+                    break;
+                    // These cases are here to prevent compiler warnings
+                case ResultWas::Unknown:
+                case ResultWas::FailureBit:
+                case ResultWas::Exception:
+                    printResultType("** internal error **"_sr);
+                    break;
+                }
+            }
+
+        private:
+            void printResultType(StringRef passOrFail) const {
+                if (!passOrFail.empty()) {
+                    stream << passOrFail << ' ' << counter << " -";
+                }
+            }
+
+            void printIssue(StringRef issue) const {
+                stream << ' ' << issue;
+            }
+
+            void printExpressionWas() {
+                if (result.hasExpression()) {
+                    stream << ';';
+                    stream << colourImpl->guardColour( tapDimColour )
+                           << " expression was:";
+                    printOriginalExpression();
+                }
+            }
+
+            void printOriginalExpression() const {
+                if (result.hasExpression()) {
+                    stream << ' ' << result.getExpression();
+                }
+            }
+
+            void printReconstructedExpression() const {
+                if (result.hasExpandedExpression()) {
+                    stream << colourImpl->guardColour( tapDimColour ) << " for: ";
+
+                    std::string expr = result.getExpandedExpression();
+                    std::replace(expr.begin(), expr.end(), '\n', ' ');
+                    stream << expr;
+                }
+            }
+
+            void printMessage() {
+                if (itMessage != messages.end()) {
+                    stream << " '" << itMessage->message << '\'';
+                    ++itMessage;
+                }
+            }
+
+            void printRemainingMessages(Colour::Code colour = tapDimColour) {
+                if (itMessage == messages.end()) {
+                    return;
+                }
+
+                // using messages.end() directly (or auto) yields compilation error:
+                std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+                const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+
+                stream << colourImpl->guardColour( colour ) << " with "
+                       << pluralise( N, "message"_sr ) << ':';
+
+                for (; itMessage != itEnd; ) {
+                    // If this assertion is a warning ignore any INFO messages
+                    if (printInfoMessages || itMessage->type != ResultWas::Info) {
+                        stream << " '" << itMessage->message << '\'';
+                        if (++itMessage != itEnd) {
+                            stream << colourImpl->guardColour(tapDimColour) << " and";
+                        }
+                    }
+                }
+            }
+
+        private:
+            std::ostream& stream;
+            AssertionResult const& result;
+            std::vector<MessageInfo> messages;
+            std::vector<MessageInfo>::const_iterator itMessage;
+            bool printInfoMessages;
+            std::size_t counter;
+            ColourImpl* colourImpl;
+        };
+
+    } // End anonymous namespace
+
+    void TAPReporter::testRunStarting( TestRunInfo const& ) {
+        m_stream << "# rng-seed: " << m_config->rngSeed() << '\n';
+    }
+
+    void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+        m_stream << "# No test cases matched '" << unmatchedSpec << "'\n";
+    }
+
+    void TAPReporter::assertionEnded(AssertionStats const& _assertionStats) {
+        ++counter;
+
+        m_stream << "# " << currentTestCaseInfo->name << '\n';
+        TapAssertionPrinter printer(m_stream, _assertionStats, counter, m_colour.get());
+        printer.print();
+
+        m_stream << '\n' << std::flush;
+    }
+
+    void TAPReporter::testRunEnded(TestRunStats const& _testRunStats) {
+        m_stream << "1.." << _testRunStats.totals.assertions.total();
+        if (_testRunStats.totals.testCases.total() == 0) {
+            m_stream << " # Skipped: No tests ran.";
+        }
+        m_stream << "\n\n" << std::flush;
+        StreamingReporterBase::testRunEnded(_testRunStats);
+    }
+
+
+
+
+} // end namespace Catch
+
+
+
+
+#include <cassert>
+#include <ostream>
+
+namespace Catch {
+
+    namespace {
+        // if string has a : in first line will set indent to follow it on
+        // subsequent lines
+        void printHeaderString(std::ostream& os, std::string const& _string, std::size_t indent = 0) {
+            std::size_t i = _string.find(": ");
+            if (i != std::string::npos)
+                i += 2;
+            else
+                i = 0;
+            os << TextFlow::Column(_string)
+                  .indent(indent + i)
+                  .initialIndent(indent) << '\n';
+        }
+
+        std::string escape(StringRef str) {
+            std::string escaped = static_cast<std::string>(str);
+            replaceInPlace(escaped, "|", "||");
+            replaceInPlace(escaped, "'", "|'");
+            replaceInPlace(escaped, "\n", "|n");
+            replaceInPlace(escaped, "\r", "|r");
+            replaceInPlace(escaped, "[", "|[");
+            replaceInPlace(escaped, "]", "|]");
+            return escaped;
+        }
+    } // end anonymous namespace
+
+
+    TeamCityReporter::~TeamCityReporter() {}
+
+    void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) {
+        m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name )
+               << "']\n";
+    }
+
+    void TeamCityReporter::testRunEnded( TestRunStats const& runStats ) {
+        m_stream << "##teamcity[testSuiteFinished name='"
+               << escape( runStats.runInfo.name ) << "']\n";
+    }
+
+    void TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) {
+        AssertionResult const& result = assertionStats.assertionResult;
+        if (!result.isOk()) {
+
+            ReusableStringStream msg;
+            if (!m_headerPrintedForThisSection)
+                printSectionHeader(msg.get());
+            m_headerPrintedForThisSection = true;
+
+            msg << result.getSourceInfo() << '\n';
+
+            switch (result.getResultType()) {
+            case ResultWas::ExpressionFailed:
+                msg << "expression failed";
+                break;
+            case ResultWas::ThrewException:
+                msg << "unexpected exception";
+                break;
+            case ResultWas::FatalErrorCondition:
+                msg << "fatal error condition";
+                break;
+            case ResultWas::DidntThrowException:
+                msg << "no exception was thrown where one was expected";
+                break;
+            case ResultWas::ExplicitFailure:
+                msg << "explicit failure";
+                break;
+
+                // We shouldn't get here because of the isOk() test
+            case ResultWas::Ok:
+            case ResultWas::Info:
+            case ResultWas::Warning:
+                CATCH_ERROR("Internal error in TeamCity reporter");
+                // These cases are here to prevent compiler warnings
+            case ResultWas::Unknown:
+            case ResultWas::FailureBit:
+            case ResultWas::Exception:
+                CATCH_ERROR("Not implemented");
+            }
+            if (assertionStats.infoMessages.size() == 1)
+                msg << " with message:";
+            if (assertionStats.infoMessages.size() > 1)
+                msg << " with messages:";
+            for (auto const& messageInfo : assertionStats.infoMessages)
+                msg << "\n  \"" << messageInfo.message << '"';
+
+
+            if (result.hasExpression()) {
+                msg <<
+                    "\n  " << result.getExpressionInMacro() << "\n"
+                    "with expansion:\n"
+                    "  " << result.getExpandedExpression() << '\n';
+            }
+
+            if (currentTestCaseInfo->okToFail()) {
+                msg << "- failure ignore as test marked as 'ok to fail'\n";
+                m_stream << "##teamcity[testIgnored"
+                    << " name='" << escape(currentTestCaseInfo->name) << '\''
+                    << " message='" << escape(msg.str()) << '\''
+                    << "]\n";
+            } else {
+                m_stream << "##teamcity[testFailed"
+                    << " name='" << escape(currentTestCaseInfo->name) << '\''
+                    << " message='" << escape(msg.str()) << '\''
+                    << "]\n";
+            }
+        }
+        m_stream.flush();
+    }
+
+    void TeamCityReporter::testCaseStarting(TestCaseInfo const& testInfo) {
+        m_testTimer.start();
+        StreamingReporterBase::testCaseStarting(testInfo);
+        m_stream << "##teamcity[testStarted name='"
+            << escape(testInfo.name) << "']\n";
+        m_stream.flush();
+    }
+
+    void TeamCityReporter::testCaseEnded(TestCaseStats const& testCaseStats) {
+        StreamingReporterBase::testCaseEnded(testCaseStats);
+        auto const& testCaseInfo = *testCaseStats.testInfo;
+        if (!testCaseStats.stdOut.empty())
+            m_stream << "##teamcity[testStdOut name='"
+            << escape(testCaseInfo.name)
+            << "' out='" << escape(testCaseStats.stdOut) << "']\n";
+        if (!testCaseStats.stdErr.empty())
+            m_stream << "##teamcity[testStdErr name='"
+            << escape(testCaseInfo.name)
+            << "' out='" << escape(testCaseStats.stdErr) << "']\n";
+        m_stream << "##teamcity[testFinished name='"
+            << escape(testCaseInfo.name) << "' duration='"
+            << m_testTimer.getElapsedMilliseconds() << "']\n";
+        m_stream.flush();
+    }
+
+    void TeamCityReporter::printSectionHeader(std::ostream& os) {
+        assert(!m_sectionStack.empty());
+
+        if (m_sectionStack.size() > 1) {
+            os << lineOfChars('-') << '\n';
+
+            std::vector<SectionInfo>::const_iterator
+                it = m_sectionStack.begin() + 1, // Skip first section (test case)
+                itEnd = m_sectionStack.end();
+            for (; it != itEnd; ++it)
+                printHeaderString(os, it->name);
+            os << lineOfChars('-') << '\n';
+        }
+
+        SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
+
+        os << lineInfo << '\n';
+        os << lineOfChars('.') << "\n\n";
+    }
+
+} // end namespace Catch
+
+
+
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+                              // Note that 4062 (not all labels are handled
+                              // and default is missing) is enabled
+#endif
+
+namespace Catch {
+    XmlReporter::XmlReporter( ReporterConfig&& _config )
+    :   StreamingReporterBase( CATCH_MOVE(_config) ),
+        m_xml(m_stream)
+    {
+        m_preferences.shouldRedirectStdOut = true;
+        m_preferences.shouldReportAllAssertions = true;
+    }
+
+    XmlReporter::~XmlReporter() = default;
+
+    std::string XmlReporter::getDescription() {
+        return "Reports test results as an XML document";
+    }
+
+    std::string XmlReporter::getStylesheetRef() const {
+        return std::string();
+    }
+
+    void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+        m_xml
+            .writeAttribute( "filename"_sr, sourceInfo.file )
+            .writeAttribute( "line"_sr, sourceInfo.line );
+    }
+
+    void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
+        StreamingReporterBase::testRunStarting( testInfo );
+        std::string stylesheetRef = getStylesheetRef();
+        if( !stylesheetRef.empty() )
+            m_xml.writeStylesheetRef( stylesheetRef );
+        m_xml.startElement("Catch2TestRun")
+             .writeAttribute("name"_sr, m_config->name())
+             .writeAttribute("rng-seed"_sr, m_config->rngSeed())
+             .writeAttribute("catch2-version"_sr, libraryVersion());
+        if (m_config->testSpec().hasFilters())
+            m_xml.writeAttribute( "filters"_sr, serializeFilters( m_config->getTestsOrTags() ) );
+    }
+
+    void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        StreamingReporterBase::testCaseStarting(testInfo);
+        m_xml.startElement( "TestCase" )
+            .writeAttribute( "name"_sr, trim( testInfo.name ) )
+            .writeAttribute( "tags"_sr, testInfo.tagsAsString() );
+
+        writeSourceInfo( testInfo.lineInfo );
+
+        if ( m_config->showDurations() == ShowDurations::Always )
+            m_testCaseTimer.start();
+        m_xml.ensureTagClosed();
+    }
+
+    void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+        StreamingReporterBase::sectionStarting( sectionInfo );
+        if( m_sectionDepth++ > 0 ) {
+            m_xml.startElement( "Section" )
+                .writeAttribute( "name"_sr, trim( sectionInfo.name ) );
+            writeSourceInfo( sectionInfo.lineInfo );
+            m_xml.ensureTagClosed();
+        }
+    }
+
+    void XmlReporter::assertionStarting( AssertionInfo const& ) { }
+
+    void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
+
+        AssertionResult const& result = assertionStats.assertionResult;
+
+        bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+        if( includeResults || result.getResultType() == ResultWas::Warning ) {
+            // Print any info messages in <Info> tags.
+            for( auto const& msg : assertionStats.infoMessages ) {
+                if( msg.type == ResultWas::Info && includeResults ) {
+                    m_xml.scopedElement( "Info" )
+                            .writeText( msg.message );
+                } else if ( msg.type == ResultWas::Warning ) {
+                    m_xml.scopedElement( "Warning" )
+                            .writeText( msg.message );
+                }
+            }
+        }
+
+        // Drop out if result was successful but we're not printing them.
+        if( !includeResults && result.getResultType() != ResultWas::Warning )
+            return;
+
+
+        // Print the expression if there is one.
+        if( result.hasExpression() ) {
+            m_xml.startElement( "Expression" )
+                .writeAttribute( "success"_sr, result.succeeded() )
+                .writeAttribute( "type"_sr, result.getTestMacroName() );
+
+            writeSourceInfo( result.getSourceInfo() );
+
+            m_xml.scopedElement( "Original" )
+                .writeText( result.getExpression() );
+            m_xml.scopedElement( "Expanded" )
+                .writeText( result.getExpandedExpression() );
+        }
+
+        // And... Print a result applicable to each result type.
+        switch( result.getResultType() ) {
+            case ResultWas::ThrewException:
+                m_xml.startElement( "Exception" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            case ResultWas::FatalErrorCondition:
+                m_xml.startElement( "FatalErrorCondition" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            case ResultWas::Info:
+                m_xml.scopedElement( "Info" )
+                     .writeText( result.getMessage() );
+                break;
+            case ResultWas::Warning:
+                // Warning will already have been written
+                break;
+            case ResultWas::ExplicitFailure:
+                m_xml.startElement( "Failure" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            default:
+                break;
+        }
+
+        if( result.hasExpression() )
+            m_xml.endElement();
+    }
+
+    void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
+        StreamingReporterBase::sectionEnded( sectionStats );
+        if( --m_sectionDepth > 0 ) {
+            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+            e.writeAttribute( "successes"_sr, sectionStats.assertions.passed );
+            e.writeAttribute( "failures"_sr, sectionStats.assertions.failed );
+            e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds );
+
+            m_xml.endElement();
+        }
+    }
+
+    void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        StreamingReporterBase::testCaseEnded( testCaseStats );
+        XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+        e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() );
+
+        if ( m_config->showDurations() == ShowDurations::Always )
+            e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() );
+
+        if( !testCaseStats.stdOut.empty() )
+            m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
+        if( !testCaseStats.stdErr.empty() )
+            m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
+
+        m_xml.endElement();
+    }
+
+    void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
+        StreamingReporterBase::testRunEnded( testRunStats );
+        m_xml.scopedElement( "OverallResults" )
+            .writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed )
+            .writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed )
+            .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk );
+        m_xml.scopedElement( "OverallResultsCases")
+            .writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed )
+            .writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed )
+            .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk );
+        m_xml.endElement();
+    }
+
+    void XmlReporter::benchmarkPreparing( StringRef name ) {
+        m_xml.startElement("BenchmarkResults")
+             .writeAttribute("name"_sr, name);
+    }
+
+    void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
+        m_xml.writeAttribute("samples"_sr, info.samples)
+            .writeAttribute("resamples"_sr, info.resamples)
+            .writeAttribute("iterations"_sr, info.iterations)
+            .writeAttribute("clockResolution"_sr, info.clockResolution)
+            .writeAttribute("estimatedDuration"_sr, info.estimatedDuration)
+            .writeComment("All values in nano seconds"_sr);
+    }
+
+    void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
+        m_xml.startElement("mean")
+            .writeAttribute("value"_sr, benchmarkStats.mean.point.count())
+            .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count())
+            .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count())
+            .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval);
+        m_xml.endElement();
+        m_xml.startElement("standardDeviation")
+            .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count())
+            .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count())
+            .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count())
+            .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval);
+        m_xml.endElement();
+        m_xml.startElement("outliers")
+            .writeAttribute("variance"_sr, benchmarkStats.outlierVariance)
+            .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild)
+            .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe)
+            .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild)
+            .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe);
+        m_xml.endElement();
+        m_xml.endElement();
+    }
+
+    void XmlReporter::benchmarkFailed(StringRef error) {
+        m_xml.scopedElement("failed").
+            writeAttribute("message"_sr, error);
+        m_xml.endElement();
+    }
+
+    void XmlReporter::listReporters(std::vector<ReporterDescription> const& descriptions) {
+        auto outerTag = m_xml.scopedElement("AvailableReporters");
+        for (auto const& reporter : descriptions) {
+            auto inner = m_xml.scopedElement("Reporter");
+            m_xml.startElement("Name", XmlFormatting::Indent)
+                 .writeText(reporter.name, XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+            m_xml.startElement("Description", XmlFormatting::Indent)
+                 .writeText(reporter.description, XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+        }
+    }
+
+    void XmlReporter::listListeners(std::vector<ListenerDescription> const& descriptions) {
+        auto outerTag = m_xml.scopedElement( "RegisteredListeners" );
+        for ( auto const& listener : descriptions ) {
+            auto inner = m_xml.scopedElement( "Listener" );
+            m_xml.startElement( "Name", XmlFormatting::Indent )
+                .writeText( listener.name, XmlFormatting::None )
+                .endElement( XmlFormatting::Newline );
+            m_xml.startElement( "Description", XmlFormatting::Indent )
+                .writeText( listener.description, XmlFormatting::None )
+                .endElement( XmlFormatting::Newline );
+        }
+    }
+
+    void XmlReporter::listTests(std::vector<TestCaseHandle> const& tests) {
+        auto outerTag = m_xml.scopedElement("MatchingTests");
+        for (auto const& test : tests) {
+            auto innerTag = m_xml.scopedElement("TestCase");
+            auto const& testInfo = test.getTestCaseInfo();
+            m_xml.startElement("Name", XmlFormatting::Indent)
+                 .writeText(testInfo.name, XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+            m_xml.startElement("ClassName", XmlFormatting::Indent)
+                 .writeText(testInfo.className, XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+            m_xml.startElement("Tags", XmlFormatting::Indent)
+                 .writeText(testInfo.tagsAsString(), XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+
+            auto sourceTag = m_xml.scopedElement("SourceInfo");
+            m_xml.startElement("File", XmlFormatting::Indent)
+                 .writeText(testInfo.lineInfo.file, XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+            m_xml.startElement("Line", XmlFormatting::Indent)
+                 .writeText(std::to_string(testInfo.lineInfo.line), XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+        }
+    }
+
+    void XmlReporter::listTags(std::vector<TagInfo> const& tags) {
+        auto outerTag = m_xml.scopedElement("TagsFromMatchingTests");
+        for (auto const& tag : tags) {
+            auto innerTag = m_xml.scopedElement("Tag");
+            m_xml.startElement("Count", XmlFormatting::Indent)
+                 .writeText(std::to_string(tag.count), XmlFormatting::None)
+                 .endElement(XmlFormatting::Newline);
+            auto aliasTag = m_xml.scopedElement("Aliases");
+            for (auto const& alias : tag.spellings) {
+                m_xml.startElement("Alias", XmlFormatting::Indent)
+                     .writeText(alias, XmlFormatting::None)
+                     .endElement(XmlFormatting::Newline);
+            }
+        }
+    }
+
+} // end namespace Catch
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif

+ 12260 - 0
lib/Catch2/extras/catch_amalgamated.hpp

@@ -0,0 +1,12260 @@
+//              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
+
+//  Catch v3.1.1
+//  Generated: 2022-10-17 18:47:20.510385
+//  ----------------------------------------------------------
+//  This file is an amalgamation of multiple different files.
+//  You probably shouldn't edit it directly.
+//  ----------------------------------------------------------
+#ifndef CATCH_AMALGAMATED_HPP_INCLUDED
+#define CATCH_AMALGAMATED_HPP_INCLUDED
+
+
+/** \file
+ * This is a convenience header for Catch2. It includes **all** of Catch2 headers.
+ *
+ * Generally the Catch2 users should use specific includes they need,
+ * but this header can be used instead for ease-of-experimentation, or
+ * just plain convenience, at the cost of (significantly) increased
+ * compilation times.
+ *
+ * When a new header is added to either the top level folder, or to the
+ * corresponding internal subfolder, it should be added here. Headers
+ * added to the various subparts (e.g. matchers, generators, etc...),
+ * should go their respective catch-all headers.
+ */
+
+#ifndef CATCH_ALL_HPP_INCLUDED
+#define CATCH_ALL_HPP_INCLUDED
+
+
+
+/** \file
+ * This is a convenience header for Catch2's benchmarking. It includes
+ * **all** of Catch2 headers related to benchmarking.
+ *
+ * Generally the Catch2 users should use specific includes they need,
+ * but this header can be used instead for ease-of-experimentation, or
+ * just plain convenience, at the cost of (significantly) increased
+ * compilation times.
+ *
+ * When a new header is added to either the `benchmark` folder, or to
+ * the corresponding internal (detail) subfolder, it should be added here.
+ */
+
+#ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED
+#define CATCH_BENCHMARK_ALL_HPP_INCLUDED
+
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_BENCHMARK_HPP_INCLUDED
+#define CATCH_BENCHMARK_HPP_INCLUDED
+
+
+
+#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED
+#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED
+
+
+
+#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED
+#define CATCH_NONCOPYABLE_HPP_INCLUDED
+
+namespace Catch {
+    namespace Detail {
+
+        //! Deriving classes become noncopyable and nonmovable
+        class NonCopyable {
+            NonCopyable( NonCopyable const& ) = delete;
+            NonCopyable( NonCopyable&& ) = delete;
+            NonCopyable& operator=( NonCopyable const& ) = delete;
+            NonCopyable& operator=( NonCopyable&& ) = delete;
+
+        protected:
+            NonCopyable() noexcept = default;
+        };
+
+    } // namespace Detail
+} // namespace Catch
+
+#endif // CATCH_NONCOPYABLE_HPP_INCLUDED
+
+
+#ifndef CATCH_STRINGREF_HPP_INCLUDED
+#define CATCH_STRINGREF_HPP_INCLUDED
+
+#include <cstddef>
+#include <string>
+#include <iosfwd>
+#include <cassert>
+
+namespace Catch {
+
+    /// A non-owning string class (similar to the forthcoming std::string_view)
+    /// Note that, because a StringRef may be a substring of another string,
+    /// it may not be null terminated.
+    class StringRef {
+    public:
+        using size_type = std::size_t;
+        using const_iterator = const char*;
+
+    private:
+        static constexpr char const* const s_empty = "";
+
+        char const* m_start = s_empty;
+        size_type m_size = 0;
+
+    public: // construction
+        constexpr StringRef() noexcept = default;
+
+        StringRef( char const* rawChars ) noexcept;
+
+        constexpr StringRef( char const* rawChars, size_type size ) noexcept
+        :   m_start( rawChars ),
+            m_size( size )
+        {}
+
+        StringRef( std::string const& stdString ) noexcept
+        :   m_start( stdString.c_str() ),
+            m_size( stdString.size() )
+        {}
+
+        explicit operator std::string() const {
+            return std::string(m_start, m_size);
+        }
+
+    public: // operators
+        auto operator == ( StringRef other ) const noexcept -> bool;
+        auto operator != (StringRef other) const noexcept -> bool {
+            return !(*this == other);
+        }
+
+        constexpr auto operator[] ( size_type index ) const noexcept -> char {
+            assert(index < m_size);
+            return m_start[index];
+        }
+
+        bool operator<(StringRef rhs) const noexcept;
+
+    public: // named queries
+        constexpr auto empty() const noexcept -> bool {
+            return m_size == 0;
+        }
+        constexpr auto size() const noexcept -> size_type {
+            return m_size;
+        }
+
+        // Returns a substring of [start, start + length).
+        // If start + length > size(), then the substring is [start, start + size()).
+        // If start > size(), then the substring is empty.
+        constexpr StringRef substr(size_type start, size_type length) const noexcept {
+            if (start < m_size) {
+                const auto shortened_size = m_size - start;
+                return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length);
+            } else {
+                return StringRef();
+            }
+        }
+
+        // Returns the current start pointer. May not be null-terminated.
+        constexpr char const* data() const noexcept {
+            return m_start;
+        }
+
+        constexpr const_iterator begin() const { return m_start; }
+        constexpr const_iterator end() const { return m_start + m_size; }
+
+
+        friend std::string& operator += (std::string& lhs, StringRef sr);
+        friend std::ostream& operator << (std::ostream& os, StringRef sr);
+        friend std::string operator+(StringRef lhs, StringRef rhs);
+
+        /**
+         * Provides a three-way comparison with rhs
+         *
+         * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive
+         * number if lhs > rhs
+         */
+        int compare( StringRef rhs ) const;
+    };
+
+
+    constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+        return StringRef( rawChars, size );
+    }
+} // namespace Catch
+
+constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+    return Catch::StringRef( rawChars, size );
+}
+
+#endif // CATCH_STRINGREF_HPP_INCLUDED
+
+#include <chrono>
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    enum class Verbosity {
+        Quiet = 0,
+        Normal,
+        High
+    };
+
+    struct WarnAbout { enum What {
+        Nothing = 0x00,
+        //! A test case or leaf section did not run any assertions
+        NoAssertions = 0x01,
+        //! A command line test spec matched no test cases
+        UnmatchedTestSpec = 0x02,
+    }; };
+
+    enum class ShowDurations {
+        DefaultForReporter,
+        Always,
+        Never
+    };
+    enum class TestRunOrder {
+        Declared,
+        LexicographicallySorted,
+        Randomized
+    };
+    enum class ColourMode : std::uint8_t {
+        //! Let Catch2 pick implementation based on platform detection
+        PlatformDefault,
+        //! Use ANSI colour code escapes
+        ANSI,
+        //! Use Win32 console colour API
+        Win32,
+        //! Don't use any colour
+        None
+    };
+    struct WaitForKeypress { enum When {
+        Never,
+        BeforeStart = 1,
+        BeforeExit = 2,
+        BeforeStartAndExit = BeforeStart | BeforeExit
+    }; };
+
+    class TestSpec;
+    class IStream;
+
+    class IConfig : public Detail::NonCopyable {
+    public:
+        virtual ~IConfig();
+
+        virtual bool allowThrows() const = 0;
+        virtual StringRef name() const = 0;
+        virtual bool includeSuccessfulResults() const = 0;
+        virtual bool shouldDebugBreak() const = 0;
+        virtual bool warnAboutMissingAssertions() const = 0;
+        virtual bool warnAboutUnmatchedTestSpecs() const = 0;
+        virtual bool zeroTestsCountAsSuccess() const = 0;
+        virtual int abortAfter() const = 0;
+        virtual bool showInvisibles() const = 0;
+        virtual ShowDurations 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 TestRunOrder runOrder() const = 0;
+        virtual uint32_t rngSeed() const = 0;
+        virtual unsigned int shardCount() const = 0;
+        virtual unsigned int shardIndex() const = 0;
+        virtual ColourMode defaultColourMode() const = 0;
+        virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+        virtual Verbosity verbosity() const = 0;
+
+        virtual bool skipBenchmarks() const = 0;
+        virtual bool benchmarkNoAnalysis() const = 0;
+        virtual unsigned int benchmarkSamples() const = 0;
+        virtual double benchmarkConfidenceInterval() const = 0;
+        virtual unsigned int benchmarkResamples() const = 0;
+        virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
+    };
+}
+
+#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED
+
+
+#ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+#define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+
+
+#ifndef CATCH_PLATFORM_HPP_INCLUDED
+#define CATCH_PLATFORM_HPP_INCLUDED
+
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
+#ifdef __APPLE__
+#  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
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+#  define CATCH_PLATFORM_WINDOWS
+
+#  if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP )
+#      define CATCH_PLATFORM_WINDOWS_UWP
+#  endif
+#endif
+
+#endif // CATCH_PLATFORM_HPP_INCLUDED
+
+#ifdef __cplusplus
+
+#  if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
+#    define CATCH_CPP14_OR_GREATER
+#  endif
+
+#  if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+#    define CATCH_CPP17_OR_GREATER
+#  endif
+
+#endif
+
+// 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" )
+
+// This only works on GCC 9+. so we have to also add a global suppression of Wparentheses
+// for older versions of GCC.
+#    define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+         _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+
+#    define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+         _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" )
+
+#    define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
+         _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" )
+
+#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
+
+#endif
+
+#if defined(__CUDACC__) && !defined(__clang__)
+#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" )
+#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "nv_diagnostic pop" )
+#    define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" )
+#endif
+
+// clang-cl defines _MSC_VER as well as __clang__, which could cause the
+// start/stop internal suppression macros to be double defined.
+#if defined(__clang__) && !defined(_MSC_VER)
+
+#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
+#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "clang diagnostic pop" )
+
+#endif // __clang__ && !_MSC_VER
+
+#if defined(__clang__)
+
+// 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")
+// ```
+//
+// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which
+// results in calls to the immediately evaluated lambda expressions to be
+// reported as unevaluated lambdas.
+// https://developer.nvidia.com/nvidia_bug/3321845.
+//
+// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
+#  if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER )
+#    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_VARIABLE_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__
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Assume that non-Windows platforms support posix signals by default
+#if !defined(CATCH_PLATFORM_WINDOWS)
+    #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
+    #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#endif
+
+#ifdef __OS400__
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Android somehow still does not support std::to_string
+#if defined(__ANDROID__)
+#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Not all Windows environments support SEH properly
+#if defined(__MINGW32__)
+#    define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// PS4
+#if defined(__ORBIS__)
+#    define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+#   define _BSD_SOURCE
+// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+           && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+
+#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+
+# endif
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#if defined(_MSC_VER)
+
+#  define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
+#  define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  __pragma( warning(pop) )
+
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+#  if defined(CATCH_PLATFORM_WINDOWS_UWP)
+#    define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
+#  else
+#    define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+#  endif
+
+// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
+// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
+// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
+#  if !defined(__clang__) // Handle Clang masquerading for msvc
+#    if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+#      define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#    endif // MSVC_TRADITIONAL
+#  endif // __clang__
+
+#endif // _MSC_VER
+
+#if defined(_REENTRANT) || defined(_MSC_VER)
+// Enable async processing, as -pthread is specified or no additional linking is required
+# define CATCH_INTERNAL_CONFIG_USE_ASYNC
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if we are compiled with -fno-exceptions or equivalent
+#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
+#  define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Embarcadero C++Build
+#if defined(__BORLANDC__)
+    #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+
+// RTX is a special version of Windows that is real time.
+// This means that it is detected as Windows, but does not provide
+// the same set of capabilities as real Windows does.
+#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
+    #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+    #define CATCH_INTERNAL_CONFIG_NO_ASYNC
+    #define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
+#endif
+
+#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
+#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Various stdlib support checks that require __has_include
+#if defined(__has_include)
+  // Check if string_view is available and usable
+  #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
+  #    define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+  #endif
+
+  // Check if optional is available and usable
+  #  if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+  #    define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
+  #  endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
+
+  // Check if byte is available and usable
+  #  if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
+  #    include <cstddef>
+  #    if 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_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
+#   define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#   define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
+#    define CATCH_CONFIG_CPP11_TO_STRING
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#  define CATCH_CONFIG_CPP17_OPTIONAL
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+#  define CATCH_CONFIG_CPP17_STRING_VIEW
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
+#  define CATCH_CONFIG_CPP17_VARIANT
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+#  define CATCH_CONFIG_CPP17_BYTE
+#endif
+
+
+#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+#  define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
+#  define CATCH_CONFIG_NEW_CAPTURE
+#endif
+
+#if !defined( CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED ) && \
+    !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) &&          \
+    !defined( CATCH_CONFIG_NO_DISABLE_EXCEPTIONS )
+#  define CATCH_CONFIG_DISABLE_EXCEPTIONS
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+#  define CATCH_CONFIG_POLYFILL_ISNAN
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC)  && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
+#  define CATCH_CONFIG_USE_ASYNC
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_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
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
+#endif
+
+// The goal of this macro is to avoid evaluation of the arguments, but
+// still have the compiler warn on problems inside...
+#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
+#   define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
+#endif
+
+#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
+#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#elif defined(__clang__) && (__clang_major__ < 5)
+#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#define CATCH_TRY if ((true))
+#define CATCH_CATCH_ALL if ((false))
+#define CATCH_CATCH_ANON(type) if ((false))
+#else
+#define CATCH_TRY try
+#define CATCH_CATCH_ALL catch (...)
+#define CATCH_CATCH_ANON(type) catch (type)
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
+#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#endif
+
+#if defined( CATCH_PLATFORM_WINDOWS ) &&       \
+    !defined( CATCH_CONFIG_COLOUR_WIN32 ) && \
+    !defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \
+    !defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 )
+#    define CATCH_CONFIG_COLOUR_WIN32
+#endif
+
+#if defined( CATCH_CONFIG_SHARED_LIBRARY ) && defined( _MSC_VER ) && \
+    !defined( CATCH_CONFIG_STATIC )
+#    ifdef Catch2_EXPORTS
+#        define CATCH_EXPORT //__declspec( dllexport ) // not needed
+#    else
+#        define CATCH_EXPORT __declspec( dllimport )
+#    endif
+#else
+#    define CATCH_EXPORT
+#endif
+
+#endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+
+#ifndef CATCH_CONTEXT_HPP_INCLUDED
+#define CATCH_CONTEXT_HPP_INCLUDED
+
+
+namespace Catch {
+
+    class IResultCapture;
+    class IConfig;
+
+    class IContext {
+    public:
+        virtual ~IContext(); // = default
+
+        virtual IResultCapture* getResultCapture() = 0;
+        virtual IConfig const* getConfig() const = 0;
+    };
+
+    class IMutableContext : public IContext {
+    public:
+        ~IMutableContext() override; // = default
+        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+        virtual void setConfig( IConfig const* config ) = 0;
+
+    private:
+        CATCH_EXPORT static IMutableContext* currentContext;
+        friend IMutableContext& getCurrentMutableContext();
+        friend void cleanUpContext();
+        static void createContext();
+    };
+
+    inline IMutableContext& getCurrentMutableContext()
+    {
+        if( !IMutableContext::currentContext )
+            IMutableContext::createContext();
+        // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+        return *IMutableContext::currentContext;
+    }
+
+    inline IContext& getCurrentContext()
+    {
+        return getCurrentMutableContext();
+    }
+
+    void cleanUpContext();
+
+    class SimplePcg32;
+    SimplePcg32& sharedRng();
+}
+
+#endif // CATCH_CONTEXT_HPP_INCLUDED
+
+
+#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED
+#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED
+
+
+
+#ifndef CATCH_SECTION_INFO_HPP_INCLUDED
+#define CATCH_SECTION_INFO_HPP_INCLUDED
+
+
+
+#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+
+#include <type_traits>
+
+//! Replacement for std::move with better compile time performance
+#define CATCH_MOVE(...) static_cast<std::remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)
+
+//! Replacement for std::forward with better compile time performance
+#define CATCH_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
+
+#endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+
+
+#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
+#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
+
+#include <cstddef>
+#include <iosfwd>
+
+namespace Catch {
+
+    struct SourceLineInfo {
+
+        SourceLineInfo() = delete;
+        constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept:
+            file( _file ),
+            line( _line )
+        {}
+
+        bool operator == ( SourceLineInfo const& other ) const noexcept;
+        bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+        char const* file;
+        std::size_t line;
+
+        friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info);
+    };
+}
+
+#define CATCH_INTERNAL_LINEINFO \
+    ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+
+#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
+
+
+#ifndef CATCH_TOTALS_HPP_INCLUDED
+#define CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstdint>
+
+namespace Catch {
+
+    struct Counts {
+        Counts operator - ( Counts const& other ) const;
+        Counts& operator += ( Counts const& other );
+
+        std::uint64_t total() const;
+        bool allPassed() const;
+        bool allOk() const;
+
+        std::uint64_t passed = 0;
+        std::uint64_t failed = 0;
+        std::uint64_t failedButOk = 0;
+    };
+
+    struct Totals {
+
+        Totals operator - ( Totals const& other ) const;
+        Totals& operator += ( Totals const& other );
+
+        Totals delta( Totals const& prevTotals ) const;
+
+        Counts assertions;
+        Counts testCases;
+    };
+}
+
+#endif // CATCH_TOTALS_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct SectionInfo {
+        // The last argument is ignored, so that people can write
+        // SECTION("ShortName", "Proper description that is long") and
+        // still use the `-c` flag comfortably.
+        SectionInfo( SourceLineInfo const& _lineInfo, std::string _name,
+                    const char* const = nullptr ):
+            name(CATCH_MOVE(_name)),
+            lineInfo(_lineInfo)
+            {}
+
+        std::string name;
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_SECTION_INFO_HPP_INCLUDED
+
+
+#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED
+#define CATCH_ASSERTION_RESULT_HPP_INCLUDED
+
+
+
+#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED
+#define CATCH_ASSERTION_INFO_HPP_INCLUDED
+
+
+
+#ifndef CATCH_RESULT_TYPE_HPP_INCLUDED
+#define CATCH_RESULT_TYPE_HPP_INCLUDED
+
+namespace Catch {
+
+    // ResultWas::OfType enum
+    struct ResultWas { enum OfType {
+        Unknown = -1,
+        Ok = 0,
+        Info = 1,
+        Warning = 2,
+
+        FailureBit = 0x10,
+
+        ExpressionFailed = FailureBit | 1,
+        ExplicitFailure = FailureBit | 2,
+
+        Exception = 0x100 | FailureBit,
+
+        ThrewException = Exception | 1,
+        DidntThrowException = Exception | 2,
+
+        FatalErrorCondition = 0x200 | FailureBit
+
+    }; };
+
+    bool isOk( ResultWas::OfType resultType );
+    bool isJustInfo( int flags );
+
+
+    // ResultDisposition::Flags enum
+    struct ResultDisposition { enum Flags {
+        Normal = 0x01,
+
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        SuppressFail = 0x08         // Failures are reported but do not fail the test
+    }; };
+
+    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
+
+    bool shouldContinueOnFailure( int flags );
+    inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+    bool shouldSuppressFailure( int flags );
+
+} // end namespace Catch
+
+#endif // CATCH_RESULT_TYPE_HPP_INCLUDED
+
+namespace Catch {
+
+    struct AssertionInfo {
+        // AssertionInfo() = delete;
+
+        StringRef macroName;
+        SourceLineInfo lineInfo;
+        StringRef capturedExpression;
+        ResultDisposition::Flags resultDisposition;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED
+
+
+#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED
+#define CATCH_LAZY_EXPR_HPP_INCLUDED
+
+#include <iosfwd>
+
+namespace Catch {
+
+    class ITransientExpression;
+
+    class LazyExpression {
+        friend class AssertionHandler;
+        friend struct AssertionStats;
+        friend class RunContext;
+
+        ITransientExpression const* m_transientExpression = nullptr;
+        bool m_isNegated;
+    public:
+        LazyExpression( bool isNegated ):
+            m_isNegated(isNegated)
+        {}
+        LazyExpression(LazyExpression const& other) = default;
+        LazyExpression& operator = ( LazyExpression const& ) = delete;
+
+        explicit operator bool() const {
+            return m_transientExpression != nullptr;
+        }
+
+        friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
+    };
+
+} // namespace Catch
+
+#endif // CATCH_LAZY_EXPR_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct AssertionResultData
+    {
+        AssertionResultData() = delete;
+
+        AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression );
+
+        std::string message;
+        mutable std::string reconstructedExpression;
+        LazyExpression lazyExpression;
+        ResultWas::OfType resultType;
+
+        std::string reconstructExpression() const;
+    };
+
+    class AssertionResult {
+    public:
+        AssertionResult() = delete;
+        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+
+        bool isOk() const;
+        bool succeeded() const;
+        ResultWas::OfType getResultType() const;
+        bool hasExpression() const;
+        bool hasMessage() const;
+        std::string getExpression() const;
+        std::string getExpressionInMacro() const;
+        bool hasExpandedExpression() const;
+        std::string getExpandedExpression() const;
+        StringRef getMessage() const;
+        SourceLineInfo getSourceInfo() const;
+        StringRef getTestMacroName() const;
+
+    //protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED
+
+
+#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED
+#define CATCH_MESSAGE_INFO_HPP_INCLUDED
+
+
+
+#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
+#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
+
+#include <string>
+#include <chrono>
+
+
+namespace Catch {
+
+    class AssertionResult;
+    struct AssertionInfo;
+    struct SectionInfo;
+    struct SectionEndInfo;
+    struct MessageInfo;
+    struct MessageBuilder;
+    struct Counts;
+    struct AssertionReaction;
+    struct SourceLineInfo;
+
+    class ITransientExpression;
+    class IGeneratorTracker;
+
+    struct BenchmarkInfo;
+    template <typename Duration = std::chrono::duration<double, std::nano>>
+    struct BenchmarkStats;
+
+    class IResultCapture {
+    public:
+        virtual ~IResultCapture();
+
+        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
+                                        Counts& assertions ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+
+        virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+
+        virtual void benchmarkPreparing( StringRef name ) = 0;
+        virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
+        virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
+        virtual void benchmarkFailed( StringRef error ) = 0;
+
+        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
+                (   AssertionInfo const& info,
+                    ITransientExpression const& expr,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleMessage
+                (   AssertionInfo const& info,
+                    ResultWas::OfType resultType,
+                    StringRef message,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleUnexpectedExceptionNotThrown
+                (   AssertionInfo const& info,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleUnexpectedInflightException
+                (   AssertionInfo const& info,
+                    std::string const& message,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleIncomplete
+                (   AssertionInfo const& info ) = 0;
+        virtual void handleNonExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    AssertionReaction &reaction ) = 0;
+
+
+
+        virtual bool lastAssertionPassed() = 0;
+        virtual void assertionPassed() = 0;
+
+        // Deprecated, do not use:
+        virtual std::string getCurrentTestName() const = 0;
+        virtual const AssertionResult* getLastResult() const = 0;
+        virtual void exceptionEarlyReported() = 0;
+    };
+
+    IResultCapture& getResultCapture();
+}
+
+#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct MessageInfo {
+        MessageInfo(    StringRef _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        StringRef macroName;
+        std::string message;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        unsigned int sequence;
+
+        bool operator == (MessageInfo const& other) const {
+            return sequence == other.sequence;
+        }
+        bool operator < (MessageInfo const& other) const {
+            return sequence < other.sequence;
+        }
+    private:
+        static unsigned int globalCount;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED
+
+
+#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED
+#define CATCH_UNIQUE_PTR_HPP_INCLUDED
+
+#include <cassert>
+#include <type_traits>
+
+
+namespace Catch {
+namespace Detail {
+    /**
+     * A reimplementation of `std::unique_ptr` for improved compilation performance
+     *
+     * Does not support arrays nor custom deleters.
+     */
+    template <typename T>
+    class unique_ptr {
+        T* m_ptr;
+    public:
+        constexpr unique_ptr(std::nullptr_t = nullptr):
+            m_ptr{}
+        {}
+        explicit constexpr unique_ptr(T* ptr):
+            m_ptr(ptr)
+        {}
+
+        template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
+        unique_ptr(unique_ptr<U>&& from):
+            m_ptr(from.release())
+        {}
+
+        template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
+        unique_ptr& operator=(unique_ptr<U>&& from) {
+            reset(from.release());
+
+            return *this;
+        }
+
+        unique_ptr(unique_ptr const&) = delete;
+        unique_ptr& operator=(unique_ptr const&) = delete;
+
+        unique_ptr(unique_ptr&& rhs) noexcept:
+            m_ptr(rhs.m_ptr) {
+            rhs.m_ptr = nullptr;
+        }
+        unique_ptr& operator=(unique_ptr&& rhs) noexcept {
+            reset(rhs.release());
+
+            return *this;
+        }
+
+        ~unique_ptr() {
+            delete m_ptr;
+        }
+
+        T& operator*() {
+            assert(m_ptr);
+            return *m_ptr;
+        }
+        T const& operator*() const {
+            assert(m_ptr);
+            return *m_ptr;
+        }
+        T* operator->() noexcept {
+            assert(m_ptr);
+            return m_ptr;
+        }
+        T const* operator->() const noexcept {
+            assert(m_ptr);
+            return m_ptr;
+        }
+
+        T* get() { return m_ptr; }
+        T const* get() const { return m_ptr; }
+
+        void reset(T* ptr = nullptr) {
+            delete m_ptr;
+            m_ptr = ptr;
+        }
+
+        T* release() {
+            auto temp = m_ptr;
+            m_ptr = nullptr;
+            return temp;
+        }
+
+        explicit operator bool() const {
+            return m_ptr;
+        }
+
+        friend void swap(unique_ptr& lhs, unique_ptr& rhs) {
+            auto temp = lhs.m_ptr;
+            lhs.m_ptr = rhs.m_ptr;
+            rhs.m_ptr = temp;
+        }
+    };
+
+    //! Specialization to cause compile-time error for arrays
+    template <typename T>
+    class unique_ptr<T[]>;
+
+    template <typename T, typename... Args>
+    unique_ptr<T> make_unique(Args&&... args) {
+        return unique_ptr<T>(new T(CATCH_FORWARD(args)...));
+    }
+
+
+} // end namespace Detail
+} // end namespace Catch
+
+#endif // CATCH_UNIQUE_PTR_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_ESTIMATE_HPP_INCLUDED
+#define CATCH_ESTIMATE_HPP_INCLUDED
+
+namespace Catch {
+    namespace Benchmark {
+        template <typename Duration>
+        struct Estimate {
+            Duration point;
+            Duration lower_bound;
+            Duration upper_bound;
+            double confidence_interval;
+
+            template <typename Duration2>
+            operator Estimate<Duration2>() const {
+                return { point, lower_bound, upper_bound, confidence_interval };
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_ESTIMATE_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED
+#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED
+
+namespace Catch {
+    namespace Benchmark {
+        struct OutlierClassification {
+            int samples_seen = 0;
+            int low_severe = 0;     // more than 3 times IQR below Q1
+            int low_mild = 0;       // 1.5 to 3 times IQR below Q1
+            int high_mild = 0;      // 1.5 to 3 times IQR above Q3
+            int high_severe = 0;    // more than 3 times IQR above Q3
+
+            int total() const {
+                return low_severe + low_mild + high_mild + high_severe;
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED
+
+
+#include <map>
+#include <string>
+#include <vector>
+#include <iosfwd>
+
+namespace Catch {
+
+    struct ReporterDescription;
+    struct ListenerDescription;
+    struct TagInfo;
+    struct TestCaseInfo;
+    class TestCaseHandle;
+    class IConfig;
+    class IStream;
+    enum class ColourMode : std::uint8_t;
+
+    struct ReporterConfig {
+        ReporterConfig( IConfig const* _fullConfig,
+                        Detail::unique_ptr<IStream> _stream,
+                        ColourMode colourMode,
+                        std::map<std::string, std::string> customOptions );
+
+        ReporterConfig( ReporterConfig&& ) = default;
+        ReporterConfig& operator=( ReporterConfig&& ) = default;
+        ~ReporterConfig(); // = default
+
+        Detail::unique_ptr<IStream> takeStream() &&;
+        IConfig const* fullConfig() const;
+        ColourMode colourMode() const;
+        std::map<std::string, std::string> const& customOptions() const;
+
+    private:
+        Detail::unique_ptr<IStream> m_stream;
+        IConfig const* m_fullConfig;
+        ColourMode m_colourMode;
+        std::map<std::string, std::string> m_customOptions;
+    };
+
+    struct TestRunInfo {
+        constexpr TestRunInfo(StringRef _name) : name(_name) {}
+        StringRef name;
+    };
+
+    struct AssertionStats {
+        AssertionStats( AssertionResult const& _assertionResult,
+                        std::vector<MessageInfo> const& _infoMessages,
+                        Totals const& _totals );
+
+        AssertionStats( AssertionStats const& )              = default;
+        AssertionStats( AssertionStats && )                  = default;
+        AssertionStats& operator = ( AssertionStats const& ) = delete;
+        AssertionStats& operator = ( AssertionStats && )     = delete;
+
+        AssertionResult assertionResult;
+        std::vector<MessageInfo> infoMessages;
+        Totals totals;
+    };
+
+    struct SectionStats {
+        SectionStats(   SectionInfo const& _sectionInfo,
+                        Counts const& _assertions,
+                        double _durationInSeconds,
+                        bool _missingAssertions );
+
+        SectionInfo sectionInfo;
+        Counts assertions;
+        double durationInSeconds;
+        bool missingAssertions;
+    };
+
+    struct TestCaseStats {
+        TestCaseStats(  TestCaseInfo const& _testInfo,
+                        Totals const& _totals,
+                        std::string const& _stdOut,
+                        std::string const& _stdErr,
+                        bool _aborting );
+
+        TestCaseInfo const * testInfo;
+        Totals totals;
+        std::string stdOut;
+        std::string stdErr;
+        bool aborting;
+    };
+
+    struct TestRunStats {
+        TestRunStats(   TestRunInfo const& _runInfo,
+                        Totals const& _totals,
+                        bool _aborting );
+
+        TestRunInfo runInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+
+    struct BenchmarkInfo {
+        std::string name;
+        double estimatedDuration;
+        int iterations;
+        unsigned int samples;
+        unsigned int resamples;
+        double clockResolution;
+        double clockCost;
+    };
+
+    template <class Duration>
+    struct BenchmarkStats {
+        BenchmarkInfo info;
+
+        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());
+            for (auto const& sample : samples) {
+                samples2.push_back(Duration2(sample));
+            }
+            return {
+                info,
+                CATCH_MOVE(samples2),
+                mean,
+                standardDeviation,
+                outliers,
+                outlierVariance,
+            };
+        }
+    };
+
+    //! By setting up its preferences, a reporter can modify Catch2's behaviour
+    //! in some regards, e.g. it can request Catch2 to capture writes to
+    //! stdout/stderr during test execution, and pass them to the reporter.
+    struct ReporterPreferences {
+        //! Catch2 should redirect writes to stdout and pass them to the
+        //! reporter
+        bool shouldRedirectStdOut = false;
+        //! Catch2 should call `Reporter::assertionEnded` even for passing
+        //! assertions
+        bool shouldReportAllAssertions = false;
+    };
+
+    /**
+     * The common base for all reporters and event listeners
+     *
+     * Implementing classes must also implement:
+     *
+     *     //! User-friendly description of the reporter/listener type
+     *     static std::string getDescription()
+     *
+     * Generally shouldn't be derived from by users of Catch2 directly,
+     * instead they should derive from one of the utility bases that
+     * derive from this class.
+     */
+    class IEventListener {
+    protected:
+        //! Derived classes can set up their preferences here
+        ReporterPreferences m_preferences;
+        //! The test run's config as filled in from CLI and defaults
+        IConfig const* m_config;
+
+    public:
+        IEventListener( IConfig const* config ): m_config( config ) {}
+
+        virtual ~IEventListener(); // = default;
+
+        // Implementing class must also provide the following static methods:
+        // static std::string getDescription();
+
+        ReporterPreferences const& getPreferences() const {
+            return m_preferences;
+        }
+
+        //! Called when no test cases match provided test spec
+        virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0;
+        //! Called for all invalid test specs from the cli
+        virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0;
+
+        /**
+         * Called once in a testing run before tests are started
+         *
+         * Not called if tests won't be run (e.g. only listing will happen)
+         */
+        virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+
+        //! Called _once_ for each TEST_CASE, no matter how many times it is entered
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+        //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+        virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0;
+        //! Called when a `SECTION` is being entered. Not called for skipped sections
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+        //! Called when user-code is being probed before the actual benchmark runs
+        virtual void benchmarkPreparing( StringRef benchmarkName ) = 0;
+        //! Called after probe but before the user-code is being benchmarked
+        virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0;
+        //! Called with the benchmark results if benchmark successfully finishes
+        virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0;
+        //! Called if running the benchmarks fails for any reason
+        virtual void benchmarkFailed( StringRef benchmarkName ) = 0;
+
+        //! Called before assertion success/failure is evaluated
+        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+        //! Called after assertion was fully evaluated
+        virtual void assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+        //! Called after a `SECTION` has finished running
+        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+        //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+        virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0;
+        //! Called _once_ for each TEST_CASE, no matter how many times it is entered
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+        /**
+         * Called once after all tests in a testing run are finished
+         *
+         * Not called if tests weren't run (e.g. only listings happened)
+         */
+        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+        //! Called with test cases that are skipped due to the test run aborting
+        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+        //! Called if a fatal error (signal/structured exception) occured
+        virtual void fatalErrorEncountered( StringRef error ) = 0;
+
+        //! Writes out information about provided reporters using reporter-specific format
+        virtual void listReporters(std::vector<ReporterDescription> const& descriptions) = 0;
+        //! Writes out the provided listeners descriptions using reporter-specific format
+        virtual void listListeners(std::vector<ListenerDescription> const& descriptions) = 0;
+        //! Writes out information about provided tests using reporter-specific format
+        virtual void listTests(std::vector<TestCaseHandle> const& tests) = 0;
+        //! Writes out information about the provided tags using reporter-specific format
+        virtual void listTags(std::vector<TagInfo> const& tags) = 0;
+    };
+    using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+
+} // end namespace Catch
+
+#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED
+
+
+#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED
+#define CATCH_UNIQUE_NAME_HPP_INCLUDED
+
+
+
+
+/** \file
+ * Wrapper for the CONFIG configuration option
+ *
+ * When generating internal unique names, there are two options. Either
+ * we mix in the current line number, or mix in an incrementing number.
+ * We prefer the latter, using `__COUNTER__`, but users might want to
+ * use the former.
+ */
+
+#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED
+#define CATCH_CONFIG_COUNTER_HPP_INCLUDED
+
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+    #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \
+    !defined( CATCH_CONFIG_NO_COUNTER ) && \
+    !defined( CATCH_CONFIG_COUNTER )
+#    define CATCH_CONFIG_COUNTER
+#endif
+
+
+#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_CHRONOMETER_HPP_INCLUDED
+#define CATCH_CHRONOMETER_HPP_INCLUDED
+
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_CLOCK_HPP_INCLUDED
+#define CATCH_CLOCK_HPP_INCLUDED
+
+#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
+
+#endif // CATCH_CLOCK_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_OPTIMIZER_HPP_INCLUDED
+#define CATCH_OPTIMIZER_HPP_INCLUDED
+
+#if defined(_MSC_VER)
+#   include <atomic> // atomic_thread_fence
+#endif
+
+
+#include <type_traits>
+
+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) -> std::enable_if_t<!std::is_same<void, decltype(fn(args...))>::value> {
+            deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...));
+        }
+
+        template <typename Fn, typename... Args>
+        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
+            CATCH_FORWARD(fn) (CATCH_FORWARD(args)...);
+        }
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_OPTIMIZER_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED
+#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED
+
+
+
+#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
+#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
+
+namespace Catch {
+
+    //! Used to signal that an assertion macro failed
+    struct TestFailureException{};
+
+} // namespace Catch
+
+#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
+
+
+#ifndef CATCH_META_HPP_INCLUDED
+#define CATCH_META_HPP_INCLUDED
+
+#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>
+        static true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> test(int);
+        template <typename...>
+        static std::false_type 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
+    template <typename Func, typename... U>
+    using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::result_of_t<Func(U...)>>>;
+#endif
+
+} // namespace Catch
+
+namespace mpl_{
+    struct na;
+}
+
+#endif // CATCH_META_HPP_INCLUDED
+
+
+#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
+#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
+
+
+#include <string>
+
+namespace Catch {
+
+    class TestCaseHandle;
+    struct TestCaseInfo;
+    class ITestCaseRegistry;
+    class IExceptionTranslatorRegistry;
+    class IExceptionTranslator;
+    class IReporterRegistry;
+    class IReporterFactory;
+    class ITagAliasRegistry;
+    class ITestInvoker;
+    class IMutableEnumValuesRegistry;
+    struct SourceLineInfo;
+
+    class StartupExceptionRegistry;
+    class EventListenerFactory;
+
+    using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
+
+    class IRegistryHub {
+    public:
+        virtual ~IRegistryHub(); // = default
+
+        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;
+    };
+
+    class IMutableRegistryHub {
+    public:
+        virtual ~IMutableRegistryHub(); // = default
+        virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0;
+        virtual void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) = 0;
+        virtual void registerTest(Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker) = 0;
+        virtual void registerTranslator( Detail::unique_ptr<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();
+    IMutableRegistryHub& getMutableRegistryHub();
+    void cleanUp();
+    std::string translateActiveException();
+
+}
+
+#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
+
+#include <type_traits>
+
+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 CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
+                }
+            };
+            template <>
+            struct CompleteInvoker<void> {
+                template <typename Fun, typename... Args>
+                static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
+                    CATCH_FORWARD(fun)(CATCH_FORWARD(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(CATCH_FORWARD(fun), CATCH_FORWARD(args)...);
+            }
+
+        } // namespace Detail
+
+        template <typename Fun>
+        Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
+            return Detail::complete_invoke(CATCH_FORWARD(fun));
+        }
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            struct ChronometerConcept {
+                virtual void start() = 0;
+                virtual void finish() = 0;
+                virtual ~ChronometerConcept(); // = default;
+
+                ChronometerConcept() = default;
+                ChronometerConcept(ChronometerConcept const&) = default;
+                ChronometerConcept& operator=(ChronometerConcept const&) = 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(CATCH_FORWARD(fun), is_callable<Fun(int)>()); }
+
+            int runs() const { return repeats; }
+
+            Chronometer(Detail::ChronometerConcept& meter, int repeats_)
+                : impl(&meter)
+                , repeats(repeats_) {}
+
+        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 < repeats; ++i) invoke_deoptimized(fun, i);
+                impl->finish();
+                Detail::optimizer_barrier();
+            }
+
+            Detail::ChronometerConcept* impl;
+            int repeats;
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_CHRONOMETER_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED
+#define CATCH_ENVIRONMENT_HPP_INCLUDED
+
+
+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
+
+#endif // CATCH_ENVIRONMENT_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED
+#define CATCH_EXECUTION_PLAN_HPP_INCLUDED
+
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
+#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
+
+
+#include <type_traits>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename T, typename U>
+            struct is_related
+                : std::is_same<std::decay_t<T>, std::decay_t<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 Catch::Detail::unique_ptr<callable> clone() const = 0;
+                    virtual ~callable(); // = default;
+
+                    callable() = default;
+                    callable(callable const&) = default;
+                    callable& operator=(callable const&) = default;
+                };
+                template <typename Fun>
+                struct model : public callable {
+                    model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
+                    model(Fun const& fun_) : fun(fun_) {}
+
+                    Catch::Detail::unique_ptr<callable> clone() const override {
+                        return Catch::Detail::make_unique<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,
+                    std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0>
+                    BenchmarkFunction(Fun&& fun)
+                    : f(new model<std::decay_t<Fun>>(CATCH_FORWARD(fun))) {}
+
+                BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
+                    f( CATCH_MOVE( that.f ) ) {}
+
+                BenchmarkFunction(BenchmarkFunction const& that)
+                    : f(that.f->clone()) {}
+
+                BenchmarkFunction&
+                operator=( BenchmarkFunction&& that ) noexcept {
+                    f = CATCH_MOVE( that.f );
+                    return *this;
+                }
+
+                BenchmarkFunction& operator=(BenchmarkFunction const& that) {
+                    f = that.f->clone();
+                    return *this;
+                }
+
+                void operator()(Chronometer meter) const { f->call(meter); }
+
+            private:
+                Catch::Detail::unique_ptr<callable> f;
+            };
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_REPEAT_HPP_INCLUDED
+#define CATCH_REPEAT_HPP_INCLUDED
+
+#include <type_traits>
+
+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<std::decay_t<Fun>> repeat(Fun&& fun) {
+                return { CATCH_FORWARD(fun) };
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_REPEAT_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
+#define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
+
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_MEASURE_HPP_INCLUDED
+#define CATCH_MEASURE_HPP_INCLUDED
+
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_TIMING_HPP_INCLUDED
+#define CATCH_TIMING_HPP_INCLUDED
+
+
+#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
+
+#endif // CATCH_TIMING_HPP_INCLUDED
+
+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, CATCH_FORWARD(args)...);
+                auto end = Clock::now();
+                auto delta = end - start;
+                return { delta, CATCH_FORWARD(r), 1 };
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_MEASURE_HPP_INCLUDED
+
+#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(), CATCH_MOVE(result), iters };
+            }
+
+            template <typename Clock, typename Fun>
+            using run_for_at_least_argument_t = std::conditional_t<is_callable<Fun(Chronometer)>::value, Chronometer, int>;
+
+
+            [[noreturn]]
+            void throw_optimized_away_error();
+
+            template <typename Clock, typename Fun>
+            TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>>
+                run_for_at_least(ClockDuration<Clock> how_long,
+                                 const int initial_iterations,
+                                 Fun&& fun) {
+                auto iters = initial_iterations;
+                while (iters < (1 << 30)) {
+                    auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
+
+                    if (Timing.elapsed >= how_long) {
+                        return { Timing.elapsed, CATCH_MOVE(Timing.result), iters };
+                    }
+                    iters *= 2;
+                }
+                throw_optimized_away_error();
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
+
+#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
+
+#endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
+#define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
+
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_STATS_HPP_INCLUDED
+#define CATCH_STATS_HPP_INCLUDED
+
+
+#include <algorithm>
+#include <vector>
+#include <numeric>
+#include <tuple>
+#include <cmath>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            using sample = std::vector<double>;
+
+            // Used when we know we want == comparison of two doubles
+            // to centralize warning suppression
+            bool directCompare( double lhs, double rhs );
+
+            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 / static_cast<double>(count);
+            }
+
+            template <typename Estimator, typename Iterator>
+            sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
+                auto n = static_cast<size_t>(last - first);
+                auto second = first;
+                ++second;
+                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));
+                long n = static_cast<long>(resample.size());
+                double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast<double>(n);
+                // degenerate case with uniform samples
+                if ( directCompare( 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 ) -> long {
+                    return std::lround( normal_cdf( x ) * static_cast<double>(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 = static_cast<size_t>((std::max)(cumn(a1), 0l));
+                auto hi = static_cast<size_t>((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, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_STATS_HPP_INCLUDED
+
+#include <algorithm>
+#include <iterator>
+#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(static_cast<size_t>(k + 1));
+                std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
+
+                std::vector<double> deltas;
+                deltas.reserve(static_cast<size_t>(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(static_cast<size_t>(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() {
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+                static Catch::Detail::unique_ptr<Environment<FloatDuration<Clock>>> env;
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+                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 = Catch::Detail::make_unique<Environment<FloatDuration<Clock>>>( Environment<FloatDuration<Clock>>{resolution, cost} );
+                return *env;
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_ANALYSE_HPP_INCLUDED
+#define CATCH_ANALYSE_HPP_INCLUDED
+
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
+#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
+
+
+#include <algorithm>
+#include <vector>
+#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 {
+                    CATCH_MOVE(samples2),
+                    mean,
+                    standard_deviation,
+                    outliers,
+                    outlier_variance,
+                };
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
+
+#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(static_cast<size_t>(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 {
+                        CATCH_MOVE(samples2),
+                        wrap_estimate(analysis.mean),
+                        wrap_estimate(analysis.standard_deviation),
+                        outliers,
+                        analysis.outlier_variance,
+                    };
+                } else {
+                    std::vector<Duration> samples;
+                    samples.reserve(static_cast<size_t>(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 {
+                        CATCH_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
+
+#endif // CATCH_ANALYSE_HPP_INCLUDED
+
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+#include <cmath>
+
+namespace Catch {
+    namespace Benchmark {
+        struct Benchmark {
+            Benchmark(std::string&& benchmarkName)
+                : name(CATCH_MOVE(benchmarkName)) {}
+
+            template <class FUN>
+            Benchmark(std::string&& benchmarkName , FUN &&func)
+                : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
+
+            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() {
+                auto const* 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_ANON (TestFailureException) {
+                    getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
+                } CATCH_CATCH_ALL{
+                    getResultCapture().benchmarkFailed(translateActiveException());
+                    // We let the exception go further up so that the
+                    // test case is marked as failed.
+                    std::rethrow_exception(std::current_exception());
+                }
+            }
+
+            // sets lambda to be used in fun *and* executes benchmark!
+            template <typename Fun, std::enable_if_t<!Detail::is_related<Fun, Benchmark>::value, int> = 0>
+                Benchmark & operator=(Fun func) {
+                auto const* cfg = getCurrentContext().getConfig();
+                if (!cfg->skipBenchmarks()) {
+                    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 = [&]
+
+#if defined(CATCH_CONFIG_PREFIX_ALL)
+
+#define CATCH_BENCHMARK(...) \
+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), 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(CATCH2_INTERNAL_BENCHMARK_), name)
+
+#else
+
+#define BENCHMARK(...) \
+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), 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(CATCH2_INTERNAL_BENCHMARK_), name)
+
+#endif
+
+#endif // CATCH_BENCHMARK_HPP_INCLUDED
+
+
+// Adapted from donated nonius code.
+
+#ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED
+#define CATCH_CONSTRUCTOR_HPP_INCLUDED
+
+
+#include <type_traits>
+
+namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename T, bool Destruct>
+            struct ObjectStorage
+            {
+                ObjectStorage() = default;
+
+                ObjectStorage(const ObjectStorage& other)
+                {
+                    new(&data) T(other.stored_object());
+                }
+
+                ObjectStorage(ObjectStorage&& other)
+                {
+                    new(data) T(CATCH_MOVE(other.stored_object()));
+                }
+
+                ~ObjectStorage() { destruct_on_exit<T>(); }
+
+                template <typename... Args>
+                void construct(Args&&... args)
+                {
+                    new (data) T(CATCH_FORWARD(args)...);
+                }
+
+                template <bool AllowManualDestruction = !Destruct>
+                std::enable_if_t<AllowManualDestruction> destruct()
+                {
+                    stored_object().~T();
+                }
+
+            private:
+                // If this is a constructor benchmark, destruct the underlying object
+                template <typename U>
+                void destruct_on_exit(std::enable_if_t<Destruct, U>* = nullptr) { destruct<true>(); }
+                // Otherwise, don't
+                template <typename U>
+                void destruct_on_exit(std::enable_if_t<!Destruct, U>* = nullptr) { }
+
+#if defined( __GNUC__ ) && __GNUC__ <= 6
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif
+                T& stored_object() { return *reinterpret_cast<T*>( data ); }
+
+                T const& stored_object() const {
+                    return *reinterpret_cast<T const*>( data );
+                }
+#if defined( __GNUC__ ) && __GNUC__ <= 6
+#    pragma GCC diagnostic pop
+#endif
+
+                alignas( T ) unsigned char data[sizeof( T )]{};
+            };
+        } // namespace Detail
+
+        template <typename T>
+        using storage_for = Detail::ObjectStorage<T, true>;
+
+        template <typename T>
+        using destructable_object = Detail::ObjectStorage<T, false>;
+    } // namespace Benchmark
+} // namespace Catch
+
+#endif // CATCH_CONSTRUCTOR_HPP_INCLUDED
+
+#endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED
+
+
+#ifndef CATCH_APPROX_HPP_INCLUDED
+#define CATCH_APPROX_HPP_INCLUDED
+
+
+
+#ifndef CATCH_TOSTRING_HPP_INCLUDED
+#define CATCH_TOSTRING_HPP_INCLUDED
+
+
+#include <vector>
+#include <cstddef>
+#include <type_traits>
+#include <string>
+
+
+
+
+/** \file
+ * Wrapper for the WCHAR configuration option
+ *
+ * We want to support platforms that do not provide `wchar_t`, so we
+ * sometimes have to disable providing wchar_t overloads through Catch2,
+ * e.g. the StringMaker specialization for `std::wstring`.
+ */
+
+#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED
+#define CATCH_CONFIG_WCHAR_HPP_INCLUDED
+
+// We assume that WCHAR should be enabled by default, and only disabled
+// for a shortlist (so far only DJGPP) of compilers.
+
+#if defined(__DJGPP__)
+#  define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
+
+#if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \
+    !defined( CATCH_CONFIG_NO_WCHAR ) && \
+    !defined( CATCH_CONFIG_WCHAR )
+#    define CATCH_CONFIG_WCHAR
+#endif
+
+#endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED
+
+
+#ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
+#define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
+
+
+#include <iosfwd>
+#include <cstddef>
+#include <ostream>
+#include <string>
+
+namespace Catch {
+
+    class ReusableStringStream : Detail::NonCopyable {
+        std::size_t m_index;
+        std::ostream* m_oss;
+    public:
+        ReusableStringStream();
+        ~ReusableStringStream();
+
+        //! Returns the serialized state
+        std::string str() const;
+        //! Sets internal state to `str`
+        void str(std::string const& str);
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+// Old versions of GCC do not understand -Wnonnull-compare
+#pragma GCC diagnostic ignored "-Wpragmas"
+// Streaming a function pointer triggers Waddress and Wnonnull-compare
+// on GCC, because it implicitly converts it to bool and then decides
+// that the check it uses (a? true : false) is tautological and cannot
+// be null...
+#pragma GCC diagnostic ignored "-Waddress"
+#pragma GCC diagnostic ignored "-Wnonnull-compare"
+#endif
+
+        template<typename T>
+        auto operator << ( T const& value ) -> ReusableStringStream& {
+            *m_oss << value;
+            return *this;
+        }
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+        auto get() -> std::ostream& { return *m_oss; }
+    };
+}
+
+#endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED
+
+
+#ifndef CATCH_VOID_TYPE_HPP_INCLUDED
+#define CATCH_VOID_TYPE_HPP_INCLUDED
+
+
+namespace Catch {
+    namespace Detail {
+
+        template <typename...>
+        struct make_void { using type = void; };
+
+        template <typename... Ts>
+        using void_t = typename make_void<Ts...>::type;
+
+    } // namespace Detail
+} // namespace Catch
+
+
+#endif // CATCH_VOID_TYPE_HPP_INCLUDED
+
+
+#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+
+
+#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
+
+    class IMutableEnumValuesRegistry {
+    public:
+        virtual ~IMutableEnumValuesRegistry(); // = default;
+
+        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
+
+#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+#include <string_view>
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
+#endif
+
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy{};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
+namespace Catch {
+    // Bring in global namespace operator<< for ADL lookup in
+    // `IsStreamInsertable` below.
+    using ::operator<<;
+
+    namespace Detail {
+
+        inline std::size_t catch_strnlen(const char *str, std::size_t n) {
+            auto ret = std::char_traits<char>::find(str, n, '\0');
+            if (ret != nullptr) {
+                return static_cast<std::size_t>(ret - str);
+            }
+            return n;
+        }
+
+        constexpr StringRef unprintableString = "{?}"_sr;
+
+        //! Encases `string in quotes, and optionally escapes invisibles
+        std::string convertIntoString( StringRef string, bool escapeInvisibles );
+
+        //! Encases `string` in quotes, and escapes invisibles if user requested
+        //! it via CLI
+        std::string convertIntoString( StringRef string );
+
+        std::string rawMemoryToString( const void *object, std::size_t size );
+
+        template<typename T>
+        std::string rawMemoryToString( const T& object ) {
+          return rawMemoryToString( &object, sizeof(object) );
+        }
+
+        template<typename T>
+        class IsStreamInsertable {
+            template<typename Stream, typename U>
+            static auto test(int)
+                -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
+
+            template<typename, typename>
+            static auto test(...)->std::false_type;
+
+        public:
+            static const bool value = decltype(test<std::ostream, const T&>(0))::value;
+        };
+
+        template<typename E>
+        std::string convertUnknownEnumToString( E e );
+
+        template<typename T>
+        std::enable_if_t<
+            !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
+        std::string> convertUnstreamable( T const& ) {
+            return std::string(Detail::unprintableString);
+        }
+        template<typename T>
+        std::enable_if_t<
+            !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
+         std::string> convertUnstreamable(T const& ex) {
+            return ex.what();
+        }
+
+
+        template<typename T>
+        std::enable_if_t<
+            std::is_enum<T>::value,
+        std::string> convertUnstreamable( T const& value ) {
+            return convertUnknownEnumToString( value );
+        }
+
+#if defined(_MANAGED)
+        //! Convert a CLR string to a utf8 std::string
+        template<typename T>
+        std::string clrReferenceToString( T^ ref ) {
+            if (ref == nullptr)
+                return std::string("null");
+            auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
+            cli::pin_ptr<System::Byte> p = &bytes[0];
+            return std::string(reinterpret_cast<char const *>(p), bytes->Length);
+        }
+#endif
+
+    } // namespace Detail
+
+
+    // If we decide for C++14, change these to enable_if_ts
+    template <typename T, typename = void>
+    struct StringMaker {
+        template <typename Fake = T>
+        static
+        std::enable_if_t<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
+            convert(const Fake& value) {
+                ReusableStringStream rss;
+                // NB: call using the function-like syntax to avoid ambiguity with
+                // user-defined templated operator<< under clang.
+                rss.operator<<(value);
+                return rss.str();
+        }
+
+        template <typename Fake = T>
+        static
+        std::enable_if_t<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
+            convert( const Fake& value ) {
+#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
+            return Detail::convertUnstreamable(value);
+#else
+            return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
+#endif
+        }
+    };
+
+    namespace Detail {
+
+        // This function dispatches all stringification requests inside of Catch.
+        // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+        template <typename T>
+        std::string stringify(const T& e) {
+            return ::Catch::StringMaker<std::remove_cv_t<std::remove_reference_t<T>>>::convert(e);
+        }
+
+        template<typename E>
+        std::string convertUnknownEnumToString( E e ) {
+            return ::Catch::Detail::stringify(static_cast<std::underlying_type_t<E>>(e));
+        }
+
+#if defined(_MANAGED)
+        template <typename T>
+        std::string stringify( T^ e ) {
+            return ::Catch::StringMaker<T^>::convert(e);
+        }
+#endif
+
+    } // namespace Detail
+
+    // Some predefined specializations
+
+    template<>
+    struct StringMaker<std::string> {
+        static std::string convert(const std::string& str);
+    };
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+    template<>
+    struct StringMaker<std::string_view> {
+        static std::string convert(std::string_view str);
+    };
+#endif
+
+    template<>
+    struct StringMaker<char const *> {
+        static std::string convert(char const * str);
+    };
+    template<>
+    struct StringMaker<char *> {
+        static std::string convert(char * str);
+    };
+
+#if defined(CATCH_CONFIG_WCHAR)
+    template<>
+    struct StringMaker<std::wstring> {
+        static std::string convert(const std::wstring& wstr);
+    };
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+    template<>
+    struct StringMaker<std::wstring_view> {
+        static std::string convert(std::wstring_view str);
+    };
+# endif
+
+    template<>
+    struct StringMaker<wchar_t const *> {
+        static std::string convert(wchar_t const * str);
+    };
+    template<>
+    struct StringMaker<wchar_t *> {
+        static std::string convert(wchar_t * str);
+    };
+#endif // CATCH_CONFIG_WCHAR
+
+    template<size_t SZ>
+    struct StringMaker<char[SZ]> {
+        static std::string convert(char const* str) {
+            return Detail::convertIntoString(
+                StringRef( str, Detail::catch_strnlen( str, SZ ) ) );
+        }
+    };
+    template<size_t SZ>
+    struct StringMaker<signed char[SZ]> {
+        static std::string convert(signed char const* str) {
+            auto reinterpreted = reinterpret_cast<char const*>(str);
+            return Detail::convertIntoString(
+                StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
+        }
+    };
+    template<size_t SZ>
+    struct StringMaker<unsigned char[SZ]> {
+        static std::string convert(unsigned char const* str) {
+            auto reinterpreted = reinterpret_cast<char const*>(str);
+            return Detail::convertIntoString(
+                StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
+        }
+    };
+
+#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);
+    };
+    template<>
+    struct StringMaker<long> {
+        static std::string convert(long value);
+    };
+    template<>
+    struct StringMaker<long long> {
+        static std::string convert(long long value);
+    };
+    template<>
+    struct StringMaker<unsigned int> {
+        static std::string convert(unsigned int value);
+    };
+    template<>
+    struct StringMaker<unsigned long> {
+        static std::string convert(unsigned long value);
+    };
+    template<>
+    struct StringMaker<unsigned long long> {
+        static std::string convert(unsigned long long value);
+    };
+
+    template<>
+    struct StringMaker<bool> {
+        static std::string convert(bool b) {
+            using namespace std::string_literals;
+            return b ? "true"s : "false"s;
+        }
+    };
+
+    template<>
+    struct StringMaker<char> {
+        static std::string convert(char c);
+    };
+    template<>
+    struct StringMaker<signed char> {
+        static std::string convert(signed char c);
+    };
+    template<>
+    struct StringMaker<unsigned char> {
+        static std::string convert(unsigned char c);
+    };
+
+    template<>
+    struct StringMaker<std::nullptr_t> {
+        static std::string convert(std::nullptr_t) {
+            using namespace std::string_literals;
+            return "nullptr"s;
+        }
+    };
+
+    template<>
+    struct StringMaker<float> {
+        static std::string convert(float value);
+        CATCH_EXPORT static int precision;
+    };
+
+    template<>
+    struct StringMaker<double> {
+        static std::string convert(double value);
+        CATCH_EXPORT static int precision;
+    };
+
+    template <typename T>
+    struct StringMaker<T*> {
+        template <typename U>
+        static std::string convert(U* p) {
+            if (p) {
+                return ::Catch::Detail::rawMemoryToString(p);
+            } else {
+                return "nullptr";
+            }
+        }
+    };
+
+    template <typename R, typename C>
+    struct StringMaker<R C::*> {
+        static std::string convert(R C::* p) {
+            if (p) {
+                return ::Catch::Detail::rawMemoryToString(p);
+            } else {
+                return "nullptr";
+            }
+        }
+    };
+
+#if defined(_MANAGED)
+    template <typename T>
+    struct StringMaker<T^> {
+        static std::string convert( T^ ref ) {
+            return ::Catch::Detail::clrReferenceToString(ref);
+        }
+    };
+#endif
+
+    namespace Detail {
+        template<typename InputIterator, typename Sentinel = InputIterator>
+        std::string rangeToString(InputIterator first, Sentinel last) {
+            ReusableStringStream rss;
+            rss << "{ ";
+            if (first != last) {
+                rss << ::Catch::Detail::stringify(*first);
+                for (++first; first != last; ++first)
+                    rss << ", " << ::Catch::Detail::stringify(*first);
+            }
+            rss << " }";
+            return rss.str();
+        }
+    }
+
+} // namespace Catch
+
+//////////////////////////////////////////////////////
+// Separate std-lib types stringification, so it can be selectively enabled
+// This means that we do not bring in their headers
+
+#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
+#  define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+#  define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+#  define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+#  define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+#endif
+
+// Separate std::pair specialization
+#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
+#include <utility>
+namespace Catch {
+    template<typename T1, typename T2>
+    struct StringMaker<std::pair<T1, T2> > {
+        static std::string convert(const std::pair<T1, T2>& pair) {
+            ReusableStringStream rss;
+            rss << "{ "
+                << ::Catch::Detail::stringify(pair.first)
+                << ", "
+                << ::Catch::Detail::stringify(pair.second)
+                << " }";
+            return rss.str();
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#include <optional>
+namespace Catch {
+    template<typename T>
+    struct StringMaker<std::optional<T> > {
+        static std::string convert(const std::optional<T>& optional) {
+            if (optional.has_value()) {
+                return ::Catch::Detail::stringify(*optional);
+            } else {
+                return "{ }";
+            }
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+
+// Separate std::tuple specialization
+#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+#include <tuple>
+namespace Catch {
+    namespace Detail {
+        template<
+            typename Tuple,
+            std::size_t N = 0,
+            bool = (N < std::tuple_size<Tuple>::value)
+            >
+            struct TupleElementPrinter {
+            static void print(const Tuple& tuple, std::ostream& os) {
+                os << (N ? ", " : " ")
+                    << ::Catch::Detail::stringify(std::get<N>(tuple));
+                TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
+            }
+        };
+
+        template<
+            typename Tuple,
+            std::size_t N
+        >
+            struct TupleElementPrinter<Tuple, N, false> {
+            static void print(const Tuple&, std::ostream&) {}
+        };
+
+    }
+
+
+    template<typename ...Types>
+    struct StringMaker<std::tuple<Types...>> {
+        static std::string convert(const std::tuple<Types...>& tuple) {
+            ReusableStringStream rss;
+            rss << '{';
+            Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
+            rss << " }";
+            return rss.str();
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
+#include <variant>
+namespace Catch {
+    template<>
+    struct StringMaker<std::monostate> {
+        static std::string convert(const std::monostate&) {
+            return "{ }";
+        }
+    };
+
+    template<typename... Elements>
+    struct StringMaker<std::variant<Elements...>> {
+        static std::string convert(const std::variant<Elements...>& variant) {
+            if (variant.valueless_by_exception()) {
+                return "{valueless variant}";
+            } else {
+                return std::visit(
+                    [](const auto& value) {
+                        return ::Catch::Detail::stringify(value);
+                    },
+                    variant
+                );
+            }
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+
+namespace Catch {
+    // Import begin/ end from std here
+    using std::begin;
+    using std::end;
+
+    namespace Detail {
+        template <typename T, typename = void>
+        struct is_range_impl : std::false_type {};
+
+        template <typename T>
+        struct is_range_impl<T, void_t<decltype(begin(std::declval<T>()))>> : std::true_type {};
+    } // namespace Detail
+
+    template <typename T>
+    struct is_range : Detail::is_range_impl<T> {};
+
+#if defined(_MANAGED) // Managed types are never ranges
+    template <typename T>
+    struct is_range<T^> {
+        static const bool value = false;
+    };
+#endif
+
+    template<typename Range>
+    std::string rangeToString( Range const& range ) {
+        return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
+    }
+
+    // Handle vector<bool> specially
+    template<typename Allocator>
+    std::string rangeToString( std::vector<bool, Allocator> const& v ) {
+        ReusableStringStream rss;
+        rss << "{ ";
+        bool first = true;
+        for( bool b : v ) {
+            if( first )
+                first = false;
+            else
+                rss << ", ";
+            rss << ::Catch::Detail::stringify( b );
+        }
+        rss << " }";
+        return rss.str();
+    }
+
+    template<typename R>
+    struct StringMaker<R, std::enable_if_t<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>> {
+        static std::string convert( R const& range ) {
+            return rangeToString( range );
+        }
+    };
+
+    template <typename T, size_t SZ>
+    struct StringMaker<T[SZ]> {
+        static std::string convert(T const(&arr)[SZ]) {
+            return rangeToString(arr);
+        }
+    };
+
+
+} // namespace Catch
+
+// Separate std::chrono::duration specialization
+#include <ctime>
+#include <ratio>
+#include <chrono>
+
+
+namespace Catch {
+
+template <class Ratio>
+struct ratio_string {
+    static std::string symbol() {
+        Catch::ReusableStringStream rss;
+        rss << '[' << Ratio::num << '/'
+            << Ratio::den << ']';
+        return rss.str();
+    }
+};
+
+template <>
+struct ratio_string<std::atto> {
+    static char symbol() { return 'a'; }
+};
+template <>
+struct ratio_string<std::femto> {
+    static char symbol() { return 'f'; }
+};
+template <>
+struct ratio_string<std::pico> {
+    static char symbol() { return 'p'; }
+};
+template <>
+struct ratio_string<std::nano> {
+    static char symbol() { return 'n'; }
+};
+template <>
+struct ratio_string<std::micro> {
+    static char symbol() { return 'u'; }
+};
+template <>
+struct ratio_string<std::milli> {
+    static char symbol() { return 'm'; }
+};
+
+    ////////////
+    // std::chrono::duration specializations
+    template<typename Value, typename Ratio>
+    struct StringMaker<std::chrono::duration<Value, Ratio>> {
+        static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " s";
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " m";
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " h";
+            return rss.str();
+        }
+    };
+
+    ////////////
+    // std::chrono::time_point specialization
+    // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
+    template<typename Clock, typename Duration>
+    struct StringMaker<std::chrono::time_point<Clock, Duration>> {
+        static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
+            return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
+        }
+    };
+    // std::chrono::time_point<system_clock> specialization
+    template<typename Duration>
+    struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
+        static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
+            auto converted = std::chrono::system_clock::to_time_t(time_point);
+
+#ifdef _MSC_VER
+            std::tm timeInfo = {};
+            gmtime_s(&timeInfo, &converted);
+#else
+            std::tm* timeInfo = std::gmtime(&converted);
+#endif
+
+            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+            char timeStamp[timeStampSize];
+            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+            return std::string(timeStamp, timeStampSize - 1);
+        }
+    };
+}
+
+
+#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
+
+#endif // CATCH_TOSTRING_HPP_INCLUDED
+
+#include <type_traits>
+
+namespace Catch {
+
+    class Approx {
+    private:
+        bool equalityComparisonImpl(double other) const;
+        // Sets and validates the new margin (margin >= 0)
+        void setMargin(double margin);
+        // Sets and validates the new epsilon (0 < epsilon < 1)
+        void setEpsilon(double epsilon);
+
+    public:
+        explicit Approx ( double value );
+
+        static Approx custom();
+
+        Approx operator-() const;
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        Approx operator()( T const& value ) const {
+            Approx approx( static_cast<double>(value) );
+            approx.m_epsilon = m_epsilon;
+            approx.m_margin = m_margin;
+            approx.m_scale = m_scale;
+            return approx;
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        explicit Approx( T const& value ): Approx(static_cast<double>(value))
+        {}
+
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator == ( const T& lhs, Approx const& rhs ) {
+            auto lhs_v = static_cast<double>(lhs);
+            return rhs.equalityComparisonImpl(lhs_v);
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator == ( Approx const& lhs, const T& rhs ) {
+            return operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator != ( T const& lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator != ( Approx const& lhs, T const& rhs ) {
+            return !operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator <= ( T const& lhs, Approx const& rhs ) {
+            return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator <= ( Approx const& lhs, T const& rhs ) {
+            return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator >= ( T const& lhs, Approx const& rhs ) {
+            return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        friend bool operator >= ( Approx const& lhs, T const& rhs ) {
+            return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        Approx& epsilon( T const& newEpsilon ) {
+            const auto epsilonAsDouble = static_cast<double>(newEpsilon);
+            setEpsilon(epsilonAsDouble);
+            return *this;
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        Approx& margin( T const& newMargin ) {
+            const auto marginAsDouble = static_cast<double>(newMargin);
+            setMargin(marginAsDouble);
+            return *this;
+        }
+
+        template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        Approx& scale( T const& newScale ) {
+            m_scale = static_cast<double>(newScale);
+            return *this;
+        }
+
+        std::string toString() const;
+
+    private:
+        double m_epsilon;
+        double m_margin;
+        double m_scale;
+        double m_value;
+    };
+
+namespace literals {
+    Approx operator ""_a(long double val);
+    Approx operator ""_a(unsigned long long val);
+} // end namespace literals
+
+template<>
+struct StringMaker<Catch::Approx> {
+    static std::string convert(Catch::Approx const& value);
+};
+
+} // end namespace Catch
+
+#endif // CATCH_APPROX_HPP_INCLUDED
+
+
+#ifndef CATCH_CONFIG_HPP_INCLUDED
+#define CATCH_CONFIG_HPP_INCLUDED
+
+
+
+#ifndef CATCH_TEST_SPEC_HPP_INCLUDED
+#define CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+
+
+#ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+#define CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+
+
+#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED
+#define CATCH_CASE_SENSITIVE_HPP_INCLUDED
+
+namespace Catch {
+
+    enum class CaseSensitive { Yes, No };
+
+} // namespace Catch
+
+#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
+
+    public:
+
+        WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity );
+        bool matches( std::string const& str ) const;
+
+    private:
+        std::string normaliseString( std::string const& str ) const;
+        CaseSensitive m_caseSensitivity;
+        WildcardPosition m_wildcard = NoWildcard;
+        std::string m_pattern;
+    };
+}
+
+#endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class IConfig;
+    struct TestCaseInfo;
+    class TestCaseHandle;
+
+    class TestSpec {
+
+        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;
+        };
+
+        class NamePattern : public Pattern {
+        public:
+            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:
+            explicit TagPattern( std::string const& tag, std::string const& filterString );
+            bool matches( TestCaseInfo const& testCase ) const override;
+        private:
+            std::string m_tag;
+        };
+
+        struct Filter {
+            std::vector<Detail::unique_ptr<Pattern>> m_required;
+            std::vector<Detail::unique_ptr<Pattern>> m_forbidden;
+
+            bool matches( TestCaseInfo const& testCase ) const;
+            std::string name() const;
+        };
+
+    public:
+        struct FilterMatch {
+            std::string name;
+            std::vector<TestCaseHandle 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<TestCaseHandle> const& testCases, IConfig const& config ) const;
+        const vectorStrings & getInvalidSpecs() const;
+
+    private:
+        std::vector<Filter> m_filters;
+        std::vector<std::string> m_invalidSpecs;
+        friend class TestSpecParser;
+    };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif // CATCH_TEST_SPEC_HPP_INCLUDED
+
+
+#ifndef CATCH_OPTIONAL_HPP_INCLUDED
+#define CATCH_OPTIONAL_HPP_INCLUDED
+
+#include <cassert>
+
+namespace Catch {
+
+    // An optional type
+    template<typename T>
+    class Optional {
+    public:
+        Optional() : nullableValue( nullptr ) {}
+        Optional( T const& _value )
+        : nullableValue( new( storage ) T( _value ) )
+        {}
+        Optional( Optional const& _other )
+        : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
+        {}
+
+        ~Optional() {
+            reset();
+        }
+
+        Optional& operator= ( Optional const& _other ) {
+            if( &_other != this ) {
+                reset();
+                if( _other )
+                    nullableValue = new( storage ) T( *_other );
+            }
+            return *this;
+        }
+        Optional& operator = ( T const& _value ) {
+            reset();
+            nullableValue = new( storage ) T( _value );
+            return *this;
+        }
+
+        void reset() {
+            if( nullableValue )
+                nullableValue->~T();
+            nullableValue = nullptr;
+        }
+
+        T& operator*() {
+            assert(nullableValue);
+            return *nullableValue;
+        }
+        T const& operator*() const {
+            assert(nullableValue);
+            return *nullableValue;
+        }
+        T* operator->() {
+            assert(nullableValue);
+            return nullableValue;
+        }
+        const T* operator->() const {
+            assert(nullableValue);
+            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();
+        }
+
+        friend bool operator==(Optional const& a, Optional const& b) {
+            if (a.none() && b.none()) {
+                return true;
+            } else if (a.some() && b.some()) {
+                return *a == *b;
+            } else {
+                return false;
+            }
+        }
+        friend bool operator!=(Optional const& a, Optional const& b) {
+            return !( a == b );
+        }
+
+    private:
+        T *nullableValue;
+        alignas(alignof(T)) char storage[sizeof(T)];
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_OPTIONAL_HPP_INCLUDED
+
+
+#ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
+#define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
+
+#include <cstdint>
+
+namespace Catch {
+
+    enum class GenerateFrom {
+        Time,
+        RandomDevice,
+        //! Currently equivalent to RandomDevice, but can change at any point
+        Default
+    };
+
+    std::uint32_t generateRandomSeed(GenerateFrom from);
+
+} // end namespace Catch
+
+#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
+#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
+
+
+
+#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+
+#include <iosfwd>
+#include <cstdint>
+
+namespace Catch {
+
+    enum class ColourMode : std::uint8_t;
+    class IStream;
+
+    struct Colour {
+        enum Code {
+            None = 0,
+
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
+
+            Bright = 0x10,
+
+            BrightRed = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey = Bright | Grey,
+            BrightWhite = Bright | White,
+            BrightYellow = Bright | Yellow,
+
+            // By intention
+            FileName = LightGrey,
+            Warning = BrightYellow,
+            ResultError = BrightRed,
+            ResultSuccess = BrightGreen,
+            ResultExpectedFailure = Warning,
+
+            Error = BrightRed,
+            Success = Green,
+
+            OriginalExpression = Cyan,
+            ReconstructedExpression = BrightYellow,
+
+            SecondaryText = LightGrey,
+            Headers = White
+        };
+    };
+
+    class ColourImpl {
+    protected:
+        //! The associated stream of this ColourImpl instance
+        IStream* m_stream;
+    public:
+        ColourImpl( IStream* stream ): m_stream( stream ) {}
+
+        //! RAII wrapper around writing specific colour of text using specific
+        //! colour impl into a stream.
+        class ColourGuard {
+            ColourImpl const* m_colourImpl;
+            Colour::Code m_code;
+            bool m_engaged = false;
+
+        public:
+            //! Does **not** engage the guard/start the colour
+            ColourGuard( Colour::Code code,
+                         ColourImpl const* colour );
+
+            ColourGuard( ColourGuard const& rhs ) = delete;
+            ColourGuard& operator=( ColourGuard const& rhs ) = delete;
+
+            ColourGuard( ColourGuard&& rhs ) noexcept;
+            ColourGuard& operator=( ColourGuard&& rhs ) noexcept;
+
+            //! Removes colour _if_ the guard was engaged
+            ~ColourGuard();
+
+            /**
+             * Explicitly engages colour for given stream.
+             *
+             * The API based on operator<< should be preferred.
+             */
+            ColourGuard& engage( std::ostream& stream ) &;
+            /**
+             * Explicitly engages colour for given stream.
+             *
+             * The API based on operator<< should be preferred.
+             */
+            ColourGuard&& engage( std::ostream& stream ) &&;
+
+        private:
+            //! Engages the guard and starts using colour
+            friend std::ostream& operator<<( std::ostream& lhs,
+                                             ColourGuard& guard ) {
+                guard.engageImpl( lhs );
+                return lhs;
+            }
+            //! Engages the guard and starts using colour
+            friend std::ostream& operator<<( std::ostream& lhs,
+                                            ColourGuard&& guard) {
+                guard.engageImpl( lhs );
+                return lhs;
+            }
+
+            void engageImpl( std::ostream& stream );
+
+        };
+
+        virtual ~ColourImpl(); // = default
+        /**
+         * Creates a guard object for given colour and this colour impl
+         *
+         * **Important:**
+         * the guard starts disengaged, and has to be engaged explicitly.
+         */
+        ColourGuard guardColour( Colour::Code colourCode );
+
+    private:
+        virtual void use( Colour::Code colourCode ) const = 0;
+    };
+
+    //! Provides ColourImpl based on global config and target compilation platform
+    Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection,
+                                                   IStream* stream );
+
+    //! Checks if specific colour impl has been compiled into the binary
+    bool isColourImplAvailable( ColourMode colourSelection );
+
+} // end namespace Catch
+
+#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    enum class ColourMode : std::uint8_t;
+
+    namespace Detail {
+        //! Splits the reporter spec into reporter name and kv-pair options
+        std::vector<std::string> splitReporterSpec( StringRef reporterSpec );
+
+        Optional<ColourMode> stringToColourMode( StringRef colourMode );
+    }
+
+    /**
+     * Structured reporter spec that a reporter can be created from
+     *
+     * Parsing has been validated, but semantics have not. This means e.g.
+     * that the colour mode is known to Catch2, but it might not be
+     * compiled into the binary, and the output filename might not be
+     * openable.
+     */
+    class ReporterSpec {
+        std::string m_name;
+        Optional<std::string> m_outputFileName;
+        Optional<ColourMode> m_colourMode;
+        std::map<std::string, std::string> m_customOptions;
+
+        friend bool operator==( ReporterSpec const& lhs,
+                                ReporterSpec const& rhs );
+        friend bool operator!=( ReporterSpec const& lhs,
+                                ReporterSpec const& rhs ) {
+            return !( lhs == rhs );
+        }
+
+    public:
+        ReporterSpec(
+            std::string name,
+            Optional<std::string> outputFileName,
+            Optional<ColourMode> colourMode,
+            std::map<std::string, std::string> customOptions );
+
+        std::string const& name() const { return m_name; }
+
+        Optional<std::string> const& outputFile() const {
+            return m_outputFileName;
+        }
+
+        Optional<ColourMode> const& colourMode() const { return m_colourMode; }
+
+        std::map<std::string, std::string> const& customOptions() const {
+            return m_customOptions;
+        }
+    };
+
+    /**
+     * Parses provided reporter spec string into
+     *
+     * Returns empty optional on errors, e.g.
+     *  * field that is not first and not a key+value pair
+     *  * duplicated keys in kv pair
+     *  * unknown catch reporter option
+     *  * empty key/value in an custom kv pair
+     *  * ...
+     */
+    Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec );
+
+}
+
+#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
+
+#include <chrono>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class IStream;
+
+    /**
+     * `ReporterSpec` but with the defaults filled in.
+     *
+     * Like `ReporterSpec`, the semantics are unchecked.
+     */
+    struct ProcessedReporterSpec {
+        std::string name;
+        std::string outputFilename;
+        ColourMode colourMode;
+        std::map<std::string, std::string> customOptions;
+        friend bool operator==( ProcessedReporterSpec const& lhs,
+                                ProcessedReporterSpec const& rhs );
+        friend bool operator!=( ProcessedReporterSpec const& lhs,
+                                ProcessedReporterSpec const& rhs ) {
+            return !( lhs == rhs );
+        }
+    };
+
+    struct ConfigData {
+
+        bool listTests = false;
+        bool listTags = false;
+        bool listReporters = false;
+        bool listListeners = false;
+
+        bool showSuccessfulTests = false;
+        bool shouldDebugBreak = false;
+        bool noThrow = false;
+        bool showHelp = false;
+        bool showInvisibles = false;
+        bool filenamesAsTags = false;
+        bool libIdentify = false;
+        bool allowZeroTests = false;
+
+        int abortAfter = -1;
+        uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default);
+
+        unsigned int shardCount = 1;
+        unsigned int shardIndex = 0;
+
+        bool skipBenchmarks = false;
+        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 showDurations = ShowDurations::DefaultForReporter;
+        double minDuration = -1;
+        TestRunOrder runOrder = TestRunOrder::Declared;
+        ColourMode defaultColourMode = ColourMode::PlatformDefault;
+        WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
+
+        std::string defaultOutputFilename;
+        std::string name;
+        std::string processName;
+        std::vector<ReporterSpec> reporterSpecifications;
+
+        std::vector<std::string> testsOrTags;
+        std::vector<std::string> sectionsToRun;
+    };
+
+
+    class Config : public IConfig {
+    public:
+
+        Config() = default;
+        Config( ConfigData const& data );
+        ~Config() override; // = default in the cpp file
+
+        bool listTests() const;
+        bool listTags() const;
+        bool listReporters() const;
+        bool listListeners() const;
+
+        std::vector<ReporterSpec> const& getReporterSpecs() const;
+        std::vector<ProcessedReporterSpec> const&
+        getProcessedReporterSpecs() const;
+
+        std::vector<std::string> const& getTestsOrTags() const override;
+        std::vector<std::string> const& getSectionsToRun() const override;
+
+        TestSpec const& testSpec() const override;
+        bool hasTestFilters() const override;
+
+        bool showHelp() const;
+
+        // IConfig interface
+        bool allowThrows() const override;
+        StringRef name() const override;
+        bool includeSuccessfulResults() const override;
+        bool warnAboutMissingAssertions() const override;
+        bool warnAboutUnmatchedTestSpecs() const override;
+        bool zeroTestsCountAsSuccess() const override;
+        ShowDurations showDurations() const override;
+        double minDuration() const override;
+        TestRunOrder runOrder() const override;
+        uint32_t rngSeed() const override;
+        unsigned int shardCount() const override;
+        unsigned int shardIndex() const override;
+        ColourMode defaultColourMode() const override;
+        bool shouldDebugBreak() const override;
+        int abortAfter() const override;
+        bool showInvisibles() const override;
+        Verbosity verbosity() const override;
+        bool skipBenchmarks() const override;
+        bool benchmarkNoAnalysis() const override;
+        unsigned int benchmarkSamples() const override;
+        double benchmarkConfidenceInterval() const override;
+        unsigned int benchmarkResamples() const override;
+        std::chrono::milliseconds benchmarkWarmupTime() const override;
+
+    private:
+        ConfigData m_data;
+        std::vector<ProcessedReporterSpec> m_processedReporterSpecs;
+        TestSpec m_testSpec;
+        bool m_hasTestFilters = false;
+    };
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_HPP_INCLUDED
+
+
+#ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED
+#define CATCH_GET_RANDOM_SEED_HPP_INCLUDED
+
+#include <cstdint>
+
+namespace Catch {
+    //! Returns Catch2's current RNG seed.
+    std::uint32_t getSeed();
+}
+
+#endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED
+
+
+#ifndef CATCH_MESSAGE_HPP_INCLUDED
+#define CATCH_MESSAGE_HPP_INCLUDED
+
+
+
+#ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED
+#define CATCH_STREAM_END_STOP_HPP_INCLUDED
+
+
+namespace Catch {
+
+    // Use this in variadic streaming macros to allow
+    //    << +StreamEndStop
+    // as well as
+    //    << stuff +StreamEndStop
+    struct StreamEndStop {
+        StringRef operator+() const { return StringRef(); }
+
+        template <typename T>
+        friend T const& operator+( T const& value, StreamEndStop ) {
+            return value;
+        }
+    };
+
+} // namespace Catch
+
+#endif // CATCH_STREAM_END_STOP_HPP_INCLUDED
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    struct SourceLineInfo;
+
+    struct MessageStream {
+
+        template<typename T>
+        MessageStream& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        ReusableStringStream m_stream;
+    };
+
+    struct MessageBuilder : MessageStream {
+        MessageBuilder( StringRef macroName,
+                        SourceLineInfo const& lineInfo,
+                        ResultWas::OfType type ):
+            m_info(macroName, lineInfo, type) {}
+
+
+        template<typename T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+    };
+
+    class ScopedMessage {
+    public:
+        explicit ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage& duplicate ) = delete;
+        ScopedMessage( ScopedMessage&& old ) noexcept;
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+        bool m_moved = false;
+    };
+
+    class Capturer {
+        std::vector<MessageInfo> m_messages;
+        IResultCapture& m_resultCapture = getResultCapture();
+        size_t m_captured = 0;
+    public:
+        Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
+
+        Capturer(Capturer const&) = delete;
+        Capturer& operator=(Capturer const&) = delete;
+
+        ~Capturer();
+
+        void captureValue( size_t index, std::string const& value );
+
+        template<typename T>
+        void captureValues( size_t index, T const& value ) {
+            captureValue( index, Catch::Detail::stringify( value ) );
+        }
+
+        template<typename T, typename... Ts>
+        void captureValues( size_t index, T const& value, Ts const&... values ) {
+            captureValue( index, Catch::Detail::stringify(value) );
+            captureValues( index+1, values... );
+        }
+    };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
+        catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
+    Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \
+    varName.captureValues( 0, __VA_ARGS__ )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( macroName, log ) \
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_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 )
+
+
+#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+
+  #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__ )
+
+#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+
+  #define CATCH_INFO( msg )          (void)(0)
+  #define CATCH_UNSCOPED_INFO( msg ) (void)(0)
+  #define CATCH_WARN( msg )          (void)(0)
+  #define CATCH_CAPTURE( ... )       (void)(0)
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+
+  #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__ )
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+
+  #define INFO( msg )          (void)(0)
+  #define UNSCOPED_INFO( msg ) (void)(0)
+  #define WARN( msg )          (void)(0)
+  #define CAPTURE( ... )       (void)(0)
+
+#endif // end of user facing macro declarations
+
+
+
+
+#endif // CATCH_MESSAGE_HPP_INCLUDED
+
+
+#ifndef CATCH_SESSION_HPP_INCLUDED
+#define CATCH_SESSION_HPP_INCLUDED
+
+
+
+#ifndef CATCH_COMMANDLINE_HPP_INCLUDED
+#define CATCH_COMMANDLINE_HPP_INCLUDED
+
+
+
+#ifndef CATCH_CLARA_HPP_INCLUDED
+#define CATCH_CLARA_HPP_INCLUDED
+
+#if defined( __clang__ )
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wweak-vtables"
+#    pragma clang diagnostic ignored "-Wshadow"
+#    pragma clang diagnostic ignored "-Wdeprecated"
+#endif
+
+#if defined( __GNUC__ )
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+#ifndef CLARA_CONFIG_OPTIONAL_TYPE
+#    ifdef __has_include
+#        if __has_include( <optional>) && __cplusplus >= 201703L
+#            include <optional>
+#            define CLARA_CONFIG_OPTIONAL_TYPE std::optional
+#        endif
+#    endif
+#endif
+
+
+#include <cassert>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace Catch {
+    namespace Clara {
+
+        class Args;
+        class Parser;
+
+        // enum of result types from a parse
+        enum class ParseResultType {
+            Matched,
+            NoMatch,
+            ShortCircuitAll,
+            ShortCircuitSame
+        };
+
+        struct accept_many_t {};
+        constexpr accept_many_t accept_many {};
+
+        namespace Detail {
+            struct fake_arg {
+                template <typename T>
+                operator T();
+            };
+
+            template <typename F, typename = void>
+            struct is_unary_function : std::false_type {};
+
+            template <typename F>
+            struct is_unary_function<
+                F,
+                Catch::Detail::void_t<decltype(
+                    std::declval<F>()( fake_arg() ) )
+                >
+            > : std::true_type {};
+
+            // Traits for extracting arg and return type of lambdas (for single
+            // argument lambdas)
+            template <typename L>
+            struct UnaryLambdaTraits
+                : UnaryLambdaTraits<decltype( &L::operator() )> {};
+
+            template <typename ClassT, typename ReturnT, typename... Args>
+            struct UnaryLambdaTraits<ReturnT ( ClassT::* )( Args... ) const> {
+                static const bool isValid = false;
+            };
+
+            template <typename ClassT, typename ReturnT, typename ArgT>
+            struct UnaryLambdaTraits<ReturnT ( ClassT::* )( ArgT ) const> {
+                static const bool isValid = true;
+                using ArgType = std::remove_const_t<std::remove_reference_t<ArgT>>;
+                using ReturnType = ReturnT;
+            };
+
+            class TokenStream;
+
+            // Wraps a token coming from a token stream. These may not directly
+            // correspond to strings as a single string may encode an option +
+            // its argument if the : or = form is used
+            enum class TokenType { Option, Argument };
+            struct Token {
+                TokenType type;
+                std::string token;
+            };
+
+            // Abstracts iterators into args as a stream of tokens, with option
+            // arguments uniformly handled
+            class TokenStream {
+                using Iterator = std::vector<std::string>::const_iterator;
+                Iterator it;
+                Iterator itEnd;
+                std::vector<Token> m_tokenBuffer;
+
+                void loadBuffer();
+
+            public:
+                explicit TokenStream( Args const& args );
+                TokenStream( Iterator it, Iterator itEnd );
+
+                explicit operator bool() const {
+                    return !m_tokenBuffer.empty() || it != itEnd;
+                }
+
+                size_t count() const {
+                    return m_tokenBuffer.size() + ( itEnd - it );
+                }
+
+                Token operator*() const {
+                    assert( !m_tokenBuffer.empty() );
+                    return m_tokenBuffer.front();
+                }
+
+                Token const* operator->() const {
+                    assert( !m_tokenBuffer.empty() );
+                    return &m_tokenBuffer.front();
+                }
+
+                TokenStream& operator++();
+            };
+
+            //! Denotes type of a parsing result
+            enum class ResultType {
+                Ok,          ///< No errors
+                LogicError,  ///< Error in user-specified arguments for
+                             ///< construction
+                RuntimeError ///< Error in parsing inputs
+            };
+
+            class ResultBase {
+            protected:
+                ResultBase( ResultType type ): m_type( type ) {}
+                virtual ~ResultBase(); // = default;
+
+
+                ResultBase(ResultBase const&) = default;
+                ResultBase& operator=(ResultBase const&) = default;
+                ResultBase(ResultBase&&) = default;
+                ResultBase& operator=(ResultBase&&) = default;
+
+                virtual void enforceOk() const = 0;
+
+                ResultType m_type;
+            };
+
+            template <typename T> class ResultValueBase : public ResultBase {
+            public:
+                auto value() const -> T const& {
+                    enforceOk();
+                    return m_value;
+                }
+
+            protected:
+                ResultValueBase( ResultType type ): ResultBase( type ) {}
+
+                ResultValueBase( ResultValueBase const& other ):
+                    ResultBase( other ) {
+                    if ( m_type == ResultType::Ok )
+                        new ( &m_value ) T( other.m_value );
+                }
+
+                ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) {
+                    new ( &m_value ) T( value );
+                }
+
+                auto operator=( ResultValueBase const& other )
+                    -> ResultValueBase& {
+                    if ( m_type == ResultType::Ok )
+                        m_value.~T();
+                    ResultBase::operator=( other );
+                    if ( m_type == ResultType::Ok )
+                        new ( &m_value ) T( other.m_value );
+                    return *this;
+                }
+
+                ~ResultValueBase() override {
+                    if ( m_type == ResultType::Ok )
+                        m_value.~T();
+                }
+
+                union {
+                    T m_value;
+                };
+            };
+
+            template <> class ResultValueBase<void> : public ResultBase {
+            protected:
+                using ResultBase::ResultBase;
+            };
+
+            template <typename T = void>
+            class BasicResult : public ResultValueBase<T> {
+            public:
+                template <typename U>
+                explicit BasicResult( BasicResult<U> const& other ):
+                    ResultValueBase<T>( other.type() ),
+                    m_errorMessage( other.errorMessage() ) {
+                    assert( type() != ResultType::Ok );
+                }
+
+                template <typename U>
+                static auto ok( U const& value ) -> BasicResult {
+                    return { ResultType::Ok, value };
+                }
+                static auto ok() -> BasicResult { return { ResultType::Ok }; }
+                static auto logicError( std::string&& message )
+                    -> BasicResult {
+                    return { ResultType::LogicError, CATCH_MOVE(message) };
+                }
+                static auto runtimeError( std::string&& message )
+                    -> BasicResult {
+                    return { ResultType::RuntimeError, CATCH_MOVE(message) };
+                }
+
+                explicit operator bool() const {
+                    return m_type == ResultType::Ok;
+                }
+                auto type() const -> ResultType { return m_type; }
+                auto errorMessage() const -> std::string const& {
+                    return m_errorMessage;
+                }
+
+            protected:
+                void enforceOk() const override {
+
+                    // Errors shouldn't reach this point, but if they do
+                    // the actual error message will be in m_errorMessage
+                    assert( m_type != ResultType::LogicError );
+                    assert( m_type != ResultType::RuntimeError );
+                    if ( m_type != ResultType::Ok )
+                        std::abort();
+                }
+
+                std::string
+                    m_errorMessage; // Only populated if resultType is an error
+
+                BasicResult( ResultType type,
+                             std::string&& message ):
+                    ResultValueBase<T>( type ), m_errorMessage( CATCH_MOVE(message) ) {
+                    assert( m_type != ResultType::Ok );
+                }
+
+                using ResultValueBase<T>::ResultValueBase;
+                using ResultBase::m_type;
+            };
+
+            class ParseState {
+            public:
+                ParseState( ParseResultType type,
+                            TokenStream const& remainingTokens );
+
+                ParseResultType type() const { return m_type; }
+                TokenStream const& remainingTokens() const {
+                    return m_remainingTokens;
+                }
+
+            private:
+                ParseResultType m_type;
+                TokenStream m_remainingTokens;
+            };
+
+            using Result = BasicResult<void>;
+            using ParserResult = BasicResult<ParseResultType>;
+            using InternalParseResult = BasicResult<ParseState>;
+
+            struct HelpColumns {
+                std::string left;
+                std::string right;
+            };
+
+            template <typename T>
+            ParserResult convertInto( std::string const& source, T& target ) {
+                std::stringstream ss( source );
+                ss >> target;
+                if ( ss.fail() ) {
+                    return ParserResult::runtimeError(
+                        "Unable to convert '" + source +
+                        "' to destination type" );
+                } else {
+                    return ParserResult::ok( ParseResultType::Matched );
+                }
+            }
+            ParserResult convertInto( std::string const& source,
+                                      std::string& target );
+            ParserResult convertInto( std::string const& source, bool& target );
+
+#ifdef CLARA_CONFIG_OPTIONAL_TYPE
+            template <typename T>
+            auto convertInto( std::string const& source,
+                              CLARA_CONFIG_OPTIONAL_TYPE<T>& target )
+                -> ParserResult {
+                T temp;
+                auto result = convertInto( source, temp );
+                if ( result )
+                    target = CATCH_MOVE( temp );
+                return result;
+            }
+#endif // CLARA_CONFIG_OPTIONAL_TYPE
+
+            struct BoundRef : Catch::Detail::NonCopyable {
+                virtual ~BoundRef() = default;
+                virtual bool isContainer() const;
+                virtual bool isFlag() const;
+            };
+            struct BoundValueRefBase : BoundRef {
+                virtual auto setValue( std::string const& arg )
+                    -> ParserResult = 0;
+            };
+            struct BoundFlagRefBase : BoundRef {
+                virtual auto setFlag( bool flag ) -> ParserResult = 0;
+                bool isFlag() const override;
+            };
+
+            template <typename T> struct BoundValueRef : BoundValueRefBase {
+                T& m_ref;
+
+                explicit BoundValueRef( T& ref ): m_ref( ref ) {}
+
+                ParserResult setValue( std::string const& arg ) override {
+                    return convertInto( arg, m_ref );
+                }
+            };
+
+            template <typename T>
+            struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
+                std::vector<T>& m_ref;
+
+                explicit BoundValueRef( std::vector<T>& ref ): m_ref( ref ) {}
+
+                auto isContainer() const -> bool override { return true; }
+
+                auto setValue( std::string const& arg )
+                    -> ParserResult override {
+                    T temp;
+                    auto result = convertInto( arg, temp );
+                    if ( result )
+                        m_ref.push_back( temp );
+                    return result;
+                }
+            };
+
+            struct BoundFlagRef : BoundFlagRefBase {
+                bool& m_ref;
+
+                explicit BoundFlagRef( bool& ref ): m_ref( ref ) {}
+
+                ParserResult setFlag( bool flag ) override;
+            };
+
+            template <typename ReturnType> struct LambdaInvoker {
+                static_assert(
+                    std::is_same<ReturnType, ParserResult>::value,
+                    "Lambda must return void or clara::ParserResult" );
+
+                template <typename L, typename ArgType>
+                static auto invoke( L const& lambda, ArgType const& arg )
+                    -> ParserResult {
+                    return lambda( arg );
+                }
+            };
+
+            template <> struct LambdaInvoker<void> {
+                template <typename L, typename ArgType>
+                static auto invoke( L const& lambda, ArgType const& arg )
+                    -> ParserResult {
+                    lambda( arg );
+                    return ParserResult::ok( ParseResultType::Matched );
+                }
+            };
+
+            template <typename ArgType, typename L>
+            auto invokeLambda( L const& lambda, std::string const& arg )
+                -> ParserResult {
+                ArgType temp{};
+                auto result = convertInto( arg, temp );
+                return !result ? result
+                               : LambdaInvoker<typename UnaryLambdaTraits<
+                                     L>::ReturnType>::invoke( lambda, temp );
+            }
+
+            template <typename L> struct BoundLambda : BoundValueRefBase {
+                L m_lambda;
+
+                static_assert(
+                    UnaryLambdaTraits<L>::isValid,
+                    "Supplied lambda must take exactly one argument" );
+                explicit BoundLambda( L const& lambda ): m_lambda( lambda ) {}
+
+                auto setValue( std::string const& arg )
+                    -> ParserResult override {
+                    return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>(
+                        m_lambda, arg );
+                }
+            };
+
+            template <typename L> struct BoundManyLambda : BoundLambda<L> {
+                explicit BoundManyLambda( L const& lambda ): BoundLambda<L>( lambda ) {}
+                bool isContainer() const override { return true; }
+            };
+
+            template <typename L> struct BoundFlagLambda : BoundFlagRefBase {
+                L m_lambda;
+
+                static_assert(
+                    UnaryLambdaTraits<L>::isValid,
+                    "Supplied lambda must take exactly one argument" );
+                static_assert(
+                    std::is_same<typename UnaryLambdaTraits<L>::ArgType,
+                                 bool>::value,
+                    "flags must be boolean" );
+
+                explicit BoundFlagLambda( L const& lambda ):
+                    m_lambda( lambda ) {}
+
+                auto setFlag( bool flag ) -> ParserResult override {
+                    return LambdaInvoker<typename UnaryLambdaTraits<
+                        L>::ReturnType>::invoke( m_lambda, flag );
+                }
+            };
+
+            enum class Optionality { Optional, Required };
+
+            class ParserBase {
+            public:
+                virtual ~ParserBase() = default;
+                virtual auto validate() const -> Result { return Result::ok(); }
+                virtual auto parse( std::string const& exeName,
+                                    TokenStream const& tokens ) const
+                    -> InternalParseResult = 0;
+                virtual size_t cardinality() const;
+
+                InternalParseResult parse( Args const& args ) const;
+            };
+
+            template <typename DerivedT>
+            class ComposableParserImpl : public ParserBase {
+            public:
+                template <typename T>
+                auto operator|( T const& other ) const -> Parser;
+            };
+
+            // Common code and state for Args and Opts
+            template <typename DerivedT>
+            class ParserRefImpl : public ComposableParserImpl<DerivedT> {
+            protected:
+                Optionality m_optionality = Optionality::Optional;
+                std::shared_ptr<BoundRef> m_ref;
+                std::string m_hint;
+                std::string m_description;
+
+                explicit ParserRefImpl( std::shared_ptr<BoundRef> const& ref ):
+                    m_ref( ref ) {}
+
+            public:
+                template <typename LambdaT>
+                ParserRefImpl( accept_many_t,
+                               LambdaT const& ref,
+                               std::string const& hint ):
+                    m_ref( std::make_shared<BoundManyLambda<LambdaT>>( ref ) ),
+                    m_hint( hint ) {}
+
+                template <typename T,
+                          typename = typename std::enable_if_t<
+                              !Detail::is_unary_function<T>::value>>
+                ParserRefImpl( T& ref, std::string const& hint ):
+                    m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
+                    m_hint( hint ) {}
+
+                template <typename LambdaT,
+                          typename = typename std::enable_if_t<
+                              Detail::is_unary_function<LambdaT>::value>>
+                ParserRefImpl( LambdaT const& ref, std::string const& hint ):
+                    m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
+                    m_hint( hint ) {}
+
+                auto operator()( std::string const& description ) -> DerivedT& {
+                    m_description = description;
+                    return static_cast<DerivedT&>( *this );
+                }
+
+                auto optional() -> DerivedT& {
+                    m_optionality = Optionality::Optional;
+                    return static_cast<DerivedT&>( *this );
+                }
+
+                auto required() -> DerivedT& {
+                    m_optionality = Optionality::Required;
+                    return static_cast<DerivedT&>( *this );
+                }
+
+                auto isOptional() const -> bool {
+                    return m_optionality == Optionality::Optional;
+                }
+
+                auto cardinality() const -> size_t override {
+                    if ( m_ref->isContainer() )
+                        return 0;
+                    else
+                        return 1;
+                }
+
+                std::string const& hint() const { return m_hint; }
+            };
+
+        } // namespace detail
+
+
+        // A parser for arguments
+        class Arg : public Detail::ParserRefImpl<Arg> {
+        public:
+            using ParserRefImpl::ParserRefImpl;
+            using ParserBase::parse;
+
+            Detail::InternalParseResult
+                parse(std::string const&,
+                      Detail::TokenStream const& tokens) const override;
+        };
+
+        // A parser for options
+        class Opt : public Detail::ParserRefImpl<Opt> {
+        protected:
+            std::vector<std::string> m_optNames;
+
+        public:
+            template <typename LambdaT>
+            explicit Opt(LambdaT const& ref) :
+                ParserRefImpl(
+                    std::make_shared<Detail::BoundFlagLambda<LambdaT>>(ref)) {}
+
+            explicit Opt(bool& ref);
+
+            template <typename LambdaT,
+                      typename = typename std::enable_if_t<
+                          Detail::is_unary_function<LambdaT>::value>>
+            Opt( LambdaT const& ref, std::string const& hint ):
+                ParserRefImpl( ref, hint ) {}
+
+            template <typename LambdaT>
+            Opt( accept_many_t, LambdaT const& ref, std::string const& hint ):
+                ParserRefImpl( accept_many, ref, hint ) {}
+
+            template <typename T,
+                      typename = typename std::enable_if_t<
+                          !Detail::is_unary_function<T>::value>>
+            Opt( T& ref, std::string const& hint ):
+                ParserRefImpl( ref, hint ) {}
+
+            auto operator[](std::string const& optName) -> Opt& {
+                m_optNames.push_back(optName);
+                return *this;
+            }
+
+            std::vector<Detail::HelpColumns> getHelpColumns() const;
+
+            bool isMatch(std::string const& optToken) const;
+
+            using ParserBase::parse;
+
+            Detail::InternalParseResult
+                parse(std::string const&,
+                      Detail::TokenStream const& tokens) const override;
+
+            Detail::Result validate() const override;
+        };
+
+        // Specifies the name of the executable
+        class ExeName : public Detail::ComposableParserImpl<ExeName> {
+            std::shared_ptr<std::string> m_name;
+            std::shared_ptr<Detail::BoundValueRefBase> m_ref;
+
+        public:
+            ExeName();
+            explicit ExeName(std::string& ref);
+
+            template <typename LambdaT>
+            explicit ExeName(LambdaT const& lambda) : ExeName() {
+                m_ref = std::make_shared<Detail::BoundLambda<LambdaT>>(lambda);
+            }
+
+            // The exe name is not parsed out of the normal tokens, but is
+            // handled specially
+            Detail::InternalParseResult
+                parse(std::string const&,
+                      Detail::TokenStream const& tokens) const override;
+
+            std::string const& name() const { return *m_name; }
+            Detail::ParserResult set(std::string const& newName);
+        };
+
+
+        // A Combined parser
+        class Parser : Detail::ParserBase {
+            mutable ExeName m_exeName;
+            std::vector<Opt> m_options;
+            std::vector<Arg> m_args;
+
+        public:
+
+            auto operator|=(ExeName const& exeName) -> Parser& {
+                m_exeName = exeName;
+                return *this;
+            }
+
+            auto operator|=(Arg const& arg) -> Parser& {
+                m_args.push_back(arg);
+                return *this;
+            }
+
+            auto operator|=(Opt const& opt) -> Parser& {
+                m_options.push_back(opt);
+                return *this;
+            }
+
+            Parser& operator|=(Parser const& other);
+
+            template <typename T>
+            auto operator|(T const& other) const -> Parser {
+                return Parser(*this) |= other;
+            }
+
+            std::vector<Detail::HelpColumns> getHelpColumns() const;
+
+            void writeToStream(std::ostream& os) const;
+
+            friend auto operator<<(std::ostream& os, Parser const& parser)
+                -> std::ostream& {
+                parser.writeToStream(os);
+                return os;
+            }
+
+            Detail::Result validate() const override;
+
+            using ParserBase::parse;
+            Detail::InternalParseResult
+                parse(std::string const& exeName,
+                      Detail::TokenStream const& tokens) const override;
+        };
+
+        // Transport for raw args (copied from main args, or supplied via
+        // init list for testing)
+        class Args {
+            friend Detail::TokenStream;
+            std::string m_exeName;
+            std::vector<std::string> m_args;
+
+        public:
+            Args(int argc, char const* const* argv);
+            Args(std::initializer_list<std::string> args);
+
+            std::string const& exeName() const { return m_exeName; }
+        };
+
+
+        // Convenience wrapper for option parser that specifies the help option
+        struct Help : Opt {
+            Help(bool& showHelpFlag);
+        };
+
+        // Result type for parser operation
+        using Detail::ParserResult;
+
+        namespace Detail {
+            template <typename DerivedT>
+            template <typename T>
+            Parser
+                ComposableParserImpl<DerivedT>::operator|(T const& other) const {
+                return Parser() | static_cast<DerivedT const&>(*this) | other;
+            }
+        }
+
+    } // namespace Clara
+} // namespace Catch
+
+#if defined( __clang__ )
+#    pragma clang diagnostic pop
+#endif
+
+#if defined( __GNUC__ )
+#    pragma GCC diagnostic pop
+#endif
+
+#endif // CATCH_CLARA_HPP_INCLUDED
+
+namespace Catch {
+
+    struct ConfigData;
+
+    Clara::Parser makeCommandLineParser( ConfigData& config );
+
+} // end namespace Catch
+
+#endif // CATCH_COMMANDLINE_HPP_INCLUDED
+
+namespace Catch {
+
+    class Session : Detail::NonCopyable {
+    public:
+
+        Session();
+        ~Session();
+
+        void showHelp() const;
+        void libIdentify();
+
+        int applyCommandLine( int argc, char const * const * argv );
+    #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+        int applyCommandLine( int argc, wchar_t const * const * argv );
+    #endif
+
+        void useConfigData( ConfigData const& configData );
+
+        template<typename CharT>
+        int run(int argc, CharT const * const argv[]) {
+            if (m_startupExceptions)
+                return 1;
+            int returnCode = applyCommandLine(argc, argv);
+            if (returnCode == 0)
+                returnCode = run();
+            return returnCode;
+        }
+
+        int run();
+
+        Clara::Parser const& cli() const;
+        void cli( Clara::Parser const& newParser );
+        ConfigData& configData();
+        Config& config();
+    private:
+        int runInternal();
+
+        Clara::Parser m_cli;
+        ConfigData m_configData;
+        Detail::unique_ptr<Config> m_config;
+        bool m_startupExceptions = false;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_SESSION_HPP_INCLUDED
+
+
+#ifndef CATCH_TAG_ALIAS_HPP_INCLUDED
+#define CATCH_TAG_ALIAS_HPP_INCLUDED
+
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias {
+        TagAlias(std::string const& _tag, SourceLineInfo _lineInfo):
+            tag(_tag),
+            lineInfo(_lineInfo)
+        {}
+
+        std::string tag;
+        SourceLineInfo lineInfo;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_TAG_ALIAS_HPP_INCLUDED
+
+
+#ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
+#define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
+
+
+namespace Catch {
+
+    struct RegistrarForTagAliases {
+        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+    };
+
+} // 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_STOP_WARNINGS_SUPPRESSION
+
+#endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
+
+
+#ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
+#define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
+
+// We need this suppression to leak, because it took until GCC 10
+// for the front end to handle local suppression via _Pragma properly
+// inside templates (so `TEMPLATE_TEST_CASE` and co).
+// **THIS IS DIFFERENT FOR STANDARD TESTS, WHERE GCC 9 IS SUFFICIENT**
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 10
+#pragma GCC diagnostic ignored "-Wparentheses"
+#endif
+
+
+
+
+#ifndef CATCH_TEST_MACROS_HPP_INCLUDED
+#define CATCH_TEST_MACROS_HPP_INCLUDED
+
+
+
+#ifndef CATCH_TEST_MACRO_IMPL_HPP_INCLUDED
+#define CATCH_TEST_MACRO_IMPL_HPP_INCLUDED
+
+
+
+#ifndef CATCH_ASSERTION_HANDLER_HPP_INCLUDED
+#define CATCH_ASSERTION_HANDLER_HPP_INCLUDED
+
+
+
+#ifndef CATCH_DECOMPOSER_HPP_INCLUDED
+#define CATCH_DECOMPOSER_HPP_INCLUDED
+
+
+#include <iosfwd>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#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
+
+#ifdef __clang__
+#  pragma clang diagnostic push
+#  pragma clang diagnostic ignored "-Wsign-compare"
+#elif defined __GNUC__
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wsign-compare"
+#endif
+
+namespace Catch {
+
+    class ITransientExpression {
+        bool m_isBinaryExpression;
+        bool m_result;
+
+    public:
+        auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
+        auto getResult() const -> bool { return m_result; }
+        virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
+
+        ITransientExpression( bool isBinaryExpression, bool result )
+        :   m_isBinaryExpression( isBinaryExpression ),
+            m_result( result )
+        {}
+
+        ITransientExpression() = default;
+        ITransientExpression(ITransientExpression const&) = default;
+        ITransientExpression& operator=(ITransientExpression const&) = default;
+
+        // We don't actually need a virtual destructor, but many static analysers
+        // complain if it's not here :-(
+        virtual ~ITransientExpression(); // = default;
+
+        friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {
+            expr.streamReconstructedExpression(out);
+            return out;
+        }
+    };
+
+    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
+
+    template<typename LhsT, typename RhsT>
+    class BinaryExpr  : public ITransientExpression {
+        LhsT m_lhs;
+        StringRef m_op;
+        RhsT m_rhs;
+
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            formatReconstructedExpression
+                    ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
+        }
+
+    public:
+        BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
+        :   ITransientExpression{ true, comparisonResult },
+            m_lhs( lhs ),
+            m_op( op ),
+            m_rhs( rhs )
+        {}
+
+        template<typename T>
+        auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename T>
+        auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
+            static_assert(always_false<T>::value,
+            "chained comparisons are not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+    };
+
+    template<typename LhsT>
+    class UnaryExpr : public ITransientExpression {
+        LhsT m_lhs;
+
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            os << Catch::Detail::stringify( m_lhs );
+        }
+
+    public:
+        explicit UnaryExpr( LhsT lhs )
+        :   ITransientExpression{ false, static_cast<bool>(lhs) },
+            m_lhs( lhs )
+        {}
+    };
+
+
+    // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
+    template<typename LhsT, typename RhsT>
+    auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }
+    template<typename T>
+    auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+    template<typename T>
+    auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+
+    template<typename LhsT, typename RhsT>
+    auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }
+    template<typename T>
+    auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+    template<typename T>
+    auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+
+
+    template<typename LhsT>
+    class ExprLhs {
+        LhsT m_lhs;
+    public:
+        explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
+
+        template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0>
+        friend auto operator == ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> {
+            return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs };
+        }
+        template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0>
+        friend auto operator == ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> {
+            return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs };
+        }
+
+        template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0>
+        friend auto operator != ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> {
+            return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs };
+        }
+        template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0>
+        friend auto operator != ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> {
+            return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs };
+        }
+
+    #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \
+        template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0> \
+        friend auto operator op ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> { \
+            return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
+        } \
+        template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0> \
+        friend auto operator op ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> { \
+            return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
+        }
+
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<=)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>=)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^)
+
+    #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR
+
+        template<typename RhsT>
+        friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
+            static_assert(always_false<RhsT>::value,
+            "operator&& is not supported inside assertions, "
+            "wrap the expression inside parentheses, or decompose it");
+        }
+
+        template<typename RhsT>
+        friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT 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 };
+        }
+    };
+
+    struct Decomposer {
+        template<typename T, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<T>>::value, int> = 0>
+        friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
+            return ExprLhs<const T&>{ lhs };
+        }
+
+        template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
+        friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
+            return ExprLhs<T>{ value };
+        }
+    };
+
+} // end namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+#ifdef __clang__
+#  pragma clang diagnostic pop
+#elif defined __GNUC__
+#  pragma GCC diagnostic pop
+#endif
+
+#endif // CATCH_DECOMPOSER_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class IResultCapture;
+
+    struct AssertionReaction {
+        bool shouldDebugBreak = false;
+        bool shouldThrow = false;
+    };
+
+    class AssertionHandler {
+        AssertionInfo m_assertionInfo;
+        AssertionReaction m_reaction;
+        bool m_completed = false;
+        IResultCapture& m_resultCapture;
+
+    public:
+        AssertionHandler
+            (   StringRef macroName,
+                SourceLineInfo const& lineInfo,
+                StringRef capturedExpression,
+                ResultDisposition::Flags resultDisposition );
+        ~AssertionHandler() {
+            if ( !m_completed ) {
+                m_resultCapture.handleIncomplete( m_assertionInfo );
+            }
+        }
+
+
+        template<typename T>
+        void handleExpr( ExprLhs<T> const& expr ) {
+            handleExpr( expr.makeUnaryExpr() );
+        }
+        void handleExpr( ITransientExpression const& expr );
+
+        void handleMessage(ResultWas::OfType resultType, StringRef message);
+
+        void handleExceptionThrownAsExpected();
+        void handleUnexpectedExceptionNotThrown();
+        void handleExceptionNotThrownAsExpected();
+        void handleThrowingCallSkipped();
+        void handleUnexpectedInflightException();
+
+        void complete();
+        void setCompleted();
+
+        // query
+        auto allowThrows() const -> bool;
+    };
+
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString );
+
+} // namespace Catch
+
+#endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED
+
+// We need this suppression to leak, because it took until GCC 10
+// for the front end to handle local suppression via _Pragma properly
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9
+  #pragma GCC diagnostic ignored "-Wparentheses"
+#endif
+
+#if !defined(CATCH_CONFIG_DISABLE)
+
+#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
+  #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__
+#else
+  #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
+#endif
+
+#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+#define INTERNAL_CATCH_TRY
+#define INTERNAL_CATCH_CATCH( capturer )
+
+#else // CATCH_CONFIG_FAST_COMPILE
+
+#define INTERNAL_CATCH_TRY try
+#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
+
+#endif
+
+#define INTERNAL_CATCH_REACT( handler ) handler.complete();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
+    do { /* NOLINT(bugprone-infinite-loop) */ \
+        /* The expression should not be evaluated, but warnings should hopefully be checked */ \
+        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_STOP_WARNINGS_SUPPRESSION \
+        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( (void)0, (false) && static_cast<const 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 &&.
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
+    if( Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
+    if( !Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
+        try { \
+            CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+            CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
+            static_cast<void>(__VA_ARGS__); \
+            CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+            catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
+        } \
+        catch( ... ) { \
+            catchAssertionHandler.handleUnexpectedInflightException(); \
+        } \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+                CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
+                static_cast<void>(__VA_ARGS__); \
+                CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( ... ) { \
+                catchAssertionHandler.handleExceptionThrownAsExpected(); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+                CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
+                static_cast<void>(expr); \
+                CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( exceptionType const& ) { \
+                catchAssertionHandler.handleExceptionThrownAsExpected(); \
+            } \
+            catch( ... ) { \
+                catchAssertionHandler.handleUnexpectedInflightException(); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Although this is matcher-based, it can be used with just a string
+#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+                CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
+                static_cast<void>(__VA_ARGS__); \
+                CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( ... ) { \
+                Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+#endif // CATCH_CONFIG_DISABLE
+
+#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED
+
+
+#ifndef CATCH_SECTION_HPP_INCLUDED
+#define CATCH_SECTION_HPP_INCLUDED
+
+
+
+#ifndef CATCH_TIMER_HPP_INCLUDED
+#define CATCH_TIMER_HPP_INCLUDED
+
+#include <cstdint>
+
+namespace Catch {
+
+    class Timer {
+        uint64_t m_nanoseconds = 0;
+    public:
+        void start();
+        auto getElapsedNanoseconds() const -> uint64_t;
+        auto getElapsedMicroseconds() const -> uint64_t;
+        auto getElapsedMilliseconds() const -> unsigned int;
+        auto getElapsedSeconds() const -> double;
+    };
+
+} // namespace Catch
+
+#endif // CATCH_TIMER_HPP_INCLUDED
+
+namespace Catch {
+
+    class Section : Detail::NonCopyable {
+    public:
+        Section( SectionInfo&& info );
+        ~Section();
+
+        // This indicates whether the section should be executed or not
+        explicit operator bool() const;
+
+    private:
+        SectionInfo m_info;
+
+        Counts m_assertions;
+        bool m_sectionIncluded;
+        Timer m_timer;
+    };
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_SECTION( ... ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#endif // CATCH_SECTION_HPP_INCLUDED
+
+
+#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED
+#define CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+
+
+#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
+#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec;
+    struct TestCaseInfo;
+
+    class ITestInvoker {
+    public:
+        virtual void invoke () const = 0;
+        virtual ~ITestInvoker(); // = default
+    };
+
+    class TestCaseHandle;
+    class IConfig;
+
+    class ITestCaseRegistry {
+    public:
+        virtual ~ITestCaseRegistry(); // = default
+        // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
+        virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
+        virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
+        virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
+    };
+
+    bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
+    bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
+
+
+#ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
+#define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
+
+#define INTERNAL_CATCH_EXPAND1( param ) INTERNAL_CATCH_EXPAND2( param )
+#define INTERNAL_CATCH_EXPAND2( ... ) INTERNAL_CATCH_NO##__VA_ARGS__
+#define INTERNAL_CATCH_DEF( ... ) INTERNAL_CATCH_DEF __VA_ARGS__
+#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+
+#define INTERNAL_CATCH_REMOVE_PARENS( ... ) \
+    INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ )
+
+#endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED
+
+// GCC 5 and older do not properly handle disabling unused-variable warning
+// with a _Pragma. This means that we have to leak the suppression to the
+// user code as well :-(
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#endif
+
+
+
+namespace Catch {
+
+template<typename C>
+class TestInvokerAsMethod : public ITestInvoker {
+    void (C::*m_testAsMethod)();
+public:
+    TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
+
+    void invoke() const override {
+        C obj;
+        (obj.*m_testAsMethod)();
+    }
+};
+
+Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() );
+
+template<typename C>
+Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
+    return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod );
+}
+
+struct NameAndTags {
+    constexpr NameAndTags( StringRef name_ = StringRef(),
+                           StringRef tags_ = StringRef() ) noexcept:
+        name( name_ ), tags( tags_ ) {}
+    StringRef name;
+    StringRef tags;
+};
+
+struct AutoReg : Detail::NonCopyable {
+    AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+};
+
+} // end namespace Catch
+
+#if defined(CATCH_CONFIG_DISABLE)
+    #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+        static inline void TestName()
+    #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+        namespace{                        \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+                void test();              \
+            };                            \
+        }                                 \
+        void TestName::test()
+#endif
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+        static void TestName(); \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( ... ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+        namespace{ \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+        } \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        void TestName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        do { \
+            CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+            CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+            CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+            CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        } while(false)
+
+
+#endif // CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+
+// All of our user-facing macros support configuration toggle, that
+// forces them to be defined prefixed with CATCH_. We also like to
+// support another toggle that can minimize (disable) their implementation.
+// Given this, we have 4 different configuration options below
+
+#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+
+  #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_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+  #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+  #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+  #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+  #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+  #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __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_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+  #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+  #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+  #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+  #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+  #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+  #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+  #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
+  #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+  #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+  #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+
+  #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+    #define CATCH_STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__ ,      #__VA_ARGS__ );     CATCH_SUCCEED( #__VA_ARGS__ )
+    #define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
+    #define CATCH_STATIC_CHECK( ... )       static_assert(   __VA_ARGS__ ,      #__VA_ARGS__ );     CATCH_SUCCEED( #__VA_ARGS__ )
+    #define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
+  #else
+    #define CATCH_STATIC_REQUIRE( ... )       CATCH_REQUIRE( __VA_ARGS__ )
+    #define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ )
+    #define CATCH_STATIC_CHECK( ... )       CATCH_CHECK( __VA_ARGS__ )
+    #define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ )
+  #endif
+
+
+  // "BDD-style" convenience wrappers
+  #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+  #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+  #define CATCH_GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
+  #define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+  #define CATCH_WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
+  #define CATCH_AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+  #define CATCH_THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
+  #define CATCH_AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
+
+#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled
+
+  #define CATCH_REQUIRE( ... )        (void)(0)
+  #define CATCH_REQUIRE_FALSE( ... )  (void)(0)
+
+  #define CATCH_REQUIRE_THROWS( ... ) (void)(0)
+  #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+  #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0)
+
+  #define CATCH_CHECK( ... )         (void)(0)
+  #define CATCH_CHECK_FALSE( ... )   (void)(0)
+  #define CATCH_CHECKED_IF( ... )    if (__VA_ARGS__)
+  #define CATCH_CHECKED_ELSE( ... )  if (!(__VA_ARGS__))
+  #define CATCH_CHECK_NOFAIL( ... )  (void)(0)
+
+  #define CATCH_CHECK_THROWS( ... )  (void)(0)
+  #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+  #define CATCH_CHECK_NOTHROW( ... ) (void)(0)
+
+  #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
+  #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
+  #define CATCH_METHOD_AS_TEST_CASE( method, ... )
+  #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
+  #define CATCH_SECTION( ... )
+  #define CATCH_DYNAMIC_SECTION( ... )
+  #define CATCH_FAIL( ... ) (void)(0)
+  #define CATCH_FAIL_CHECK( ... ) (void)(0)
+  #define CATCH_SUCCEED( ... ) (void)(0)
+
+  #define CATCH_STATIC_REQUIRE( ... )       (void)(0)
+  #define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
+  #define CATCH_STATIC_CHECK( ... )       (void)(0)
+  #define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0)
+
+  // "BDD-style" convenience wrappers
+  #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
+  #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
+  #define CATCH_GIVEN( desc )
+  #define CATCH_AND_GIVEN( desc )
+  #define CATCH_WHEN( desc )
+  #define CATCH_AND_WHEN( desc )
+  #define CATCH_THEN( desc )
+  #define CATCH_AND_THEN( desc )
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented
+
+  #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
+  #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+
+  #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+  #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+  #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+  #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+  #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+  #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+  #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+  #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+
+  #define CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+  #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+  #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+  #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+  #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+  #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+  #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+  #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+  #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
+  #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+  #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+  #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+
+  #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+    #define STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__,  #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
+    #define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
+    #define STATIC_CHECK( ... )       static_assert(   __VA_ARGS__,  #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
+    #define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
+  #else
+    #define STATIC_REQUIRE( ... )       REQUIRE( __VA_ARGS__ )
+    #define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ )
+    #define STATIC_CHECK( ... )       CHECK( __VA_ARGS__ )
+    #define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ )
+  #endif
+
+  // "BDD-style" convenience wrappers
+  #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+  #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+  #define GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
+  #define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+  #define WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
+  #define AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+  #define THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
+  #define AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled
+
+  #define REQUIRE( ... )       (void)(0)
+  #define REQUIRE_FALSE( ... ) (void)(0)
+
+  #define REQUIRE_THROWS( ... ) (void)(0)
+  #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+  #define REQUIRE_NOTHROW( ... ) (void)(0)
+
+  #define CHECK( ... ) (void)(0)
+  #define CHECK_FALSE( ... ) (void)(0)
+  #define CHECKED_IF( ... ) if (__VA_ARGS__)
+  #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
+  #define CHECK_NOFAIL( ... ) (void)(0)
+
+  #define CHECK_THROWS( ... )  (void)(0)
+  #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+  #define CHECK_NOTHROW( ... ) (void)(0)
+
+  #define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
+  #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
+  #define METHOD_AS_TEST_CASE( method, ... )
+  #define REGISTER_TEST_CASE( Function, ... ) (void)(0)
+  #define SECTION( ... )
+  #define DYNAMIC_SECTION( ... )
+  #define FAIL( ... ) (void)(0)
+  #define FAIL_CHECK( ... ) (void)(0)
+  #define SUCCEED( ... ) (void)(0)
+
+  #define STATIC_REQUIRE( ... )       (void)(0)
+  #define STATIC_REQUIRE_FALSE( ... ) (void)(0)
+  #define STATIC_CHECK( ... )       (void)(0)
+  #define STATIC_CHECK_FALSE( ... ) (void)(0)
+
+  // "BDD-style" convenience wrappers
+  #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) )
+  #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
+
+  #define GIVEN( desc )
+  #define AND_GIVEN( desc )
+  #define WHEN( desc )
+  #define AND_WHEN( desc )
+  #define THEN( desc )
+  #define AND_THEN( desc )
+
+#endif // ^^ unprefixed, disabled
+
+// end of user facing macros
+
+#endif // CATCH_TEST_MACROS_HPP_INCLUDED
+
+
+#ifndef CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
+#define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
+
+
+
+#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED
+#define CATCH_PREPROCESSOR_HPP_INCLUDED
+
+
+#if defined(__GNUC__)
+// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work
+#pragma GCC system_header
+#endif
+
+
+#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
+#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
+
+#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
+// MSVC needs more evaluations
+#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
+#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
+#else
+#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL5(__VA_ARGS__)
+#endif
+
+#define CATCH_REC_END(...)
+#define CATCH_REC_OUT
+
+#define CATCH_EMPTY()
+#define CATCH_DEFER(id) id CATCH_EMPTY()
+
+#define CATCH_REC_GET_END2() 0, CATCH_REC_END
+#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
+#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
+#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
+#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
+#define CATCH_REC_NEXT(test, next)  CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
+
+#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2(f, x, peek, ...)   f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+
+#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...)   f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+
+// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
+// and passes userdata as the first parameter to each invocation,
+// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
+#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define INTERNAL_CATCH_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)
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
+#else
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#endif
+
+#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
+    CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
+
+#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
+#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
+#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
+#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
+#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
+#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
+#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
+#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
+#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
+#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
+
+#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define INTERNAL_CATCH_TYPE_GEN\
+    template<typename...> struct TypeList {};\
+    template<typename...Ts>\
+    constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
+    template<template<typename...> class...> struct TemplateTypeList{};\
+    template<template<typename...> class...Cs>\
+    constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
+    template<typename...>\
+    struct append;\
+    template<typename...>\
+    struct rewrap;\
+    template<template<typename...> class, typename...>\
+    struct create;\
+    template<template<typename...> class, typename>\
+    struct convert;\
+    \
+    template<typename T> \
+    struct append<T> { using type = T; };\
+    template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
+    struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
+    template< template<typename...> class L1, typename...E1, typename...Rest>\
+    struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
+    \
+    template< template<typename...> class Container, template<typename...> class List, typename...elems>\
+    struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
+    template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
+    struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
+    \
+    template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
+    struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
+    template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
+    struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
+    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
+    constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
+    \
+    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
+    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
+    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
+    template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
+    struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    static void TestName()
+
+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
+    template<typename Type>\
+    void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
+    template<typename Type>\
+    void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+    void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+    {\
+        Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+    }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
+    template<typename TestType> \
+    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
+        void test();\
+    }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
+        void test();\
+    }
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
+    template<typename TestType> \
+    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
+    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
+    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_NTTP_0
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+#else
+#define INTERNAL_CATCH_NTTP_0(signature)
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
+#endif
+
+#endif // CATCH_PREPROCESSOR_HPP_INCLUDED
+
+
+// GCC 5 and older do not properly handle disabling unused-variable warning
+// with a _Pragma. This means that we have to leak the suppression to the
+// user code as well :-(
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#endif
+
+#if defined(CATCH_CONFIG_DISABLE)
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... )  \
+        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... )    \
+        namespace{                                                                                  \
+            namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                      \
+            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+        }                                                                                           \
+        }                                                                                           \
+        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+    #endif
+#endif
+
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+        INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+            INTERNAL_CATCH_TYPE_GEN\
+            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            template<typename...Types> \
+            struct TestName{\
+                TestName(){\
+                    size_t index = 0;                                    \
+                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)}; /* NOLINT(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays,hicpp-avoid-c-arrays) */\
+                    using expander = size_t[]; /* NOLINT(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays,hicpp-avoid-c-arrays) */\
+                    (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+                }\
+            };\
+            static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+            TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+            return 0;\
+        }();\
+        }\
+        }\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION                      \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                      \
+        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS                \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS       \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_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() {                                          \
+                    size_t index = 0;                                    \
+                    using expander = size_t[];                           \
+                    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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_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() {                                          \
+                size_t index = 0;                                    \
+                using expander = size_t[];                           \
+                (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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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 \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_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(){\
+                    size_t index = 0;                                    \
+                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+                    using expander = size_t[];\
+                    (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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , 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 \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+        template<typename TestType> \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+                void test();\
+            };\
+        namespace {\
+        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
+            INTERNAL_CATCH_TYPE_GEN                  \
+            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+            template<typename...Types>\
+            struct TestNameClass{\
+                void reg_tests(){\
+                    std::size_t index = 0;\
+                    using expander = std::size_t[];\
+                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + std::string(types_list[index % num_types]) + '>', Tags } ), index++)... };/* NOLINT */ \
+                }\
+            };\
+            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+                using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
+                TestInit t;\
+                t.reg_tests();\
+                return 0;\
+            }(); \
+        }\
+        }\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        template<typename TestType> \
+        void TestName<TestType>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), 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 \
+        CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_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(){\
+                    size_t index = 0;\
+                    using expander = size_t[];\
+                    (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( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, TmplList )
+
+
+#endif // CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
+
+
+#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+
+  #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__ )
+    #define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
+    #define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( 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__ ) )
+    #define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
+    #define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+  #endif
+
+#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+
+  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+    #define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+    #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+    #define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+  #else
+    #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+    #define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+    #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+    #define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+  #endif
+
+  // When disabled, these can be shared between proper preprocessor and MSVC preprocessor
+  #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__ )
+  #define CATCH_TEMPLATE_LIST_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)
+  #define CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+
+  #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
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+
+  #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
+    #define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
+    #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
+    #define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
+  #else
+    #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
+    #define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
+    #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
+    #define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
+  #endif
+
+  // When disabled, these can be shared between proper preprocessor and MSVC preprocessor
+  #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__ )
+  #define TEMPLATE_LIST_TEST_CASE( ... ) TEMPLATE_TEST_CASE(__VA_ARGS__)
+  #define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+
+#endif // end of user facing macro declarations
+
+
+#endif // CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED
+
+
+#ifndef CATCH_TEST_CASE_INFO_HPP_INCLUDED
+#define CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+
+
+#include <string>
+#include <vector>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    /**
+     * A **view** of a tag string that provides case insensitive comparisons
+     *
+     * Note that in Catch2 internals, the square brackets around tags are
+     * not a part of tag's representation, so e.g. "[cool-tag]" is represented
+     * as "cool-tag" internally.
+     */
+    struct Tag {
+        constexpr Tag(StringRef original_):
+            original(original_)
+        {}
+        StringRef original;
+
+        friend bool operator< ( Tag const& lhs, Tag const& rhs );
+        friend bool operator==( Tag const& lhs, Tag const& rhs );
+    };
+
+    class ITestInvoker;
+
+    enum class TestCaseProperties : uint8_t {
+        None = 0,
+        IsHidden = 1 << 1,
+        ShouldFail = 1 << 2,
+        MayFail = 1 << 3,
+        Throws = 1 << 4,
+        NonPortable = 1 << 5,
+        Benchmark = 1 << 6
+    };
+
+    /**
+     * Various metadata about the test case.
+     *
+     * A test case is uniquely identified by its (class)name and tags
+     * combination, with source location being ignored, and other properties
+     * being determined from tags.
+     *
+     * Tags are kept sorted.
+     */
+    struct TestCaseInfo : Detail::NonCopyable {
+
+        TestCaseInfo(StringRef _className,
+                     NameAndTags const& _tags,
+                     SourceLineInfo const& _lineInfo);
+
+        bool isHidden() const;
+        bool throws() const;
+        bool okToFail() const;
+        bool expectedToFail() const;
+
+        // Adds the tag(s) with test's filename (for the -# flag)
+        void addFilenameTag();
+
+        //! Orders by name, classname and tags
+        friend bool operator<( TestCaseInfo const& lhs,
+                               TestCaseInfo const& rhs );
+
+
+        std::string tagsAsString() const;
+
+        std::string name;
+        StringRef className;
+    private:
+        std::string backingTags;
+        // Internally we copy tags to the backing storage and then add
+        // refs to this storage to the tags vector.
+        void internalAppendTag(StringRef tagString);
+    public:
+        std::vector<Tag> tags;
+        SourceLineInfo lineInfo;
+        TestCaseProperties properties = TestCaseProperties::None;
+    };
+
+    /**
+     * Wrapper over the test case information and the test case invoker
+     *
+     * Does not own either, and is specifically made to be cheap
+     * to copy around.
+     */
+    class TestCaseHandle {
+        TestCaseInfo* m_info;
+        ITestInvoker* m_invoker;
+    public:
+        TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
+            m_info(info), m_invoker(invoker) {}
+
+        void invoke() const {
+            m_invoker->invoke();
+        }
+
+        TestCaseInfo const& getTestCaseInfo() const;
+    };
+
+    Detail::unique_ptr<TestCaseInfo>
+    makeTestCaseInfo( StringRef className,
+                      NameAndTags const& nameAndTags,
+                      SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif // CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+
+#ifndef CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
+#define CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
+
+
+
+#ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
+#define CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
+
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+    using exceptionTranslateFunction = std::string(*)();
+
+    class IExceptionTranslator;
+    using ExceptionTranslators = std::vector<Detail::unique_ptr<IExceptionTranslator const>>;
+
+    class IExceptionTranslator {
+    public:
+        virtual ~IExceptionTranslator(); // = default
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+    };
+
+    class IExceptionTranslatorRegistry {
+    public:
+        virtual ~IExceptionTranslatorRegistry(); // = default
+        virtual std::string translateActiveException() const = 0;
+    };
+
+} // namespace Catch
+
+#endif // CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
+
+#include <exception>
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
+
+            ExceptionTranslator( std::string(*translateFunction)( T const& ) )
+            : m_translateFunction( translateFunction )
+            {}
+
+            std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+                try {
+                    if( it == itEnd )
+                        std::rethrow_exception(std::current_exception());
+                    else
+                        return (*it)->translate( it+1, itEnd );
+                }
+                catch( T const& ex ) {
+                    return m_translateFunction( ex );
+                }
+#else
+                return "You should never get here!";
+#endif
+            }
+
+        protected:
+            std::string(*m_translateFunction)( T const& );
+        };
+
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
+            getMutableRegistryHub().registerTranslator(
+                Detail::make_unique<ExceptionTranslator<T>>(translateFunction)
+            );
+        }
+    };
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+    static std::string translatorName( signature ); \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
+    CATCH_INTERNAL_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 )
+
+#if defined(CATCH_CONFIG_DISABLE)
+    #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \
+            static std::string translatorName( signature )
+#endif
+
+
+// This macro is always prefixed
+#if !defined(CATCH_CONFIG_DISABLE)
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+#else
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+#endif
+
+
+#endif // CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
+
+
+#ifndef CATCH_VERSION_HPP_INCLUDED
+#define CATCH_VERSION_HPP_INCLUDED
+
+#include <iosfwd>
+
+namespace Catch {
+
+    // Versioning information
+    struct Version {
+        Version( Version const& ) = delete;
+        Version& operator=( Version const& ) = delete;
+        Version(    unsigned int _majorVersion,
+                    unsigned int _minorVersion,
+                    unsigned int _patchNumber,
+                    char const * const _branchName,
+                    unsigned int _buildNumber );
+
+        unsigned int const majorVersion;
+        unsigned int const minorVersion;
+        unsigned int const patchNumber;
+
+        // buildNumber is only used if branchName is not null
+        char const * const branchName;
+        unsigned int const buildNumber;
+
+        friend std::ostream& operator << ( std::ostream& os, Version const& version );
+    };
+
+    Version const& libraryVersion();
+}
+
+#endif // CATCH_VERSION_HPP_INCLUDED
+
+
+#ifndef CATCH_VERSION_MACROS_HPP_INCLUDED
+#define CATCH_VERSION_MACROS_HPP_INCLUDED
+
+#define CATCH_VERSION_MAJOR 3
+#define CATCH_VERSION_MINOR 1
+#define CATCH_VERSION_PATCH 1
+
+#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
+
+
+/** \file
+ * This is a convenience header for Catch2's Generator support. It includes
+ * **all** of Catch2 headers related to generators.
+ *
+ * Generally the Catch2 users should use specific includes they need,
+ * but this header can be used instead for ease-of-experimentation, or
+ * just plain convenience, at the cost of (significantly) increased
+ * compilation times.
+ *
+ * When a new header is added to either the `generators` folder,
+ * or to the corresponding internal subfolder, it should be added here.
+ */
+
+#ifndef CATCH_GENERATORS_ALL_HPP_INCLUDED
+#define CATCH_GENERATORS_ALL_HPP_INCLUDED
+
+
+
+#ifndef CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED
+#define CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED
+
+#include <exception>
+
+namespace Catch {
+
+    // Exception type to be thrown when a Generator runs into an error,
+    // e.g. it cannot initialize the first return value based on
+    // runtime information
+    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;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED
+
+
+#ifndef CATCH_GENERATORS_HPP_INCLUDED
+#define CATCH_GENERATORS_HPP_INCLUDED
+
+
+
+#ifndef CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED
+#define CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED
+
+
+#include <string>
+
+namespace Catch {
+
+    namespace Generators {
+        class GeneratorUntypedBase {
+            // Caches result from `toStringImpl`, assume that when it is an
+            // empty string, the cache is invalidated.
+            mutable std::string m_stringReprCache;
+
+            // Counts based on `next` returning true
+            std::size_t m_currentElementIndex = 0;
+
+            /**
+             * 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;
+
+            //! Customization point for `currentElementAsString`
+            virtual std::string stringifyImpl() const = 0;
+
+        public:
+            GeneratorUntypedBase() = default;
+            // Generation of copy ops is deprecated (and Clang will complain)
+            // if there is a user destructor defined
+            GeneratorUntypedBase(GeneratorUntypedBase const&) = default;
+            GeneratorUntypedBase& operator=(GeneratorUntypedBase const&) = default;
+
+            virtual ~GeneratorUntypedBase(); // = default;
+
+            /**
+             * Attempts to move the generator to the next element
+             *
+             * Serves as a non-virtual interface to `next`, so that the
+             * top level interface can provide sanity checking and shared
+             * features.
+             *
+             * As with `next`, returns true iff the move succeeded and
+             * the generator has new valid element to provide.
+             */
+            bool countedNext();
+
+            std::size_t currentElementIndex() const { return m_currentElementIndex; }
+
+            /**
+             * Returns generator's current element as user-friendly string.
+             *
+             * By default returns string equivalent to calling
+             * `Catch::Detail::stringify` on the current element, but generators
+             * can customize their implementation as needed.
+             *
+             * Not thread-safe due to internal caching.
+             *
+             * The returned ref is valid only until the generator instance
+             * is destructed, or it moves onto the next element, whichever
+             * comes first.
+             */
+            StringRef currentElementAsString() const;
+        };
+        using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
+
+    } // namespace Generators
+
+    class IGeneratorTracker {
+    public:
+        virtual ~IGeneratorTracker(); // = default;
+        virtual auto hasGenerator() const -> bool = 0;
+        virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
+        virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
+    };
+
+} // namespace Catch
+
+#endif // CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED
+
+#include <vector>
+#include <tuple>
+
+namespace Catch {
+
+namespace Generators {
+
+namespace Detail {
+
+    //! Throws GeneratorException with the provided message
+    [[noreturn]]
+    void throw_generator_exception(char const * msg);
+
+} // end namespace detail
+
+    template<typename T>
+    class IGenerator : public GeneratorUntypedBase {
+        std::string stringifyImpl() const override {
+            return ::Catch::Detail::stringify( get() );
+        }
+
+    public:
+        ~IGenerator() override = default;
+        IGenerator() = default;
+        IGenerator(IGenerator const&) = default;
+        IGenerator& operator=(IGenerator const&) = 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>
+    using GeneratorPtr = Catch::Detail::unique_ptr<IGenerator<T>>;
+
+    template <typename T>
+    class GeneratorWrapper final {
+        GeneratorPtr<T> m_generator;
+    public:
+        //! Takes ownership of the passed pointer.
+        GeneratorWrapper(IGenerator<T>* generator):
+            m_generator(generator) {}
+        GeneratorWrapper(GeneratorPtr<T> generator):
+            m_generator(CATCH_MOVE(generator)) {}
+
+        T const& get() const {
+            return m_generator->get();
+        }
+        bool next() {
+            return m_generator->countedNext();
+        }
+    };
+
+
+    template<typename T>
+    class SingleValueGenerator final : public IGenerator<T> {
+        T m_value;
+    public:
+        SingleValueGenerator(T const& value) :
+            m_value(value)
+        {}
+        SingleValueGenerator(T&& value):
+            m_value(CATCH_MOVE(value))
+        {}
+
+        T const& get() const override {
+            return m_value;
+        }
+        bool next() override {
+            return false;
+        }
+    };
+
+    template<typename T>
+    class FixedValuesGenerator final : public IGenerator<T> {
+        static_assert(!std::is_same<T, bool>::value,
+            "FixedValuesGenerator does not support bools because of std::vector<bool>"
+            "specialization, use SingleValue Generator instead.");
+        std::vector<T> m_values;
+        size_t m_idx = 0;
+    public:
+        FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
+
+        T const& get() const override {
+            return m_values[m_idx];
+        }
+        bool next() override {
+            ++m_idx;
+            return m_idx < m_values.size();
+        }
+    };
+
+    template <typename T, typename DecayedT = std::decay_t<T>>
+    GeneratorWrapper<DecayedT> value( T&& value ) {
+        return GeneratorWrapper<DecayedT>(
+            Catch::Detail::make_unique<SingleValueGenerator<DecayedT>>(
+                CATCH_FORWARD( value ) ) );
+    }
+    template <typename T>
+    GeneratorWrapper<T> values(std::initializer_list<T> values) {
+        return GeneratorWrapper<T>(Catch::Detail::make_unique<FixedValuesGenerator<T>>(values));
+    }
+
+    template<typename T>
+    class Generators : public IGenerator<T> {
+        std::vector<GeneratorWrapper<T>> m_generators;
+        size_t m_current = 0;
+
+        void add_generator( GeneratorWrapper<T>&& generator ) {
+            m_generators.emplace_back( CATCH_MOVE( generator ) );
+        }
+        void add_generator( T const& val ) {
+            m_generators.emplace_back( value( val ) );
+        }
+        void add_generator( T&& val ) {
+            m_generators.emplace_back( value( CATCH_MOVE( val ) ) );
+        }
+        template <typename U>
+        std::enable_if_t<!std::is_same<std::decay_t<U>, T>::value>
+        add_generator( U&& val ) {
+            add_generator( T( CATCH_FORWARD( val ) ) );
+        }
+
+        template <typename U> void add_generators( U&& valueOrGenerator ) {
+            add_generator( CATCH_FORWARD( valueOrGenerator ) );
+        }
+
+        template <typename U, typename... Gs>
+        void add_generators( U&& valueOrGenerator, Gs&&... moreGenerators ) {
+            add_generator( CATCH_FORWARD( valueOrGenerator ) );
+            add_generators( CATCH_FORWARD( moreGenerators )... );
+        }
+
+    public:
+        template <typename... Gs>
+        Generators(Gs &&... moreGenerators) {
+            m_generators.reserve(sizeof...(Gs));
+            add_generators(CATCH_FORWARD(moreGenerators)...);
+        }
+
+        T const& get() const override {
+            return m_generators[m_current].get();
+        }
+
+        bool next() override {
+            if (m_current >= m_generators.size()) {
+                return false;
+            }
+            const bool current_status = m_generators[m_current].next();
+            if (!current_status) {
+                ++m_current;
+            }
+            return m_current < m_generators.size();
+        }
+    };
+
+
+    template <typename... Ts>
+    GeneratorWrapper<std::tuple<std::decay_t<Ts>...>>
+    table( std::initializer_list<std::tuple<std::decay_t<Ts>...>> 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>(CATCH_MOVE(generator), CATCH_FORWARD(moreGenerators)...);
+    }
+    template<typename T>
+    auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
+        return Generators<T>(CATCH_MOVE(generator));
+    }
+    template<typename T, typename... Gs>
+    auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<std::decay_t<T>> {
+        return makeGenerators( value( CATCH_FORWARD( val ) ), CATCH_FORWARD( moreGenerators )... );
+    }
+    template<typename T, typename U, typename... Gs>
+    auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
+        return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... );
+    }
+
+    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+
+    template<typename L>
+    // Note: The type after -> is weird, because VS2015 cannot parse
+    //       the expression used in the typedef inside, when it is in
+    //       return type. Yeah.
+    auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
+        using UnderlyingType = typename decltype(generatorExpression())::type;
+
+        IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
+        if (!tracker.hasGenerator()) {
+            tracker.setGenerator(Catch::Detail::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+        }
+
+        auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
+        return generator.get();
+    }
+
+} // namespace Generators
+} // namespace Catch
+
+#define GENERATE( ... ) \
+    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+                                 CATCH_INTERNAL_LINEINFO, \
+                                 [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_COPY( ... ) \
+    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+                                 CATCH_INTERNAL_LINEINFO, \
+                                 [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_REF( ... ) \
+    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+                                 CATCH_INTERNAL_LINEINFO, \
+                                 [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+
+#endif // CATCH_GENERATORS_HPP_INCLUDED
+
+
+#ifndef CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED
+#define CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED
+
+
+#include <cassert>
+
+namespace Catch {
+namespace Generators {
+
+    template <typename T>
+    class TakeGenerator final : public IGenerator<T> {
+        GeneratorWrapper<T> m_generator;
+        size_t m_returned = 0;
+        size_t m_target;
+    public:
+        TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
+            m_generator(CATCH_MOVE(generator)),
+            m_target(target)
+        {
+            assert(target != 0 && "Empty generators are not allowed");
+        }
+        T const& get() const override {
+            return m_generator.get();
+        }
+        bool next() override {
+            ++m_returned;
+            if (m_returned >= m_target) {
+                return false;
+            }
+
+            const auto success = m_generator.next();
+            // If the underlying generator does not contain enough values
+            // then we cut short as well
+            if (!success) {
+                m_returned = m_target;
+            }
+            return success;
+        }
+    };
+
+    template <typename T>
+    GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(Catch::Detail::make_unique<TakeGenerator<T>>(target, CATCH_MOVE(generator)));
+    }
+
+
+    template <typename T, typename Predicate>
+    class FilterGenerator final : public IGenerator<T> {
+        GeneratorWrapper<T> m_generator;
+        Predicate m_predicate;
+    public:
+        template <typename P = Predicate>
+        FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
+            m_generator(CATCH_MOVE(generator)),
+            m_predicate(CATCH_FORWARD(pred))
+        {
+            if (!m_predicate(m_generator.get())) {
+                // It might happen that there are no values that pass the
+                // filter. In that case we throw an exception.
+                auto has_initial_value = next();
+                if (!has_initial_value) {
+                    Detail::throw_generator_exception("No valid value found in filtered generator");
+                }
+            }
+        }
+
+        T const& get() const override {
+            return m_generator.get();
+        }
+
+        bool next() override {
+            bool success = m_generator.next();
+            if (!success) {
+                return false;
+            }
+            while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
+            return success;
+        }
+    };
+
+
+    template <typename T, typename Predicate>
+    GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
+    }
+
+    template <typename T>
+    class RepeatGenerator final : 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(CATCH_MOVE(generator)),
+            m_target_repeats(repeats)
+        {
+            assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
+        }
+
+        T const& get() const override {
+            if (m_current_repeat == 0) {
+                m_returned.push_back(m_generator.get());
+                return m_returned.back();
+            }
+            return m_returned[m_repeat_index];
+        }
+
+        bool next() override {
+            // There are 2 basic cases:
+            // 1) We are still reading the generator
+            // 2) We are reading our own cache
+
+            // In the first case, we need to poke the underlying generator.
+            // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
+            if (m_current_repeat == 0) {
+                const auto success = m_generator.next();
+                if (!success) {
+                    ++m_current_repeat;
+                }
+                return m_current_repeat < m_target_repeats;
+            }
+
+            // In the second case, we need to move indices forward and check that we haven't run up against the end
+            ++m_repeat_index;
+            if (m_repeat_index == m_returned.size()) {
+                m_repeat_index = 0;
+                ++m_current_repeat;
+            }
+            return m_current_repeat < m_target_repeats;
+        }
+    };
+
+    template <typename T>
+    GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(Catch::Detail::make_unique<RepeatGenerator<T>>(repeats, CATCH_MOVE(generator)));
+    }
+
+    template <typename T, typename U, typename Func>
+    class MapGenerator final : 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(CATCH_MOVE(generator)),
+            m_function(CATCH_FORWARD(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>(
+            Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
+        );
+    }
+
+    template <typename T, typename U, typename Func>
+    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+        return GeneratorWrapper<T>(
+            Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
+        );
+    }
+
+    template <typename T>
+    class ChunkGenerator final : public IGenerator<std::vector<T>> {
+        std::vector<T> m_chunk;
+        size_t m_chunk_size;
+        GeneratorWrapper<T> m_generator;
+        bool m_used_up = false;
+    public:
+        ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
+            m_chunk_size(size), m_generator(CATCH_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()) {
+                        Detail::throw_generator_exception("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>>(
+            Catch::Detail::make_unique<ChunkGenerator<T>>(size, CATCH_MOVE(generator))
+        );
+    }
+
+} // namespace Generators
+} // namespace Catch
+
+
+#endif // CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED
+
+
+#ifndef CATCH_GENERATORS_RANDOM_HPP_INCLUDED
+#define CATCH_GENERATORS_RANDOM_HPP_INCLUDED
+
+
+
+#ifndef CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED
+#define CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED
+
+#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
+
+#endif // CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED
+
+#include <random>
+
+namespace Catch {
+namespace Generators {
+namespace Detail {
+    // Returns a suitable seed for a random floating generator based off
+    // the primary internal rng. It does so by taking current value from
+    // the rng and returning it as the seed.
+    std::uint32_t getSeed();
+}
+
+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, std::uint32_t seed ):
+        m_rng(seed),
+        m_dist(a, b) {
+        static_cast<void>(next());
+    }
+
+    Float const& get() const override {
+        return m_current_number;
+    }
+    bool next() override {
+        m_current_number = m_dist(m_rng);
+        return true;
+    }
+};
+
+template <typename Integer>
+class RandomIntegerGenerator final : public IGenerator<Integer> {
+    Catch::SimplePcg32 m_rng;
+    std::uniform_int_distribution<Integer> m_dist;
+    Integer m_current_number;
+public:
+    RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
+        m_rng(seed),
+        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;
+    }
+};
+
+template <typename T>
+std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>>
+random(T a, T b) {
+    static_assert(
+        !std::is_same<T, char>::value &&
+        !std::is_same<T, int8_t>::value &&
+        !std::is_same<T, uint8_t>::value &&
+        !std::is_same<T, signed char>::value &&
+        !std::is_same<T, unsigned char>::value &&
+        !std::is_same<T, bool>::value,
+        "The requested type is not supported by the underlying random distributions from std" );
+    return GeneratorWrapper<T>(
+        Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
+    );
+}
+
+template <typename T>
+std::enable_if_t<std::is_floating_point<T>::value,
+GeneratorWrapper<T>>
+random(T a, T b) {
+    return GeneratorWrapper<T>(
+        Catch::Detail::make_unique<RandomFloatingGenerator<T>>(a, b, Detail::getSeed())
+    );
+}
+
+
+} // namespace Generators
+} // namespace Catch
+
+
+#endif // CATCH_GENERATORS_RANDOM_HPP_INCLUDED
+
+
+#ifndef CATCH_GENERATORS_RANGE_HPP_INCLUDED
+#define CATCH_GENERATORS_RANGE_HPP_INCLUDED
+
+
+#include <iterator>
+#include <type_traits>
+
+namespace Catch {
+namespace Generators {
+
+
+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>(Catch::Detail::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>(Catch::Detail::make_unique<RangeGenerator<T>>(start, end));
+}
+
+
+template <typename T>
+class IteratorGenerator final : public IGenerator<T> {
+    static_assert(!std::is_same<T, bool>::value,
+        "IteratorGenerator currently does not support bools"
+        "because of std::vector<bool> specialization");
+
+    std::vector<T> m_elems;
+    size_t m_current = 0;
+public:
+    template <typename InputIterator, typename InputSentinel>
+    IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
+        if (m_elems.empty()) {
+            Detail::throw_generator_exception("IteratorGenerator received no valid values");
+        }
+    }
+
+    T const& get() const override {
+        return m_elems[m_current];
+    }
+
+    bool next() override {
+        ++m_current;
+        return m_current != m_elems.size();
+    }
+};
+
+template <typename InputIterator,
+          typename InputSentinel,
+          typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
+GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
+    return GeneratorWrapper<ResultType>(Catch::Detail::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>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
+}
+
+
+} // namespace Generators
+} // namespace Catch
+
+
+#endif // CATCH_GENERATORS_RANGE_HPP_INCLUDED
+
+#endif // CATCH_GENERATORS_ALL_HPP_INCLUDED
+
+
+/** \file
+ * This is a convenience header for Catch2's interfaces. It includes
+ * **all** of Catch2 headers related to interfaces.
+ *
+ * Generally the Catch2 users should use specific includes they need,
+ * but this header can be used instead for ease-of-experimentation, or
+ * just plain convenience, at the cost of somewhat increased compilation
+ * times.
+ *
+ * When a new header is added to either the `interfaces` folder, or to
+ * the corresponding internal subfolder, it should be added here.
+ */
+
+
+#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED
+#define CATCH_INTERFACES_ALL_HPP_INCLUDED
+
+
+
+#ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
+#define CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
+
+
+#include <string>
+
+namespace Catch {
+
+    struct ReporterConfig;
+    class IConfig;
+    class IEventListener;
+    using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+
+
+    class IReporterFactory {
+    public:
+        virtual ~IReporterFactory(); // = default
+
+        virtual IEventListenerPtr
+        create( ReporterConfig&& config ) const = 0;
+        virtual std::string getDescription() const = 0;
+    };
+    using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
+
+    class EventListenerFactory {
+    public:
+        virtual ~EventListenerFactory(); // = default
+        virtual IEventListenerPtr create( IConfig const* config ) const = 0;
+        //! Return a meaningful name for the listener, e.g. its type name
+        virtual StringRef getName() const = 0;
+        //! Return listener's description if available
+        virtual std::string getDescription() const = 0;
+    };
+} // namespace Catch
+
+#endif // CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
+
+
+#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
+#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
+
+
+
+#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
+#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
+
+
+namespace Catch {
+    namespace Detail {
+        //! Provides case-insensitive `op<` semantics when called
+        struct CaseInsensitiveLess {
+            bool operator()( StringRef lhs,
+                             StringRef rhs ) const;
+        };
+
+        //! Provides case-insensitive `op==` semantics when called
+        struct CaseInsensitiveEqualTo {
+            bool operator()( StringRef lhs,
+                             StringRef rhs ) const;
+        };
+
+    } // namespace Detail
+} // namespace Catch
+
+#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace Catch {
+
+    class IConfig;
+
+    class IEventListener;
+    using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+    class IReporterFactory;
+    using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
+    struct ReporterConfig;
+    class EventListenerFactory;
+
+    class IReporterRegistry {
+    public:
+        using FactoryMap = std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>;
+        using Listeners = std::vector<Detail::unique_ptr<EventListenerFactory>>;
+
+        virtual ~IReporterRegistry(); // = default
+        virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0;
+        virtual FactoryMap const& getFactories() const = 0;
+        virtual Listeners const& getListeners() const = 0;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
+
+
+#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias;
+
+    class ITagAliasRegistry {
+    public:
+        virtual ~ITagAliasRegistry(); // = default
+        // Nullptr if not present
+        virtual TagAlias const* find( std::string const& alias ) const = 0;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+        static ITagAliasRegistry const& get();
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED
+
+
+
+/** \file
+ * Wrapper for ANDROID_LOGWRITE configuration option
+ *
+ * We want to default to enabling it when compiled for android, but
+ * users of the library should also be able to disable it if they want
+ * to.
+ */
+
+#ifndef CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
+#define CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
+
+
+#if defined(__ANDROID__)
+#    define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+#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
+
+#endif // CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED
+
+
+
+/** \file
+ * Wrapper for UNCAUGHT_EXCEPTIONS configuration option
+ *
+ * For some functionality, Catch2 requires to know whether there is
+ * an active exception. Because `std::uncaught_exception` is deprecated
+ * in C++17, we want to use `std::uncaught_exceptions` if possible.
+ */
+
+#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
+#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
+
+#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_INCLUDED
+
+
+#ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED
+#define CATCH_CONSOLE_WIDTH_HPP_INCLUDED
+
+// This include must be kept so that user's configured value for CONSOLE_WIDTH
+// is used before we attempt to provide a default value
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+#endif // CATCH_CONSOLE_WIDTH_HPP_INCLUDED
+
+
+#ifndef CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
+#define CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
+
+
+#include <cstddef>
+#include <initializer_list>
+
+// We want a simple polyfill over `std::empty`, `std::size` and so on
+// for C++14 or C++ libraries with incomplete support.
+// We also have to handle that MSVC std lib will happily provide these
+// under older standards.
+#if defined(CATCH_CPP17_OR_GREATER) || defined(_MSC_VER)
+
+// We are already using this header either way, so there shouldn't
+// be much additional overhead in including it to get the feature
+// test macros
+#include <string>
+
+#  if !defined(__cpp_lib_nonmember_container_access)
+#      define CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS
+#  endif
+
+#else
+#define CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS
+#endif
+
+
+
+namespace Catch {
+namespace Detail {
+
+#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
+    template <typename Container>
+    constexpr auto empty(Container const& cont) -> decltype(cont.empty()) {
+        return cont.empty();
+    }
+    template <typename T, std::size_t N>
+    constexpr bool empty(const T (&)[N]) noexcept {
+        // GCC < 7 does not support the const T(&)[] parameter syntax
+        // so we have to ignore the length explicitly
+        (void)N;
+        return false;
+    }
+    template <typename T>
+    constexpr bool empty(std::initializer_list<T> list) noexcept {
+        return list.size() > 0;
+    }
+
+
+    template <typename Container>
+    constexpr auto size(Container const& cont) -> decltype(cont.size()) {
+        return cont.size();
+    }
+    template <typename T, std::size_t N>
+    constexpr std::size_t size(const T(&)[N]) noexcept {
+        return N;
+    }
+#endif // CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS
+
+} // end namespace Detail
+} // end namespace Catch
+
+
+
+#endif // CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED
+
+
+#ifndef CATCH_DEBUG_CONSOLE_HPP_INCLUDED
+#define CATCH_DEBUG_CONSOLE_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+    void writeToDebugConsole( std::string const& text );
+}
+
+#endif // CATCH_DEBUG_CONSOLE_HPP_INCLUDED
+
+
+#ifndef CATCH_DEBUGGER_HPP_INCLUDED
+#define CATCH_DEBUGGER_HPP_INCLUDED
+
+
+namespace Catch {
+    bool isDebuggerActive();
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    #if defined(__i386__) || defined(__x86_64__)
+        #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
+    #elif defined(__aarch64__)
+        #define CATCH_TRAP()  __asm__(".inst 0xd43e0000")
+    #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
+    // directly at the location of the failing check instead of breaking inside
+    // raise() called from it, i.e. one stack frame below.
+    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
+    #else // Fall back to the generic way.
+        #include <signal.h>
+
+        #define CATCH_TRAP() raise(SIGTRAP)
+    #endif
+#elif defined(_MSC_VER)
+    #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+    #ifdef CATCH_TRAP
+        #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
+    #else
+        #define CATCH_BREAK_INTO_DEBUGGER() []{}()
+    #endif
+#endif
+
+#endif // CATCH_DEBUGGER_HPP_INCLUDED
+
+
+#ifndef CATCH_ENFORCE_HPP_INCLUDED
+#define CATCH_ENFORCE_HPP_INCLUDED
+
+
+#include <exception>
+
+namespace Catch {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    template <typename Ex>
+    [[noreturn]]
+    void throw_exception(Ex const& e) {
+        throw e;
+    }
+#else // ^^ Exceptions are enabled //  Exceptions are disabled vv
+    [[noreturn]]
+    void throw_exception(std::exception const& e);
+#endif
+
+    [[noreturn]]
+    void throw_logic_error(std::string const& msg);
+    [[noreturn]]
+    void throw_domain_error(std::string const& msg);
+    [[noreturn]]
+    void throw_runtime_error(std::string const& msg);
+
+} // namespace Catch;
+
+#define CATCH_MAKE_MSG(...) \
+    (Catch::ReusableStringStream() << __VA_ARGS__).str()
+
+#define CATCH_INTERNAL_ERROR(...) \
+    Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
+
+#define CATCH_ERROR(...) \
+    Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_RUNTIME_ERROR(...) \
+    Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
+
+#define CATCH_ENFORCE( condition, ... ) \
+    do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+
+
+#endif // CATCH_ENFORCE_HPP_INCLUDED
+
+
+#ifndef CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+#define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+
+
+#include <vector>
+
+namespace Catch {
+
+    namespace Detail {
+
+        Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
+
+        class EnumValuesRegistry : public IMutableEnumValuesRegistry {
+
+            std::vector<Catch::Detail::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
+
+#endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+
+
+#ifndef CATCH_ERRNO_GUARD_HPP_INCLUDED
+#define CATCH_ERRNO_GUARD_HPP_INCLUDED
+
+namespace Catch {
+
+    //! Simple RAII class that stores the value of `errno`
+    //! at construction and restores it at destruction.
+    class ErrnoGuard {
+    public:
+        // Keep these outlined to avoid dragging in macros from <cerrno>
+
+        ErrnoGuard();
+        ~ErrnoGuard();
+    private:
+        int m_oldErrno;
+    };
+
+}
+
+#endif // CATCH_ERRNO_GUARD_HPP_INCLUDED
+
+
+#ifndef CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+#define CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+
+#include <vector>
+#include <string>
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+    public:
+        ~ExceptionTranslatorRegistry() override;
+        void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
+        std::string translateActiveException() const override;
+        std::string tryTranslators() const;
+
+    private:
+        ExceptionTranslators m_translators;
+    };
+}
+
+#endif // CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+
+#ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
+#define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
+
+
+#include <cassert>
+
+namespace Catch {
+
+    /**
+     * 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() noexcept;
+    public:
+        // Should also have platform-specific implementations as needed
+        FatalConditionHandler();
+        ~FatalConditionHandler();
+
+        void engage() {
+            assert(!m_started && "Handler cannot be installed twice.");
+            m_started = true;
+            engage_platform();
+        }
+
+        void disengage() noexcept {
+            assert(m_started && "Handler cannot be uninstalled without being installed first");
+            m_started = false;
+            disengage_platform();
+        }
+    };
+
+    //! 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();
+        }
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
+
+
+#ifndef CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
+#define CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
+
+
+
+#ifndef CATCH_POLYFILLS_HPP_INCLUDED
+#define CATCH_POLYFILLS_HPP_INCLUDED
+
+namespace Catch {
+    bool isnan(float f);
+    bool isnan(double d);
+}
+
+#endif // CATCH_POLYFILLS_HPP_INCLUDED
+
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <utility>
+#include <limits>
+
+namespace Catch {
+    namespace Detail {
+
+        uint32_t convertToBits(float f);
+        uint64_t convertToBits(double d);
+
+    } // end namespace Detail
+
+
+
+#if defined( __GNUC__ ) || defined( __clang__ )
+#    pragma GCC diagnostic push
+    // We do a bunch of direct compensations of floating point numbers,
+    // because we know what we are doing and actually do want the direct
+    // comparison behaviour.
+#    pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+
+    /**
+     * Calculates the ULP distance between two floating point numbers
+     *
+     * The ULP distance of two floating point numbers is the count of
+     * valid floating point numbers representable between them.
+     *
+     * There are some exceptions between how this function counts the
+     * distance, and the interpretation of the standard as implemented.
+     * by e.g. `nextafter`. For this function it always holds that:
+     * * `(x == y) => ulpDistance(x, y) == 0` (so `ulpDistance(-0, 0) == 0`)
+     * * `ulpDistance(maxFinite, INF) == 1`
+     * * `ulpDistance(x, -x) == 2 * ulpDistance(x, 0)`
+     *
+     * \pre `!isnan( lhs )`
+     * \pre `!isnan( rhs )`
+     * \pre floating point numbers are represented in IEEE-754 format
+     */
+    template <typename FP>
+    uint64_t ulpDistance( FP lhs, FP rhs ) {
+        assert( std::numeric_limits<FP>::is_iec559 &&
+            "ulpDistance assumes IEEE-754 format for floating point types" );
+        assert( !Catch::isnan( lhs ) &&
+                "Distance between NaN and number is not meaningful" );
+        assert( !Catch::isnan( rhs ) &&
+                "Distance between NaN and number is not meaningful" );
+
+        // We want X == Y to imply 0 ULP distance even if X and Y aren't
+        // bit-equal (-0 and 0), or X - Y != 0 (same sign infinities).
+        if ( lhs == rhs ) { return 0; }
+
+        // We need a properly typed positive zero for type inference.
+        static constexpr FP positive_zero{};
+
+        // We want to ensure that +/- 0 is always represented as positive zero
+        if ( lhs == positive_zero ) { lhs = positive_zero; }
+        if ( rhs == positive_zero ) { rhs = positive_zero; }
+
+        // If arguments have different signs, we can handle them by summing
+        // how far are they from 0 each.
+        if ( std::signbit( lhs ) != std::signbit( rhs ) ) {
+            return ulpDistance( std::abs( lhs ), positive_zero ) +
+                   ulpDistance( std::abs( rhs ), positive_zero );
+        }
+
+        // When both lhs and rhs are of the same sign, we can just
+        // read the numbers bitwise as integers, and then subtract them
+        // (assuming IEEE).
+        uint64_t lc = Detail::convertToBits( lhs );
+        uint64_t rc = Detail::convertToBits( rhs );
+
+        // The ulp distance between two numbers is symmetric, so to avoid
+        // dealing with overflows we want the bigger converted number on the lhs
+        if ( lc < rc ) {
+            std::swap( lc, rc );
+        }
+
+        return lc - rc;
+    }
+
+#if defined( __GNUC__ ) || defined( __clang__ )
+#    pragma GCC diagnostic pop
+#endif
+
+
+} // end namespace Catch
+
+#endif // CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
+
+
+#ifndef CATCH_ISTREAM_HPP_INCLUDED
+#define CATCH_ISTREAM_HPP_INCLUDED
+
+
+#include <iosfwd>
+#include <cstddef>
+#include <ostream>
+#include <string>
+
+namespace Catch {
+
+    class IStream {
+    public:
+        virtual ~IStream(); // = default
+        virtual std::ostream& stream() = 0;
+        /**
+         * Best guess on whether the instance is writing to a console (e.g. via stdout/stderr)
+         *
+         * This is useful for e.g. Win32 colour support, because the Win32
+         * API manipulates console directly, unlike POSIX escape codes,
+         * that can be written anywhere.
+         *
+         * Due to variety of ways to change where the stdout/stderr is
+         * _actually_ being written, users should always assume that
+         * the answer might be wrong.
+         */
+        virtual bool isConsole() const { return false; }
+    };
+
+    /**
+     * Creates a stream wrapper that writes to specific file.
+     *
+     * Also recognizes 4 special filenames
+     * * `-` for stdout
+     * * `%stdout` for stdout
+     * * `%stderr` for stderr
+     * * `%debug` for platform specific debugging output
+     *
+     * \throws if passed an unrecognized %-prefixed stream
+     */
+    auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream>;
+
+}
+
+#endif // CATCH_STREAM_HPP_INCLUDED
+
+
+#ifndef CATCH_LEAK_DETECTOR_HPP_INCLUDED
+#define CATCH_LEAK_DETECTOR_HPP_INCLUDED
+
+namespace Catch {
+
+    struct LeakDetector {
+        LeakDetector();
+        ~LeakDetector();
+    };
+
+}
+#endif // CATCH_LEAK_DETECTOR_HPP_INCLUDED
+
+
+#ifndef CATCH_LIST_HPP_INCLUDED
+#define CATCH_LIST_HPP_INCLUDED
+
+
+#include <set>
+#include <string>
+
+
+namespace Catch {
+
+    class IEventListener;
+    class Config;
+
+
+    struct ReporterDescription {
+        std::string name, description;
+    };
+    struct ListenerDescription {
+        StringRef name;
+        std::string description;
+    };
+
+    struct TagInfo {
+        void add(StringRef spelling);
+        std::string all() const;
+
+        std::set<StringRef> spellings;
+        std::size_t count = 0;
+    };
+
+    bool list( IEventListener& reporter, Config const& config );
+
+} // end namespace Catch
+
+#endif // CATCH_LIST_HPP_INCLUDED
+
+
+#ifndef CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
+#define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
+
+
+#include <cstdio>
+#include <iosfwd>
+#include <string>
+
+namespace Catch {
+
+    class RedirectedStream {
+        std::ostream& m_originalStream;
+        std::ostream& m_redirectionStream;
+        std::streambuf* m_prevBuf;
+
+    public:
+        RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
+        ~RedirectedStream();
+    };
+
+    class RedirectedStdOut {
+        ReusableStringStream m_rss;
+        RedirectedStream m_cout;
+    public:
+        RedirectedStdOut();
+        auto str() const -> std::string;
+    };
+
+    // StdErr has two constituent streams in C++, std::cerr and std::clog
+    // This means that we need to redirect 2 streams into 1 to keep proper
+    // order of writes
+    class RedirectedStdErr {
+        ReusableStringStream m_rss;
+        RedirectedStream m_cerr;
+        RedirectedStream m_clog;
+    public:
+        RedirectedStdErr();
+        auto str() const -> std::string;
+    };
+
+    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
+    // to create a file inside system folder, thus requiring elevated
+    // privileges for the binary), so we have to use tmpnam(_s) and
+    // create the file ourselves there.
+    class TempFile {
+    public:
+        TempFile(TempFile const&) = delete;
+        TempFile& operator=(TempFile const&) = delete;
+        TempFile(TempFile&&) = delete;
+        TempFile& operator=(TempFile&&) = delete;
+
+        TempFile();
+        ~TempFile();
+
+        std::FILE* getFile();
+        std::string getContents();
+
+    private:
+        std::FILE* m_file = nullptr;
+    #if defined(_MSC_VER)
+        char m_buffer[L_tmpnam] = { 0 };
+    #endif
+    };
+
+
+    class OutputRedirect {
+    public:
+        OutputRedirect(OutputRedirect const&) = delete;
+        OutputRedirect& operator=(OutputRedirect const&) = delete;
+        OutputRedirect(OutputRedirect&&) = delete;
+        OutputRedirect& operator=(OutputRedirect&&) = delete;
+
+
+        OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
+        ~OutputRedirect();
+
+    private:
+        int m_originalStdout = -1;
+        int m_originalStderr = -1;
+        TempFile m_stdoutFile;
+        TempFile m_stderrFile;
+        std::string& m_stdoutDest;
+        std::string& m_stderrDest;
+    };
+
+#endif
+
+} // end namespace Catch
+
+#endif // CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+#define CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+
+#include <map>
+
+namespace Catch {
+
+    class ReporterRegistry : public IReporterRegistry {
+    public:
+
+        ReporterRegistry();
+        ~ReporterRegistry() override; // = default, out of line to allow fwd decl
+
+        IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override;
+
+        void registerReporter( std::string const& name, IReporterFactoryPtr factory );
+        void registerListener( Detail::unique_ptr<EventListenerFactory> factory );
+
+        FactoryMap const& getFactories() const override;
+        Listeners const& getListeners() const override;
+
+    private:
+        FactoryMap m_factories;
+        Listeners m_listeners;
+    };
+}
+
+#endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+
+#ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED
+#define CATCH_RUN_CONTEXT_HPP_INCLUDED
+
+
+
+#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+namespace TestCaseTracking {
+
+    struct NameAndLocation {
+        std::string name;
+        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;
+        }
+    };
+
+    class ITracker;
+
+    using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>;
+
+    class ITracker {
+        NameAndLocation m_nameAndLocation;
+
+        using Children = std::vector<ITrackerPtr>;
+
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+
+        ITracker* m_parent = nullptr;
+        Children m_children;
+        CycleState m_runState = NotStarted;
+
+    public:
+        ITracker( NameAndLocation const& nameAndLoc, ITracker* parent ):
+            m_nameAndLocation( nameAndLoc ),
+            m_parent( parent )
+        {}
+
+
+        // static queries
+        NameAndLocation const& nameAndLocation() const {
+            return m_nameAndLocation;
+        }
+        ITracker* parent() const {
+            return m_parent;
+        }
+
+        virtual ~ITracker(); // = default
+
+
+        // dynamic queries
+
+        //! Returns true if tracker run to completion (successfully or not)
+        virtual bool isComplete() const = 0;
+        //! Returns true if tracker run to completion succesfully
+        bool isSuccessfullyCompleted() const;
+        //! Returns true if tracker has started but hasn't been completed
+        bool isOpen() const;
+        //! Returns true iff tracker has started
+        bool hasStarted() const;
+
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        void markAsNeedingAnotherRun();
+
+        //! Register a nested ITracker
+        void addChild( ITrackerPtr&& child );
+        /**
+         * Returns ptr to specific child if register with this tracker.
+         *
+         * Returns nullptr if not found.
+         */
+        ITracker* findChild( NameAndLocation const& nameAndLocation );
+        //! Have any children been added?
+        bool hasChildren() const {
+            return !m_children.empty();
+        }
+
+
+        //! Marks tracker as executing a child, doing se recursively up the tree
+        void openChild();
+
+        /**
+         * Returns true if the instance is a section tracker
+         *
+         * Subclasses should override to true if they are, replaces RTTI
+         * for internal debug checks.
+         */
+        virtual bool isSectionTracker() const;
+        /**
+         * Returns true if the instance is a generator tracker
+         *
+         * Subclasses should override to true if they are, replaces RTTI
+         * for internal debug checks.
+         */
+        virtual bool isGeneratorTracker() const;
+    };
+
+    class TrackerContext {
+
+        enum RunState {
+            NotStarted,
+            Executing,
+            CompletedCycle
+        };
+
+        ITrackerPtr m_rootTracker;
+        ITracker* m_currentTracker = nullptr;
+        RunState m_runState = NotStarted;
+
+    public:
+
+        ITracker& startRun();
+        void endRun();
+
+        void startCycle();
+        void completeCycle();
+
+        bool completedCycle() const;
+        ITracker& currentTracker();
+        void setCurrentTracker( ITracker* tracker );
+    };
+
+    class TrackerBase : public ITracker {
+    protected:
+
+        TrackerContext& m_ctx;
+
+    public:
+        TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+        bool isComplete() const override;
+
+        void open();
+
+        void close() override;
+        void fail() override;
+
+    private:
+        void moveToParent();
+        void moveToThis();
+    };
+
+    class SectionTracker : public TrackerBase {
+        std::vector<StringRef> 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<StringRef> const& filters );
+        //! Returns filters active in this tracker
+        std::vector<StringRef> const& getFilters() const;
+        //! Returns whitespace-trimmed name of the tracked section
+        StringRef trimmedName() const;
+    };
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+
+} // namespace Catch
+
+#endif // CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class IMutableContext;
+    class IGeneratorTracker;
+    class IConfig;
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class RunContext : public IResultCapture {
+
+    public:
+        RunContext( RunContext const& ) = delete;
+        RunContext& operator =( RunContext const& ) = delete;
+
+        explicit RunContext( IConfig const* _config, IEventListenerPtr&& reporter );
+
+        ~RunContext() override;
+
+        Totals runTest(TestCaseHandle const& testCase);
+
+    public: // IResultCapture
+
+        // Assertion handlers
+        void handleExpr
+                (   AssertionInfo const& info,
+                    ITransientExpression const& expr,
+                    AssertionReaction& reaction ) override;
+        void handleMessage
+                (   AssertionInfo const& info,
+                    ResultWas::OfType resultType,
+                    StringRef message,
+                    AssertionReaction& reaction ) override;
+        void handleUnexpectedExceptionNotThrown
+                (   AssertionInfo const& info,
+                    AssertionReaction& reaction ) override;
+        void handleUnexpectedInflightException
+                (   AssertionInfo const& info,
+                    std::string const& message,
+                    AssertionReaction& reaction ) override;
+        void handleIncomplete
+                (   AssertionInfo const& info ) override;
+        void handleNonExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    AssertionReaction &reaction ) override;
+
+        bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
+
+        void sectionEnded( SectionEndInfo const& endInfo ) override;
+        void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
+
+        auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
+
+        void benchmarkPreparing( StringRef name ) override;
+        void benchmarkStarting( BenchmarkInfo const& info ) override;
+        void benchmarkEnded( BenchmarkStats<> const& stats ) override;
+        void benchmarkFailed( StringRef error ) override;
+
+        void pushScopedMessage( MessageInfo const& message ) override;
+        void popScopedMessage( MessageInfo const& message ) override;
+
+        void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
+
+        std::string getCurrentTestName() const override;
+
+        const AssertionResult* getLastResult() const override;
+
+        void exceptionEarlyReported() override;
+
+        void handleFatalErrorCondition( StringRef message ) override;
+
+        bool lastAssertionPassed() override;
+
+        void assertionPassed() override;
+
+    public:
+        // !TBD We need to do this another way!
+        bool aborting() const;
+
+    private:
+
+        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
+        void invokeActiveTestCase();
+
+        void resetAssertionInfo();
+        bool testForMissingAssertions( Counts& assertions );
+
+        void assertionEnded( AssertionResult const& result );
+        void reportExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    ITransientExpression const *expr,
+                    bool negated );
+
+        void populateReaction( AssertionReaction& reaction );
+
+    private:
+
+        void handleUnfinishedSections();
+
+        TestRunInfo m_runInfo;
+        IMutableContext& m_context;
+        TestCaseHandle const* m_activeTestCase = nullptr;
+        ITracker* m_testCaseTracker = nullptr;
+        Optional<AssertionResult> m_lastResult;
+
+        IConfig const* m_config;
+        Totals m_totals;
+        IEventListenerPtr 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
+
+#endif // CATCH_RUN_CONTEXT_HPP_INCLUDED
+
+
+#ifndef CATCH_SHARDING_HPP_INCLUDED
+#define CATCH_SHARDING_HPP_INCLUDED
+
+
+#include <cmath>
+#include <algorithm>
+
+namespace Catch {
+
+    template<typename Container>
+    Container createShard(Container const& container, std::size_t const shardCount, std::size_t const shardIndex) {
+        assert(shardCount > shardIndex);
+
+        if (shardCount == 1) {
+            return container;
+        }
+
+        const std::size_t totalTestCount = container.size();
+
+        const std::size_t shardSize = totalTestCount / shardCount;
+        const std::size_t leftoverTests = totalTestCount % shardCount;
+
+        const std::size_t startIndex = shardIndex * shardSize + (std::min)(shardIndex, leftoverTests);
+        const std::size_t endIndex = (shardIndex + 1) * shardSize + (std::min)(shardIndex + 1, leftoverTests);
+
+        auto startIterator = std::next(container.begin(), static_cast<std::ptrdiff_t>(startIndex));
+        auto endIterator = std::next(container.begin(), static_cast<std::ptrdiff_t>(endIndex));
+
+        return Container(startIterator, endIterator);
+    }
+
+}
+
+#endif // CATCH_SHARDING_HPP_INCLUDED
+
+
+#ifndef CATCH_SINGLETONS_HPP_INCLUDED
+#define CATCH_SINGLETONS_HPP_INCLUDED
+
+namespace Catch {
+
+    struct ISingleton {
+        virtual ~ISingleton(); // = default
+    };
+
+
+    void addSingleton( ISingleton* singleton );
+    void cleanupSingletons();
+
+
+    template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT>
+    class Singleton : SingletonImplT, public ISingleton {
+
+        static auto getInternal() -> Singleton* {
+            static Singleton* s_instance = nullptr;
+            if( !s_instance ) {
+                s_instance = new Singleton;
+                addSingleton( s_instance );
+            }
+            return s_instance;
+        }
+
+    public:
+        static auto get() -> InterfaceT const& {
+            return *getInternal();
+        }
+        static auto getMutable() -> MutableInterfaceT& {
+            return *getInternal();
+        }
+    };
+
+} // namespace Catch
+
+#endif // CATCH_SINGLETONS_HPP_INCLUDED
+
+
+#ifndef CATCH_STARTUP_EXCEPTION_REGISTRY_HPP_INCLUDED
+#define CATCH_STARTUP_EXCEPTION_REGISTRY_HPP_INCLUDED
+
+
+#include <vector>
+#include <exception>
+
+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
+
+#endif // CATCH_STARTUP_EXCEPTION_REGISTRY_HPP_INCLUDED
+
+
+
+#ifndef CATCH_STDSTREAMS_HPP_INCLUDED
+#define CATCH_STDSTREAMS_HPP_INCLUDED
+
+#include <iosfwd>
+
+namespace Catch {
+
+    std::ostream& cout();
+    std::ostream& cerr();
+    std::ostream& clog();
+
+} // namespace Catch
+
+#endif
+
+
+#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
+#define CATCH_STRING_MANIP_HPP_INCLUDED
+
+
+#include <string>
+#include <iosfwd>
+#include <vector>
+
+namespace Catch {
+
+    bool startsWith( std::string const& s, std::string const& prefix );
+    bool startsWith( StringRef s, char prefix );
+    bool endsWith( std::string const& s, std::string const& suffix );
+    bool endsWith( std::string const& s, char suffix );
+    bool contains( std::string const& s, std::string const& infix );
+    void toLowerInPlace( std::string& s );
+    std::string toLower( std::string const& s );
+    char toLower( char c );
+    //! 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 );
+
+    /**
+     * Helper for streaming a "count [maybe-plural-of-label]" human-friendly string
+     *
+     * Usage example:
+     * ```cpp
+     * std::cout << "Found " << pluralise(count, "error") << '\n';
+     * ```
+     *
+     * **Important:** The provided string must outlive the instance
+     */
+    class pluralise {
+        std::uint64_t m_count;
+        StringRef m_label;
+
+    public:
+        constexpr pluralise(std::uint64_t count, StringRef label):
+            m_count(count),
+            m_label(label)
+        {}
+
+        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+    };
+}
+
+#endif // CATCH_STRING_MANIP_HPP_INCLUDED
+
+
+#ifndef CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+#define CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+
+#include <map>
+#include <string>
+
+namespace Catch {
+    struct SourceLineInfo;
+
+    class TagAliasRegistry : public ITagAliasRegistry {
+    public:
+        ~TagAliasRegistry() override;
+        TagAlias const* find( std::string const& alias ) const override;
+        std::string expandAliases( std::string const& unexpandedTestSpec ) const override;
+        void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+    private:
+        std::map<std::string, TagAlias> m_registry;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+
+#ifndef CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED
+#define CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED
+
+#include <cstdint>
+
+namespace Catch {
+
+    struct TestCaseInfo;
+
+    class TestCaseInfoHasher {
+    public:
+        using hash_t = std::uint64_t;
+        TestCaseInfoHasher( hash_t seed );
+        uint32_t operator()( TestCaseInfo const& t ) const;
+
+    private:
+        hash_t m_seed;
+    };
+
+} // namespace Catch
+
+#endif /* CATCH_TEST_CASE_INFO_HASHER_HPP_INCLUDED */
+
+
+#ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+#define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+
+#include <vector>
+
+namespace Catch {
+
+    class TestCaseHandle;
+    class IConfig;
+    class TestSpec;
+
+    std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases );
+
+    bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
+    bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
+
+    void enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& functions );
+
+    std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
+
+    class TestRegistry : public ITestCaseRegistry {
+    public:
+        ~TestRegistry() override = default;
+
+        void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker );
+
+        std::vector<TestCaseInfo*> const& getAllInfos() const override;
+        std::vector<TestCaseHandle> const& getAllTests() const override;
+        std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const override;
+
+    private:
+        std::vector<Detail::unique_ptr<TestCaseInfo>> m_owned_test_infos;
+        // Keeps a materialized vector for `getAllInfos`.
+        // We should get rid of that eventually (see interface note)
+        std::vector<TestCaseInfo*> m_viewed_test_infos;
+
+        std::vector<Detail::unique_ptr<ITestInvoker>> m_invokers;
+        std::vector<TestCaseHandle> m_handles;
+        mutable TestRunOrder m_currentSortOrder = TestRunOrder::Declared;
+        mutable std::vector<TestCaseHandle> m_sortedFunctions;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class TestInvokerAsFunction final : public ITestInvoker {
+        using TestType = void(*)();
+        TestType m_testAsFunction;
+    public:
+        TestInvokerAsFunction(TestType testAsFunction) noexcept:
+            m_testAsFunction(testAsFunction) {}
+
+        void invoke() const override;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+
+} // end namespace Catch
+
+
+#endif // CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+
+#ifndef CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+#define CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+
+#include <vector>
+#include <string>
+
+namespace Catch {
+
+    class ITagAliasRegistry;
+
+    class TestSpecParser {
+        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+        Mode m_mode = None;
+        Mode lastMode = None;
+        bool m_exclusion = false;
+        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;
+        ITagAliasRegistry const* m_tagAliases = nullptr;
+
+    public:
+        TestSpecParser( ITagAliasRegistry const& tagAliases );
+
+        TestSpecParser& parse( std::string const& arg );
+        TestSpec testSpec();
+
+    private:
+        bool visitChar( char c );
+        void startNewMode( Mode mode );
+        bool processNoneChar( char c );
+        void processNameChar( char c );
+        bool processOtherChar( char c );
+        void endMode();
+        void escape();
+        bool isControlChar( char c ) const;
+        void saveLastMode();
+        void revertBackToLastMode();
+        void addFilter();
+        bool separate();
+
+        // 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++;
+        }
+
+    };
+    TestSpec parseTestSpec( std::string const& arg );
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif // CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+
+#ifndef CATCH_TEXTFLOW_HPP_INCLUDED
+#define CATCH_TEXTFLOW_HPP_INCLUDED
+
+#include <cassert>
+#include <string>
+#include <vector>
+
+namespace Catch {
+    namespace TextFlow {
+
+        class Columns;
+
+        /**
+         * Represents a column of text with specific width and indentation
+         *
+         * When written out to a stream, it will perform linebreaking
+         * of the provided text so that the written lines fit within
+         * target width.
+         */
+        class Column {
+            // String to be written out
+            std::string m_string;
+            // Width of the column for linebreaking
+            size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1;
+            // Indentation of other lines (including first if initial indent is unset)
+            size_t m_indent = 0;
+            // Indentation of the first line
+            size_t m_initialIndent = std::string::npos;
+
+        public:
+            /**
+             * Iterates "lines" in `Column` and return sthem
+             */
+            class const_iterator {
+                friend Column;
+                struct EndTag {};
+
+                Column const& m_column;
+                // Where does the current line start?
+                size_t m_lineStart = 0;
+                // How long should the current line be?
+                size_t m_lineLength = 0;
+                // How far have we checked the string to iterate?
+                size_t m_parsedTo = 0;
+                // Should a '-' be appended to the line?
+                bool m_addHyphen = false;
+
+                const_iterator( Column const& column, EndTag ):
+                    m_column( column ), m_lineStart( m_column.m_string.size() ) {}
+
+                // Calculates the length of the current line
+                void calcLength();
+
+                // Returns current indention width
+                size_t indentSize() const;
+
+                // Creates an indented and (optionally) suffixed string from
+                // current iterator position, indentation and length.
+                std::string addIndentAndSuffix( size_t position,
+                                                size_t length ) const;
+
+            public:
+                using difference_type = std::ptrdiff_t;
+                using value_type = std::string;
+                using pointer = value_type*;
+                using reference = value_type&;
+                using iterator_category = std::forward_iterator_tag;
+
+                explicit const_iterator( Column const& column );
+
+                std::string operator*() const;
+
+                const_iterator& operator++();
+                const_iterator operator++( int );
+
+                bool operator==( const_iterator const& other ) const {
+                    return m_lineStart == other.m_lineStart && &m_column == &other.m_column;
+                }
+                bool operator!=( const_iterator const& other ) const {
+                    return !operator==( other );
+                }
+            };
+            using iterator = const_iterator;
+
+            explicit Column( std::string const& text ): m_string( text ) {}
+
+            Column& width( size_t newWidth ) {
+                assert( newWidth > 0 );
+                m_width = newWidth;
+                return *this;
+            }
+            Column& indent( size_t newIndent ) {
+                m_indent = newIndent;
+                return *this;
+            }
+            Column& initialIndent( size_t newIndent ) {
+                m_initialIndent = newIndent;
+                return *this;
+            }
+
+            size_t width() const { return m_width; }
+            const_iterator begin() const { return const_iterator( *this ); }
+            const_iterator end() const { return { *this, const_iterator::EndTag{} }; }
+
+            friend std::ostream& operator<<( std::ostream& os,
+                                             Column const& col );
+
+            Columns operator+( Column const& other );
+        };
+
+        //! Creates a column that serves as an empty space of specific width
+        Column Spacer( size_t spaceWidth );
+
+        class Columns {
+            std::vector<Column> m_columns;
+
+        public:
+            class iterator {
+                friend Columns;
+                struct EndTag {};
+
+                std::vector<Column> const& m_columns;
+                std::vector<Column::const_iterator> m_iterators;
+                size_t m_activeIterators;
+
+                iterator( Columns const& columns, EndTag );
+
+            public:
+                using difference_type = std::ptrdiff_t;
+                using value_type = std::string;
+                using pointer = value_type*;
+                using reference = value_type&;
+                using iterator_category = std::forward_iterator_tag;
+
+                explicit iterator( Columns const& columns );
+
+                auto operator==( iterator const& other ) const -> bool {
+                    return m_iterators == other.m_iterators;
+                }
+                auto operator!=( iterator const& other ) const -> bool {
+                    return m_iterators != other.m_iterators;
+                }
+                std::string operator*() const;
+                iterator& operator++();
+                iterator operator++( int );
+            };
+            using const_iterator = iterator;
+
+            iterator begin() const { return iterator( *this ); }
+            iterator end() const { return { *this, iterator::EndTag() }; }
+
+            Columns& operator+=( Column const& col );
+            Columns operator+( Column const& col );
+
+            friend std::ostream& operator<<( std::ostream& os,
+                                             Columns const& cols );
+        };
+
+    } // namespace TextFlow
+} // namespace Catch
+#endif // CATCH_TEXTFLOW_HPP_INCLUDED
+
+
+#ifndef CATCH_TO_STRING_HPP_INCLUDED
+#define CATCH_TO_STRING_HPP_INCLUDED
+
+#include <string>
+
+
+namespace Catch {
+    template <typename T>
+    std::string to_string(T const& t) {
+#if defined(CATCH_CONFIG_CPP11_TO_STRING)
+        return std::to_string(t);
+#else
+        ReusableStringStream rss;
+        rss << t;
+        return rss.str();
+#endif
+    }
+} // end namespace Catch
+
+#endif // CATCH_TO_STRING_HPP_INCLUDED
+
+
+#ifndef CATCH_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
+#define CATCH_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
+
+namespace Catch {
+    bool uncaught_exceptions();
+} // end namespace Catch
+
+#endif // CATCH_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
+
+
+#ifndef CATCH_XMLWRITER_HPP_INCLUDED
+#define CATCH_XMLWRITER_HPP_INCLUDED
+
+
+#include <iosfwd>
+#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);
+
+    /**
+     * Helper for XML-encoding text (escaping angle brackets, quotes, etc)
+     *
+     * Note: doesn't take ownership of passed strings, and thus the
+     *       encoded string must outlive the encoding instance.
+     */
+    class XmlEncode {
+    public:
+        enum ForWhat { ForTextNodes, ForAttributes };
+
+        XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes );
+
+        void encodeTo( std::ostream& os ) const;
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
+
+    private:
+        StringRef m_str;
+        ForWhat m_forWhat;
+    };
+
+    class XmlWriter {
+    public:
+
+        class ScopedElement {
+        public:
+            ScopedElement( XmlWriter* writer, XmlFormatting fmt );
+
+            ScopedElement( ScopedElement&& other ) noexcept;
+            ScopedElement& operator=( ScopedElement&& other ) noexcept;
+
+            ~ScopedElement();
+
+            ScopedElement&
+            writeText( StringRef text,
+                       XmlFormatting fmt = XmlFormatting::Newline |
+                                           XmlFormatting::Indent );
+
+            ScopedElement& writeAttribute( StringRef name,
+                                           StringRef attribute );
+            template <typename T,
+                      // Without this SFINAE, this overload is a better match
+                      // for `std::string`, `char const*`, `char const[N]` args.
+                      // While it would still work, it would cause code bloat
+                      // and multiple iteration over the strings
+                      typename = typename std::enable_if_t<
+                          !std::is_convertible<T, StringRef>::value>>
+            ScopedElement& writeAttribute( StringRef name,
+                                           T const& attribute ) {
+                m_writer->writeAttribute( name, attribute );
+                return *this;
+            }
+
+        private:
+            XmlWriter* m_writer = nullptr;
+            XmlFormatting m_fmt;
+        };
+
+        XmlWriter( std::ostream& os );
+        ~XmlWriter();
+
+        XmlWriter( XmlWriter const& ) = delete;
+        XmlWriter& operator=( XmlWriter const& ) = delete;
+
+        XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+
+        //! The attribute content is XML-encoded
+        XmlWriter& writeAttribute( StringRef name, StringRef attribute );
+
+        //! Writes the attribute as "true/false"
+        XmlWriter& writeAttribute( StringRef name, bool attribute );
+
+        //! The attribute content is XML-encoded
+        XmlWriter& writeAttribute( StringRef name, char const* attribute );
+
+        //! The attribute value must provide op<<(ostream&, T). The resulting
+        //! serialization is XML-encoded
+        template <typename T,
+                  // Without this SFINAE, this overload is a better match
+                  // for `std::string`, `char const*`, `char const[N]` args.
+                  // While it would still work, it would cause code bloat
+                  // and multiple iteration over the strings
+                  typename = typename std::enable_if_t<
+                      !std::is_convertible<T, StringRef>::value>>
+        XmlWriter& writeAttribute( StringRef name, T const& attribute ) {
+            ReusableStringStream rss;
+            rss << attribute;
+            return writeAttribute( name, rss.str() );
+        }
+
+        //! Writes escaped `text` in a element
+        XmlWriter& writeText( StringRef text,
+                              XmlFormatting fmt = XmlFormatting::Newline |
+                                                  XmlFormatting::Indent );
+
+        //! Writes XML comment as "<!-- text -->"
+        XmlWriter& writeComment( StringRef text,
+                                 XmlFormatting fmt = XmlFormatting::Newline |
+                                                     XmlFormatting::Indent );
+
+        void writeStylesheetRef( StringRef url );
+
+        void ensureTagClosed();
+
+    private:
+
+        void applyFormatting(XmlFormatting fmt);
+
+        void writeDeclaration();
+
+        void newlineIfNecessary();
+
+        bool m_tagIsOpen = false;
+        bool m_needsNewline = false;
+        std::vector<std::string> m_tags;
+        std::string m_indent;
+        std::ostream& m_os;
+    };
+
+}
+
+#endif // CATCH_XMLWRITER_HPP_INCLUDED
+
+
+/** \file
+ * This is a convenience header for Catch2's Matcher support. It includes
+ * **all** of Catch2 headers related to matchers.
+ *
+ * Generally the Catch2 users should use specific includes they need,
+ * but this header can be used instead for ease-of-experimentation, or
+ * just plain convenience, at the cost of increased compilation times.
+ *
+ * When a new header is added to either the `matchers` folder, or to
+ * the corresponding internal subfolder, it should be added here.
+ */
+
+#ifndef CATCH_MATCHERS_ALL_HPP_INCLUDED
+#define CATCH_MATCHERS_ALL_HPP_INCLUDED
+
+
+
+#ifndef CATCH_MATCHERS_HPP_INCLUDED
+#define CATCH_MATCHERS_HPP_INCLUDED
+
+
+
+#ifndef CATCH_MATCHERS_IMPL_HPP_INCLUDED
+#define CATCH_MATCHERS_IMPL_HPP_INCLUDED
+
+
+namespace Catch {
+
+    template<typename ArgT, typename MatcherT>
+    class MatchExpr : public ITransientExpression {
+        ArgT && m_arg;
+        MatcherT const& m_matcher;
+        StringRef m_matcherString;
+    public:
+        MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString )
+        :   ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose
+            m_arg( CATCH_FORWARD(arg) ),
+            m_matcher( matcher ),
+            m_matcherString( matcherString )
+        {}
+
+        void streamReconstructedExpression( std::ostream& os ) const override {
+            os << Catch::Detail::stringify( m_arg )
+               << ' '
+               << m_matcher.toString();
+        }
+    };
+
+    namespace Matchers {
+        template <typename ArgT>
+        class MatcherBase;
+    }
+
+    using StringMatcher = Matchers::MatcherBase<std::string>;
+
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString  );
+
+    template<typename ArgT, typename MatcherT>
+    auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString  ) -> MatchExpr<ArgT, MatcherT> {
+        return MatchExpr<ArgT, MatcherT>( CATCH_FORWARD(arg), matcher, matcherString );
+    }
+
+} // namespace Catch
+
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        INTERNAL_CATCH_TRY { \
+            catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \
+        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                static_cast<void>(__VA_ARGS__ ); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( exceptionType const& ex ) { \
+                catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \
+            } \
+            catch( ... ) { \
+                catchAssertionHandler.handleUnexpectedInflightException(); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+
+#endif // CATCH_MATCHERS_IMPL_HPP_INCLUDED
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+namespace Matchers {
+
+    class MatcherUntypedBase {
+    public:
+        MatcherUntypedBase() = default;
+
+        MatcherUntypedBase(MatcherUntypedBase const&) = default;
+        MatcherUntypedBase(MatcherUntypedBase&&) = default;
+
+        MatcherUntypedBase& operator = (MatcherUntypedBase const&) = delete;
+        MatcherUntypedBase& operator = (MatcherUntypedBase&&) = delete;
+
+        std::string toString() const;
+
+    protected:
+        virtual ~MatcherUntypedBase(); // = default;
+        virtual std::string describe() const = 0;
+        mutable std::string m_cachedToString;
+    };
+
+
+    template<typename T>
+    class MatcherBase : public MatcherUntypedBase {
+    public:
+        virtual bool match( T const& arg ) const = 0;
+    };
+
+    namespace Detail {
+
+        template<typename ArgT>
+        class MatchAllOf final : public MatcherBase<ArgT> {
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+
+        public:
+            MatchAllOf() = default;
+            MatchAllOf(MatchAllOf const&) = delete;
+            MatchAllOf& operator=(MatchAllOf const&) = delete;
+            MatchAllOf(MatchAllOf&&) = default;
+            MatchAllOf& operator=(MatchAllOf&&) = default;
+
+
+            bool match( ArgT const& arg ) const override {
+                for( auto matcher : m_matchers ) {
+                    if (!matcher->match(arg))
+                        return false;
+                }
+                return true;
+            }
+            std::string describe() const override {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                bool first = true;
+                for( auto matcher : m_matchers ) {
+                    if( first )
+                        first = false;
+                    else
+                        description += " and ";
+                    description += matcher->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
+                lhs.m_matchers.push_back(&rhs);
+                return CATCH_MOVE(lhs);
+            }
+            friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
+                rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
+                return CATCH_MOVE(rhs);
+            }
+        };
+
+        //! lvalue overload is intentionally deleted, users should
+        //! not be trying to compose stored composition matchers
+        template<typename ArgT>
+        MatchAllOf<ArgT> operator&& (MatchAllOf<ArgT> const& lhs, MatcherBase<ArgT> const& rhs) = delete;
+        //! lvalue overload is intentionally deleted, users should
+        //! not be trying to compose stored composition matchers
+        template<typename ArgT>
+        MatchAllOf<ArgT> operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf<ArgT> const& rhs) = delete;
+
+        template<typename ArgT>
+        class MatchAnyOf final : public MatcherBase<ArgT> {
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        public:
+            MatchAnyOf() = default;
+            MatchAnyOf(MatchAnyOf const&) = delete;
+            MatchAnyOf& operator=(MatchAnyOf const&) = delete;
+            MatchAnyOf(MatchAnyOf&&) = default;
+            MatchAnyOf& operator=(MatchAnyOf&&) = default;
+
+            bool match( ArgT const& arg ) const override {
+                for( auto matcher : m_matchers ) {
+                    if (matcher->match(arg))
+                        return true;
+                }
+                return false;
+            }
+            std::string describe() const override {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                bool first = true;
+                for( auto matcher : m_matchers ) {
+                    if( first )
+                        first = false;
+                    else
+                        description += " or ";
+                    description += matcher->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
+                lhs.m_matchers.push_back(&rhs);
+                return CATCH_MOVE(lhs);
+            }
+            friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
+                rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
+                return CATCH_MOVE(rhs);
+            }
+        };
+
+        //! lvalue overload is intentionally deleted, users should
+        //! not be trying to compose stored composition matchers
+        template<typename ArgT>
+        MatchAnyOf<ArgT> operator|| (MatchAnyOf<ArgT> const& lhs, MatcherBase<ArgT> const& rhs) = delete;
+        //! lvalue overload is intentionally deleted, users should
+        //! not be trying to compose stored composition matchers
+        template<typename ArgT>
+        MatchAnyOf<ArgT> operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf<ArgT> const& rhs) = delete;
+
+        template<typename ArgT>
+        class MatchNotOf final : public MatcherBase<ArgT> {
+            MatcherBase<ArgT> const& m_underlyingMatcher;
+
+        public:
+            explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
+                m_underlyingMatcher( underlyingMatcher )
+            {}
+
+            bool match( ArgT const& arg ) const override {
+                return !m_underlyingMatcher.match( arg );
+            }
+
+            std::string describe() const override {
+                return "not " + m_underlyingMatcher.toString();
+            }
+        };
+
+    } // namespace Detail
+
+    template <typename T>
+    Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
+        return Detail::MatchAllOf<T>{} && lhs && rhs;
+    }
+    template <typename T>
+    Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
+        return Detail::MatchAnyOf<T>{} || lhs || rhs;
+    }
+
+    template <typename T>
+    Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) {
+        return Detail::MatchNotOf<T>{ matcher };
+    }
+
+
+} // namespace Matchers
+} // namespace Catch
+
+
+#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+  #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+  #define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+
+  #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+  #define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+
+  #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+  #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+
+#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+
+  #define CATCH_REQUIRE_THROWS_WITH( expr, matcher )                   (void)(0)
+  #define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+
+  #define CATCH_CHECK_THROWS_WITH( expr, matcher )                     (void)(0)
+  #define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher )   (void)(0)
+
+  #define CATCH_CHECK_THAT( arg, matcher )                             (void)(0)
+  #define CATCH_REQUIRE_THAT( arg, matcher )                           (void)(0)
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+
+  #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+  #define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+
+  #define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+  #define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+
+  #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+  #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+
+#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+
+  #define REQUIRE_THROWS_WITH( expr, matcher )                   (void)(0)
+  #define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+
+  #define CHECK_THROWS_WITH( expr, matcher )                     (void)(0)
+  #define CHECK_THROWS_MATCHES( expr, exceptionType, matcher )   (void)(0)
+
+  #define CHECK_THAT( arg, matcher )                             (void)(0)
+  #define REQUIRE_THAT( arg, matcher )                           (void)(0)
+
+#endif // end of user facing macro declarations
+
+#endif // CATCH_MATCHERS_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_CONTAINER_PROPERTIES_HPP_INCLUDED
+#define CATCH_MATCHERS_CONTAINER_PROPERTIES_HPP_INCLUDED
+
+
+
+#ifndef CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED
+#define CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED
+
+
+#include <array>
+#include <algorithm>
+#include <string>
+#include <type_traits>
+
+namespace Catch {
+namespace Matchers {
+    class MatcherGenericBase : public MatcherUntypedBase {
+    public:
+        MatcherGenericBase() = default;
+        ~MatcherGenericBase() override; // = default;
+
+        MatcherGenericBase(MatcherGenericBase&) = default;
+        MatcherGenericBase(MatcherGenericBase&&) = default;
+
+        MatcherGenericBase& operator=(MatcherGenericBase const&) = delete;
+        MatcherGenericBase& operator=(MatcherGenericBase&&) = delete;
+    };
+
+
+    namespace Detail {
+        template<std::size_t N, std::size_t M>
+        std::array<void const*, N + M> array_cat(std::array<void const*, N> && lhs, std::array<void const*, M> && rhs) {
+            std::array<void const*, N + M> arr{};
+            std::copy_n(lhs.begin(), N, arr.begin());
+            std::copy_n(rhs.begin(), M, arr.begin() + N);
+            return arr;
+        }
+
+        template<std::size_t N>
+        std::array<void const*, N+1> array_cat(std::array<void const*, N> && lhs, void const* rhs) {
+            std::array<void const*, N+1> arr{};
+            std::copy_n(lhs.begin(), N, arr.begin());
+            arr[N] = rhs;
+            return arr;
+        }
+
+        template<std::size_t N>
+        std::array<void const*, N+1> array_cat(void const* lhs, std::array<void const*, N> && rhs) {
+            std::array<void const*, N + 1> arr{ {lhs} };
+            std::copy_n(rhs.begin(), N, arr.begin() + 1);
+            return arr;
+        }
+
+#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
+
+        using std::conjunction;
+
+#else // __cpp_lib_logical_traits
+
+        template<typename... Cond>
+        struct conjunction : std::true_type {};
+
+        template<typename Cond, typename... Rest>
+        struct conjunction<Cond, Rest...> : std::integral_constant<bool, Cond::value && conjunction<Rest...>::value> {};
+
+#endif // __cpp_lib_logical_traits
+
+        template<typename T>
+        using is_generic_matcher = std::is_base_of<
+            Catch::Matchers::MatcherGenericBase,
+            std::remove_cv_t<std::remove_reference_t<T>>
+        >;
+
+        template<typename... Ts>
+        using are_generic_matchers = conjunction<is_generic_matcher<Ts>...>;
+
+        template<typename T>
+        using is_matcher = std::is_base_of<
+            Catch::Matchers::MatcherUntypedBase,
+            std::remove_cv_t<std::remove_reference_t<T>>
+        >;
+
+
+        template<std::size_t N, typename Arg>
+        bool match_all_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
+            return true;
+        }
+
+        template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
+        bool match_all_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
+            return static_cast<T const*>(matchers[Idx])->match(arg) && match_all_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
+        }
+
+
+        template<std::size_t N, typename Arg>
+        bool match_any_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
+            return false;
+        }
+
+        template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
+        bool match_any_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
+            return static_cast<T const*>(matchers[Idx])->match(arg) || match_any_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
+        }
+
+        std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end);
+
+        template<typename... MatcherTs, std::size_t... Idx>
+        std::string describe_multi_matcher(StringRef combine, std::array<void const*, sizeof...(MatcherTs)> const& matchers, std::index_sequence<Idx...>) {
+            std::array<std::string, sizeof...(MatcherTs)> descriptions {{
+                static_cast<MatcherTs const*>(matchers[Idx])->toString()...
+            }};
+
+            return describe_multi_matcher(combine, descriptions.data(), descriptions.data() + descriptions.size());
+        }
+
+
+        template<typename... MatcherTs>
+        class MatchAllOfGeneric final : public MatcherGenericBase {
+        public:
+            MatchAllOfGeneric(MatchAllOfGeneric const&) = delete;
+            MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete;
+            MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
+            MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
+
+            MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
+            explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
+
+            template<typename Arg>
+            bool match(Arg&& arg) const {
+                return match_all_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
+            }
+
+            std::string describe() const override {
+                return describe_multi_matcher<MatcherTs...>(" and "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
+            }
+
+            // Has to be public to enable the concatenating operators
+            // below, because they are not friend of the RHS, only LHS,
+            // and thus cannot access private fields of RHS
+            std::array<void const*, sizeof...( MatcherTs )> m_matchers;
+
+
+            //! Avoids type nesting for `GenericAllOf && GenericAllOf` case
+            template<typename... MatchersRHS>
+            friend
+            MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
+                    MatchAllOfGeneric<MatcherTs...>&& lhs,
+                    MatchAllOfGeneric<MatchersRHS...>&& rhs) {
+                return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
+            }
+
+            //! Avoids type nesting for `GenericAllOf && some matcher` case
+            template<typename MatcherRHS>
+            friend std::enable_if_t<is_matcher<MatcherRHS>::value,
+            MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
+                    MatchAllOfGeneric<MatcherTs...>&& lhs,
+                    MatcherRHS const& rhs) {
+                return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
+            }
+
+            //! Avoids type nesting for `some matcher && GenericAllOf` case
+            template<typename MatcherLHS>
+            friend std::enable_if_t<is_matcher<MatcherLHS>::value,
+            MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
+                    MatcherLHS const& lhs,
+                    MatchAllOfGeneric<MatcherTs...>&& rhs) {
+                return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
+            }
+        };
+
+
+        template<typename... MatcherTs>
+        class MatchAnyOfGeneric final : public MatcherGenericBase {
+        public:
+            MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete;
+            MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete;
+            MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
+            MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
+
+            MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
+            explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
+
+            template<typename Arg>
+            bool match(Arg&& arg) const {
+                return match_any_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
+            }
+
+            std::string describe() const override {
+                return describe_multi_matcher<MatcherTs...>(" or "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{});
+            }
+
+
+            // Has to be public to enable the concatenating operators
+            // below, because they are not friend of the RHS, only LHS,
+            // and thus cannot access private fields of RHS
+            std::array<void const*, sizeof...( MatcherTs )> m_matchers;
+
+            //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
+            template<typename... MatchersRHS>
+            friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
+                    MatchAnyOfGeneric<MatcherTs...>&& lhs,
+                    MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
+                return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
+            }
+
+            //! Avoids type nesting for `GenericAnyOf || some matcher` case
+            template<typename MatcherRHS>
+            friend std::enable_if_t<is_matcher<MatcherRHS>::value,
+            MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
+                    MatchAnyOfGeneric<MatcherTs...>&& lhs,
+                    MatcherRHS const& rhs) {
+                return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
+            }
+
+            //! Avoids type nesting for `some matcher || GenericAnyOf` case
+            template<typename MatcherLHS>
+            friend std::enable_if_t<is_matcher<MatcherLHS>::value,
+            MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
+                MatcherLHS const& lhs,
+                MatchAnyOfGeneric<MatcherTs...>&& rhs) {
+                return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
+            }
+        };
+
+
+        template<typename MatcherT>
+        class MatchNotOfGeneric final : public MatcherGenericBase {
+            MatcherT const& m_matcher;
+
+        public:
+            MatchNotOfGeneric(MatchNotOfGeneric const&) = delete;
+            MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete;
+            MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
+            MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
+
+            explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {}
+
+            template<typename Arg>
+            bool match(Arg&& arg) const {
+                return !m_matcher.match(arg);
+            }
+
+            std::string describe() const override {
+                return "not " + m_matcher.toString();
+            }
+
+            //! Negating negation can just unwrap and return underlying matcher
+            friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
+                return matcher.m_matcher;
+            }
+        };
+    } // namespace Detail
+
+
+    // compose only generic matchers
+    template<typename MatcherLHS, typename MatcherRHS>
+    std::enable_if_t<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
+        operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
+        return { lhs, rhs };
+    }
+
+    template<typename MatcherLHS, typename MatcherRHS>
+    std::enable_if_t<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
+        operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
+        return { lhs, rhs };
+    }
+
+    //! Wrap provided generic matcher in generic negator
+    template<typename MatcherT>
+    std::enable_if_t<Detail::is_generic_matcher<MatcherT>::value, Detail::MatchNotOfGeneric<MatcherT>>
+        operator ! (MatcherT const& matcher) {
+        return Detail::MatchNotOfGeneric<MatcherT>{matcher};
+    }
+
+
+    // compose mixed generic and non-generic matchers
+    template<typename MatcherLHS, typename ArgRHS>
+    std::enable_if_t<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
+        operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
+        return { lhs, rhs };
+    }
+
+    template<typename ArgLHS, typename MatcherRHS>
+    std::enable_if_t<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
+        operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
+        return { lhs, rhs };
+    }
+
+    template<typename MatcherLHS, typename ArgRHS>
+    std::enable_if_t<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
+        operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
+        return { lhs, rhs };
+    }
+
+    template<typename ArgLHS, typename MatcherRHS>
+    std::enable_if_t<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
+        operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
+        return { lhs, rhs };
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_MATCHERS_TEMPLATED_HPP_INCLUDED
+
+namespace Catch {
+    namespace Matchers {
+
+        class IsEmptyMatcher final : public MatcherGenericBase {
+        public:
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
+                using Catch::Detail::empty;
+#else
+                using std::empty;
+#endif
+                return empty(rng);
+            }
+
+            std::string describe() const override;
+        };
+
+        class HasSizeMatcher final : public MatcherGenericBase {
+            std::size_t m_target_size;
+        public:
+            explicit HasSizeMatcher(std::size_t target_size):
+                m_target_size(target_size)
+            {}
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
+                using Catch::Detail::size;
+#else
+                using std::size;
+#endif
+                return size(rng) == m_target_size;
+            }
+
+            std::string describe() const override;
+        };
+
+        template <typename Matcher>
+        class SizeMatchesMatcher final : public MatcherGenericBase {
+            Matcher m_matcher;
+        public:
+            explicit SizeMatchesMatcher(Matcher m):
+                m_matcher(CATCH_MOVE(m))
+            {}
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
+                using Catch::Detail::size;
+#else
+                using std::size;
+#endif
+                return m_matcher.match(size(rng));
+            }
+
+            std::string describe() const override {
+                return "size matches " + m_matcher.describe();
+            }
+        };
+
+
+        //! Creates a matcher that accepts empty ranges/containers
+        IsEmptyMatcher IsEmpty();
+        //! Creates a matcher that accepts ranges/containers with specific size
+        HasSizeMatcher SizeIs(std::size_t sz);
+        template <typename Matcher>
+        std::enable_if_t<Detail::is_matcher<Matcher>::value,
+        SizeMatchesMatcher<Matcher>> SizeIs(Matcher&& m) {
+            return SizeMatchesMatcher<Matcher>{CATCH_FORWARD(m)};
+        }
+
+    } // end namespace Matchers
+} // end namespace Catch
+
+#endif // CATCH_MATCHERS_CONTAINER_PROPERTIES_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
+#define CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
+
+
+#include <algorithm>
+#include <functional>
+
+namespace Catch {
+    namespace Matchers {
+        //! Matcher for checking that an element in range is equal to specific element
+        template <typename T, typename Equality>
+        class ContainsElementMatcher final : public MatcherGenericBase {
+            T m_desired;
+            Equality m_eq;
+        public:
+            template <typename T2, typename Equality2>
+            ContainsElementMatcher(T2&& target, Equality2&& predicate):
+                m_desired(CATCH_FORWARD(target)),
+                m_eq(CATCH_FORWARD(predicate))
+            {}
+
+            std::string describe() const override {
+                return "contains element " + Catch::Detail::stringify(m_desired);
+            }
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                using std::begin; using std::end;
+
+                return end(rng) != std::find_if(begin(rng), end(rng),
+                                               [&](auto const& elem) {
+                                                    return m_eq(elem, m_desired);
+                                               });
+            }
+        };
+
+        //! Meta-matcher for checking that an element in a range matches a specific matcher
+        template <typename Matcher>
+        class ContainsMatcherMatcher final : public MatcherGenericBase {
+            Matcher m_matcher;
+        public:
+            // Note that we do a copy+move to avoid having to SFINAE this
+            // constructor (and also avoid some perfect forwarding failure
+            // cases)
+            ContainsMatcherMatcher(Matcher matcher):
+                m_matcher(CATCH_MOVE(matcher))
+            {}
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                for (auto&& elem : rng) {
+                    if (m_matcher.match(elem)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            std::string describe() const override {
+                return "contains element matching " + m_matcher.describe();
+            }
+        };
+
+        /**
+         * Creates a matcher that checks whether a range contains a specific element.
+         *
+         * Uses `std::equal_to` to do the comparison
+         */
+        template <typename T>
+        std::enable_if_t<!Detail::is_matcher<T>::value,
+        ContainsElementMatcher<T, std::equal_to<>>> Contains(T&& elem) {
+            return { CATCH_FORWARD(elem), std::equal_to<>{} };
+        }
+
+        //! Creates a matcher that checks whether a range contains element matching a matcher
+        template <typename Matcher>
+        std::enable_if_t<Detail::is_matcher<Matcher>::value,
+        ContainsMatcherMatcher<Matcher>> Contains(Matcher&& matcher) {
+            return { CATCH_FORWARD(matcher) };
+        }
+
+        /**
+         * Creates a matcher that checks whether a range contains a specific element.
+         *
+         * Uses `eq` to do the comparisons
+         */
+        template <typename T, typename Equality>
+        ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
+            return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) };
+        }
+
+    }
+}
+
+#endif // CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
+#define CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
+
+
+namespace Catch {
+namespace Matchers {
+
+class ExceptionMessageMatcher final : 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;
+};
+
+//! Creates a matcher that checks whether a std derived exception has the provided message
+ExceptionMessageMatcher Message(std::string const& message);
+
+} // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
+#define CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
+
+
+namespace Catch {
+namespace Matchers {
+
+    namespace Detail {
+        enum class FloatingPointKind : uint8_t;
+    }
+
+    class  WithinAbsMatcher final : public MatcherBase<double> {
+    public:
+        WithinAbsMatcher(double target, double margin);
+        bool match(double const& matchee) const override;
+        std::string describe() const override;
+    private:
+        double m_target;
+        double m_margin;
+    };
+
+    class WithinUlpsMatcher final : public MatcherBase<double> {
+    public:
+        WithinUlpsMatcher( double target,
+                           uint64_t ulps,
+                           Detail::FloatingPointKind baseType );
+        bool match(double const& matchee) const override;
+        std::string describe() const override;
+    private:
+        double m_target;
+        uint64_t m_ulps;
+        Detail::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.
+    class WithinRelMatcher final : public MatcherBase<double> {
+    public:
+        WithinRelMatcher( double target, double epsilon );
+        bool match(double const& matchee) const override;
+        std::string describe() const override;
+    private:
+        double m_target;
+        double m_epsilon;
+    };
+
+    //! Creates a matcher that accepts doubles within certain ULP range of target
+    WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
+    //! Creates a matcher that accepts floats within certain ULP range of target
+    WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
+    //! Creates a matcher that accepts numbers within certain range of target
+    WithinAbsMatcher WithinAbs(double target, double margin);
+
+    //! Creates a matcher that accepts doubles within certain relative range of target
+    WithinRelMatcher WithinRel(double target, double eps);
+    //! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target
+    WithinRelMatcher WithinRel(double target);
+    //! Creates a matcher that accepts doubles within certain relative range of target
+    WithinRelMatcher WithinRel(float target, float eps);
+    //! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target
+    WithinRelMatcher WithinRel(float target);
+
+} // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_MATCHERS_FLOATING_POINT_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
+#define CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
+
+
+#include <string>
+
+namespace Catch {
+namespace Matchers {
+
+namespace Detail {
+    std::string finalizeDescription(const std::string& desc);
+} // namespace Detail
+
+template <typename T, typename Predicate>
+class PredicateMatcher final : public MatcherBase<T> {
+    Predicate m_predicate;
+    std::string m_description;
+public:
+
+    PredicateMatcher(Predicate&& elem, std::string const& descr)
+        :m_predicate(CATCH_FORWARD(elem)),
+        m_description(Detail::finalizeDescription(descr))
+    {}
+
+    bool match( T const& item ) const override {
+        return m_predicate(item);
+    }
+
+    std::string describe() const override {
+        return m_description;
+    }
+};
+
+    /**
+     * Creates a matcher that calls delegates `match` to the provided predicate.
+     *
+     * The user has to explicitly specify the argument type to the matcher
+     */
+    template<typename T, typename Pred>
+    PredicateMatcher<T, Pred> Predicate(Pred&& predicate, std::string const& description = "") {
+        static_assert(is_callable<Pred(T)>::value, "Predicate not callable with argument T");
+        static_assert(std::is_same<bool, FunctionReturnType<Pred, T>>::value, "Predicate does not return bool");
+        return PredicateMatcher<T, Pred>(CATCH_FORWARD(predicate), description);
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_MATCHERS_PREDICATE_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
+#define CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
+
+
+namespace Catch {
+    namespace Matchers {
+        // Matcher for checking that all elements in range matches a given matcher.
+        template <typename Matcher>
+        class AllMatchMatcher final : public MatcherGenericBase {
+            Matcher m_matcher;
+        public:
+            AllMatchMatcher(Matcher matcher):
+                m_matcher(CATCH_MOVE(matcher))
+            {}
+
+            std::string describe() const override {
+                return "all match " + m_matcher.describe();
+            }
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                for (auto&& elem : rng) {
+                    if (!m_matcher.match(elem)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        };
+
+        // Matcher for checking that no element in range matches a given matcher.
+        template <typename Matcher>
+        class NoneMatchMatcher final : public MatcherGenericBase {
+            Matcher m_matcher;
+        public:
+            NoneMatchMatcher(Matcher matcher):
+                m_matcher(CATCH_MOVE(matcher))
+            {}
+
+            std::string describe() const override {
+                return "none match " + m_matcher.describe();
+            }
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                for (auto&& elem : rng) {
+                    if (m_matcher.match(elem)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        };
+
+        // Matcher for checking that at least one element in range matches a given matcher.
+        template <typename Matcher>
+        class AnyMatchMatcher final : public MatcherGenericBase {
+            Matcher m_matcher;
+        public:
+            AnyMatchMatcher(Matcher matcher):
+                m_matcher(CATCH_MOVE(matcher))
+            {}
+
+            std::string describe() const override {
+                return "any match " + m_matcher.describe();
+            }
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                for (auto&& elem : rng) {
+                    if (m_matcher.match(elem)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        };
+
+        // Matcher for checking that all elements in range are true.
+        class AllTrueMatcher final : public MatcherGenericBase {
+        public:
+            std::string describe() const override;
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                for (auto&& elem : rng) {
+                    if (!elem) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        };
+
+        // Matcher for checking that no element in range is true.
+        class NoneTrueMatcher final : public MatcherGenericBase {
+        public:
+            std::string describe() const override;
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                for (auto&& elem : rng) {
+                    if (elem) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        };
+
+        // Matcher for checking that any element in range is true.
+        class AnyTrueMatcher final : public MatcherGenericBase {
+        public:
+            std::string describe() const override;
+
+            template <typename RangeLike>
+            bool match(RangeLike&& rng) const {
+                for (auto&& elem : rng) {
+                    if (elem) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        };
+
+        // Creates a matcher that checks whether all elements in a range match a matcher
+        template <typename Matcher>
+        AllMatchMatcher<Matcher> AllMatch(Matcher&& matcher) {
+            return { CATCH_FORWARD(matcher) };
+        }
+
+        // Creates a matcher that checks whether no element in a range matches a matcher.
+        template <typename Matcher>
+        NoneMatchMatcher<Matcher> NoneMatch(Matcher&& matcher) {
+            return { CATCH_FORWARD(matcher) };
+        }
+
+        // Creates a matcher that checks whether any element in a range matches a matcher.
+        template <typename Matcher>
+        AnyMatchMatcher<Matcher> AnyMatch(Matcher&& matcher) {
+            return { CATCH_FORWARD(matcher) };
+        }
+
+        // Creates a matcher that checks whether all elements in a range are true
+        AllTrueMatcher AllTrue();
+
+        // Creates a matcher that checks whether no element in a range is true
+        NoneTrueMatcher NoneTrue();
+
+        // Creates a matcher that checks whether any element in a range is true
+        AnyTrueMatcher AnyTrue();
+    }
+}
+
+#endif // CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_STRING_HPP_INCLUDED
+#define CATCH_MATCHERS_STRING_HPP_INCLUDED
+
+
+#include <string>
+
+namespace Catch {
+namespace Matchers {
+
+    struct CasedString {
+        CasedString( std::string const& str, CaseSensitive caseSensitivity );
+        std::string adjustString( std::string const& str ) const;
+        StringRef caseSensitivitySuffix() const;
+
+        CaseSensitive m_caseSensitivity;
+        std::string m_str;
+    };
+
+    class StringMatcherBase : public MatcherBase<std::string> {
+    protected:
+        CasedString m_comparator;
+        StringRef m_operation;
+
+    public:
+        StringMatcherBase( StringRef operation,
+                           CasedString const& comparator );
+        std::string describe() const override;
+    };
+
+    class StringEqualsMatcher final : public StringMatcherBase {
+    public:
+        StringEqualsMatcher( CasedString const& comparator );
+        bool match( std::string const& source ) const override;
+    };
+    class StringContainsMatcher final : public StringMatcherBase {
+    public:
+        StringContainsMatcher( CasedString const& comparator );
+        bool match( std::string const& source ) const override;
+    };
+    class StartsWithMatcher final : public StringMatcherBase {
+    public:
+        StartsWithMatcher( CasedString const& comparator );
+        bool match( std::string const& source ) const override;
+    };
+    class EndsWithMatcher final : public StringMatcherBase {
+    public:
+        EndsWithMatcher( CasedString const& comparator );
+        bool match( std::string const& source ) const override;
+    };
+
+    class RegexMatcher final : public MatcherBase<std::string> {
+        std::string m_regex;
+        CaseSensitive m_caseSensitivity;
+
+    public:
+        RegexMatcher( std::string regex, CaseSensitive caseSensitivity );
+        bool match( std::string const& matchee ) const override;
+        std::string describe() const override;
+    };
+
+    //! Creates matcher that accepts strings that are exactly equal to `str`
+    StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
+    //! Creates matcher that accepts strings that contain `str`
+    StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
+    //! Creates matcher that accepts strings that _end_ with `str`
+    EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
+    //! Creates matcher that accepts strings that _start_ with `str`
+    StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
+    //! Creates matcher that accepts strings matching `regex`
+    RegexMatcher Matches( std::string const& regex, CaseSensitive caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_MATCHERS_STRING_HPP_INCLUDED
+
+
+#ifndef CATCH_MATCHERS_VECTOR_HPP_INCLUDED
+#define CATCH_MATCHERS_VECTOR_HPP_INCLUDED
+
+
+#include <algorithm>
+
+namespace Catch {
+namespace Matchers {
+
+    template<typename T, typename Alloc>
+    class VectorContainsElementMatcher final : public MatcherBase<std::vector<T, Alloc>> {
+        T const& m_comparator;
+
+    public:
+        VectorContainsElementMatcher(T const& comparator):
+            m_comparator(comparator)
+        {}
+
+        bool match(std::vector<T, Alloc> const& v) const override {
+            for (auto const& el : v) {
+                if (el == m_comparator) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        std::string describe() const override {
+            return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+        }
+    };
+
+    template<typename T, typename AllocComp, typename AllocMatch>
+    class ContainsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+        std::vector<T, AllocComp> const& m_comparator;
+
+    public:
+        ContainsMatcher(std::vector<T, AllocComp> const& comparator):
+            m_comparator( comparator )
+        {}
+
+        bool match(std::vector<T, AllocMatch> const& v) const override {
+            // !TBD: see note in EqualsMatcher
+            if (m_comparator.size() > v.size())
+                return false;
+            for (auto const& comparator : m_comparator) {
+                auto present = false;
+                for (const auto& el : v) {
+                    if (el == comparator) {
+                        present = true;
+                        break;
+                    }
+                }
+                if (!present) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        std::string describe() const override {
+            return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+        }
+    };
+
+    template<typename T, typename AllocComp, typename AllocMatch>
+    class EqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+        std::vector<T, AllocComp> const& m_comparator;
+
+    public:
+        EqualsMatcher(std::vector<T, AllocComp> const& comparator):
+            m_comparator( comparator )
+        {}
+
+        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
+            // - then just call that directly
+            if (m_comparator.size() != v.size())
+                return false;
+            for (std::size_t i = 0; i < v.size(); ++i)
+                if (m_comparator[i] != v[i])
+                    return false;
+            return true;
+        }
+        std::string describe() const override {
+            return "Equals: " + ::Catch::Detail::stringify( m_comparator );
+        }
+    };
+
+    template<typename T, typename AllocComp, typename AllocMatch>
+    class ApproxMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+        std::vector<T, AllocComp> const& m_comparator;
+        mutable Catch::Approx approx = Catch::Approx::custom();
+
+    public:
+        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 = std::enable_if_t<std::is_constructible<double, T>::value>>
+        ApproxMatcher& epsilon( T const& newEpsilon ) {
+            approx.epsilon(static_cast<double>(newEpsilon));
+            return *this;
+        }
+        template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        ApproxMatcher& margin( T const& newMargin ) {
+            approx.margin(static_cast<double>(newMargin));
+            return *this;
+        }
+        template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
+        ApproxMatcher& scale( T const& newScale ) {
+            approx.scale(static_cast<double>(newScale));
+            return *this;
+        }
+    };
+
+    template<typename T, typename AllocComp, typename AllocMatch>
+    class UnorderedEqualsMatcher final : public MatcherBase<std::vector<T, AllocMatch>> {
+        std::vector<T, AllocComp> const& m_target;
+
+    public:
+        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);
+        }
+    };
+
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    //! Creates a matcher that matches vectors that contain all elements in `comparator`
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
+        return ContainsMatcher<T, AllocComp, AllocMatch>(comparator);
+    }
+
+    //! Creates a matcher that matches vectors that contain `comparator` as an element
+    template<typename T, typename Alloc = std::allocator<T>>
+    VectorContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
+        return VectorContainsElementMatcher<T, Alloc>(comparator);
+    }
+
+    //! Creates a matcher that matches vectors that are exactly equal to `comparator`
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
+        return EqualsMatcher<T, AllocComp, AllocMatch>(comparator);
+    }
+
+    //! Creates a matcher that matches vectors that `comparator` as an element
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
+        return ApproxMatcher<T, AllocComp, AllocMatch>(comparator);
+    }
+
+    //! Creates a matcher that matches vectors that is equal to `target` modulo permutation
+    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
+    UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
+        return UnorderedEqualsMatcher<T, AllocComp, AllocMatch>(target);
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_MATCHERS_VECTOR_HPP_INCLUDED
+
+#endif // CATCH_MATCHERS_ALL_HPP_INCLUDED
+
+
+/** \file
+ * This is a convenience header for Catch2's Reporter support. It includes
+ * **all** of Catch2 headers related to reporters, including all reporters.
+ *
+ * Generally the Catch2 users should use specific includes they need,
+ * but this header can be used instead for ease-of-experimentation, or
+ * just plain convenience, at the cost of (significantly) increased
+ * compilation times.
+ *
+ * When a new header (reporter) is added to either the `reporter` folder,
+ * or to the corresponding internal subfolder, it should be added here.
+ */
+
+#ifndef CATCH_REPORTERS_ALL_HPP_INCLUDED
+#define CATCH_REPORTERS_ALL_HPP_INCLUDED
+
+
+
+#ifndef CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
+#define CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
+
+
+
+#ifndef CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
+#define CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
+
+
+
+#ifndef CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
+#define CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
+
+
+#include <map>
+#include <string>
+
+namespace Catch {
+    class ColourImpl;
+
+    /**
+     * This is the base class for all reporters.
+     *
+     * If are writing a reporter, you must derive from this type, or one
+     * of the helper reporter bases that are derived from this type.
+     *
+     * ReporterBase centralizes handling of various common tasks in reporters,
+     * like storing the right stream for the reporters to write to, and
+     * providing the default implementation of the different listing events.
+     */
+    class ReporterBase : public IEventListener {
+    protected:
+        //! The stream wrapper as passed to us by outside code
+        Detail::unique_ptr<IStream> m_wrapped_stream;
+        //! Cached output stream from `m_wrapped_stream` to reduce
+        //! number of indirect calls needed to write output.
+        std::ostream& m_stream;
+        //! Colour implementation this reporter was configured for
+        Detail::unique_ptr<ColourImpl> m_colour;
+        //! The custom reporter options user passed down to the reporter
+        std::map<std::string, std::string> m_customOptions;
+
+    public:
+        ReporterBase( ReporterConfig&& config );
+        ~ReporterBase() override; // = default;
+
+        /**
+         * Provides a simple default listing of reporters.
+         *
+         * Should look roughly like the reporter listing in v2 and earlier
+         * versions of Catch2.
+         */
+        void listReporters(
+            std::vector<ReporterDescription> const& descriptions ) override;
+        /**
+         * Provides a simple default listing of listeners
+         *
+         * Looks similarly to listing of reporters, but with listener type
+         * instead of reporter name.
+         */
+        void listListeners(
+            std::vector<ListenerDescription> const& descriptions ) override;
+        /**
+         * Provides a simple default listing of tests.
+         *
+         * Should look roughly like the test listing in v2 and earlier versions
+         * of Catch2. Especially supports low-verbosity listing that mimics the
+         * old `--list-test-names-only` output.
+         */
+        void listTests( std::vector<TestCaseHandle> const& tests ) override;
+        /**
+         * Provides a simple default listing of tags.
+         *
+         * Should look roughly like the tag listing in v2 and earlier versions
+         * of Catch2.
+         */
+        void listTags( std::vector<TagInfo> const& tags ) override;
+    };
+} // namespace Catch
+
+#endif // CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+    class StreamingReporterBase : public ReporterBase {
+    public:
+        // GCC5 compat: we cannot use inherited constructor, because it
+        //              doesn't implement backport of P0136
+        StreamingReporterBase(ReporterConfig&& _config):
+            ReporterBase(CATCH_MOVE(_config))
+        {}
+        ~StreamingReporterBase() override;
+
+        void benchmarkPreparing( StringRef ) override {}
+        void benchmarkStarting( BenchmarkInfo const& ) override {}
+        void benchmarkEnded( BenchmarkStats<> const& ) override {}
+        void benchmarkFailed( StringRef ) override {}
+
+        void fatalErrorEncountered( StringRef /*error*/ ) override {}
+        void noMatchingTestCases( StringRef /*unmatchedSpec*/ ) override {}
+        void reportInvalidTestSpec( StringRef /*invalidArgument*/ ) override {}
+
+        void testRunStarting( TestRunInfo const& _testRunInfo ) override;
+
+        void testCaseStarting(TestCaseInfo const& _testInfo) override  {
+            currentTestCaseInfo = &_testInfo;
+        }
+        void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
+        void sectionStarting(SectionInfo const& _sectionInfo) override {
+            m_sectionStack.push_back(_sectionInfo);
+        }
+
+        void assertionStarting( AssertionInfo const& ) override {}
+        void assertionEnded( AssertionStats const& ) override {}
+
+        void sectionEnded(SectionStats const& /* _sectionStats */) override {
+            m_sectionStack.pop_back();
+        }
+        void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
+        void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
+            currentTestCaseInfo = nullptr;
+        }
+        void testRunEnded( TestRunStats const& /* _testRunStats */ ) override;
+
+        void skipTest(TestCaseInfo const&) override {
+            // Don't do anything with this by default.
+            // It can optionally be overridden in the derived class.
+        }
+
+    protected:
+        TestRunInfo currentTestRunInfo{ "test run has not started yet"_sr };
+        TestCaseInfo const* currentTestCaseInfo = nullptr;
+
+        //! Stack of all _active_ sections in the _current_ test case
+        std::vector<SectionInfo> m_sectionStack;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class AutomakeReporter final : public StreamingReporterBase {
+    public:
+        // GCC5 compat: we cannot use inherited constructor, because it
+        //              doesn't implement backport of P0136
+        AutomakeReporter(ReporterConfig&& _config):
+            StreamingReporterBase(CATCH_MOVE(_config))
+        {}
+        ~AutomakeReporter() override;
+
+        static std::string getDescription() {
+            using namespace std::string_literals;
+            return "Reports test results in the format of Automake .trs files"s;
+        }
+
+        void testCaseEnded(TestCaseStats const& _testCaseStats) override;
+        void skipTest(TestCaseInfo const& testInfo) override;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_COMPACT_HPP_INCLUDED
+#define CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+
+
+
+namespace Catch {
+
+    class CompactReporter final : public StreamingReporterBase {
+    public:
+        using StreamingReporterBase::StreamingReporterBase;
+
+        ~CompactReporter() override;
+
+        static std::string getDescription();
+
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
+
+        void testRunStarting( TestRunInfo const& _testInfo ) override;
+
+        void assertionEnded(AssertionStats const& _assertionStats) override;
+
+        void sectionEnded(SectionStats const& _sectionStats) override;
+
+        void testRunEnded(TestRunStats const& _testRunStats) override;
+
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+#define CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+
+namespace Catch {
+    // Fwd decls
+    struct SummaryColumn;
+    class TablePrinter;
+
+    class ConsoleReporter final : public StreamingReporterBase {
+        Detail::unique_ptr<TablePrinter> m_tablePrinter;
+
+    public:
+        ConsoleReporter(ReporterConfig&& config);
+        ~ConsoleReporter() override;
+        static std::string getDescription();
+
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
+        void reportInvalidTestSpec( StringRef arg ) override;
+
+        void assertionStarting(AssertionInfo const&) override;
+
+        void assertionEnded(AssertionStats const& _assertionStats) override;
+
+        void sectionStarting(SectionInfo const& _sectionInfo) override;
+        void sectionEnded(SectionStats const& _sectionStats) override;
+
+        void benchmarkPreparing( StringRef name ) override;
+        void benchmarkStarting(BenchmarkInfo const& info) override;
+        void benchmarkEnded(BenchmarkStats<> const& stats) override;
+        void benchmarkFailed( StringRef error ) override;
+
+        void testCaseEnded(TestCaseStats const& _testCaseStats) override;
+        void testRunEnded(TestRunStats const& _testRunStats) override;
+        void testRunStarting(TestRunInfo const& _testRunInfo) override;
+
+    private:
+        void lazyPrint();
+
+        void lazyPrintWithoutClosingBenchmarkTable();
+        void lazyPrintRunInfo();
+        void printTestCaseAndSectionHeader();
+
+        void printClosedHeader(std::string const& _name);
+        void printOpenHeader(std::string const& _name);
+
+        // if string has a : in first line will set indent to follow it on
+        // subsequent lines
+        void printHeaderString(std::string const& _string, std::size_t indent = 0);
+
+
+        void printTotals(Totals const& totals);
+        void printSummaryRow(StringRef label, std::vector<SummaryColumn> const& cols, std::size_t row);
+
+        void printTotalsDivider(Totals const& totals);
+        void printSummaryDivider();
+
+        bool m_headerPrinted = false;
+        bool m_testRunInfoPrinted = false;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
+#define CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
+
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    namespace Detail {
+
+        //! Represents either an assertion or a benchmark result to be handled by cumulative reporter later
+        class AssertionOrBenchmarkResult {
+            // This should really be a variant, but this is much faster
+            // to write and the data layout here is already terrible
+            // enough that we do not have to care about the object size.
+            Optional<AssertionStats> m_assertion;
+            Optional<BenchmarkStats<>> m_benchmark;
+        public:
+            AssertionOrBenchmarkResult(AssertionStats const& assertion);
+            AssertionOrBenchmarkResult(BenchmarkStats<> const& benchmark);
+
+            bool isAssertion() const;
+            bool isBenchmark() const;
+
+            AssertionStats const& asAssertion() const;
+            BenchmarkStats<> const& asBenchmark() const;
+        };
+    }
+
+    /**
+     * Utility base for reporters that need to handle all results at once
+     *
+     * It stores tree of all test cases, sections and assertions, and after the
+     * test run is finished, calls into `testRunEndedCumulative` to pass the
+     * control to the deriving class.
+     *
+     * If you are deriving from this class and override any testing related
+     * member functions, you should first call into the base's implementation to
+     * avoid breaking the tree construction.
+     *
+     * Due to the way this base functions, it has to expand assertions up-front,
+     * even if they are later unused (e.g. because the deriving reporter does
+     * not report successful assertions, or because the deriving reporter does
+     * not use assertion expansion at all). Derived classes can use two
+     * customization points, `m_shouldStoreSuccesfulAssertions` and
+     * `m_shouldStoreFailedAssertions`, to disable the expansion and gain extra
+     * performance. **Accessing the assertion expansions if it wasn't stored is
+     * UB.**
+     */
+    class CumulativeReporterBase : public ReporterBase {
+    public:
+        template<typename T, typename ChildNodeT>
+        struct Node {
+            explicit Node( T const& _value ) : value( _value ) {}
+
+            using ChildNodes = std::vector<Detail::unique_ptr<ChildNodeT>>;
+            T value;
+            ChildNodes children;
+        };
+        struct SectionNode {
+            explicit SectionNode(SectionStats const& _stats) : stats(_stats) {}
+
+            bool operator == (SectionNode const& other) const {
+                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+            }
+
+            bool hasAnyAssertions() const;
+
+            SectionStats stats;
+            std::vector<Detail::unique_ptr<SectionNode>> childSections;
+            std::vector<Detail::AssertionOrBenchmarkResult> assertionsAndBenchmarks;
+            std::string stdOut;
+            std::string stdErr;
+        };
+
+
+        using TestCaseNode = Node<TestCaseStats, SectionNode>;
+        using TestRunNode = Node<TestRunStats, TestCaseNode>;
+
+        // GCC5 compat: we cannot use inherited constructor, because it
+        //              doesn't implement backport of P0136
+        CumulativeReporterBase(ReporterConfig&& _config):
+            ReporterBase(CATCH_MOVE(_config))
+        {}
+        ~CumulativeReporterBase() override;
+
+        void benchmarkPreparing( StringRef ) override {}
+        void benchmarkStarting( BenchmarkInfo const& ) override {}
+        void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+        void benchmarkFailed( StringRef ) override {}
+
+        void noMatchingTestCases( StringRef ) override {}
+        void reportInvalidTestSpec( StringRef ) override {}
+        void fatalErrorEncountered( StringRef /*error*/ ) override {}
+
+        void testRunStarting( TestRunInfo const& ) override {}
+
+        void testCaseStarting( TestCaseInfo const& ) override {}
+        void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
+        void sectionStarting( SectionInfo const& sectionInfo ) override;
+
+        void assertionStarting( AssertionInfo const& ) override {}
+
+        void assertionEnded( AssertionStats const& assertionStats ) override;
+        void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
+        void testCaseEnded( TestCaseStats const& testCaseStats ) override;
+        void testRunEnded( TestRunStats const& testRunStats ) override;
+        //! Customization point: called after last test finishes (testRunEnded has been handled)
+        virtual void testRunEndedCumulative() = 0;
+
+        void skipTest(TestCaseInfo const&) override {}
+
+    protected:
+        //! Should the cumulative base store the assertion expansion for succesful assertions?
+        bool m_shouldStoreSuccesfulAssertions = true;
+        //! Should the cumulative base store the assertion expansion for failed assertions?
+        bool m_shouldStoreFailedAssertions = true;
+
+        // We need lazy construction here. We should probably refactor it
+        // later, after the events are redone.
+        //! The root node of the test run tree.
+        Detail::unique_ptr<TestRunNode> m_testRun;
+
+    private:
+        // Note: We rely on pointer identity being stable, which is why
+        //       we store pointers to the nodes rather than the values.
+        std::vector<Detail::unique_ptr<TestCaseNode>> m_testCases;
+        // Root section of the _current_ test case
+        Detail::unique_ptr<SectionNode> m_rootSection;
+        // Deepest section of the _current_ test case
+        SectionNode* m_deepestSection = nullptr;
+        // Stack of _active_ sections in the _current_ test case
+        std::vector<SectionNode*> m_sectionStack;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_EVENT_LISTENER_HPP_INCLUDED
+#define CATCH_REPORTER_EVENT_LISTENER_HPP_INCLUDED
+
+
+namespace Catch {
+
+    /**
+     * Base class to simplify implementing listeners.
+     *
+     * Provides empty default implementation for all IEventListener member
+     * functions, so that a listener implementation can pick which
+     * member functions it actually cares about.
+     */
+    class EventListenerBase : public IEventListener {
+    public:
+        using IEventListener::IEventListener;
+
+        void reportInvalidTestSpec( StringRef unmatchedSpec ) override;
+        void fatalErrorEncountered( StringRef error ) override;
+
+        void benchmarkPreparing( StringRef name ) override;
+        void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+        void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+        void benchmarkFailed( StringRef error ) override;
+
+        void assertionStarting( AssertionInfo const& assertionInfo ) override;
+        void assertionEnded( AssertionStats const& assertionStats ) override;
+
+        void listReporters(
+            std::vector<ReporterDescription> const& descriptions ) override;
+        void listListeners(
+            std::vector<ListenerDescription> const& descriptions ) override;
+        void listTests( std::vector<TestCaseHandle> const& tests ) override;
+        void listTags( std::vector<TagInfo> const& tagInfos ) override;
+
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
+        void testRunStarting( TestRunInfo const& testRunInfo ) override;
+        void testCaseStarting( TestCaseInfo const& testInfo ) override;
+        void testCasePartialStarting( TestCaseInfo const& testInfo,
+                                      uint64_t partNumber ) override;
+        void sectionStarting( SectionInfo const& sectionInfo ) override;
+        void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCasePartialEnded( TestCaseStats const& testCaseStats,
+                                   uint64_t partNumber ) override;
+        void testCaseEnded( TestCaseStats const& testCaseStats ) override;
+        void testRunEnded( TestRunStats const& testRunStats ) override;
+        void skipTest( TestCaseInfo const& testInfo ) override;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_EVENT_LISTENER_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_HELPERS_HPP_INCLUDED
+#define CATCH_REPORTER_HELPERS_HPP_INCLUDED
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+
+namespace Catch {
+
+    class IConfig;
+    class TestCaseHandle;
+    class ColourImpl;
+
+    // Returns double formatted as %.3f (format expected on output)
+    std::string getFormattedDuration( double duration );
+
+    //! Should the reporter show duration of test given current configuration?
+    bool shouldShowDuration( IConfig const& config, double duration );
+
+    std::string serializeFilters( std::vector<std::string> const& filters );
+
+    struct lineOfChars {
+        char c;
+        constexpr lineOfChars( char c_ ): c( c_ ) {}
+
+        friend std::ostream& operator<<( std::ostream& out, lineOfChars value );
+    };
+
+    /**
+     * Lists reporter descriptions to the provided stream in user-friendly
+     * format
+     *
+     * Used as the default listing implementation by the first party reporter
+     * bases. The output should be backwards compatible with the output of
+     * Catch2 v2 binaries.
+     */
+    void
+    defaultListReporters( std::ostream& out,
+                          std::vector<ReporterDescription> const& descriptions,
+                          Verbosity verbosity );
+
+    /**
+     * Lists listeners descriptions to the provided stream in user-friendly
+     * format
+     */
+    void defaultListListeners( std::ostream& out,
+                               std::vector<ListenerDescription> const& descriptions );
+
+    /**
+     * Lists tag information to the provided stream in user-friendly format
+     *
+     * Used as the default listing implementation by the first party reporter
+     * bases. The output should be backwards compatible with the output of
+     * Catch2 v2 binaries.
+     */
+    void defaultListTags( std::ostream& out, std::vector<TagInfo> const& tags, bool isFiltered );
+
+    /**
+     * Lists test case information to the provided stream in user-friendly
+     * format
+     *
+     * Used as the default listing implementation by the first party reporter
+     * bases. The output is backwards compatible with the output of Catch2
+     * v2 binaries, and also supports the format specific to the old
+     * `--list-test-names-only` option, for people who used it in integrations.
+     */
+    void defaultListTests( std::ostream& out,
+                           ColourImpl* streamColour,
+                           std::vector<TestCaseHandle> const& tests,
+                           bool isFiltered,
+                           Verbosity verbosity );
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_JUNIT_HPP_INCLUDED
+#define CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+
+
+namespace Catch {
+
+    class JunitReporter final : public CumulativeReporterBase {
+    public:
+        JunitReporter(ReporterConfig&& _config);
+
+        ~JunitReporter() override = default;
+
+        static std::string getDescription();
+
+        void testRunStarting(TestRunInfo const& runInfo) override;
+
+        void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
+        void assertionEnded(AssertionStats const& assertionStats) override;
+
+        void testCaseEnded(TestCaseStats const& testCaseStats) override;
+
+        void testRunEndedCumulative() override;
+
+    private:
+        void writeRun(TestRunNode const& testRunNode, double suiteTime);
+
+        void writeTestCase(TestCaseNode const& testCaseNode);
+
+        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);
+
+        XmlWriter xml;
+        Timer suiteTimer;
+        std::string stdOutForSuite;
+        std::string stdErrForSuite;
+        unsigned int unexpectedExceptions = 0;
+        bool m_okToFail = false;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_MULTI_HPP_INCLUDED
+#define CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+
+namespace Catch {
+
+    class MultiReporter final : public IEventListener {
+        /*
+         * Stores all added reporters and listeners
+         *
+         * All Listeners are stored before all reporters, and individual
+         * listeners/reporters are stored in order of insertion.
+         */
+        std::vector<IEventListenerPtr> m_reporterLikes;
+        bool m_haveNoncapturingReporters = false;
+
+        // Keep track of how many listeners we have already inserted,
+        // so that we can insert them into the main vector at the right place
+        size_t m_insertedListeners = 0;
+
+        void updatePreferences(IEventListener const& reporterish);
+
+    public:
+        using IEventListener::IEventListener;
+
+        void addListener( IEventListenerPtr&& listener );
+        void addReporter( IEventListenerPtr&& reporter );
+
+    public: // IEventListener
+
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
+        void fatalErrorEncountered( StringRef error ) override;
+        void reportInvalidTestSpec( StringRef arg ) override;
+
+        void benchmarkPreparing( StringRef name ) override;
+        void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+        void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+        void benchmarkFailed( StringRef error ) override;
+
+        void testRunStarting( TestRunInfo const& testRunInfo ) override;
+        void testCaseStarting( TestCaseInfo const& testInfo ) override;
+        void testCasePartialStarting(TestCaseInfo const& testInfo, uint64_t partNumber) override;
+        void sectionStarting( SectionInfo const& sectionInfo ) override;
+        void assertionStarting( AssertionInfo const& assertionInfo ) override;
+
+        void assertionEnded( AssertionStats const& assertionStats ) override;
+        void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCasePartialEnded(TestCaseStats const& testInfo, uint64_t partNumber) override;
+        void testCaseEnded( TestCaseStats const& testCaseStats ) override;
+        void testRunEnded( TestRunStats const& testRunStats ) override;
+
+        void skipTest( TestCaseInfo const& testInfo ) override;
+
+        void listReporters(std::vector<ReporterDescription> const& descriptions) override;
+        void listListeners(std::vector<ListenerDescription> const& descriptions) override;
+        void listTests(std::vector<TestCaseHandle> const& tests) override;
+        void listTags(std::vector<TagInfo> const& tags) override;
+
+
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+#define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+
+#include <type_traits>
+
+namespace Catch {
+
+    namespace Detail {
+
+        template <typename T, typename = void>
+        struct has_description : std::false_type {};
+
+        template <typename T>
+        struct has_description<
+            T,
+            void_t<decltype( T::getDescription() )>>
+            : std::true_type {};
+
+        //! Indirection for reporter registration, so that the error handling is
+        //! independent on the reporter's concrete type
+        void registerReporterImpl( std::string const& name,
+                                   IReporterFactoryPtr reporterPtr );
+
+    } // namespace Detail
+
+    class IEventListener;
+    using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+
+    template <typename T>
+    class ReporterFactory : public IReporterFactory {
+
+        IEventListenerPtr create( ReporterConfig&& config ) const override {
+            return Detail::make_unique<T>( CATCH_MOVE(config) );
+        }
+
+        std::string getDescription() const override {
+            return T::getDescription();
+        }
+    };
+
+
+    template<typename T>
+    class ReporterRegistrar {
+    public:
+        explicit ReporterRegistrar( std::string const& name ) {
+            registerReporterImpl( name,
+                                  Detail::make_unique<ReporterFactory<T>>() );
+        }
+    };
+
+    template<typename T>
+    class ListenerRegistrar {
+
+        class TypedListenerFactory : public EventListenerFactory {
+            StringRef m_listenerName;
+
+            std::string getDescriptionImpl( std::true_type ) const {
+                return T::getDescription();
+            }
+
+            std::string getDescriptionImpl( std::false_type ) const {
+                return "(No description provided)";
+            }
+
+        public:
+            TypedListenerFactory( StringRef listenerName ):
+                m_listenerName( listenerName ) {}
+
+            IEventListenerPtr create( IConfig const* config ) const override {
+                return Detail::make_unique<T>( config );
+            }
+
+            StringRef getName() const override {
+                return m_listenerName;
+            }
+
+            std::string getDescription() const override {
+                return getDescriptionImpl( Detail::has_description<T>{} );
+            }
+        };
+
+    public:
+        ListenerRegistrar(StringRef listenerName) {
+            getMutableRegistryHub().registerListener( Detail::make_unique<TypedListenerFactory>(listenerName) );
+        }
+    };
+}
+
+#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> INTERNAL_CATCH_UNIQUE_NAME( \
+                catch_internal_RegistrarFor )( name );                         \
+        }                                                                      \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#    define CATCH_REGISTER_LISTENER( listenerType )                            \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION                              \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                               \
+        namespace {                                                            \
+            Catch::ListenerRegistrar<listenerType> INTERNAL_CATCH_UNIQUE_NAME( \
+                catch_internal_RegistrarFor )( #listenerType );                \
+        }                                                                      \
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#else // CATCH_CONFIG_DISABLE
+
+#define CATCH_REGISTER_REPORTER(name, reporterType)
+#define CATCH_REGISTER_LISTENER(listenerType)
+
+#endif // CATCH_CONFIG_DISABLE
+
+#endif // CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
+#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
+
+
+
+namespace Catch {
+
+    class SonarQubeReporter final : public CumulativeReporterBase {
+    public:
+        SonarQubeReporter(ReporterConfig&& config)
+        : CumulativeReporterBase(CATCH_MOVE(config))
+        , xml(m_stream) {
+            m_preferences.shouldRedirectStdOut = true;
+            m_preferences.shouldReportAllAssertions = true;
+            m_shouldStoreSuccesfulAssertions = false;
+        }
+
+        ~SonarQubeReporter() override = default;
+
+        static std::string getDescription() {
+            using namespace std::string_literals;
+            return "Reports test results in the Generic Test Data SonarQube XML format"s;
+        }
+
+        void testRunStarting( TestRunInfo const& testRunInfo ) override;
+
+        void testRunEndedCumulative() override {
+            writeRun( *m_testRun );
+            xml.endElement();
+        }
+
+        void writeRun( TestRunNode const& groupNode );
+
+        void writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes);
+
+        void writeTestCase(TestCaseNode const& testCaseNode);
+
+        void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail);
+
+        void writeAssertions(SectionNode const& sectionNode, bool okToFail);
+
+        void writeAssertion(AssertionStats const& stats, bool okToFail);
+
+    private:
+        XmlWriter xml;
+    };
+
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_TAP_HPP_INCLUDED
+#define CATCH_REPORTER_TAP_HPP_INCLUDED
+
+
+namespace Catch {
+
+    class TAPReporter final : public StreamingReporterBase {
+    public:
+        TAPReporter( ReporterConfig&& config ):
+            StreamingReporterBase( CATCH_MOVE(config) ) {
+            m_preferences.shouldReportAllAssertions = true;
+        }
+        ~TAPReporter() override = default;
+
+        static std::string getDescription() {
+            using namespace std::string_literals;
+            return "Reports test results in TAP format, suitable for test harnesses"s;
+        }
+
+        void testRunStarting( TestRunInfo const& testInfo ) override;
+
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
+
+        void assertionEnded(AssertionStats const& _assertionStats) override;
+
+        void testRunEnded(TestRunStats const& _testRunStats) override;
+
+    private:
+        std::size_t counter = 0;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_TAP_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
+#define CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
+
+
+#include <cstring>
+
+#ifdef __clang__
+#   pragma clang diagnostic push
+#   pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    class TeamCityReporter final : public StreamingReporterBase {
+    public:
+        TeamCityReporter( ReporterConfig&& _config )
+        :   StreamingReporterBase( CATCH_MOVE(_config) )
+        {
+            m_preferences.shouldRedirectStdOut = true;
+        }
+
+        ~TeamCityReporter() override;
+
+        static std::string getDescription() {
+            using namespace std::string_literals;
+            return "Reports test results as TeamCity service messages"s;
+        }
+
+        void testRunStarting( TestRunInfo const& groupInfo ) override;
+        void testRunEnded( TestRunStats const& testGroupStats ) override;
+
+
+        void assertionEnded(AssertionStats const& assertionStats) override;
+
+        void sectionStarting(SectionInfo const& sectionInfo) override {
+            m_headerPrintedForThisSection = false;
+            StreamingReporterBase::sectionStarting( sectionInfo );
+        }
+
+        void testCaseStarting(TestCaseInfo const& testInfo) override;
+
+        void testCaseEnded(TestCaseStats const& testCaseStats) override;
+
+    private:
+        void printSectionHeader(std::ostream& os);
+
+        bool m_headerPrintedForThisSection = false;
+        Timer m_testTimer;
+    };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#   pragma clang diagnostic pop
+#endif
+
+#endif // CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
+
+
+#ifndef CATCH_REPORTER_XML_HPP_INCLUDED
+#define CATCH_REPORTER_XML_HPP_INCLUDED
+
+
+
+
+namespace Catch {
+    class XmlReporter : public StreamingReporterBase {
+    public:
+        XmlReporter(ReporterConfig&& _config);
+
+        ~XmlReporter() override;
+
+        static std::string getDescription();
+
+        virtual std::string getStylesheetRef() const;
+
+        void writeSourceInfo(SourceLineInfo const& sourceInfo);
+
+    public: // StreamingReporterBase
+
+        void testRunStarting(TestRunInfo const& testInfo) override;
+
+        void testCaseStarting(TestCaseInfo const& testInfo) override;
+
+        void sectionStarting(SectionInfo const& sectionInfo) override;
+
+        void assertionStarting(AssertionInfo const&) override;
+
+        void assertionEnded(AssertionStats const& assertionStats) override;
+
+        void sectionEnded(SectionStats const& sectionStats) override;
+
+        void testCaseEnded(TestCaseStats const& testCaseStats) override;
+
+        void testRunEnded(TestRunStats const& testRunStats) override;
+
+        void benchmarkPreparing( StringRef name ) override;
+        void benchmarkStarting(BenchmarkInfo const&) override;
+        void benchmarkEnded(BenchmarkStats<> const&) override;
+        void benchmarkFailed( StringRef error ) override;
+
+        void listReporters(std::vector<ReporterDescription> const& descriptions) override;
+        void listListeners(std::vector<ListenerDescription> const& descriptions) override;
+        void listTests(std::vector<TestCaseHandle> const& tests) override;
+        void listTags(std::vector<TagInfo> const& tags) override;
+
+    private:
+        Timer m_testCaseTimer;
+        XmlWriter m_xml;
+        int m_sectionDepth = 0;
+    };
+
+} // end namespace Catch
+
+#endif // CATCH_REPORTER_XML_HPP_INCLUDED
+
+#endif // CATCH_REPORTERS_ALL_HPP_INCLUDED
+
+#endif // CATCH_ALL_HPP_INCLUDED
+#endif // CATCH_AMALGAMATED_HPP_INCLUDED

lib/Catch2/contrib/gdbinit → lib/Catch2/extras/gdbinit


lib/Catch2/contrib/lldbinit → lib/Catch2/extras/lldbinit


+ 20 - 0
lib/Catch2/fuzzing/CMakeLists.txt

@@ -0,0 +1,20 @@
+# License: Boost 1.0
+# By Paul Dreik 2020
+
+# add a library that brings in the main() function from libfuzzer
+# and has all the dependencies, so the individual fuzzers can be
+# added one line each.
+add_library(fuzzhelper NullOStream.h NullOStream.cpp)
+target_link_libraries(fuzzhelper PUBLIC Catch2::Catch2)
+
+# use C++17 so we can get string_view
+target_compile_features(fuzzhelper PUBLIC cxx_std_17)
+
+# This should be possible to set from the outside to be oss-fuzz compatible,
+# fix later. For now, target libFuzzer only.
+target_link_options(fuzzhelper PUBLIC "-fsanitize=fuzzer")
+
+foreach(fuzzer TestSpecParser XmlWriter textflow)
+add_executable(fuzz_${fuzzer} fuzz_${fuzzer}.cpp)
+target_link_libraries(fuzz_${fuzzer} PRIVATE fuzzhelper)
+endforeach()

+ 10 - 0
lib/Catch2/fuzzing/NullOStream.cpp

@@ -0,0 +1,10 @@
+#include "NullOStream.h"
+
+void NullOStream::avoidOutOfLineVirtualCompilerWarning()
+{
+}
+
+int NullStreambuf::overflow(int c){
+    setp(dummyBuffer, dummyBuffer + sizeof(dummyBuffer));
+    return (c == traits_type::eof()) ? '\0' : c;
+}

+ 20 - 0
lib/Catch2/fuzzing/NullOStream.h

@@ -0,0 +1,20 @@
+#pragma once
+
+#include <ostream>
+#include <streambuf>
+
+// from https://stackoverflow.com/a/8244052
+class NullStreambuf : public std::streambuf {
+  char dummyBuffer[64];
+
+protected:
+  virtual int overflow(int c) override final;
+};
+
+class NullOStream final : private NullStreambuf, public std::ostream {
+public:
+  NullOStream() : std::ostream(this) {}
+  NullStreambuf *rdbuf() { return this; }
+  virtual void avoidOutOfLineVirtualCompilerWarning();
+};
+

+ 33 - 0
lib/Catch2/fuzzing/build_fuzzers.sh

@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Builds the fuzzers
+#
+# By Paul Dreik 20200923
+set -exu
+
+CATCHROOT=$(readlink -f $(dirname $0)/..)
+
+
+BUILDDIR=$CATCHROOT/build-fuzzers
+mkdir -p $BUILDDIR
+cd $BUILDDIR
+
+if which /usr/lib/ccache/clang++ >/dev/null 2>&1 ; then
+ CXX=/usr/lib/ccache/clang++
+else
+ CXX=clang++
+fi
+
+cmake $CATCHROOT \
+  -DCMAKE_CXX_COMPILER=$CXX \
+  -DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link,address,undefined -O3 -g" \
+  -DCATCH_DEVELOPMENT_BUILD=On \
+  -DCATCH_BUILD_EXAMPLES=Off \
+  -DCATCH_BUILD_EXTRA_TESTS=Off \
+  -DCATCH_BUILD_TESTING=Off \
+  -DBUILD_TESTING=Off \
+  -DCATCH_ENABLE_WERROR=Off \
+  -DCATCH_BUILD_FUZZERS=On
+
+cmake --build . -j $(nproc)
+

+ 16 - 0
lib/Catch2/fuzzing/fuzz_TestSpecParser.cpp

@@ -0,0 +1,16 @@
+//License: Boost 1.0
+//By Paul Dreik 2020
+
+#include <catch2/internal/catch_test_spec_parser.hpp>
+#include <catch2/internal/catch_tag_alias_registry.hpp>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+
+    Catch::TagAliasRegistry tar;
+    Catch::TestSpecParser tsp(tar);
+
+    std::string buf(Data,Data+Size);
+    tsp.parse(buf);
+
+    return 0;
+}

+ 16 - 0
lib/Catch2/fuzzing/fuzz_XmlWriter.cpp

@@ -0,0 +1,16 @@
+//License: Boost 1.0
+//By Paul Dreik 2020
+
+#include <catch2/internal/catch_xmlwriter.hpp>
+
+#include "NullOStream.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+
+    std::string buf(Data,Data+Size);
+    NullOStream nul;
+    Catch::XmlEncode encode(buf);
+    encode.encodeTo(nul);
+    return 0;
+}
+

+ 0 - 0
lib/Catch2/fuzzing/fuzz_textflow.cpp


Some files were not shown because too many files changed in this diff