From 04fc22a5b0c95070fe40c61fe2db5c6bdac384d5 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Tue, 30 Jan 2024 19:35:56 +0100 Subject: [PATCH] git subrepo pull --force packages/Catch2 subrepo: subdir: "packages/Catch2" merged: "1078e7e95" upstream: origin: "git@github.com:catchorg/Catch2.git" branch: "devel" commit: "1078e7e95" git-subrepo: version: "0.4.6" origin: "git@github.com:ingydotnet/git-subrepo.git" commit: "110b9eb" --- packages/Catch2/.bazelrc | 1 + .../.github/workflows/linux-bazel-builds.yml | 2 +- .../.github/workflows/linux-meson-builds.yml | 7 +- .../.github/workflows/linux-other-builds.yml | 17 +- .../.github/workflows/linux-simple-builds.yml | 9 +- .../Catch2/.github/workflows/mac-builds.yml | 7 +- .../workflows/validate-header-guards.yml | 2 +- .../workflows/windows-simple-builds.yml | 2 +- packages/Catch2/.gitrepo | 4 +- packages/Catch2/BUILD.bazel | 1 + .../Catch2/CMake/CatchConfigOptions.cmake | 5 + .../Catch2/CMake/CatchMiscFunctions.cmake | 2 +- packages/Catch2/CMakeLists.txt | 4 +- packages/Catch2/CMakePresets.json | 3 +- packages/Catch2/Doxyfile | 308 +- packages/Catch2/MODULE.bazel | 3 + packages/Catch2/WORKSPACE.bazel | 6 +- packages/Catch2/appveyor.yml | 11 - packages/Catch2/docs/benchmarks.md | 2 +- packages/Catch2/docs/ci-and-misc.md | 2 +- packages/Catch2/docs/cmake-integration.md | 4 +- packages/Catch2/docs/command-line.md | 109 +- packages/Catch2/docs/configuration.md | 5 +- packages/Catch2/docs/faq.md | 25 +- packages/Catch2/docs/generators.md | 25 + packages/Catch2/docs/limitations.md | 10 - packages/Catch2/docs/matchers.md | 44 +- packages/Catch2/docs/release-notes.md | 87 +- packages/Catch2/docs/reporters.md | 2 +- .../Catch2/docs/skipping-passing-failing.md | 2 +- .../Catch2/docs/test-cases-and-sections.md | 2 +- packages/Catch2/docs/tostring.md | 2 +- packages/Catch2/docs/tutorial.md | 9 +- packages/Catch2/docs/why-catch.md | 6 +- packages/Catch2/examples/010-TestCase.cpp | 8 + packages/Catch2/examples/020-TestCase-1.cpp | 8 + packages/Catch2/examples/020-TestCase-2.cpp | 8 + .../Catch2/examples/030-Asn-Require-Check.cpp | 8 + packages/Catch2/examples/100-Fix-Section.cpp | 8 + .../Catch2/examples/110-Fix-ClassFixture.cpp | 8 + .../120-Bdd-ScenarioGivenWhenThen.cpp | 8 + .../examples/210-Evt-EventListeners.cpp | 8 + .../Catch2/examples/231-Cfg-OutputStreams.cpp | 8 + .../Catch2/examples/232-Cfg-CustomMain.cpp | 41 + .../Catch2/examples/300-Gen-OwnGenerator.cpp | 8 + .../examples/301-Gen-MapTypeConversion.cpp | 8 + packages/Catch2/examples/302-Gen-Table.cpp | 8 + .../310-Gen-VariablesInGenerators.cpp | 8 + .../Catch2/examples/311-Gen-CustomCapture.cpp | 8 + packages/Catch2/examples/CMakeLists.txt | 3 +- packages/Catch2/extras/Catch.cmake | 31 +- packages/Catch2/extras/CatchAddTests.cmake | 21 +- packages/Catch2/extras/catch_amalgamated.cpp | 2042 +++++--- packages/Catch2/extras/catch_amalgamated.hpp | 4289 ++++++++++------- packages/Catch2/fuzzing/NullOStream.cpp | 8 + packages/Catch2/fuzzing/NullOStream.h | 8 + .../Catch2/fuzzing/fuzz_TestSpecParser.cpp | 8 +- packages/Catch2/fuzzing/fuzz_XmlWriter.cpp | 8 +- packages/Catch2/fuzzing/fuzz_textflow.cpp | 8 +- packages/Catch2/meson.build | 2 +- packages/Catch2/src/CMakeLists.txt | 17 +- .../src/catch2/benchmark/catch_benchmark.hpp | 14 +- .../catch2/benchmark/catch_chronometer.hpp | 5 +- .../src/catch2/benchmark/catch_clock.hpp | 16 +- .../catch2/benchmark/catch_environment.hpp | 14 +- .../src/catch2/benchmark/catch_estimate.hpp | 13 +- .../catch2/benchmark/catch_execution_plan.hpp | 24 +- .../src/catch2/benchmark/catch_optimizer.hpp | 2 +- .../benchmark/catch_sample_analysis.hpp | 25 +- .../catch2/benchmark/detail/catch_analyse.cpp | 85 + .../catch2/benchmark/detail/catch_analyse.hpp | 63 +- .../detail/catch_benchmark_stats.hpp | 28 +- .../detail/catch_benchmark_stats_fwd.hpp | 4 +- .../benchmark/detail/catch_estimate_clock.hpp | 26 +- .../catch2/benchmark/detail/catch_measure.hpp | 2 +- .../detail/catch_run_for_at_least.hpp | 8 +- .../catch2/benchmark/detail/catch_stats.cpp | 154 +- .../catch2/benchmark/detail/catch_stats.hpp | 99 +- .../catch2/benchmark/detail/catch_timing.hpp | 8 +- packages/Catch2/src/catch2/catch_all.hpp | 6 + packages/Catch2/src/catch2/catch_approx.cpp | 4 +- packages/Catch2/src/catch2/catch_config.hpp | 2 +- packages/Catch2/src/catch2/catch_message.hpp | 10 +- .../src/catch2/catch_test_case_info.cpp | 1 + .../src/catch2/catch_test_case_info.hpp | 3 +- packages/Catch2/src/catch2/catch_tostring.hpp | 6 + .../src/catch2/catch_user_config.hpp.in | 1 + packages/Catch2/src/catch2/catch_version.cpp | 2 +- .../src/catch2/catch_version_macros.hpp | 2 +- .../catch2/generators/catch_generators.hpp | 6 - .../generators/catch_generators_random.cpp | 32 +- .../generators/catch_generators_random.hpp | 35 +- .../generators/catch_generators_range.hpp | 9 +- .../interfaces/catch_interfaces_reporter.cpp | 8 - .../interfaces/catch_interfaces_reporter.hpp | 1 - .../internal/catch_assertion_handler.cpp | 2 - .../src/catch2/internal/catch_clara.cpp | 122 +- .../src/catch2/internal/catch_clara.hpp | 134 +- .../src/catch2/internal/catch_commandline.cpp | 5 +- .../internal/catch_compiler_capabilities.hpp | 4 +- .../internal/catch_config_prefix_messages.hpp | 29 + .../internal/catch_enum_values_registry.cpp | 2 +- .../catch_exception_translator_registry.cpp | 5 +- .../catch_fatal_condition_handler.cpp | 1 + .../catch_fatal_condition_handler.hpp | 3 - .../internal/catch_floating_point_helpers.cpp | 11 + .../internal/catch_floating_point_helpers.hpp | 5 + .../src/catch2/internal/catch_istream.cpp | 5 - .../src/catch2/internal/catch_jsonwriter.cpp | 148 + .../src/catch2/internal/catch_jsonwriter.hpp | 120 + .../catch2/internal/catch_leak_detector.cpp | 2 +- .../Catch2/src/catch2/internal/catch_list.cpp | 3 - .../src/catch2/internal/catch_polyfills.cpp | 8 + .../src/catch2/internal/catch_polyfills.hpp | 5 + .../catch_random_floating_point_helpers.hpp | 94 + .../internal/catch_random_integer_helpers.hpp | 202 + .../internal/catch_random_seed_generation.cpp | 9 +- .../internal/catch_reporter_registry.cpp | 5 +- .../internal/catch_reporter_spec_parser.hpp | 2 +- .../src/catch2/internal/catch_run_context.cpp | 13 +- .../src/catch2/internal/catch_section.cpp | 2 +- .../src/catch2/internal/catch_section.hpp | 4 +- .../src/catch2/internal/catch_sharding.hpp | 3 +- .../src/catch2/internal/catch_stringref.hpp | 4 +- .../internal/catch_tag_alias_registry.cpp | 3 +- .../catch_test_case_registry_impl.cpp | 4 - .../catch_test_case_registry_impl.hpp | 2 - .../catch2/internal/catch_test_macro_impl.hpp | 4 +- .../catch2/internal/catch_test_registry.hpp | 2 +- .../src/catch2/internal/catch_textflow.cpp | 33 +- .../src/catch2/internal/catch_textflow.hpp | 36 +- .../internal/catch_uncaught_exceptions.cpp | 1 - ...ch_uniform_floating_point_distribution.hpp | 131 + .../catch_uniform_integer_distribution.hpp | 124 + .../catch_matchers_floating_point.cpp | 15 - packages/Catch2/src/catch2/meson.build | 22 + .../reporters/catch_reporter_automake.cpp | 2 +- .../reporters/catch_reporter_compact.cpp | 2 +- .../reporters/catch_reporter_console.cpp | 39 +- .../catch2/reporters/catch_reporter_json.cpp | 372 ++ .../catch2/reporters/catch_reporter_json.hpp | 95 + .../catch2/reporters/catch_reporter_junit.hpp | 2 - .../reporters/catch_reporter_sonarqube.hpp | 2 - .../catch2/reporters/catch_reporter_tap.hpp | 1 - .../reporters/catch_reporter_teamcity.cpp | 2 +- .../catch2/reporters/catch_reporter_xml.cpp | 9 +- .../catch2/reporters/catch_reporters_all.hpp | 1 + packages/Catch2/tests/CMakeLists.txt | 20 +- ...ingEventGoesBeforeAssertionIsEvaluated.cpp | 10 +- .../Baselines/automake.sw.approved.txt | 8 + .../Baselines/automake.sw.multi.approved.txt | 8 + .../Baselines/compact.sw.approved.txt | 157 +- .../Baselines/compact.sw.multi.approved.txt | 157 +- .../Baselines/console.std.approved.txt | 49 +- .../Baselines/console.sw.approved.txt | 529 +- .../Baselines/console.sw.multi.approved.txt | 529 +- .../SelfTest/Baselines/junit.sw.approved.txt | 61 +- .../Baselines/junit.sw.multi.approved.txt | 61 +- .../Baselines/sonarqube.sw.approved.txt | 60 + .../Baselines/sonarqube.sw.multi.approved.txt | 60 + .../SelfTest/Baselines/tap.sw.approved.txt | 76 +- .../Baselines/tap.sw.multi.approved.txt | 76 +- .../Baselines/teamcity.sw.approved.txt | 20 + .../Baselines/teamcity.sw.multi.approved.txt | 20 + .../SelfTest/Baselines/xml.sw.approved.txt | 521 +- .../Baselines/xml.sw.multi.approved.txt | 521 +- .../AssertionHandler.tests.cpp | 17 + .../FloatingPoint.tests.cpp | 65 + .../GeneratorsImpl.tests.cpp | 29 + .../IntrospectiveTests/Integer.tests.cpp | 150 + .../InternalBenchmark.tests.cpp | 54 +- .../IntrospectiveTests/Json.tests.cpp | 152 + .../RandomNumberGeneration.tests.cpp | 528 ++ .../IntrospectiveTests/Reporters.tests.cpp | 4 +- .../IntrospectiveTests/String.tests.cpp | 2 +- .../SelfTest/UsageTests/Generators.tests.cpp | 12 +- .../SelfTest/UsageTests/Message.tests.cpp | 31 +- .../tests/SelfTest/UsageTests/Misc.tests.cpp | 12 + .../UsageTests/ToStringOptional.tests.cpp | 4 + .../TestScripts/DiscoverTests/CMakeLists.txt | 16 + .../DiscoverTests/VerifyRegistration.py | 123 + .../DiscoverTests/register-tests.cpp | 16 + packages/Catch2/tests/meson.build | 1 + packages/Catch2/tools/scripts/checkLicense.py | 3 +- .../tools/scripts/generateAmalgamatedFiles.py | 10 + .../Catch2/tools/scripts/releaseCommon.py | 4 +- 186 files changed, 10913 insertions(+), 3165 deletions(-) create mode 100644 packages/Catch2/MODULE.bazel create mode 100644 packages/Catch2/examples/232-Cfg-CustomMain.cpp create mode 100644 packages/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp create mode 100644 packages/Catch2/src/catch2/internal/catch_config_prefix_messages.hpp create mode 100644 packages/Catch2/src/catch2/internal/catch_jsonwriter.cpp create mode 100644 packages/Catch2/src/catch2/internal/catch_jsonwriter.hpp create mode 100644 packages/Catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp create mode 100644 packages/Catch2/src/catch2/internal/catch_random_integer_helpers.hpp create mode 100644 packages/Catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp create mode 100644 packages/Catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp create mode 100644 packages/Catch2/src/catch2/reporters/catch_reporter_json.cpp create mode 100644 packages/Catch2/src/catch2/reporters/catch_reporter_json.hpp create mode 100644 packages/Catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp create mode 100644 packages/Catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp create mode 100644 packages/Catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp create mode 100644 packages/Catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt create mode 100644 packages/Catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py create mode 100644 packages/Catch2/tests/TestScripts/DiscoverTests/register-tests.cpp diff --git a/packages/Catch2/.bazelrc b/packages/Catch2/.bazelrc index c01cb39f1..9cb0aa1b8 100644 --- a/packages/Catch2/.bazelrc +++ b/packages/Catch2/.bazelrc @@ -8,3 +8,4 @@ build:vs2022 --cxxopt=/std:c++17 build:windows --config=vs2022 build:linux --config=gcc11 +build:macos --cxxopt=-std=c++2b diff --git a/packages/Catch2/.github/workflows/linux-bazel-builds.yml b/packages/Catch2/.github/workflows/linux-bazel-builds.yml index 9006652e2..dc826ac0d 100644 --- a/packages/Catch2/.github/workflows/linux-bazel-builds.yml +++ b/packages/Catch2/.github/workflows/linux-bazel-builds.yml @@ -11,7 +11,7 @@ jobs: compilation_mode: [fastbuild, dbg, opt] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Mount bazel cache uses: actions/cache@v3 diff --git a/packages/Catch2/.github/workflows/linux-meson-builds.yml b/packages/Catch2/.github/workflows/linux-meson-builds.yml index dec701b61..4a6cfd5bb 100644 --- a/packages/Catch2/.github/workflows/linux-meson-builds.yml +++ b/packages/Catch2/.github/workflows/linux-meson-builds.yml @@ -18,10 +18,12 @@ jobs: other_pkgs: clang-11 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Prepare environment - run: sudo apt-get install -y meson ninja-build ${{matrix.other_pkgs}} + run: | + sudo apt-get update + sudo apt-get install -y meson ninja-build ${{matrix.other_pkgs}} - name: Configure build env: @@ -38,6 +40,5 @@ jobs: - name: Run tests working-directory: ${{runner.workspace}}/meson-build - # Hardcode 2 cores we know are there run: | meson test --verbose diff --git a/packages/Catch2/.github/workflows/linux-other-builds.yml b/packages/Catch2/.github/workflows/linux-other-builds.yml index cf4e2c06b..9afd231af 100644 --- a/packages/Catch2/.github/workflows/linux-other-builds.yml +++ b/packages/Catch2/.github/workflows/linux-other-builds.yml @@ -29,13 +29,13 @@ jobs: build_type: Debug std: 14 other_pkgs: g++-7 - cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON + cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=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 + cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON # Extras and examples with Clang-10 - cxx: clang++-10 @@ -43,13 +43,13 @@ jobs: build_type: Debug std: 17 other_pkgs: clang-10 - cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON + cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=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 + cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON # Configure tests with Clang-10 - cxx: clang++-10 @@ -70,10 +70,12 @@ jobs: steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Prepare environment - run: sudo apt-get install -y ninja-build ${{matrix.other_pkgs}} + run: | + sudo apt-get update + sudo apt-get install -y ninja-build ${{matrix.other_pkgs}} - name: Configure build working-directory: ${{runner.workspace}} @@ -100,5 +102,4 @@ jobs: 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}} + run: ctest -C ${{matrix.build_type}} -j `nproc` ${{matrix.other_ctest_args}} diff --git a/packages/Catch2/.github/workflows/linux-simple-builds.yml b/packages/Catch2/.github/workflows/linux-simple-builds.yml index 989c4942e..4cca31619 100644 --- a/packages/Catch2/.github/workflows/linux-simple-builds.yml +++ b/packages/Catch2/.github/workflows/linux-simple-builds.yml @@ -83,7 +83,7 @@ jobs: other_pkgs: g++-10 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Add repositories for older GCC run: | @@ -92,7 +92,9 @@ jobs: if: ${{ matrix.cxx == 'g++-5' || matrix.cxx == 'g++-6' }} - name: Prepare environment - run: sudo apt-get install -y ninja-build ${{matrix.other_pkgs}} + run: | + sudo apt-get update + sudo apt-get install -y ninja-build ${{matrix.other_pkgs}} - name: Configure build working-directory: ${{runner.workspace}} @@ -118,5 +120,4 @@ jobs: 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 + run: ctest -C ${{matrix.build_type}} -j `nproc` diff --git a/packages/Catch2/.github/workflows/mac-builds.yml b/packages/Catch2/.github/workflows/mac-builds.yml index 955b81fcc..259d8b367 100644 --- a/packages/Catch2/.github/workflows/mac-builds.yml +++ b/packages/Catch2/.github/workflows/mac-builds.yml @@ -22,7 +22,7 @@ jobs: extra_tests: ON steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Configure build working-directory: ${{runner.workspace}} @@ -42,11 +42,10 @@ jobs: - name: Build tests + lib working-directory: ${{runner.workspace}}/build - run: make -j 2 + run: make -j `sysctl -n hw.ncpu` - 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 + run: ctest -C ${{matrix.build_type}} -j `sysctl -n hw.ncpu` diff --git a/packages/Catch2/.github/workflows/validate-header-guards.yml b/packages/Catch2/.github/workflows/validate-header-guards.yml index c02b5d49e..fa9d1574b 100644 --- a/packages/Catch2/.github/workflows/validate-header-guards.yml +++ b/packages/Catch2/.github/workflows/validate-header-guards.yml @@ -9,7 +9,7 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Dependencies uses: actions/setup-python@v2 diff --git a/packages/Catch2/.github/workflows/windows-simple-builds.yml b/packages/Catch2/.github/workflows/windows-simple-builds.yml index 197fa219e..5fb7b8fe7 100644 --- a/packages/Catch2/.github/workflows/windows-simple-builds.yml +++ b/packages/Catch2/.github/workflows/windows-simple-builds.yml @@ -13,7 +13,7 @@ jobs: build_type: [Debug, Release] std: [14, 17] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Configure build working-directory: ${{runner.workspace}} diff --git a/packages/Catch2/.gitrepo b/packages/Catch2/.gitrepo index 0b35a79b5..c3d079d0b 100644 --- a/packages/Catch2/.gitrepo +++ b/packages/Catch2/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = git@github.com:catchorg/Catch2.git branch = devel - commit = 0631b607ee2bbc07c7c238f0b15b23ef21926960 - parent = 446a6502fc29873ba84cd5964ea2e0787dc48e49 + commit = 1078e7e95b3a06d4dadc75188de48bc4afffb955 + parent = a3b573d7e88e70c258be089529fd58ad41a10a64 method = merge cmdver = 0.4.6 diff --git a/packages/Catch2/BUILD.bazel b/packages/Catch2/BUILD.bazel index 02ec92265..c51bf57e7 100644 --- a/packages/Catch2/BUILD.bazel +++ b/packages/Catch2/BUILD.bazel @@ -49,6 +49,7 @@ expand_template( "#cmakedefine CATCH_CONFIG_NOSTDOUT": "", "#cmakedefine CATCH_CONFIG_POSIX_SIGNALS": "", "#cmakedefine CATCH_CONFIG_PREFIX_ALL": "", + "#cmakedefine CATCH_CONFIG_PREFIX_MESSAGES": "", "#cmakedefine CATCH_CONFIG_SHARED_LIBRARY": "", "#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "", "#cmakedefine CATCH_CONFIG_USE_ASYNC": "", diff --git a/packages/Catch2/CMake/CatchConfigOptions.cmake b/packages/Catch2/CMake/CatchConfigOptions.cmake index 067739dc9..6eae220df 100644 --- a/packages/Catch2/CMake/CatchConfigOptions.cmake +++ b/packages/Catch2/CMake/CatchConfigOptions.cmake @@ -18,10 +18,12 @@ 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) + mark_as_advanced(CATCH_CONFIG_${OptionBaseName} CATCH_CONFIG_NO_${OptionBaseName}) endmacro() macro(AddConfigOption OptionBaseName) option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF) + mark_as_advanced(CATCH_CONFIG_${OptionBaseName}) endmacro() set(_OverridableOptions @@ -62,6 +64,7 @@ set(_OtherConfigOptions "FAST_COMPILE" "NOSTDOUT" "PREFIX_ALL" + "PREFIX_MESSAGES" "WINDOWS_CRTDBG" ) @@ -78,6 +81,8 @@ endif() 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.") +mark_as_advanced(CATCH_CONFIG_SHARED_LIBRARY CATCH_CONFIG_DEFAULT_REPORTER CATCH_CONFIG_CONSOLE_WIDTH) + # 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. diff --git a/packages/Catch2/CMake/CatchMiscFunctions.cmake b/packages/Catch2/CMake/CatchMiscFunctions.cmake index 44c875007..84bd7cc79 100644 --- a/packages/Catch2/CMake/CatchMiscFunctions.cmake +++ b/packages/Catch2/CMake/CatchMiscFunctions.cmake @@ -46,7 +46,6 @@ function(add_warnings_to_targets targets) set(CHECKED_WARNING_FLAGS "-Wabsolute-value" "-Wall" - "-Wc++20-compat" "-Wcall-to-pure-virtual-from-ctor-dtor" "-Wcast-align" "-Wcatch-value" @@ -79,6 +78,7 @@ function(add_warnings_to_targets targets) "-Wreturn-std-move" "-Wshadow" "-Wstrict-aliasing" + "-Wsubobject-linkage" "-Wsuggest-destructor-override" "-Wsuggest-override" "-Wundef" diff --git a/packages/Catch2/CMakeLists.txt b/packages/Catch2/CMakeLists.txt index b3e811538..78ac4c8ad 100644 --- a/packages/Catch2/CMakeLists.txt +++ b/packages/Catch2/CMakeLists.txt @@ -11,6 +11,7 @@ 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) +option(CATCH_ENABLE_REPRODUCIBLE_BUILD "Add compiler flags for improving build reproducibility" ON) include(CMakeDependentOption) cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATCH_DEVELOPMENT_BUILD" OFF) @@ -21,6 +22,7 @@ cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" 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) +cmake_dependent_option(CATCH_ENABLE_CMAKE_HELPER_TESTS "Enable CMake helper tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF) # Catch2's build breaks if done in-tree. You probably should not build @@ -31,7 +33,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.3.2 # CML version placeholder, don't delete + VERSION 3.5.2 # CML version placeholder, don't delete LANGUAGES CXX # HOMEPAGE_URL is not supported until CMake version 3.12, which # we do not target yet. diff --git a/packages/Catch2/CMakePresets.json b/packages/Catch2/CMakePresets.json index 00f3a6d3a..885412850 100644 --- a/packages/Catch2/CMakePresets.json +++ b/packages/Catch2/CMakePresets.json @@ -18,7 +18,8 @@ "CATCH_BUILD_EXAMPLES": "ON", "CATCH_BUILD_EXTRA_TESTS": "ON", "CATCH_BUILD_SURROGATES": "ON", - "CATCH_ENABLE_CONFIGURE_TESTS": "ON" + "CATCH_ENABLE_CONFIGURE_TESTS": "ON", + "CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON" } } ] diff --git a/packages/Catch2/Doxyfile b/packages/Catch2/Doxyfile index 07b385ec1..914e59848 100644 --- a/packages/Catch2/Doxyfile +++ b/packages/Catch2/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.16 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Catch2" +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 @@ -51,6 +51,7 @@ PROJECT_BRIEF = "Popular C++ unit testing framework" # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. +PROJECT_LOGO = # 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 @@ -216,6 +217,14 @@ QT_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # 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. @@ -251,13 +260,7 @@ TAB_SIZE = 4 # 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 = + noexcept=**Noexcept** # 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 @@ -299,19 +302,22 @@ OPTIMIZE_OUTPUT_SLICE = NO # 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, +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # 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. +# default for Fortran type files). 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. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = @@ -445,6 +451,19 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -508,6 +527,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # 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 @@ -525,8 +551,8 @@ HIDE_UNDOC_MEMBERS = 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. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -545,11 +571,18 @@ HIDE_IN_BODY_DOCS = 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. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = NO @@ -788,7 +821,10 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO @@ -819,13 +855,13 @@ WARN_LOGFILE = doxygen.errors # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = "src/catch2" +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. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 @@ -838,13 +874,61 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # 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 = +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.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 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -968,6 +1052,7 @@ FILTER_SOURCE_PATTERNS = # (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. +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1055,6 +1140,44 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1066,13 +1189,6 @@ VERBATIM_HEADERS = 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 @@ -1211,9 +1327,9 @@ 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 +# 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, +# 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. @@ -1243,10 +1359,11 @@ 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 +# 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. @@ -1288,8 +1405,8 @@ 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. +# (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 @@ -1364,7 +1481,8 @@ 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). +# (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. @@ -1372,8 +1490,8 @@ 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). +# 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. @@ -1381,16 +1499,16 @@ 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). +# 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). +# 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 = @@ -1402,9 +1520,9 @@ QHP_CUST_FILTER_ATTRS = 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. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) 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 = @@ -1481,6 +1599,17 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # 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 @@ -1501,8 +1630,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# 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 @@ -1514,7 +1649,7 @@ 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. +# http://docs.mathjax.org/en/v2.7-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. @@ -1530,7 +1665,7 @@ MATHJAX_FORMAT = HTML-CSS # 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/. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1545,7 +1680,8 @@ MATHJAX_EXTENSIONS = TeX/AMSmath \ # 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 +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1573,7 +1709,7 @@ MATHJAX_CODEFILE = 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 +# 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 @@ -1592,7 +1728,8 @@ SERVER_BASED_SEARCH = NO # # 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/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1605,8 +1742,9 @@ EXTERNAL_SEARCH = NO # # 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. +# 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 = @@ -1770,9 +1908,11 @@ LATEX_EXTRA_FILES = 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. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2204,7 +2344,7 @@ HIDE_UNDOC_RELATIONS = YES # 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. +# The default value is: YES. HAVE_DOT = YES @@ -2283,10 +2423,32 @@ UML_LOOK = NO # 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. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2360,7 +2522,9 @@ DIRECTORY_GRAPH = NO # 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, +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, 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. @@ -2476,9 +2640,11 @@ DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/packages/Catch2/MODULE.bazel b/packages/Catch2/MODULE.bazel new file mode 100644 index 000000000..a7846cd60 --- /dev/null +++ b/packages/Catch2/MODULE.bazel @@ -0,0 +1,3 @@ +module(name = "catch2") + +bazel_dep(name = "bazel_skylib", version = "1.5.0") diff --git a/packages/Catch2/WORKSPACE.bazel b/packages/Catch2/WORKSPACE.bazel index d962a9954..357e6f944 100644 --- a/packages/Catch2/WORKSPACE.bazel +++ b/packages/Catch2/WORKSPACE.bazel @@ -4,10 +4,10 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "bazel_skylib", - sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7", + sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", ], ) diff --git a/packages/Catch2/appveyor.yml b/packages/Catch2/appveyor.yml index 3b6580d8b..7a0ad83ff 100644 --- a/packages/Catch2/appveyor.yml +++ b/packages/Catch2/appveyor.yml @@ -70,14 +70,3 @@ environment: 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 Coverage - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - coverage: 1 - platform: x64 - configuration: Debug \ No newline at end of file diff --git a/packages/Catch2/docs/benchmarks.md b/packages/Catch2/docs/benchmarks.md index 548913c76..9edbb93c7 100644 --- a/packages/Catch2/docs/benchmarks.md +++ b/packages/Catch2/docs/benchmarks.md @@ -93,7 +93,7 @@ Fibonacci ------------------------------------------------------------------------------- C:\path\to\Catch2\Benchmark.tests.cpp(10) ............................................................................... -benchmark name samples iterations estimated +benchmark name samples iterations est run time mean low mean high mean std dev low std dev high std dev ------------------------------------------------------------------------------- diff --git a/packages/Catch2/docs/ci-and-misc.md b/packages/Catch2/docs/ci-and-misc.md index c07da29f0..49bbd9891 100644 --- a/packages/Catch2/docs/ci-and-misc.md +++ b/packages/Catch2/docs/ci-and-misc.md @@ -82,7 +82,7 @@ variable set to "1". ### CodeCoverage module (GCOV, LCOV...) -If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/fkromer/catch_cmake_coverage +If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/claremacrae/catch_cmake_coverage ### pkg-config diff --git a/packages/Catch2/docs/cmake-integration.md b/packages/Catch2/docs/cmake-integration.md index e38d5c2f4..86666efe2 100644 --- a/packages/Catch2/docs/cmake-integration.md +++ b/packages/Catch2/docs/cmake-integration.md @@ -51,7 +51,7 @@ Include(FetchContent) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.0.1 # or a later release + GIT_TAG v3.4.0 # or a later release ) FetchContent_MakeAvailable(Catch2) @@ -203,7 +203,7 @@ the output file name e.g. ".xml". If specified allows control over when test discovery is performed. For a value of `POST_BUILD` (default) test discovery is performed at build time. -For a a value of `PRE_TEST` test discovery is delayed until just prior to test +For a value of `PRE_TEST` test discovery is delayed until just prior to test execution (useful e.g. in cross-compilation environments). ``DISCOVERY_MODE`` defaults to the value of the ``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when diff --git a/packages/Catch2/docs/command-line.md b/packages/Catch2/docs/command-line.md index a15a21314..bb483959d 100644 --- a/packages/Catch2/docs/command-line.md +++ b/packages/Catch2/docs/command-line.md @@ -85,43 +85,102 @@ Click one of the following links to take you straight to that option - or scroll <pre><test-spec> ...</pre> -Test cases, wildcarded test cases, tags and tag expressions are all passed directly as arguments. Tags are distinguished by being enclosed in square brackets. +By providing a test spec, you filter which tests will be run. If you call +Catch2 without any test spec, then it will run all non-hidden test +cases. A test case is hidden if it has the `[!benchmark]` tag, any tag +with a dot at the start, e.g. `[.]` or `[.foo]`. -If no test specs are supplied then all test cases, except "hidden" tests, are run. -A test is hidden by giving it any tag starting with (or just) a period (```.```) - or, in the deprecated case, tagged ```[hide]``` or given name starting with `'./'`. To specify hidden tests from the command line ```[.]``` or ```[hide]``` can be used *regardless of how they were declared*. +There are three basic test specs that can then be combined into more +complex specs: -Specs must be enclosed in quotes if they contain spaces. If they do not contain spaces the quotes are optional. + * Full test name, e.g. `"Test 1"`. -Wildcards consist of the `*` character at the beginning and/or end of test case names and can substitute for any number of any characters (including none). + This allows only test cases whose name is "Test 1". -Test specs are case insensitive. + * Wildcarded test name, e.g. `"*Test"`, or `"Test*"`, or `"*Test*"`. -If a spec is prefixed with `exclude:` or the `~` character then the pattern matches an exclusion. This means that tests matching the pattern are excluded from the set - even if a prior inclusion spec included them. Subsequent inclusion specs will take precedence, however. -Inclusions and exclusions are evaluated in left-to-right order. + This allows any test case whose name ends with, starts with, or contains + in the middle the string "Test". Note that the wildcard can only be at + the start or end. -Test case examples: + * Tag name, e.g. `[some-tag]`. + This allows any test case tagged with "[some-tag]". Remember that some + tags are special, e.g. those that start with "." or with "!". + + +You can also combine the basic test specs to create more complex test +specs. You can: + + * Concatenate specs to apply all of them, e.g. `[some-tag][other-tag]`. + + This allows test cases that are tagged with **both** "[some-tag]" **and** + "[other-tag]". A test case with just "[some-tag]" will not pass the filter, + nor will test case with just "[other-tag]". + + * Comma-join specs to apply any of them, e.g. `[some-tag],[other-tag]`. + + This allows test cases that are tagged with **either** "[some-tag]" **or** + "[other-tag]". A test case with both will obviously also pass the filter. + + Note that commas take precendence over simple concatenation. This means + that `[a][b],[c]` accepts tests that are tagged with either both "[a]" and + "[b]", or tests that are tagged with just "[c]". + + * Negate the spec by prepending it with `~`, e.g. `~[some-tag]`. + + This rejects any test case that is tagged with "[some-tag]". Note that + rejection takes precedence over other filters. + + Note that negations always binds to the following _basic_ test spec. + This means that `~[foo][bar]` negates only the "[foo]" tag and not the + "[bar]" tag. + +Note that when Catch2 is deciding whether to include a test, first it +checks whether the test matches any negative filters. If it does, +the test is rejected. After that, the behaviour depends on whether there +are positive filters as well. If there are no positive filters, all +remaining non-hidden tests are included. If there are positive filters, +only tests that match the positive filters are included. + +You can also match test names with special characters by escaping them +with a backslash (`"\"`), e.g. a test named `"Do A, then B"` is matched +by "Do A\, then B" test spec. Backslash also escapes itself. + + +### Examples + +Given these TEST_CASEs, ``` -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' -~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' +TEST_CASE("Test 1") {} + +TEST_CASE("Test 2", "[.foo]") {} + +TEST_CASE("Test 3", "[.bar]") {} + +TEST_CASE("Test 4", "[.][foo][bar]") {} ``` -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.: +this is the result of these filters +``` +./tests # Selects only the first test, others are hidden +./tests "Test 1" # Selects only the first test, other do not match +./tests ~"Test 1" # Selects no tests. Test 1 is rejected, other tests are hidden +./tests "Test *" # Selects all tests. +./tests [bar] # Selects tests 3 and 4. Other tests are not tagged [bar] +./tests ~[foo] # Selects test 1, because it is the only non-hidden test without [foo] tag +./tests [foo][bar] # Selects test 4. +./tests [foo],[bar] # Selects tests 2, 3, 4. +./tests ~[foo][bar] # Selects test 3. 2 and 4 are rejected due to having [foo] tag +./tests ~"Test 2"[foo] # Selects test 4, because test 2 is explicitly rejected +./tests [foo][bar],"Test 1" # Selects tests 1 and 4. +./tests "Test 1*" # Selects test 1, wildcard can match zero characters +``` -<pre>[one][two],[three]</pre> -This matches all tests tagged `[one]` and `[two]`, as well as all tests tagged `[three]` +_Note: Using plain asterisk on a command line can cause issues with shell +expansion. Make sure that the asterisk is passed to Catch2 and is not +interpreted by the shell._ -Test names containing special characters, such as `,` or `[` can specify them on the command line using `\`. -`\` also escapes itself. <a id="choosing-a-reporter-to-use"></a> ## Choosing a reporter to use diff --git a/packages/Catch2/docs/configuration.md b/packages/Catch2/docs/configuration.md index d6e159e52..8a3ddfab5 100644 --- a/packages/Catch2/docs/configuration.md +++ b/packages/Catch2/docs/configuration.md @@ -26,7 +26,8 @@ with the same name. ## Prefixing Catch macros - CATCH_CONFIG_PREFIX_ALL + CATCH_CONFIG_PREFIX_ALL // Prefix all macros with CATCH_ + CATCH_CONFIG_PREFIX_MESSAGES // Prefix only INFO, UNSCOPED_INFO, WARN and CAPTURE To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```). @@ -267,7 +268,7 @@ must compile and must break into debugger. ## Static analysis support -> Introduced in Catch2 X.Y.Z. +> Introduced in Catch2 3.4.0. Some parts of Catch2, e.g. `SECTION`s, can be hard for static analysis tools to reason about. Catch2 can change its internals to help static diff --git a/packages/Catch2/docs/faq.md b/packages/Catch2/docs/faq.md index a7d0455a3..80923d26e 100644 --- a/packages/Catch2/docs/faq.md +++ b/packages/Catch2/docs/faq.md @@ -10,6 +10,7 @@ [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> [What repeatability guarantees does Catch2 provide?](#what-repeatability-guarantees-does-catch2-provide)<br> +[My build cannot find `catch2/catch_user_config.hpp`, how can I fix it?](#my-build-cannot-find-catch2catch_user_confighpp-how-can-i-fix-it)<br> ## How do I run global setup/teardown only if tests will be run? @@ -83,12 +84,30 @@ and it is also generally repeatable across versions, but we might break it from time to time. E.g. we broke repeatability with previous versions in v2.13.4 so that test cases with similar names are shuffled better. -Random generators currently rely on platform's stdlib, specifically -the distributions from `<random>`. We thus provide no extra guarantee -above what your platform does. **Important: `<random>`'s distributions +Since Catch2 3.5.0 the random generators use custom distributions, +that should be repeatable across different platforms, with few caveats. +For details see the section on random generators in the [Generator +documentation](generators.md#random-number-generators-details). + +Before this version, random generators relied on distributions from +platform's stdlib. We thus can provide no extra guarantee on top of the +ones given by your platform. **Important: `<random>`'s distributions are not specified to be repeatable across different platforms.** +## My build cannot find `catch2/catch_user_config.hpp`, how can I fix it? + +`catch2/catch_user_config.hpp` is a generated header that contains user +compile time configuration. It is generated by CMake/Meson/Bazel during +build. If you are not using either of these, your three options are to + +1) Build Catch2 separately using build tool that will generate the header +2) Use the amalgamated files to build Catch2 +3) Use CMake to configure a build. This will generate the header and you + can copy it into your own checkout of Catch2. + + + --- [Home](Readme.md#top) diff --git a/packages/Catch2/docs/generators.md b/packages/Catch2/docs/generators.md index 097997521..8bca54c75 100644 --- a/packages/Catch2/docs/generators.md +++ b/packages/Catch2/docs/generators.md @@ -189,6 +189,31 @@ TEST_CASE("type conversion", "[generators]") { } ``` + +### Random number generators: details + +> This section applies from Catch2 3.5.0. Before that, random generators +> were a thin wrapper around distributions from `<random>`. + +All of the `random(a, b)` generators in Catch2 currently generate uniformly +distributed number in closed interval \[a; b\]. This is different from +`std::uniform_real_distribution`, which should return numbers in interval +\[a; b) (but due to rounding can end up returning b anyway), but the +difference is intentional, so that `random(a, a)` makes sense. If there is +enough interest from users, we can provide API to pick any of CC, CO, OC, +or OO ranges. + +Unlike `std::uniform_int_distribution`, Catch2's generators also support +various single-byte integral types, such as `char` or `bool`. + +Given the same seed, the output from the integral generators is +reproducible across different platforms. For floating point generators, +we only promise reproducibility on platforms that obey the IEEE 754 +standard, and where `float` is 4 bytes and `double` is 8 bytes. We provide +no guarantees for `long double`, as the internals of `long double` can +vary wildly across different platforms. + + ## Generator interface You can also implement your own generators, by deriving from the diff --git a/packages/Catch2/docs/limitations.md b/packages/Catch2/docs/limitations.md index cc0ed05d1..099dd82a5 100644 --- a/packages/Catch2/docs/limitations.md +++ b/packages/Catch2/docs/limitations.md @@ -173,13 +173,3 @@ TEST_CASE("b") { 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 - -Running a Catch2 binary compiled against libstdc++ with `_GLIBCXX_DEBUG` -macro defined with `--order rand` will cause a debug check to trigger and -abort the run due to self-assignment. -[This is a known bug inside libstdc++](https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle/23691322) - -Workaround: Don't use `--order rand` when compiling against debug-enabled -libstdc++. diff --git a/packages/Catch2/docs/matchers.md b/packages/Catch2/docs/matchers.md index 14c158982..d5be1f5a3 100644 --- a/packages/Catch2/docs/matchers.md +++ b/packages/Catch2/docs/matchers.md @@ -50,25 +50,43 @@ 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>`. +### Combining operators and lifetimes + **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): +matcher objects being combined. + +This means that if you store combined matcher object, you have to ensure +that the individual matchers being combined outlive the combined matcher. +Note that the negation matcher from `!` also counts as combining matcher +for this. +Explained on an example, this is fine ```cpp -#include <catch2/catch_test_macros.hpp> -#include <catch2/matchers/catch_matchers_string.hpp> +CHECK_THAT(value, WithinAbs(0, 2e-2) && !WithinULP(0., 1)); +``` -TEST_CASE("Bugs, bugs, bugs", "[Bug]"){ - std::string str = "Bugs as a service"; +and so is this +```cpp +auto is_close_to_zero = WithinAbs(0, 2e-2); +auto is_zero = WithinULP(0., 1); - auto match_expression = Catch::Matchers::EndsWith( "as a service" ) || - (Catch::Matchers::StartsWith( "Big data" ) && !Catch::Matchers::ContainsSubstring( "web scale" ) ); - REQUIRE_THAT(str, match_expression); -} +CHECK_THAT(value, is_close_to_zero && !is_zero); ``` +but this is not +```cpp +auto is_close_to_zero = WithinAbs(0, 2e-2); +auto is_zero = WithinULP(0., 1); +auto is_close_to_but_not_zero = is_close_to_zero && !is_zero; + +CHECK_THAT(a_value, is_close_to_but_not_zero); // UAF +``` + +because `!is_zero` creates a temporary instance of Negation matcher, +which the `is_close_to_but_not_zero` refers to. After the line ends, +the temporary is destroyed and the combined `is_close_to_but_not_zero` +matcher now refers to non-existent object, so using it causes use-after-free. + ## Built-in matchers @@ -286,7 +304,7 @@ comparable. (e.g. you may compare `std::vector<int>` to `std::array<char>`). `UnorderedRangeEquals` is similar to `RangeEquals`, but the order does not matter. For example "1, 2, 3" would match "3, 2, 1", but not "1, 1, 2, 3" As with `RangeEquals`, `UnorderedRangeEquals` compares -the individual elements using using `operator==` by default. +the individual elements using `operator==` by default. Both `RangeEquals` and `UnorderedRangeEquals` optionally accept a predicate which can be used to compare the containers element-wise. diff --git a/packages/Catch2/docs/release-notes.md b/packages/Catch2/docs/release-notes.md index 8b413b156..ac78866f6 100644 --- a/packages/Catch2/docs/release-notes.md +++ b/packages/Catch2/docs/release-notes.md @@ -2,6 +2,10 @@ # Release notes **Contents**<br> +[3.5.2](#352)<br> +[3.5.1](#351)<br> +[3.5.0](#350)<br> +[3.4.0](#340)<br> [3.3.2](#332)<br> [3.3.1](#331)<br> [3.3.0](#330)<br> @@ -56,6 +60,87 @@ [Even Older versions](#even-older-versions)<br> +## 3.5.2 + +### Fixes +* Fixed `-Wsubobject-linkage` in the Console reporter (#2794) +* Fixed adding new CLI Options to lvalue parser using `|` (#2787) + + +## 3.5.1 + +### Improvements +* Significantly improved performance of the CLI parsing. + * This includes the cost of preparing the CLI parser, so Catch2's binaries start much faster. + +### Miscellaneous +* Added support for Bazel modules (#2781) +* Added CMake option to disable the build reproducibility settings (#2785) +* Added `log` library linking to the Meson build (#2784) + + +## 3.5.0 + +### Improvements +* Introduced `CATCH_CONFIG_PREFIX_MESSAGES` to prefix only logging macros (#2544) + * This means `INFO`, `UNSCOPED_INFO`, `WARN` and `CAPTURE`. +* Section hints in static analysis mode are now `const` + * This prevents Clang-Tidy from complaining about `misc-const-correctness`. +* `from_range` generator supports C arrays and ranges that require ADL (#2737) +* Stringification support for `std::optional` now also includes `std::nullopt` (#2740) +* The Console reporter flushes output after writing benchmark runtime estimate. + * This means that you can immediately see for how long the benchmark is expected to run. +* Added workaround to enable compilation with ICC 19.1 (#2551, #2766) +* Compiling Catch2 for XBox should work out of the box (#2772) + * Catch2 should automatically disable getenv when compiled for XBox. +* Compiling Catch2 with exceptions disabled no longer triggers `Wunused-function` (#2726) +* **`random` Generators for integral types are now reproducible across different platforms** + * Unlike `<random>`, Catch2's generators also support 1 byte integral types (`char`, `bool`, ...) +* **`random` Generators for `float` and `double` are now reproducible across different platforms** + * `long double` varies across different platforms too much to be reproducible + * This guarantee applies only to platforms with IEEE 754 floats. + +### Fixes +* UDL declaration inside Catch2 are now strictly conforming to the standard + * `operator "" _a` is UB, `operator ""_a` is fine. Seriously. +* Fixed `CAPTURE` tests failing to compile in C++23 mode (#2744) +* Fixed missing include in `catch_message.hpp` (#2758) +* Fixed `CHECK_ELSE` suppressing failure from uncaught exceptions(#2723) + +### Miscellaneous +* The documentation for specifying which tests to run through commandline has been completely rewritten (#2738) +* Fixed installation when building Catch2 with meson (#2722, #2742) +* Fixed `catch_discover_tests` when using custom reporter and `PRE_TEST` discovery mode (#2747) +* `catch_discover_tests` supports multi-config CMake generator in `PRE_TEST` discovery mode (#2739, #2746) + + +## 3.4.0 + +### Improvements +* `VectorEquals` supports elements that provide only `==` and not `!=` (#2648) +* Catch2 supports compiling with IAR compiler (#2651) +* Various small internal performance improvements +* Various small internal compilation time improvements +* XMLReporter now reports location info for INFO and WARN (#1251) + * This bumps up the xml format version to 3 +* Documented that `SKIP` in generator constructor can be used to handle empty generator (#1593) +* Added experimental static analysis support to `TEST_CASE` and `SECTION` macros (#2681) + * The two macros are redefined in a way that helps the SA tools reason about the possible paths through a test case with sections. + * The support is controlled by the `CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT` option and autodetects clang-tidy and Coverity. +* `*_THROWS`, `*_THROWS_AS`, etc now suppress warning coming from `__attribute__((warn_unused_result))` on GCC (#2691) + * Unlike plain `[[nodiscard]]`, this warning is not silenced by void cast. WTF GCC? + +### Fixes +* Fixed `assertionStarting` events being sent after the expr is evaluated (#2678) +* Errors in `TEST_CASE` tags are now reported nicely (#2650) + +### Miscellaneous +* Bunch of improvements to `catch_discover_tests` + * Added DISCOVERY_MODE option, so the discovery can happen either post build or pre-run. + * Fixed handling of semicolons and backslashes in test names (#2674, #2676) +* meson build can disable building tests (#2693) +* meson build properly sets meson version 0.54.1 as the minimal supported version (#2688) + ## 3.3.2 @@ -352,7 +437,7 @@ v3 releases. * 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. + * Test identity includes test's name, test's tags 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 diff --git a/packages/Catch2/docs/reporters.md b/packages/Catch2/docs/reporters.md index 496c61a92..e2abfe34d 100644 --- a/packages/Catch2/docs/reporters.md +++ b/packages/Catch2/docs/reporters.md @@ -52,7 +52,7 @@ its machine-readable XML output to file `result-junit.xml`, and the uses ANSI colour codes for colouring the output. Using multiple reporters (or one reporter and one-or-more [event -listeners](event-listener.md#top)) can have surprisingly complex semantics +listeners](event-listeners.md#top)) can have surprisingly complex semantics when using customization points provided to reporters by Catch2, namely capturing stdout/stderr from test cases. diff --git a/packages/Catch2/docs/skipping-passing-failing.md b/packages/Catch2/docs/skipping-passing-failing.md index d866b418d..52bb18f76 100644 --- a/packages/Catch2/docs/skipping-passing-failing.md +++ b/packages/Catch2/docs/skipping-passing-failing.md @@ -9,7 +9,7 @@ In some situations it may not be possible to meaningfully execute a test case, for example when the system under test is missing certain hardware capabilities. If the required conditions can only be determined at runtime, it often doesn't make sense to consider such a test case as either passed or failed, -because it simply can not run at all. +because it simply cannot run at all. To properly express such scenarios, Catch2 provides a way to explicitly _skip_ test cases, using the `SKIP` macro: diff --git a/packages/Catch2/docs/test-cases-and-sections.md b/packages/Catch2/docs/test-cases-and-sections.md index acebcc51d..01c898bb6 100644 --- a/packages/Catch2/docs/test-cases-and-sections.md +++ b/packages/Catch2/docs/test-cases-and-sections.md @@ -231,7 +231,7 @@ TEMPLATE_TEST_CASE( "vectors can be sized and resized", "[vector][template]", in > [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch2 2.6.0. -_template-type1_ through _template-typen_ is list of template template +_template-type1_ through _template-typen_ is list of template types which should be combined with each of _template-arg1_ through _template-argm_, resulting in _n * m_ test cases. Inside the test case, the resulting type is available under the name of `TestType`. diff --git a/packages/Catch2/docs/tostring.md b/packages/Catch2/docs/tostring.md index adce3cc76..b99b67426 100644 --- a/packages/Catch2/docs/tostring.md +++ b/packages/Catch2/docs/tostring.md @@ -75,7 +75,7 @@ CATCH_TRANSLATE_EXCEPTION( MyType const& ex ) { 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. -However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialiation for you with minimal code. +However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialisation for you with minimal code. Simply provide it the (qualified) enum name, followed by all the enum values, and you're done! E.g. diff --git a/packages/Catch2/docs/tutorial.md b/packages/Catch2/docs/tutorial.md index 342c73818..dfccac888 100644 --- a/packages/Catch2/docs/tutorial.md +++ b/packages/Catch2/docs/tutorial.md @@ -119,7 +119,7 @@ This is best explained through an example ([code](../examples/100-Fix-Section.cp ```c++ TEST_CASE( "vectors can be sized and resized", "[vector]" ) { - + // This setup will be done 4 times in total, once for each section std::vector<int> v( 5 ); REQUIRE( v.size() == 5 ); @@ -152,11 +152,12 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) { } ``` -For each `SECTION` the `TEST_CASE` is executed from the start. This means +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. +are also checked before the section is entered. This behaviour may not be +ideal for tests where setup is expensive. Each run through a test case will +execute one, and only one, leaf section. Section can also be nested, in which case the parent section can be entered multiple times, once for each leaf section. Nested sections are diff --git a/packages/Catch2/docs/why-catch.md b/packages/Catch2/docs/why-catch.md index 2c0178ca5..b7367496b 100644 --- a/packages/Catch2/docs/why-catch.md +++ b/packages/Catch2/docs/why-catch.md @@ -30,7 +30,7 @@ So what does Catch2 bring to the party that differentiates it from these? Apart * 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. +* A command line parser is provided and can still be used if you choose to provide your own main() function. * Alternative assertion macro(s) report failures but don't abort the test case * 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 @@ -41,8 +41,8 @@ So what does Catch2 bring to the party that differentiates it from these? Apart ## Who else is using Catch2? -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 +A whole lot of people. According to [the 2022 JetBrains C++ ecosystem survey](https://www.jetbrains.com/lp/devecosystem-2022/cpp/#Which-unit-testing-frameworks-do-you-regularly-use), +about 12% 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) diff --git a/packages/Catch2/examples/010-TestCase.cpp b/packages/Catch2/examples/010-TestCase.cpp index 7ec208d5f..9e5cd8cd3 100644 --- a/packages/Catch2/examples/010-TestCase.cpp +++ b/packages/Catch2/examples/010-TestCase.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 010-TestCase.cpp // And write tests in the same file: #include <catch2/catch_test_macros.hpp> diff --git a/packages/Catch2/examples/020-TestCase-1.cpp b/packages/Catch2/examples/020-TestCase-1.cpp index cec55799a..a9d87dbcb 100644 --- a/packages/Catch2/examples/020-TestCase-1.cpp +++ b/packages/Catch2/examples/020-TestCase-1.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 020-TestCase-1.cpp #include <catch2/catch_test_macros.hpp> diff --git a/packages/Catch2/examples/020-TestCase-2.cpp b/packages/Catch2/examples/020-TestCase-2.cpp index 3f5767b34..72dd0ffb6 100644 --- a/packages/Catch2/examples/020-TestCase-2.cpp +++ b/packages/Catch2/examples/020-TestCase-2.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 020-TestCase-2.cpp // main() provided by Catch in file 020-TestCase-1.cpp. diff --git a/packages/Catch2/examples/030-Asn-Require-Check.cpp b/packages/Catch2/examples/030-Asn-Require-Check.cpp index 0d027ca93..62cd3cfc4 100644 --- a/packages/Catch2/examples/030-Asn-Require-Check.cpp +++ b/packages/Catch2/examples/030-Asn-Require-Check.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 030-Asn-Require-Check.cpp // Catch has two natural expression assertion macro's: diff --git a/packages/Catch2/examples/100-Fix-Section.cpp b/packages/Catch2/examples/100-Fix-Section.cpp index cfbfa79f9..7c8d8aa86 100644 --- a/packages/Catch2/examples/100-Fix-Section.cpp +++ b/packages/Catch2/examples/100-Fix-Section.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 100-Fix-Section.cpp // Catch has two ways to express fixtures: diff --git a/packages/Catch2/examples/110-Fix-ClassFixture.cpp b/packages/Catch2/examples/110-Fix-ClassFixture.cpp index 75c10da62..614c37979 100644 --- a/packages/Catch2/examples/110-Fix-ClassFixture.cpp +++ b/packages/Catch2/examples/110-Fix-ClassFixture.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 110-Fix-ClassFixture.cpp // Catch has two ways to express fixtures: diff --git a/packages/Catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp b/packages/Catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp index 99cdf9ab9..345d53c38 100644 --- a/packages/Catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp +++ b/packages/Catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 120-Bdd-ScenarioGivenWhenThen.cpp // main() provided by linkage with Catch2WithMain diff --git a/packages/Catch2/examples/210-Evt-EventListeners.cpp b/packages/Catch2/examples/210-Evt-EventListeners.cpp index 6cedb885c..56b050d41 100644 --- a/packages/Catch2/examples/210-Evt-EventListeners.cpp +++ b/packages/Catch2/examples/210-Evt-EventListeners.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 210-Evt-EventListeners.cpp // Contents: diff --git a/packages/Catch2/examples/231-Cfg-OutputStreams.cpp b/packages/Catch2/examples/231-Cfg-OutputStreams.cpp index b77c12735..da1713cf8 100644 --- a/packages/Catch2/examples/231-Cfg-OutputStreams.cpp +++ b/packages/Catch2/examples/231-Cfg-OutputStreams.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 231-Cfg-OutputStreams.cpp // Show how to replace the streams with a simple custom made streambuf. diff --git a/packages/Catch2/examples/232-Cfg-CustomMain.cpp b/packages/Catch2/examples/232-Cfg-CustomMain.cpp new file mode 100644 index 000000000..69fba7f16 --- /dev/null +++ b/packages/Catch2/examples/232-Cfg-CustomMain.cpp @@ -0,0 +1,41 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +// 232-Cfg-CustomMain.cpp +// Show how to use custom main and add a custom option to the CLI parser + +#include <catch2/catch_session.hpp> + +#include <iostream> + +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 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 + ["--height"] // the option names it will respond to + ("how high?"); // description string for the help output + + // 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; + + // if set on the command line then 'height' is now set at this point + std::cout << "height: " << height << std::endl; + + return session.run(); +} diff --git a/packages/Catch2/examples/300-Gen-OwnGenerator.cpp b/packages/Catch2/examples/300-Gen-OwnGenerator.cpp index 09643d6f7..b5d951ac4 100644 --- a/packages/Catch2/examples/300-Gen-OwnGenerator.cpp +++ b/packages/Catch2/examples/300-Gen-OwnGenerator.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 300-Gen-OwnGenerator.cpp // Shows how to define a custom generator. diff --git a/packages/Catch2/examples/301-Gen-MapTypeConversion.cpp b/packages/Catch2/examples/301-Gen-MapTypeConversion.cpp index ba55f65f1..a065d87ae 100644 --- a/packages/Catch2/examples/301-Gen-MapTypeConversion.cpp +++ b/packages/Catch2/examples/301-Gen-MapTypeConversion.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 301-Gen-MapTypeConversion.cpp // Shows how to use map to modify generator's return type. diff --git a/packages/Catch2/examples/302-Gen-Table.cpp b/packages/Catch2/examples/302-Gen-Table.cpp index 97809889c..3cdb14301 100644 --- a/packages/Catch2/examples/302-Gen-Table.cpp +++ b/packages/Catch2/examples/302-Gen-Table.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 302-Gen-Table.cpp // Shows how to use table to run a test many times with different inputs. Lifted from examples on // issue #850. diff --git a/packages/Catch2/examples/310-Gen-VariablesInGenerators.cpp b/packages/Catch2/examples/310-Gen-VariablesInGenerators.cpp index 0339c5f18..5d24d45a1 100644 --- a/packages/Catch2/examples/310-Gen-VariablesInGenerators.cpp +++ b/packages/Catch2/examples/310-Gen-VariablesInGenerators.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 310-Gen-VariablesInGenerator.cpp // Shows how to use variables when creating generators. diff --git a/packages/Catch2/examples/311-Gen-CustomCapture.cpp b/packages/Catch2/examples/311-Gen-CustomCapture.cpp index d12ee7090..ee3103835 100644 --- a/packages/Catch2/examples/311-Gen-CustomCapture.cpp +++ b/packages/Catch2/examples/311-Gen-CustomCapture.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 311-Gen-CustomCapture.cpp // Shows how to provide custom capture list to the generator expression diff --git a/packages/Catch2/examples/CMakeLists.txt b/packages/Catch2/examples/CMakeLists.txt index f99333418..82734adab 100644 --- a/packages/Catch2/examples/CMakeLists.txt +++ b/packages/Catch2/examples/CMakeLists.txt @@ -30,6 +30,7 @@ set( SOURCES_IDIOMATIC_EXAMPLES 110-Fix-ClassFixture.cpp 120-Bdd-ScenarioGivenWhenThen.cpp 210-Evt-EventListeners.cpp + 232-Cfg-CustomMain.cpp 300-Gen-OwnGenerator.cpp 301-Gen-MapTypeConversion.cpp 302-Gen-Table.cpp @@ -53,7 +54,7 @@ set(ALL_EXAMPLE_TARGETS ) foreach( name ${ALL_EXAMPLE_TARGETS} ) - target_link_libraries( ${name} Catch2 Catch2WithMain ) + target_link_libraries( ${name} Catch2WithMain ) endforeach() diff --git a/packages/Catch2/extras/Catch.cmake b/packages/Catch2/extras/Catch.cmake index b37b0bf6d..8f30688c5 100644 --- a/packages/Catch2/extras/Catch.cmake +++ b/packages/Catch2/extras/Catch.cmake @@ -176,8 +176,10 @@ function(catch_discover_tests TARGET) string(SUBSTRING ${args_hash} 0 7 args_hash) # Define rule to generate test list for aforementioned test executable - set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") - set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") + set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-${args_hash}") + set(ctest_include_file "${ctest_file_base}_include.cmake") + set(ctest_tests_file "${ctest_file_base}_tests.cmake") + get_property(crosscompiling_emulator TARGET ${TARGET} PROPERTY CROSSCOMPILING_EMULATOR @@ -218,6 +220,14 @@ function(catch_discover_tests TARGET) elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") + get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL + PROPERTY GENERATOR_IS_MULTI_CONFIG + ) + + if(GENERATOR_IS_MULTI_CONFIG) + set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake") + endif() + string(CONCAT ctest_include_content "if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n" " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" @@ -249,7 +259,22 @@ function(catch_discover_tests TARGET) "endif()" "\n" ) - file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_content}") + if(GENERATOR_IS_MULTI_CONFIG) + foreach(_config ${CMAKE_CONFIGURATION_TYPES}) + file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>) + endforeach() + string(CONCAT ctest_include_multi_content + "if(NOT CTEST_CONFIGURATION_TYPE)" "\n" + " message(\"No configuration for testing specified, use '-C <cfg>'.\")" "\n" + "else()" "\n" + " include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" "\n" + "endif()" "\n" + ) + file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_multi_content}") + else() + file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}") + file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")") + endif() endif() if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") diff --git a/packages/Catch2/extras/CatchAddTests.cmake b/packages/Catch2/extras/CatchAddTests.cmake index 91f79f3c5..692e34056 100644 --- a/packages/Catch2/extras/CatchAddTests.cmake +++ b/packages/Catch2/extras/CatchAddTests.cmake @@ -74,6 +74,10 @@ function(catch_discover_tests_impl) ) endif() + # Make sure to escape ; (semicolons) in test names first, because + # that'd break the foreach loop for "Parse output" later and create + # wrongly splitted and thus failing test cases (false positives) + string(REPLACE ";" "\;" output "${output}") string(REPLACE "\n" ";" output "${output}") # Prepare reporter @@ -84,10 +88,10 @@ function(catch_discover_tests_impl) # note that the use of --list-reporters is not the important part, # we only want to check whether the execution succeeds with ${reporter_arg} execute_process( - COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters + COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters OUTPUT_VARIABLE reporter_check_output RESULT_VARIABLE reporter_check_result - WORKING_DIRECTORY "${TEST_WORKING_DIR}" + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" ) if(${reporter_check_result} EQUAL 255) message(FATAL_ERROR @@ -95,7 +99,7 @@ function(catch_discover_tests_impl) ) elseif(NOT ${reporter_check_result} EQUAL 0) message(FATAL_ERROR - "Error running test executable '${TEST_EXECUTABLE}':\n" + "Error running test executable '${_TEST_EXECUTABLE}':\n" " Result: ${reporter_check_result}\n" " Output: ${reporter_check_output}\n" ) @@ -119,15 +123,16 @@ function(catch_discover_tests_impl) # Parse output foreach(line ${output}) - set(test ${line}) + set(test "${line}") # Escape characters in test case names that would be parsed by Catch2 - set(test_name ${test}) - foreach(char , [ ]) - string(REPLACE ${char} "\\${char}" test_name ${test_name}) + # Note that the \ escaping must happen FIRST! Do not change the order. + set(test_name "${test}") + foreach(char \\ , [ ]) + string(REPLACE ${char} "\\${char}" test_name "${test_name}") endforeach(char) # ...add output dir if(output_dir) - string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean ${test_name}) + string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean "${test_name}") set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}") endif() diff --git a/packages/Catch2/extras/catch_amalgamated.cpp b/packages/Catch2/extras/catch_amalgamated.cpp index a81b1b6ae..f68c9005b 100644 --- a/packages/Catch2/extras/catch_amalgamated.cpp +++ b/packages/Catch2/extras/catch_amalgamated.cpp @@ -1,3 +1,4 @@ + // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -5,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.2 -// Generated: 2023-02-26 10:28:48.270752 +// Catch v3.5.2 +// Generated: 2024-01-15 14:06:36.675713 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -48,6 +49,80 @@ namespace Catch { } // namespace Catch +// Adapted from donated nonius code. + + +#include <vector> + +namespace Catch { + namespace Benchmark { + namespace Detail { + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector<double> samples; + samples.reserve(static_cast<size_t>(last - first)); + for (auto current = first; current != last; ++current) { + samples.push_back( current->count() ); + } + + auto analysis = Catch::Benchmark::Detail::analyse_samples( + cfg.benchmarkConfidenceInterval(), + cfg.benchmarkResamples(), + samples.data(), + samples.data() + samples.size() ); + auto outliers = Catch::Benchmark::Detail::classify_outliers( + samples.data(), samples.data() + samples.size() ); + + auto wrap_estimate = [](Estimate<double> e) { + return Estimate<FDuration> { + FDuration(e.point), + FDuration(e.lower_bound), + FDuration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector<FDuration> samples2; + samples2.reserve(samples.size()); + for (auto s : samples) { + samples2.push_back( FDuration( s ) ); + } + + return { + CATCH_MOVE(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector<FDuration> samples; + samples.reserve(static_cast<size_t>(last - first)); + + FDuration mean = FDuration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(FDuration(*it)); + mean += FDuration(*it); + } + mean /= i; + + return SampleAnalysis{ + CATCH_MOVE(samples), + Estimate<FDuration>{ mean, mean, mean, 0.0 }, + Estimate<FDuration>{ FDuration( 0 ), + FDuration( 0 ), + FDuration( 0 ), + 0.0 }, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + namespace Catch { @@ -60,6 +135,7 @@ namespace Catch { + #include <exception> namespace Catch { @@ -86,9 +162,11 @@ namespace Catch { +#include <algorithm> #include <cassert> +#include <cmath> #include <cstddef> -#include <iterator> +#include <numeric> #include <random> @@ -96,139 +174,199 @@ namespace Catch { #include <future> #endif -namespace { +namespace Catch { + namespace Benchmark { + namespace Detail { + namespace { + + template <typename URng, typename Estimator> + static sample + resample( URng& rng, + unsigned int resamples, + double const* first, + double const* last, + Estimator& estimator ) { + auto n = static_cast<size_t>( last - first ); + std::uniform_int_distribution<size_t> dist( 0, n - 1 ); + + sample out; + out.reserve( resamples ); + std::vector<double> resampled; + resampled.reserve( n ); + for ( size_t i = 0; i < resamples; ++i ) { + resampled.clear(); + for ( size_t s = 0; s < n; ++s ) { + resampled.push_back( first[dist( rng )] ); + } + const auto estimate = + estimator( resampled.data(), resampled.data() + resampled.size() ); + out.push_back( estimate ); + } + std::sort( out.begin(), out.end() ); + return out; + } -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 ); - } + static 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; + } -} + static 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; + } + + static double + standard_deviation( double const* first, double const* 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 ); + } + + static sample jackknife( double ( *estimator )( double const*, + double const* ), + double* first, + double* last ) { + const auto second = first + 1; + sample results; + results.reserve( static_cast<size_t>( last - first ) ); + + for ( auto it = first; it != last; ++it ) { + std::iter_swap( it, first ); + results.push_back( estimator( second, last ) ); + } + + return results; + } + + + } // namespace + } // namespace Detail + } // namespace Benchmark +} // namespace Catch 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) { + double weighted_average_quantile( int k, + int q, + double* first, + double* 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 ) ) { + if ( Catch::Detail::directCompare( g, 0 ) ) { return xj; } @@ -236,6 +374,48 @@ namespace Catch { return xj + g * (xj1 - xj); } + OutlierClassification + classify_outliers( double const* first, double const* last ) { + std::vector<double> copy( first, last ); + + auto q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() ); + auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() ); + 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 ) { + const double 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; + } + + double mean( double const* first, double const* last ) { + auto count = last - first; + double sum = 0.; + while (first != last) { + sum += *first; + ++first; + } + return sum / static_cast<double>(count); + } + + double normal_cdf( double x ) { + return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0; + } double erfc_inv(double x) { return erf_inv(1.0 - x); @@ -257,50 +437,77 @@ namespace Catch { return result; } + Estimate<double> + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ) { + 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.data(), jack.data() + jack.size() ); + double sum_squares = 0, sum_cubes = 0; + for ( double x : jack ) { + auto difference = jack_mean - x; + auto square = difference * difference; + auto cube = square * difference; + sum_squares += square; + sum_cubes += cube; + } - 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; + 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 ( Catch::Detail::directCompare( prob_n, 0. ) ) { + return { point, point, point, confidence_level }; + } - 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))); - }; + double bias = normal_quantile( prob_n ); + double z1 = normal_quantile( ( 1. - confidence_level ) / 2. ); - auto var_out = [n, sb2, sg2](double c) { - double nc = n - c; - return (nc / n) * (sb2 - nc * sg2); + auto cumn = [n]( double x ) -> long { + return std::lround( normal_cdf( x ) * + static_cast<double>( n ) ); }; - - 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 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 }; + } + + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double* first, + double* last) { + auto mean = &Detail::mean; 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(); + auto Estimate = [=](double(*f)(double const*, double const*)) { + std::random_device rd; + auto seed = rd(); return std::async(std::launch::async, [=] { - std::mt19937 rng(seed); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }); @@ -312,9 +519,10 @@ namespace Catch { 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 Estimate = [=](double(*f)(double const* , double const*)) { + std::random_device rd; + auto seed = rd(); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }; @@ -323,6 +531,7 @@ namespace Catch { auto stddev_estimate = Estimate(stddev); #endif // CATCH_USE_ASYNC + auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); return { mean_estimate, stddev_estimate, outlier_variance }; @@ -394,10 +603,10 @@ namespace Catch { } namespace literals { - Approx operator "" _a(long double val) { + Approx operator ""_a(long double val) { return Approx(val); } - Approx operator "" _a(unsigned long long val) { + Approx operator ""_a(unsigned long long val) { return Approx(val); } } // end namespace literals @@ -596,7 +805,7 @@ namespace Catch { elem = trim(elem); } - // Insert the default reporter if user hasn't asked for a specfic one + // Insert the default reporter if user hasn't asked for a specific one if ( m_data.reporterSpecifications.empty() ) { m_data.reporterSpecifications.push_back( { #if defined( CATCH_CONFIG_DEFAULT_REPORTER ) @@ -775,7 +984,11 @@ namespace Catch { } - Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + Capturer::Capturer( StringRef macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType resultType, + StringRef names ): + m_resultCapture( getResultCapture() ) { auto trimmed = [&] (size_t start, size_t end) { while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) { ++start; @@ -852,6 +1065,8 @@ namespace Catch { +#include <exception> + namespace Catch { namespace { @@ -862,7 +1077,7 @@ namespace Catch { public: // IRegistryHub RegistryHub() = default; - IReporterRegistry const& getReporterRegistry() const override { + ReporterRegistry const& getReporterRegistry() const override { return m_reporterRegistry; } ITestCaseRegistry const& getTestCaseRegistry() const override { @@ -938,6 +1153,7 @@ namespace Catch { #include <algorithm> #include <cassert> +#include <exception> #include <iomanip> #include <set> @@ -1420,12 +1636,20 @@ namespace Catch { for (size_t idx = 0; idx < originalTags.size(); ++idx) { auto c = originalTags[idx]; if (c == '[') { - assert(!inTag); + CATCH_ENFORCE( + !inTag, + "Found '[' inside a tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = true; tagStart = idx; } if (c == ']') { - assert(inTag); + CATCH_ENFORCE( + inTag, + "Found unmatched ']' while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = false; tagEnd = idx; assert(tagStart < tagEnd); @@ -1434,7 +1658,11 @@ namespace Catch { // 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"); + CATCH_ENFORCE( !tagStr.empty(), + "Found an empty tag while registering test case '" + << _nameAndTags.name << "' at " + << _lineInfo ); + enforceNotReservedTag(tagStr, lineInfo); properties |= parseSpecialTag(tagStr); // When copying a tag to the backing storage, we need to @@ -1448,8 +1676,12 @@ namespace Catch { // the tags. internalAppendTag(tagStr); } - (void)inTag; // Silence "set-but-unused" warning in release mode. } + CATCH_ENFORCE( !inTag, + "Found an unclosed tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + + // Add [.] if relevant if (isHidden()) { internalAppendTag("."_sr); @@ -1625,16 +1857,18 @@ namespace Catch { 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 ){ + TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const { + Matches matches; + matches.reserve( m_filters.size() ); + for ( auto const& filter : m_filters ) { std::vector<TestCaseHandle const*> currentMatches; - for( auto const& test : testCases ) - if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) ) + for ( auto const& test : testCases ) + if ( isThrowSafe( test, config ) && + filter.matches( test.getTestCaseInfo() ) ) currentMatches.emplace_back( &test ); - return FilterMatch{ extractFilterName(filter), currentMatches }; - } ); + matches.push_back( + FilterMatch{ extractFilterName( filter ), currentMatches } ); + } return matches; } @@ -1991,6 +2225,19 @@ namespace Catch { } + + +namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr<IExceptionTranslator>&& translator ) { + getMutableRegistryHub().registerTranslator( + CATCH_MOVE( translator ) ); + } + } // namespace Detail +} // namespace Catch + + #include <ostream> namespace Catch { @@ -2021,7 +2268,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 3, 2, "", 0 ); + static Version version( 3, 5, 2, "", 0 ); return version; } @@ -2074,8 +2321,36 @@ namespace Detail { +#include <random> -std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } +namespace Catch { + namespace Generators { + namespace Detail { + std::uint32_t getSeed() { return sharedRng()(); } + } // namespace Detail + + struct RandomFloatingGenerator<long double>::PImpl { + PImpl( long double a, long double b, uint32_t seed ): + rng( seed ), dist( a, b ) {} + + Catch::SimplePcg32 rng; + std::uniform_real_distribution<long double> dist; + }; + + RandomFloatingGenerator<long double>::RandomFloatingGenerator( + long double a, long double b, std::uint32_t seed) : + m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) { + static_cast<void>( next() ); + } + + RandomFloatingGenerator<long double>::~RandomFloatingGenerator() = + default; + bool RandomFloatingGenerator<long double>::next() { + m_current_number = m_pimpl->dist( m_pimpl->rng ); + return true; + } + } // namespace Generators +} // namespace Catch @@ -2135,9 +2410,7 @@ namespace Catch { -#include <algorithm> #include <cassert> -#include <iomanip> namespace Catch { @@ -2172,8 +2445,6 @@ namespace Catch { 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 @@ -2232,14 +2503,6 @@ namespace Catch { namespace Catch { - IReporterRegistry::~IReporterRegistry() = default; -} - - - - -namespace Catch { - ITestInvoker::~ITestInvoker() = default; ITestCaseRegistry::~ITestCaseRegistry() = default; } @@ -2254,7 +2517,9 @@ namespace Catch { ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, m_resultCapture( getResultCapture() ) - {} + { + m_resultCapture.notifyAssertionStarted( m_assertionInfo ); + } void AssertionHandler::handleExpr( ITransientExpression const& expr ) { m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); @@ -2268,7 +2533,7 @@ namespace Catch { } void AssertionHandler::complete() { - setCompleted(); + m_completed = true; if( m_reaction.shouldDebugBreak ) { // If you find your debugger stopping you here then go one level up on the @@ -2281,16 +2546,9 @@ namespace Catch { throw_test_failure_exception(); } if ( m_reaction.shouldSkip ) { -#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); -#else - CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); -#endif + throw_test_skip_exception(); } } - void AssertionHandler::setCompleted() { - m_completed = true; - } void AssertionHandler::handleUnexpectedInflightException() { m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); @@ -2362,13 +2620,29 @@ namespace { ; } - std::string normaliseOpt( std::string const& optName ) { -#ifdef CATCH_PLATFORM_WINDOWS - if ( optName[0] == '/' ) - return "-" + optName.substr( 1 ); - else + Catch::StringRef normaliseOpt( Catch::StringRef optName ) { + if ( optName[0] == '-' +#if defined(CATCH_PLATFORM_WINDOWS) + || optName[0] == '/' #endif - return optName; + ) { + return optName.substr( 1, optName.size() ); + } + + return optName; + } + + static size_t find_first_separator(Catch::StringRef sr) { + auto is_separator = []( char c ) { + return c == ' ' || c == ':' || c == '='; + }; + size_t pos = 0; + while (pos < sr.size()) { + if (is_separator(sr[pos])) { return pos; } + ++pos; + } + + return Catch::StringRef::npos; } } // namespace @@ -2386,23 +2660,23 @@ namespace Catch { } if ( it != itEnd ) { - auto const& next = *it; + StringRef next = *it; if ( isOptPrefix( next[0] ) ) { - auto delimiterPos = next.find_first_of( " :=" ); - if ( delimiterPos != std::string::npos ) { + auto delimiterPos = find_first_separator(next); + if ( delimiterPos != StringRef::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); m_tokenBuffer.push_back( { TokenType::Argument, - next.substr( delimiterPos + 1 ) } ); + next.substr( delimiterPos + 1, next.size() ) } ); } else { if ( next[1] != '-' && next.size() > 2 ) { - std::string opt = "- "; + // Combined short args, e.g. "-ab" for "-a -b" for ( size_t i = 1; i < next.size(); ++i ) { - opt[1] = next[i]; m_tokenBuffer.push_back( - { TokenType::Option, opt } ); + { TokenType::Option, + next.substr( i, 1 ) } ); } } else { m_tokenBuffer.push_back( @@ -2462,12 +2736,12 @@ namespace Catch { size_t ParserBase::cardinality() const { return 1; } InternalParseResult ParserBase::parse( Args const& args ) const { - return parse( args.exeName(), TokenStream( args ) ); + return parse( static_cast<std::string>(args.exeName()), TokenStream( args ) ); } ParseState::ParseState( ParseResultType type, - TokenStream const& remainingTokens ): - m_type( type ), m_remainingTokens( remainingTokens ) {} + TokenStream remainingTokens ): + m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {} ParserResult BoundFlagRef::setFlag( bool flag ) { m_ref = flag; @@ -2485,34 +2759,34 @@ namespace Catch { } // namespace Detail Detail::InternalParseResult Arg::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { auto validationResult = validate(); if (!validationResult) return Detail::InternalParseResult(validationResult); - auto remainingTokens = tokens; - auto const& token = *remainingTokens; + auto token = *tokens; if (token.type != Detail::TokenType::Argument) return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::NoMatch, remainingTokens)); + ParseResultType::NoMatch, CATCH_MOVE(tokens))); 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); + auto result = valueRef->setValue(static_cast<std::string>(token.token)); + if ( !result ) + return Detail::InternalParseResult( result ); else - return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + return Detail::InternalParseResult::ok( + Detail::ParseState( ParseResultType::Matched, + CATCH_MOVE( ++tokens ) ) ); } Opt::Opt(bool& ref) : ParserRefImpl(std::make_shared<Detail::BoundFlagRef>(ref)) {} - std::vector<Detail::HelpColumns> Opt::getHelpColumns() const { - std::ostringstream oss; + Detail::HelpColumns Opt::getHelpColumns() const { + ReusableStringStream oss; bool first = true; for (auto const& opt : m_optNames) { if (first) @@ -2523,10 +2797,10 @@ namespace Catch { } if (!m_hint.empty()) oss << " <" << m_hint << '>'; - return { { oss.str(), m_description } }; + return { oss.str(), m_description }; } - bool Opt::isMatch(std::string const& optToken) const { + bool Opt::isMatch(StringRef optToken) const { auto normalisedToken = normaliseOpt(optToken); for (auto const& name : m_optNames) { if (normaliseOpt(name) == normalisedToken) @@ -2536,15 +2810,14 @@ namespace Catch { } Detail::InternalParseResult Opt::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream 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 (tokens && + tokens->type == Detail::TokenType::Option) { + auto const& token = *tokens; if (isMatch(token.token)) { if (m_ref->isFlag()) { auto flagRef = @@ -2556,35 +2829,35 @@ namespace Catch { if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } else { auto valueRef = static_cast<Detail::BoundValueRefBase*>( m_ref.get()); - ++remainingTokens; - if (!remainingTokens) + ++tokens; + if (!tokens) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - auto const& argToken = *remainingTokens; + auto const& argToken = *tokens; if (argToken.type != Detail::TokenType::Argument) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - const auto result = valueRef->setValue(argToken.token); + const auto result = valueRef->setValue(static_cast<std::string>(argToken.token)); if (!result) return Detail::InternalParseResult(result); if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + ParseResultType::Matched, CATCH_MOVE(++tokens))); } } return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, remainingTokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } Detail::Result Opt::validate() const { @@ -2616,9 +2889,9 @@ namespace Catch { Detail::InternalParseResult ExeName::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, tokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } ParserResult ExeName::set(std::string const& newName) { @@ -2648,9 +2921,9 @@ namespace Catch { std::vector<Detail::HelpColumns> Parser::getHelpColumns() const { std::vector<Detail::HelpColumns> cols; + cols.reserve( m_options.size() ); for ( auto const& o : m_options ) { - auto childCols = o.getHelpColumns(); - cols.insert( cols.end(), childCols.begin(), childCols.end() ); + cols.push_back(o.getHelpColumns()); } return cols; } @@ -2688,12 +2961,12 @@ namespace Catch { optWidth = ( std::min )( optWidth, consoleWidth / 2 ); - for ( auto const& cols : rows ) { - auto row = TextFlow::Column( cols.left ) + for ( auto& cols : rows ) { + auto row = TextFlow::Column( CATCH_MOVE(cols.left) ) .width( optWidth ) .indent( 2 ) + TextFlow::Spacer( 4 ) + - TextFlow::Column( cols.right ) + TextFlow::Column( static_cast<std::string>(cols.descriptions) ) .width( consoleWidth - 7 - optWidth ); os << row << '\n'; } @@ -2715,7 +2988,7 @@ namespace Catch { Detail::InternalParseResult Parser::parse( std::string const& exeName, - Detail::TokenStream const& tokens ) const { + Detail::TokenStream tokens ) const { struct ParserInfo { ParserBase const* parser = nullptr; @@ -2733,7 +3006,7 @@ namespace Catch { m_exeName.set( exeName ); auto result = Detail::InternalParseResult::ok( - Detail::ParseState( ParseResultType::NoMatch, tokens ) ); + Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) ); while ( result.value().remainingTokens() ) { bool tokenParsed = false; @@ -2741,7 +3014,7 @@ namespace Catch { if ( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { result = parseInfo.parser->parse( - exeName, result.value().remainingTokens() ); + exeName, CATCH_MOVE(result).value().remainingTokens() ); if ( !result ) return result; if ( result.value().type() != @@ -2767,7 +3040,7 @@ namespace Catch { 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) : + Args::Args(std::initializer_list<StringRef> args) : m_exeName(*args.begin()), m_args(args.begin() + 1, args.end()) {} @@ -2917,7 +3190,7 @@ namespace Catch { auto const& reporterSpec = *parsed; - IReporterRegistry::FactoryMap const& factories = + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); auto result = factories.find( reporterSpec.name() ); @@ -3073,8 +3346,8 @@ namespace Catch { ( "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 ) + ( "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" ) @@ -3155,7 +3428,7 @@ namespace Catch { 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 { + class NoColourImpl final : public ColourImpl { public: NoColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3173,7 +3446,7 @@ namespace Catch { namespace Catch { namespace { - class Win32ColourImpl : public ColourImpl { + class Win32ColourImpl final : public ColourImpl { public: Win32ColourImpl(IStream* stream): ColourImpl(stream) { @@ -3239,7 +3512,7 @@ namespace { namespace Catch { namespace { - class ANSIColourImpl : public ColourImpl { + class ANSIColourImpl final : public ColourImpl { public: ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3355,49 +3628,27 @@ namespace Catch { 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; - } + Context* Context::currentContext = nullptr; - friend IMutableContext& getCurrentMutableContext(); - - private: - IConfig const* m_config = nullptr; - IResultCapture* m_resultCapture = nullptr; - }; - - IMutableContext *IMutableContext::currentContext = nullptr; - - void IMutableContext::createContext() - { + void cleanUpContext() { + delete Context::currentContext; + Context::currentContext = nullptr; + } + void Context::createContext() { currentContext = new Context(); } - void cleanUpContext() { - delete IMutableContext::currentContext; - IMutableContext::currentContext = nullptr; + Context& getCurrentMutableContext() { + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } - IContext::~IContext() = default; - IMutableContext::~IMutableContext() = default; - Context::~Context() = default; + void Context::setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + + void Context::setConfig( IConfig const* config ) { m_config = config; } SimplePcg32& sharedRng() { static SimplePcg32 s_rng; @@ -3635,7 +3886,7 @@ namespace Catch { return parsed; } - EnumInfo::~EnumInfo() {} + EnumInfo::~EnumInfo() = default; StringRef EnumInfo::lookup( int value ) const { for( auto const& valueToName : m_values ) { @@ -3680,10 +3931,27 @@ namespace Catch { +#include <exception> + namespace Catch { - ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + namespace { + static std::string tryTranslators( + std::vector< + Detail::unique_ptr<IExceptionTranslator const>> const& translators ) { + if ( translators.empty() ) { + std::rethrow_exception( std::current_exception() ); + } else { + return translators[0]->translate( translators.begin() + 1, + translators.end() ); + } + } + } +#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) { m_translators.push_back( CATCH_MOVE( translator ) ); @@ -3706,7 +3974,7 @@ namespace Catch { // 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(); + return tryTranslators(m_translators); } // To avoid having to handle TFE explicitly everywhere, we just // rethrow it so that it goes back up the caller. @@ -3730,25 +3998,12 @@ namespace Catch { } } - 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 - } @@ -4005,6 +4260,17 @@ namespace Catch { return i; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + bool directCompare( float lhs, float rhs ) { return lhs == rhs; } + bool directCompare( double lhs, double rhs ) { return lhs == rhs; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } // end namespace Detail } // end namespace Catch @@ -4053,7 +4319,7 @@ namespace Catch { namespace Detail { namespace { template<typename WriterF, std::size_t bufferSize=256> - class StreamBufImpl : public std::streambuf { + class StreamBufImpl final : public std::streambuf { char data[bufferSize]; WriterF m_writer; @@ -4101,7 +4367,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class FileStream : public IStream { + class FileStream final : public IStream { std::ofstream m_ofs; public: FileStream( std::string const& filename ) { @@ -4109,7 +4375,6 @@ namespace Detail { 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; @@ -4118,13 +4383,12 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class CoutStream : public IStream { + class CoutStream final : 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; } @@ -4138,7 +4402,6 @@ namespace Detail { // 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; } @@ -4147,7 +4410,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class DebugOutStream : public IStream { + class DebugOutStream final : public IStream { Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; std::ostream m_os; public: @@ -4156,8 +4419,6 @@ namespace Detail { m_os( m_streamBuf.get() ) {} - ~DebugOutStream() override = default; - public: // IStream std::ostream& stream() override { return m_os; } }; @@ -4189,6 +4450,147 @@ namespace Detail { +namespace Catch { + void JsonUtils::indent( std::ostream& os, std::uint64_t level ) { + for ( std::uint64_t i = 0; i < level; ++i ) { + os << " "; + } + } + void JsonUtils::appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ) { + if ( should_comma ) { os << ','; } + should_comma = true; + os << '\n'; + indent( os, level ); + } + + JsonObjectWriter::JsonObjectWriter( std::ostream& os ): + JsonObjectWriter{ os, 0 } {} + + JsonObjectWriter::JsonObjectWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '{'; + } + JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ): + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + + JsonObjectWriter::~JsonObjectWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << '}'; + } + + JsonValueWriter JsonObjectWriter::write( StringRef key ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + + m_os << '"' << key << "\": "; + return JsonValueWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter::JsonArrayWriter( std::ostream& os ): + JsonArrayWriter{ os, 0 } {} + JsonArrayWriter::JsonArrayWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '['; + } + JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ): + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + JsonArrayWriter::~JsonArrayWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << ']'; + } + + JsonObjectWriter JsonArrayWriter::writeObject() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonObjectWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter JsonArrayWriter::writeArray() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonArrayWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter& JsonArrayWriter::write( bool value ) { + return writeImpl( value ); + } + + JsonValueWriter::JsonValueWriter( std::ostream& os ): + JsonValueWriter{ os, 0 } {} + + JsonValueWriter::JsonValueWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } {} + + JsonObjectWriter JsonValueWriter::writeObject() && { + return JsonObjectWriter{ m_os, m_indent_level }; + } + + JsonArrayWriter JsonValueWriter::writeArray() && { + return JsonArrayWriter{ m_os, m_indent_level }; + } + + void JsonValueWriter::write( Catch::StringRef value ) && { + writeImpl( value, true ); + } + + void JsonValueWriter::write( bool value ) && { + writeImpl( value ? "true"_sr : "false"_sr, false ); + } + + void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { + if ( quote ) { m_os << '"'; } + for (char c : value) { + // Escape list taken from https://www.json.org/json-en.html, + // string definition. + // Note that while forward slash _can_ be escaped, it does + // not have to be, if JSON is not further embedded somewhere + // where forward slash is meaningful. + if ( c == '"' ) { + m_os << "\\\""; + } else if ( c == '\\' ) { + m_os << "\\\\"; + } else if ( c == '\b' ) { + m_os << "\\b"; + } else if ( c == '\f' ) { + m_os << "\\f"; + } else if ( c == '\n' ) { + m_os << "\\n"; + } else if ( c == '\r' ) { + m_os << "\\r"; + } else if ( c == '\t' ) { + m_os << "\\t"; + } else { + m_os << c; + } + } + if ( quote ) { m_os << '"'; } + } + +} // namespace Catch + + + namespace Catch { @@ -4231,7 +4633,7 @@ namespace Catch { #else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv - Catch::LeakDetector::LeakDetector() {} + Catch::LeakDetector::LeakDetector() = default; #endif // CATCH_CONFIG_WINDOWS_CRTDBG @@ -4242,7 +4644,6 @@ Catch::LeakDetector::~LeakDetector() { - namespace Catch { namespace { @@ -4277,7 +4678,7 @@ namespace Catch { void listReporters(IEventListener& reporter) { std::vector<ReporterDescription> descriptions; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); descriptions.reserve(factories.size()); for (auto const& fac : factories) { descriptions.push_back({ fac.first, fac.second->getDescription() }); @@ -4599,6 +5000,14 @@ namespace Catch { } #endif +#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER ) + float nextafter( float x, float y ) { return std::nextafter( x, y ); } + double nextafter( double x, double y ) { return std::nextafter( x, y ); } +#else + float nextafter( float x, float y ) { return ::nextafterf( x, y ); } + double nextafter( double x, double y ) { return ::nextafter( x, y ); } +#endif + } // end namespace Catch @@ -4680,10 +5089,10 @@ namespace Catch { 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{}() ); + case GenerateFrom::RandomDevice: { + std::random_device rd; + return Detail::fillBitsFrom<std::uint32_t>( rd ); + } default: CATCH_ERROR("Unknown generation method"); @@ -4696,49 +5105,73 @@ namespace Catch { namespace Catch { + struct ReporterRegistry::ReporterRegistryImpl { + std::vector<Detail::unique_ptr<EventListenerFactory>> listeners; + std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess> + factories; + }; - ReporterRegistry::ReporterRegistry() { + ReporterRegistry::ReporterRegistry(): + m_impl( Detail::make_unique<ReporterRegistryImpl>() ) { // 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>>(); + m_impl->factories["Automake"] = + Detail::make_unique<ReporterFactory<AutomakeReporter>>(); + m_impl->factories["compact"] = + Detail::make_unique<ReporterFactory<CompactReporter>>(); + m_impl->factories["console"] = + Detail::make_unique<ReporterFactory<ConsoleReporter>>(); + m_impl->factories["JUnit"] = + Detail::make_unique<ReporterFactory<JunitReporter>>(); + m_impl->factories["SonarQube"] = + Detail::make_unique<ReporterFactory<SonarQubeReporter>>(); + m_impl->factories["TAP"] = + Detail::make_unique<ReporterFactory<TAPReporter>>(); + m_impl->factories["TeamCity"] = + Detail::make_unique<ReporterFactory<TeamCityReporter>>(); + m_impl->factories["XML"] = + Detail::make_unique<ReporterFactory<XmlReporter>>(); + m_impl->factories["JSON"] = + Detail::make_unique<ReporterFactory<JsonReporter>>(); } 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) ); + IEventListenerPtr + ReporterRegistry::create( std::string const& name, + ReporterConfig&& config ) const { + auto it = m_impl->factories.find( name ); + if ( it == m_impl->factories.end() ) return nullptr; + return it->second->create( CATCH_MOVE( config ) ); } - void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { + 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" ); + "'::' is not allowed in reporter name: '" + name + + '\'' ); + auto ret = m_impl->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) ); + m_impl->listeners.push_back( CATCH_MOVE( factory ) ); } - IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { - return m_factories; - } - IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { - return m_listeners; + std::map<std::string, + IReporterFactoryPtr, + Detail::CaseInsensitiveLess> const& + ReporterRegistry::getFactories() const { + return m_impl->factories; } -} + std::vector<Detail::unique_ptr<EventListenerFactory>> const& + ReporterRegistry::getListeners() const { + return m_impl->listeners; + } +} // namespace Catch @@ -4754,9 +5187,9 @@ namespace Catch { }; kvPair splitKVPair(StringRef kvString) { - auto splitPos = static_cast<size_t>( std::distance( - kvString.begin(), - std::find( kvString.begin(), kvString.end(), '=' ) ) ); + auto splitPos = static_cast<size_t>( + std::find( kvString.begin(), kvString.end(), '=' ) - + kvString.begin() ); return { kvString.substr( 0, splitPos ), kvString.substr( splitPos + 1, kvString.size() ) }; @@ -4988,146 +5421,151 @@ namespace Catch { namespace Catch { namespace Generators { - struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { - GeneratorBasePtr m_generator; + namespace { + struct GeneratorTracker final : TestCaseTracking::TrackerBase, + IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( + TestCaseTracking::NameAndLocation&& nameAndLocation, + TrackerContext& ctx, + ITracker* parent ): + TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} + + static GeneratorTracker* + acquire( TrackerContext& ctx, + TestCaseTracking::NameAndLocationRef 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 { + return nullptr; + } - GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ) - {} - ~GeneratorTracker() override; - - static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef 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 { - return nullptr; - } + if ( !tracker->isComplete() ) { tracker->open(); } - if( !tracker->isComplete() ) { - tracker->open(); + return tracker; } - 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; - } + // 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; + } - 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; + // 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. + assert( m_generator && "Tracker without generator" ); + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->countedNext() ) ) { + m_children.clear(); + m_runState = Executing; } - 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. - assert( m_generator && "Tracker without generator" ); - 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; + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = CATCH_MOVE( generator ); + } + }; + } // namespace } 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); + getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); } @@ -5222,7 +5660,7 @@ namespace Catch { } - void RunContext::assertionEnded(AssertionResult const & result) { + void RunContext::assertionEnded(AssertionResult&& result) { if (result.getResultType() == ResultWas::Ok) { m_totals.assertions.passed++; m_lastAssertionPassed = true; @@ -5244,19 +5682,27 @@ namespace Catch { m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); - if (result.getResultType() != ResultWas::Warning) + if ( result.getResultType() != ResultWas::Warning ) { m_messageScopes.clear(); + } - // Reset working state - resetAssertionInfo(); - m_lastResult = result; + // Reset working state. assertion info will be reset after + // populateReaction is run if it is needed + m_lastResult = CATCH_MOVE( result ); } void RunContext::resetAssertionInfo() { m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal; + } + + void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { + m_reporter->assertionStarting( info ); } - bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) { + bool RunContext::sectionStarted( StringRef sectionName, + SourceLineInfo const& sectionLineInfo, + Counts& assertions ) { ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocationRef( @@ -5394,7 +5840,8 @@ namespace Catch { tempResult.message = static_cast<std::string>(message); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); - assertionEnded(result); + assertionEnded(CATCH_MOVE(result) ); + resetAssertionInfo(); handleUnfinishedSections(); @@ -5516,8 +5963,6 @@ namespace Catch { ITransientExpression const& expr, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - bool negated = isFalseTest( info.resultDisposition ); bool result = expr.getResult() != negated; @@ -5533,6 +5978,7 @@ namespace Catch { reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); populateReaction( reaction ); } + resetAssertionInfo(); } void RunContext::reportExpr( AssertionInfo const &info, @@ -5546,7 +5992,7 @@ namespace Catch { AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); } void RunContext::handleMessage( @@ -5555,22 +6001,23 @@ namespace Catch { 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, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if ( !assertionResult.isOk() ) { + + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } else if ( resultType == ResultWas::ExplicitSkip ) { // TODO: Need to handle this explicitly, as ExplicitSkip is // considered "OK" reaction.shouldSkip = true; } + resetAssertionInfo(); } void RunContext::handleUnexpectedExceptionNotThrown( AssertionInfo const& info, @@ -5581,16 +6028,17 @@ namespace Catch { void RunContext::handleUnexpectedInflightException( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) { m_lastAssertionInfo = info; AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = message; + data.message = CATCH_MOVE(message); AssertionResult assertionResult{ info, CATCH_MOVE(data) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); populateReaction( reaction ); + resetAssertionInfo(); } void RunContext::populateReaction( AssertionReaction& reaction ) { @@ -5607,7 +6055,8 @@ namespace Catch { AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); + resetAssertionInfo(); } void RunContext::handleNonExpr( AssertionInfo const &info, @@ -5618,10 +6067,11 @@ namespace Catch { AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if( !assertionResult.isOk() ) - populateReaction( reaction ); + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } + resetAssertionInfo(); } @@ -5790,7 +6240,6 @@ namespace Catch { -#include <algorithm> #include <ostream> #include <cstring> #include <cctype> @@ -5814,9 +6263,9 @@ namespace Catch { 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 ); - } ); + for ( char& c : s ) { + c = toLower( c ); + } } std::string toLower( std::string const& s ) { std::string lc = s; @@ -5949,7 +6398,7 @@ namespace Catch { namespace Catch { - TagAliasRegistry::~TagAliasRegistry() {} + TagAliasRegistry::~TagAliasRegistry() = default; TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); @@ -6030,6 +6479,38 @@ namespace Catch { namespace Catch { + namespace { + static 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 ); + } + } + + static bool matchTest( TestCaseHandle const& testCase, + TestSpec const& testSpec, + IConfig const& config ) { + return testSpec.matches( testCase.getTestCaseInfo() ) && + isThrowSafe( testCase, config ); + } + + } // end unnamed namespace + std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) { switch (config.runOrder()) { case TestRunOrder::Declared: @@ -6047,7 +6528,6 @@ namespace Catch { return sorted; } case TestRunOrder::Randomized: { - seedRng(config); using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>; TestCaseInfoHasher h{ config.rngSeed() }; @@ -6086,29 +6566,6 @@ namespace Catch { 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() ); @@ -6149,13 +6606,6 @@ namespace Catch { return m_sortedFunctions; } - - - /////////////////////////////////////////////////////////////////////////// - void TestInvokerAsFunction::invoke() const { - m_testAsFunction(); - } - } // end namespace Catch @@ -6401,6 +6851,14 @@ namespace Catch { #endif } + void throw_test_skip_exception() { +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + throw Catch::TestSkipException(); +#else + CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); +#endif + } + } // namespace Catch @@ -6409,9 +6867,10 @@ namespace Catch { #include <iterator> namespace Catch { + ITestInvoker::~ITestInvoker() = default; namespace { - StringRef extractClassName( StringRef classOrMethodName ) { + static StringRef extractClassName( StringRef classOrMethodName ) { if ( !startsWith( classOrMethodName, '&' ) ) { return classOrMethodName; } @@ -6438,6 +6897,18 @@ namespace Catch { static_cast<std::size_t>( startIdx ), static_cast<std::size_t>( classNameSize ) ); } + + class TestInvokerAsFunction final : public ITestInvoker { + using TestType = void ( * )(); + TestType m_testAsFunction; + + public: + TestInvokerAsFunction( TestType testAsFunction ) noexcept: + m_testAsFunction( testAsFunction ) {} + + void invoke() const override { m_testAsFunction(); } + }; + } // namespace Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) { @@ -6919,23 +7390,36 @@ namespace Catch { return os; } - Columns Column::operator+( Column const& other ) { + Columns operator+(Column const& lhs, Column const& rhs) { Columns cols; - cols += *this; - cols += other; + cols += lhs; + cols += rhs; return cols; } - - Columns& Columns::operator+=( Column const& col ) { - m_columns.push_back( col ); - return *this; + Columns operator+(Column&& lhs, Column&& rhs) { + Columns cols; + cols += CATCH_MOVE( lhs ); + cols += CATCH_MOVE( rhs ); + return cols; } - Columns Columns::operator+( Column const& col ) { - Columns combined = *this; - combined += col; + Columns& operator+=(Columns& lhs, Column const& rhs) { + lhs.m_columns.push_back( rhs ); + return lhs; + } + Columns& operator+=(Columns& lhs, Column&& rhs) { + lhs.m_columns.push_back( CATCH_MOVE(rhs) ); + return lhs; + } + Columns operator+( Columns const& lhs, Column const& rhs ) { + auto combined( lhs ); + combined += rhs; return combined; } + Columns operator+( Columns&& lhs, Column&& rhs ) { + lhs += CATCH_MOVE( rhs ); + return CATCH_MOVE( lhs ); + } } // namespace TextFlow } // namespace Catch @@ -7431,26 +7915,11 @@ namespace { 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; } @@ -7824,7 +8293,7 @@ namespace Catch { namespace Catch { - AutomakeReporter::~AutomakeReporter() {} + AutomakeReporter::~AutomakeReporter() = default; void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. @@ -8046,7 +8515,7 @@ private: return; const auto itEnd = messages.cend(); - const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd)); + const auto N = static_cast<std::size_t>(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -8124,7 +8593,7 @@ private: StreamingReporterBase::testRunEnded( _testRunStats ); } - CompactReporter::~CompactReporter() {} + CompactReporter::~CompactReporter() = default; } // end namespace Catch @@ -8319,15 +8788,9 @@ findMax( std::size_t& i, std::size_t& j, std::size_t& k, std::size_t& l ) { return l; } -enum class Justification { Left, Right }; - -struct ColumnInfo { - std::string name; - std::size_t width; - Justification justification; -}; struct ColumnBreak {}; struct RowBreak {}; +struct OutputFlush {}; class Duration { enum class Unit { @@ -8402,6 +8865,14 @@ public: }; } // end anon namespace +enum class Justification { Left, Right }; + +struct ColumnInfo { + std::string name; + std::size_t width; + Justification justification; +}; + class TablePrinter { std::ostream& m_os; std::vector<ColumnInfo> m_columnInfos; @@ -8424,11 +8895,10 @@ public: *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; + headerCols += TextFlow::Spacer( 2 ); } m_os << headerCols << '\n'; @@ -8444,12 +8914,12 @@ public: } template<typename T> - friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + friend TablePrinter& operator<< (TablePrinter& tp, T const& value) { tp.m_oss << value; return tp; } - friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) { auto colStr = tp.m_oss.str(); const auto strSize = colStr.size(); tp.m_oss.str(""); @@ -8471,13 +8941,18 @@ public: return tp; } - friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, RowBreak) { if (tp.m_currentColumn > 0) { tp.m_os << '\n'; tp.m_currentColumn = -1; } return tp; } + + friend TablePrinter& operator<<(TablePrinter& tp, OutputFlush) { + tp.m_os << std::flush; + return tp; + } }; ConsoleReporter::ConsoleReporter(ReporterConfig&& config): @@ -8499,7 +8974,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config): { "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 } + { "est run time high mean high std dev", 14, Justification::Right } }; } }())) {} @@ -8583,8 +9058,11 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { (*m_tablePrinter) << info.samples << ColumnBreak() << info.iterations << ColumnBreak(); - if (!m_config->benchmarkNoAnalysis()) - (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); + if ( !m_config->benchmarkNoAnalysis() ) { + ( *m_tablePrinter ) + << Duration( info.estimatedDuration ) << ColumnBreak(); + } + ( *m_tablePrinter ) << OutputFlush{}; } void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { if (m_config->benchmarkNoAnalysis()) @@ -9280,6 +9758,366 @@ namespace Catch { } // namespace Catch +// + +namespace Catch { + namespace { + void writeSourceInfo( JsonObjectWriter& writer, + SourceLineInfo const& sourceInfo ) { + auto source_location_writer = + writer.write( "source-location"_sr ).writeObject(); + source_location_writer.write( "filename"_sr ) + .write( sourceInfo.file ); + source_location_writer.write( "line"_sr ).write( sourceInfo.line ); + } + + void writeTags( JsonArrayWriter writer, std::vector<Tag> const& tags ) { + for ( auto const& tag : tags ) { + writer.write( tag.original ); + } + } + + void writeProperties( JsonArrayWriter writer, + TestCaseInfo const& info ) { + if ( info.isHidden() ) { writer.write( "is-hidden"_sr ); } + if ( info.okToFail() ) { writer.write( "ok-to-fail"_sr ); } + if ( info.expectedToFail() ) { + writer.write( "expected-to-fail"_sr ); + } + if ( info.throws() ) { writer.write( "throws"_sr ); } + } + + } // namespace + + JsonReporter::JsonReporter( ReporterConfig&& config ): + StreamingReporterBase{ CATCH_MOVE( config ) } { + + m_preferences.shouldRedirectStdOut = true; + // TBD: Do we want to report all assertions? XML reporter does + // not, but for machine-parseable reporters I think the answer + // should be yes. + m_preferences.shouldReportAllAssertions = true; + + m_objectWriters.emplace( m_stream ); + m_writers.emplace( Writer::Object ); + auto& writer = m_objectWriters.top(); + + writer.write( "version"_sr ).write( 1 ); + + { + auto metadata_writer = writer.write( "metadata"_sr ).writeObject(); + metadata_writer.write( "name"_sr ).write( m_config->name() ); + metadata_writer.write( "rng-seed"_sr ).write( m_config->rngSeed() ); + metadata_writer.write( "catch2-version"_sr ) + .write( libraryVersion() ); + if ( m_config->testSpec().hasFilters() ) { + metadata_writer.write( "filters"_sr ) + .write( m_config->testSpec() ); + } + } + } + + JsonReporter::~JsonReporter() { + endListing(); + // TODO: Ensure this closes the top level object, add asserts + assert( m_writers.size() == 1 && "Only the top level object should be open" ); + assert( m_writers.top() == Writer::Object ); + endObject(); + m_stream << '\n' << std::flush; + assert( m_writers.empty() ); + } + + JsonArrayWriter& JsonReporter::startArray() { + m_arrayWriters.emplace( m_arrayWriters.top().writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + JsonArrayWriter& JsonReporter::startArray( StringRef key ) { + m_arrayWriters.emplace( + m_objectWriters.top().write( key ).writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + + JsonObjectWriter& JsonReporter::startObject() { + m_objectWriters.emplace( m_arrayWriters.top().writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + JsonObjectWriter& JsonReporter::startObject( StringRef key ) { + m_objectWriters.emplace( + m_objectWriters.top().write( key ).writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + + void JsonReporter::endObject() { + assert( isInside( Writer::Object ) ); + m_objectWriters.pop(); + m_writers.pop(); + } + void JsonReporter::endArray() { + assert( isInside( Writer::Array ) ); + m_arrayWriters.pop(); + m_writers.pop(); + } + + bool JsonReporter::isInside( Writer writer ) { + return !m_writers.empty() && m_writers.top() == writer; + } + + void JsonReporter::startListing() { + if ( !m_startedListing ) { startObject( "listings"_sr ); } + m_startedListing = true; + } + void JsonReporter::endListing() { + if ( m_startedListing ) { endObject(); } + m_startedListing = false; + } + + std::string JsonReporter::getDescription() { + return "Outputs listings as JSON. Test listing is Work-in-Progress!"; + } + + void JsonReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + endListing(); + + assert( isInside( Writer::Object ) ); + startObject( "test-run"_sr ); + startArray( "test-cases"_sr ); + } + + static void writeCounts( JsonObjectWriter&& writer, Counts const& counts ) { + writer.write( "passed"_sr ).write( counts.passed ); + writer.write( "failed"_sr ).write( counts.failed ); + writer.write( "fail-but-ok"_sr ).write( counts.failedButOk ); + writer.write( "skipped"_sr ).write( counts.skipped ); + } + + void JsonReporter::testRunEnded(TestRunStats const& runStats) { + assert( isInside( Writer::Array ) ); + // End "test-cases" + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + runStats.totals.assertions ); + writeCounts( totals.write( "test-cases"_sr ).writeObject(), + runStats.totals.testCases ); + } + + // End the "test-run" object + endObject(); + } + + void JsonReporter::testCaseStarting( TestCaseInfo const& tcInfo ) { + StreamingReporterBase::testCaseStarting( tcInfo ); + + assert( isInside( Writer::Array ) && + "We should be in the 'test-cases' array" ); + startObject(); + // "test-info" prelude + { + auto testInfo = + m_objectWriters.top().write( "test-info"_sr ).writeObject(); + // TODO: handle testName vs className!! + testInfo.write( "name"_sr ).write( tcInfo.name ); + writeSourceInfo(testInfo, tcInfo.lineInfo); + writeTags( testInfo.write( "tags"_sr ).writeArray(), tcInfo.tags ); + writeProperties( testInfo.write( "properties"_sr ).writeArray(), + tcInfo ); + } + + + // Start the array for individual test runs (testCasePartial pairs) + startArray( "runs"_sr ); + } + + void JsonReporter::testCaseEnded( TestCaseStats const& tcStats ) { + StreamingReporterBase::testCaseEnded( tcStats ); + + // We need to close the 'runs' array before finishing the test case + assert( isInside( Writer::Array ) ); + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in partial result? + } + // We do not write out stderr/stdout, because we instead wrote those out in partial runs + + // TODO: aborting? + + // And we also close this test case's object + assert( isInside( Writer::Object ) ); + endObject(); + } + + void JsonReporter::testCasePartialStarting( TestCaseInfo const& /*tcInfo*/, + uint64_t index ) { + startObject(); + m_objectWriters.top().write( "run-idx"_sr ).write( index ); + startArray( "path"_sr ); + // TODO: we want to delay most of the printing to the 'root' section + // TODO: childSection key name? + } + + void JsonReporter::testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t /*index*/ ) { + // Fixme: the top level section handles this. + //// path object + endArray(); + if ( !tcStats.stdOut.empty() ) { + m_objectWriters.top() + .write( "captured-stdout"_sr ) + .write( tcStats.stdOut ); + } + if ( !tcStats.stdErr.empty() ) { + m_objectWriters.top() + .write( "captured-stderr"_sr ) + .write( tcStats.stdErr ); + } + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will + // always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in + // partial result? + } + // TODO: aborting? + // run object + endObject(); + } + + void JsonReporter::sectionStarting( SectionInfo const& sectionInfo ) { + assert( isInside( Writer::Array ) && + "Section should always start inside an object" ); + // We want to nest top level sections, even though it shares name + // and source loc with the TEST_CASE + auto& sectionObject = startObject(); + sectionObject.write( "kind"_sr ).write( "section"_sr ); + sectionObject.write( "name"_sr ).write( sectionInfo.name ); + writeSourceInfo( m_objectWriters.top(), sectionInfo.lineInfo ); + + + // TBD: Do we want to create this event lazily? It would become + // rather complex, but we could do it, and it would look + // better for empty sections. OTOH, empty sections should + // be rare. + startArray( "path"_sr ); + } + void JsonReporter::sectionEnded( SectionStats const& /*sectionStats */) { + // End the subpath array + endArray(); + // TODO: metadata + // TODO: what info do we have here? + + // End the section object + endObject(); + } + + void JsonReporter::assertionStarting( AssertionInfo const& /*assertionInfo*/ ) {} + void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) { + // TODO: There is lot of different things to handle here, but + // we can fill it in later, after we show that the basic + // outline and streaming reporter impl works well enough. + //if ( !m_config->includeSuccessfulResults() + // && assertionStats.assertionResult.isOk() ) { + // return; + //} + assert( isInside( Writer::Array ) ); + auto assertionObject = m_arrayWriters.top().writeObject(); + + assertionObject.write( "kind"_sr ).write( "assertion"_sr ); + writeSourceInfo( assertionObject, + assertionStats.assertionResult.getSourceInfo() ); + assertionObject.write( "status"_sr ) + .write( assertionStats.assertionResult.isOk() ); + // TODO: handling of result. + // TODO: messages + // TODO: totals? + } + + + void JsonReporter::benchmarkPreparing( StringRef name ) { (void)name; } + void JsonReporter::benchmarkStarting( BenchmarkInfo const& ) {} + void JsonReporter::benchmarkEnded( BenchmarkStats<> const& ) {} + void JsonReporter::benchmarkFailed( StringRef error ) { (void)error; } + + void JsonReporter::listReporters( + std::vector<ReporterDescription> const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "reporters"_sr ).writeArray(); + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listListeners( + std::vector<ListenerDescription> const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "listeners"_sr ).writeArray(); + + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listTests( std::vector<TestCaseHandle> const& tests ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tests"_sr ).writeArray(); + + for ( auto const& test : tests ) { + auto desc_writer = writer.writeObject(); + auto const& info = test.getTestCaseInfo(); + + desc_writer.write( "name"_sr ).write( info.name ); + desc_writer.write( "class-name"_sr ).write( info.className ); + { + auto tag_writer = desc_writer.write( "tags"_sr ).writeArray(); + for ( auto const& tag : info.tags ) { + tag_writer.write( tag.original ); + } + } + writeSourceInfo( desc_writer, info.lineInfo ); + } + } + void JsonReporter::listTags( std::vector<TagInfo> const& tags ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tags"_sr ).writeArray(); + for ( auto const& tag : tags ) { + auto tag_writer = writer.writeObject(); + { + auto aliases_writer = + tag_writer.write( "aliases"_sr ).writeArray(); + for ( auto alias : tag.spellings ) { + aliases_writer.write( alias ); + } + } + tag_writer.write( "count"_sr ).write( tag.count ); + } + } +} // namespace Catch + + #include <cassert> @@ -9299,6 +10137,8 @@ namespace Catch { gmtime_s(&timeInfo, &rawtime); #elif defined (CATCH_PLATFORM_PLAYSTATION) gmtime_s(&rawtime, &timeInfo); +#elif defined (__IAR_SYSTEMS_ICC__) + timeInfo = *std::gmtime(&rawtime); #else gmtime_r(&rawtime, &timeInfo); #endif @@ -9559,7 +10399,7 @@ namespace Catch { } } - if( !result.getMessage().empty() ) + if( result.hasMessage() ) rss << result.getMessage() << '\n'; for( auto const& msg : stats.infoMessages ) if( msg.type == ResultWas::Info ) @@ -9678,7 +10518,6 @@ namespace Catch { } } - // 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 || @@ -9781,6 +10620,11 @@ namespace Catch { } } + void registerListenerImpl( Detail::unique_ptr<EventListenerFactory> listenerFactory ) { + getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) ); + } + + } // namespace Detail } // namespace Catch @@ -9920,7 +10764,7 @@ namespace Catch { } } - if (!result.getMessage().empty()) + if (result.hasMessage()) textRss << result.getMessage() << '\n'; for (auto const& msg : stats.infoMessages) @@ -9954,7 +10798,6 @@ namespace Catch { #include <algorithm> -#include <iterator> #include <ostream> namespace Catch { @@ -10105,7 +10948,7 @@ namespace Catch { // 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)); + const std::size_t N = static_cast<std::size_t>(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -10203,7 +11046,7 @@ namespace Catch { } // end anonymous namespace - TeamCityReporter::~TeamCityReporter() {} + TeamCityReporter::~TeamCityReporter() = default; void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name ) @@ -10377,7 +11220,7 @@ namespace Catch { m_xml.startElement("Catch2TestRun") .writeAttribute("name"_sr, m_config->name()) .writeAttribute("rng-seed"_sr, m_config->rngSeed()) - .writeAttribute("xml-format-version"_sr, 2) + .writeAttribute("xml-format-version"_sr, 3) .writeAttribute("catch2-version"_sr, libraryVersion()); if ( m_config->testSpec().hasFilters() ) { m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); @@ -10419,11 +11262,13 @@ namespace Catch { // 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 ); + auto t = m_xml.scopedElement( "Info" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } else if ( msg.type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Warning" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } } } @@ -10553,26 +11398,23 @@ namespace Catch { } void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { - m_xml.startElement("mean") + m_xml.scopedElement("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") + m_xml.scopedElement("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") + m_xml.scopedElement("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) { diff --git a/packages/Catch2/extras/catch_amalgamated.hpp b/packages/Catch2/extras/catch_amalgamated.hpp index 321cec5da..fdba759a7 100644 --- a/packages/Catch2/extras/catch_amalgamated.hpp +++ b/packages/Catch2/extras/catch_amalgamated.hpp @@ -1,3 +1,4 @@ + // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -5,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.2 -// Generated: 2023-02-26 10:28:46.785908 +// Catch v3.5.2 +// Generated: 2024-01-15 14:06:34.036475 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -59,238 +60,6 @@ -#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> - -#include <cstring> - -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 { - return m_size == other.m_size - && (std::memcmp( m_start, other.m_start, m_size ) == 0); - } - 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 @@ -366,12 +135,18 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ + _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) + # 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_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) + # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif @@ -444,6 +219,9 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) + #endif // __clang__ @@ -463,7 +241,9 @@ namespace Catch { //////////////////////////////////////////////////////////////////////////////// // Assume that some platforms do not support getenv. -#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION) +#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ + defined( CATCH_PLATFORM_PLAYSTATION ) || \ + defined( _GAMING_XBOX ) # define CATCH_INTERNAL_CONFIG_NO_GETENV #else # define CATCH_INTERNAL_CONFIG_GETENV @@ -681,6 +461,9 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS #endif @@ -690,6 +473,16 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif +#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS +#endif + // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... @@ -703,13 +496,6 @@ namespace Catch { # 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_INTERNAL_SUPPRESS_COMMA_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS -#endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) @@ -755,38 +541,31 @@ namespace Catch { class IResultCapture; class IConfig; - class IContext { - public: - virtual ~IContext(); // = default + class Context { + IConfig const* m_config = nullptr; + IResultCapture* m_resultCapture = nullptr; - virtual IResultCapture* getResultCapture() = 0; - virtual IConfig const* getConfig() const = 0; - }; + CATCH_EXPORT static Context* currentContext; + friend Context& getCurrentMutableContext(); + friend Context const& getCurrentContext(); + static void createContext(); + friend void cleanUpContext(); - 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(); + IResultCapture* getResultCapture() const { return m_resultCapture; } + IConfig const* getConfig() const { return m_config; } + void setResultCapture( IResultCapture* resultCapture ); + void setConfig( IConfig const* config ); }; - inline IMutableContext& getCurrentMutableContext() - { - if( !IMutableContext::currentContext ) - IMutableContext::createContext(); + Context& getCurrentMutableContext(); + + inline Context const& getCurrentContext() { + // We duplicate the logic from `getCurrentMutableContext` here, + // to avoid paying the call overhead in debug mode. + if ( !Context::currentContext ) { Context::createContext(); } // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) - return *IMutableContext::currentContext; - } - - inline IContext& getCurrentContext() - { - return getCurrentMutableContext(); + return *Context::currentContext; } void cleanUpContext(); @@ -798,16 +577,6 @@ namespace Catch { #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 @@ -822,110 +591,201 @@ namespace Catch { #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> +#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED namespace Catch { - struct SourceLineInfo { + //! Used to signal that an assertion macro failed + struct TestFailureException{}; + //! Used to signal that the remainder of a test should be skipped + struct TestSkipException {}; - SourceLineInfo() = delete; - constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: - file( _file ), - line( _line ) - {} + /** + * Outlines throwing of `TestFailureException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_failure_exception(); - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; + /** + * Outlines throwing of `TestSkipException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_skip_exception(); - char const* file; - std::size_t line; +} // namespace Catch - friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); - }; -} +#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) -#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED +#define CATCH_UNIQUE_NAME_HPP_INCLUDED -#ifndef CATCH_TOTALS_HPP_INCLUDED -#define CATCH_TOTALS_HPP_INCLUDED -#include <cstdint> -namespace Catch { +/** \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. + */ - struct Counts { - Counts operator - ( Counts const& other ) const; - Counts& operator += ( Counts const& other ); +#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define CATCH_CONFIG_COUNTER_HPP_INCLUDED - 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; - std::uint64_t skipped = 0; - }; +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif - struct Totals { +#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ + !defined( CATCH_CONFIG_NO_COUNTER ) && \ + !defined( CATCH_CONFIG_COUNTER ) +# define CATCH_CONFIG_COUNTER +#endif - Totals operator - ( Totals const& other ) const; - Totals& operator += ( Totals const& other ); - Totals delta( Totals const& prevTotals ) const; +#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 - Counts assertions; - Counts testCases; - }; -} +#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED -#endif // CATCH_TOTALS_HPP_INCLUDED +#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED + +#include <string> +#include <chrono> + + + +#ifndef CATCH_STRINGREF_HPP_INCLUDED +#define CATCH_STRINGREF_HPP_INCLUDED + +#include <cstddef> #include <string> +#include <iosfwd> +#include <cassert> + +#include <cstring> 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) - {} + /// 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*; - std::string name; - SourceLineInfo lineInfo; - }; + static constexpr size_type npos{ static_cast<size_type>( -1 ) }; - struct SectionEndInfo { - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; + private: + static constexpr char const* const s_empty = ""; -} // end namespace Catch + char const* m_start = s_empty; + size_type m_size = 0; -#endif // CATCH_SECTION_INFO_HPP_INCLUDED + public: // construction + constexpr StringRef() noexcept = default; + StringRef( char const* rawChars ) noexcept; -#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED -#define CATCH_ASSERTION_RESULT_HPP_INCLUDED + 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 { + return m_size == other.m_size + && (std::memcmp( m_start, other.m_start, m_size ) == 0); + } + auto operator != (StringRef other) const noexcept -> bool { + return !(*this == other); + } -#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED -#define CATCH_ASSERTION_INFO_HPP_INCLUDED + 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, 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 #ifndef CATCH_RESULT_TYPE_HPP_INCLUDED @@ -979,120 +839,12 @@ 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_UNIQUE_PTR_HPP_INCLUDED +#define CATCH_UNIQUE_PTR_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&& 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> - - - -#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED -#define CATCH_UNIQUE_PTR_HPP_INCLUDED - -#include <cassert> -#include <type_traits> +#include <cassert> +#include <type_traits> namespace Catch { @@ -1199,6 +951,45 @@ namespace Detail { #endif // CATCH_UNIQUE_PTR_HPP_INCLUDED + +#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_CLOCK_HPP_INCLUDED +#define CATCH_CLOCK_HPP_INCLUDED + +#include <chrono> + +namespace Catch { + namespace Benchmark { + using IDuration = std::chrono::nanoseconds; + using FDuration = std::chrono::duration<double, std::nano>; + + template <typename Clock> + using TimePoint = typename Clock::time_point; + + using default_clock = std::chrono::steady_clock; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CLOCK_HPP_INCLUDED + +namespace Catch { + + // We cannot forward declare the type with default template argument + // multiple times, so it is split out into a separate header so that + // we can prevent multiple declarations in dependees + template <typename Duration = Benchmark::FDuration> + struct BenchmarkStats; + +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + namespace Catch { class AssertionResult; @@ -1215,8 +1006,6 @@ namespace Catch { class IGeneratorTracker; struct BenchmarkInfo; - template <typename Duration = std::chrono::duration<double, std::nano>> - struct BenchmarkStats; namespace Generators { class GeneratorUntypedBase; @@ -1228,6 +1017,7 @@ namespace Catch { public: virtual ~IResultCapture(); + virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; virtual bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) = 0; @@ -1268,7 +1058,7 @@ namespace Catch { AssertionReaction& reaction ) = 0; virtual void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleIncomplete ( AssertionInfo const& info ) = 0; @@ -1293,415 +1083,308 @@ namespace Catch { #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_INTERFACES_CONFIG_HPP_INCLUDED +#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED -// Adapted from donated nonius code. -#ifndef CATCH_ESTIMATE_HPP_INCLUDED -#define CATCH_ESTIMATE_HPP_INCLUDED +#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED +#define CATCH_NONCOPYABLE_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 Detail { -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 + //! Deriving classes become noncopyable and nonmovable + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable&& ) = delete; + NonCopyable& operator=( NonCopyable const& ) = delete; + NonCopyable& operator=( NonCopyable&& ) = delete; - int total() const { - return low_severe + low_mild + high_mild + high_severe; - } + protected: + NonCopyable() noexcept = default; }; - } // namespace Benchmark -} // namespace Catch -#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED + } // namespace Detail +} // namespace Catch +#endif // CATCH_NONCOPYABLE_HPP_INCLUDED -#include <map> +#include <chrono> +#include <iosfwd> #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 + enum class Verbosity { + Quiet = 0, + Normal, + High + }; - Detail::unique_ptr<IStream> takeStream() &&; - IConfig const* fullConfig() const; - ColourMode colourMode() const; - std::map<std::string, std::string> const& customOptions() const; + 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, + }; }; - private: - Detail::unique_ptr<IStream> m_stream; - IConfig const* m_fullConfig; - ColourMode m_colourMode; - std::map<std::string, std::string> m_customOptions; + enum class ShowDurations { + DefaultForReporter, + Always, + Never }; - - struct TestRunInfo { - constexpr TestRunInfo(StringRef _name) : name(_name) {} - StringRef name; + enum class TestRunOrder { + Declared, + LexicographicallySorted, + Randomized }; - - 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; + 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 + }; }; - struct SectionStats { - SectionStats( SectionInfo&& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ); + class TestSpec; + class IStream; - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; + class IConfig : public Detail::NonCopyable { + public: + virtual ~IConfig(); - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string&& _stdOut, - std::string&& _stdErr, - bool _aborting ); + 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; - TestCaseInfo const * testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; + 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; }; +} - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ); +#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; +#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - 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; +#include <string> - std::vector<Duration> samples; - Benchmark::Estimate<Duration> mean; - Benchmark::Estimate<Duration> standardDeviation; - Benchmark::OutlierClassification outliers; - double outlierVariance; +namespace Catch { - 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, - }; - } - }; + class TestCaseHandle; + struct TestCaseInfo; + class ITestCaseRegistry; + class IExceptionTranslatorRegistry; + class IExceptionTranslator; + class ReporterRegistry; + class IReporterFactory; + class ITagAliasRegistry; + class ITestInvoker; + class IMutableEnumValuesRegistry; + struct SourceLineInfo; - //! 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; - }; + class StartupExceptionRegistry; + class EventListenerFactory; - /** - * 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; + using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>; + class IRegistryHub { public: - IEventListener( IConfig const* config ): m_config( config ) {} + virtual ~IRegistryHub(); // = default - virtual ~IEventListener(); // = default; + virtual ReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; - // Implementing class must also provide the following static methods: - // static std::string getDescription(); - ReporterPreferences const& getPreferences() const { - return m_preferences; - } + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; - //! 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; + 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; + }; - /** - * 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; + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); - //! 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; +#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - //! 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; +#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_HPP_INCLUDED - //! 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. - * NOT called for test cases that are explicitly skipped using the `SKIP` macro. - * - * Deprecated - will be removed in the next major release. - */ - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - //! Called if a fatal error (signal/structured exception) occured - virtual void fatalErrorEncountered( StringRef error ) = 0; +// Adapted from donated nonius code. - //! 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>; +#ifndef CATCH_ESTIMATE_HPP_INCLUDED +#define CATCH_ESTIMATE_HPP_INCLUDED -} // end namespace Catch +namespace Catch { + namespace Benchmark { + template <typename Type> + struct Estimate { + Type point; + Type lower_bound; + Type upper_bound; + double confidence_interval; + }; + } // namespace Benchmark +} // namespace Catch -#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED +#endif // CATCH_ESTIMATE_HPP_INCLUDED -#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED -#define CATCH_UNIQUE_NAME_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 -/** \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. - */ +#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED +// The fwd decl & default specialization needs to be seen by VS2017 before +// BenchmarkStats itself, or VS2017 will report compilation error. -#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED -#define CATCH_CONFIG_COUNTER_HPP_INCLUDED +#include <string> +#include <vector> -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif +namespace Catch { -#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ - !defined( CATCH_CONFIG_NO_COUNTER ) && \ - !defined( CATCH_CONFIG_COUNTER ) -# define CATCH_CONFIG_COUNTER -#endif + struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + unsigned int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; + // We need to keep template parameter for backwards compatibility, + // but we also do not want to use the template paraneter. + template <class Dummy> + struct BenchmarkStats { + BenchmarkInfo info; -#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 + std::vector<Benchmark::FDuration> samples; + Benchmark::Estimate<Benchmark::FDuration> mean; + Benchmark::Estimate<Benchmark::FDuration> standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + }; -#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED + +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED // Adapted from donated nonius code. -#ifndef CATCH_CHRONOMETER_HPP_INCLUDED -#define CATCH_CHRONOMETER_HPP_INCLUDED +#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED +#define CATCH_ENVIRONMENT_HPP_INCLUDED +namespace Catch { + namespace Benchmark { + struct EnvironmentEstimate { + FDuration mean; + OutlierClassification outliers; + }; + struct Environment { + EnvironmentEstimate clock_resolution; + EnvironmentEstimate clock_cost; + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ENVIRONMENT_HPP_INCLUDED + // Adapted from donated nonius code. -#ifndef CATCH_CLOCK_HPP_INCLUDED -#define CATCH_CLOCK_HPP_INCLUDED +#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED +#define CATCH_EXECUTION_PLAN_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; +// Adapted from donated nonius code. - using default_clock = std::chrono::steady_clock; +#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED - 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_CHRONOMETER_HPP_INCLUDED +#define CATCH_CHRONOMETER_HPP_INCLUDED + // Adapted from donated nonius code. @@ -1709,7 +1392,7 @@ namespace Catch { #ifndef CATCH_OPTIMIZER_HPP_INCLUDED #define CATCH_OPTIMIZER_HPP_INCLUDED -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) # include <atomic> // atomic_thread_fence #endif @@ -1730,16 +1413,23 @@ namespace Catch { namespace Detail { inline void optimizer_barrier() { keep_memory(); } } // namespace Detail -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) +#if defined(_MSVC_VER) #pragma optimize("", off) +#elif defined(__IAR_SYSTEMS_ICC__) +// For IAR the pragma only affects the following function +#pragma optimize=disable +#endif 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() +#if defined(_MSVC_VER) #pragma optimize("", on) +#endif namespace Detail { inline void optimizer_barrier() { @@ -1751,52 +1441,22 @@ namespace Catch { 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{}; - - /** - * Outlines throwing of `TestFailureException` into a single TU - * - * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. - */ - [[noreturn]] void throw_test_failure_exception(); + keep_memory(&x); + } - //! Used to signal that the remainder of a test should be skipped - struct TestSkipException{}; + 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_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +#endif // CATCH_OPTIMIZER_HPP_INCLUDED #ifndef CATCH_META_HPP_INCLUDED @@ -1840,112 +1500,6 @@ namespace mpl_{ #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 { @@ -1963,7 +1517,10 @@ namespace Catch { void start() override { started = Clock::now(); } void finish() override { finished = Clock::now(); } - ClockDuration<Clock> elapsed() const { return finished - started; } + IDuration elapsed() const { + return std::chrono::duration_cast<std::chrono::nanoseconds>( + finished - started ); + } TimePoint<Clock> started; TimePoint<Clock> finished; @@ -2004,50 +1561,6 @@ 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 { @@ -2184,6 +1697,57 @@ namespace Catch { +// Adapted from donated nonius code. + +#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED +#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + +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 + + // Adapted from donated nonius code. #ifndef CATCH_TIMING_HPP_INCLUDED @@ -2194,14 +1758,14 @@ namespace Catch { namespace Catch { namespace Benchmark { - template <typename Duration, typename Result> + template <typename Result> struct Timing { - Duration elapsed; + IDuration elapsed; Result result; int iterations; }; - template <typename Clock, typename Func, typename... Args> - using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>; + template <typename Func, typename... Args> + using TimingOf = Timing<Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>; } // namespace Benchmark } // namespace Catch @@ -2211,7 +1775,7 @@ namespace Catch { namespace Benchmark { namespace Detail { template <typename Clock, typename Fun, typename... Args> - TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) { + TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) { auto start = Clock::now(); auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); auto end = Clock::now(); @@ -2230,11 +1794,11 @@ namespace Catch { namespace Benchmark { namespace Detail { template <typename Clock, typename Fun> - TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) { + TimingOf<Fun, int> measure_one(Fun&& fun, int iters, std::false_type) { return Detail::measure<Clock>(fun, iters); } template <typename Clock, typename Fun> - TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) { + TimingOf<Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) { Detail::ChronometerModel<Clock> meter; auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); @@ -2249,8 +1813,8 @@ namespace Catch { 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, + TimingOf<Fun, run_for_at_least_argument_t<Clock, Fun>> + run_for_at_least(IDuration how_long, const int initial_iterations, Fun&& fun) { auto iters = initial_iterations; @@ -2270,38 +1834,38 @@ namespace Catch { #endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED -#include <algorithm> -#include <iterator> +#include <vector> namespace Catch { namespace Benchmark { - template <typename Duration> struct ExecutionPlan { int iterations_per_sample; - Duration estimated_duration; + FDuration estimated_duration; Detail::BenchmarkFunction benchmark; - Duration warmup_time; + FDuration 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 { + std::vector<FDuration> run(const IConfig &cfg, Environment 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>{})); + Detail::run_for_at_least<Clock>( + std::chrono::duration_cast<IDuration>( warmup_time ), + warmup_iterations, + Detail::repeat( []() { return Clock::now(); } ) + ); - std::vector<FloatDuration<Clock>> times; - times.reserve(cfg.benchmarkSamples()); - std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + std::vector<FDuration> times; + const auto num_samples = cfg.benchmarkSamples(); + times.reserve( num_samples ); + for ( size_t i = 0; i < num_samples; ++i ) { Detail::ChronometerModel<Clock> model; - this->benchmark(Chronometer(model, iterations_per_sample)); + 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; - }); + if ( sample_time < FDuration::zero() ) { + sample_time = FDuration::zero(); + } + times.push_back(sample_time / iterations_per_sample); + } return times; } }; @@ -2324,122 +1888,35 @@ namespace Catch { #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); - } + double weighted_average_quantile( int k, + int q, + double* first, + double* last ); - 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); + OutlierClassification + classify_outliers( double const* first, double const* last ); - for (auto it = first; it != last; ++it) { - std::iter_swap(it, first); - results.push_back(estimator(second, last)); - } - - return results; - } + double mean( double const* first, double const* last ); - inline double normal_cdf(double x) { - return std::erfc(-x / std::sqrt(2.0)) / 2.0; - } + double normal_cdf( double x ); 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); + Estimate<double> + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ); struct bootstrap_analysis { Estimate<double> mean; @@ -2447,7 +1924,10 @@ namespace Catch { double outlier_variance; }; - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last); + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double* first, + double* last); } // namespace Detail } // namespace Benchmark } // namespace Catch @@ -2455,7 +1935,6 @@ namespace Catch { #endif // CATCH_STATS_HPP_INCLUDED #include <algorithm> -#include <iterator> #include <vector> #include <cmath> @@ -2466,46 +1945,49 @@ namespace Catch { 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>{}); + for ( int i = 0; i < k + 1; ++i ) { + times.push_back( Clock::now() ); + } 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()); }); + for ( size_t idx = 1; idx < times.size(); ++idx ) { + deltas.push_back( static_cast<double>( + ( times[idx] - times[idx - 1] ).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; + constexpr auto warmup_iterations = 10000; + constexpr auto warmup_time = std::chrono::milliseconds(100); + constexpr auto minimum_ticks = 1000; + constexpr auto warmup_seed = 10000; + constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + constexpr auto clock_cost_estimation_tick_limit = 100000; + constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); + constexpr 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>) + return run_for_at_least<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>) + EnvironmentEstimate estimate_clock_resolution(int iterations) { + auto r = run_for_at_least<Clock>(clock_resolution_estimation_time, iterations, &resolution<Clock>) .result; return { - FloatDuration<Clock>(mean(r.begin(), r.end())), - classify_outliers(r.begin(), r.end()), + FDuration(mean(r.data(), r.data() + r.size())), + classify_outliers(r.data(), r.data() + r.size()), }; } template <typename Clock> - EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { + EnvironmentEstimate estimate_clock_cost(FDuration resolution) { auto time_limit = (std::min)( resolution * clock_cost_estimation_tick_limit, - FloatDuration<Clock>(clock_cost_estimation_time_limit)); + FDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure<Clock>([k] { for (int i = 0; i < k; ++i) { @@ -2516,26 +1998,28 @@ namespace Catch { }; 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); + auto&& r = run_for_at_least<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()); - }); + for ( int s = 0; s < nsamples; ++s ) { + times.push_back( static_cast<double>( + ( time_clock( r.iterations ) / r.iterations ) + .count() ) ); + } return { - FloatDuration<Clock>(mean(times.begin(), times.end())), - classify_outliers(times.begin(), times.end()), + FDuration(mean(times.data(), times.data() + times.size())), + classify_outliers(times.data(), times.data() + times.size()), }; } template <typename Clock> - Environment<FloatDuration<Clock>> measure_environment() { + Environment 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; + static Catch::Detail::unique_ptr<Environment> env; #if defined(__clang__) # pragma clang diagnostic pop #endif @@ -2547,7 +2031,7 @@ namespace Catch { 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} ); + env = Catch::Detail::make_unique<Environment>( Environment{resolution, cost} ); return *env; } } // namespace Detail @@ -2570,95 +2054,29 @@ namespace Catch { #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; + std::vector<FDuration> samples; + Estimate<FDuration> mean; + Estimate<FDuration> 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 { + class IConfig; + 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 - }; - } - } + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); } // namespace Detail } // namespace Benchmark } // namespace Catch @@ -2666,9 +2084,9 @@ namespace Catch { #endif // CATCH_ANALYSE_HPP_INCLUDED #include <algorithm> -#include <functional> +#include <chrono> +#include <exception> #include <string> -#include <vector> #include <cmath> namespace Catch { @@ -2682,16 +2100,18 @@ namespace Catch { : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} template <typename Clock> - ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { + ExecutionPlan prepare(const IConfig &cfg, Environment 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); + auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(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 }; + return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; } template <typename Clock = default_clock> void run() { + static_assert( Clock::is_steady, + "Benchmarking clock should be steady" ); auto const* cfg = getCurrentContext().getConfig(); auto env = Detail::measure_environment<Clock>(); @@ -2718,10 +2138,10 @@ namespace Catch { return plan.template run<Clock>(*cfg, env); }); - auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); - BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; + auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); + BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); - } CATCH_CATCH_ANON (TestFailureException) { + } CATCH_CATCH_ANON (TestFailureException const&) { getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); } CATCH_CATCH_ALL{ getResultCapture().benchmarkFailed(translateActiveException()); @@ -2889,6 +2309,7 @@ namespace Catch { #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. @@ -3112,7 +2533,6 @@ namespace Catch { } // 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> @@ -3395,6 +2815,12 @@ namespace Catch { } } }; + template <> + struct StringMaker<std::nullopt_t> { + static std::string convert(const std::nullopt_t&) { + return "{ }"; + } + }; } #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER @@ -3781,6 +3207,143 @@ struct StringMaker<Catch::Approx> { #endif // CATCH_APPROX_HPP_INCLUDED +#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED +#define CATCH_ASSERTION_INFO_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 + +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_ASSERTION_RESULT_HPP_INCLUDED +#define CATCH_ASSERTION_RESULT_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&& 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_CONFIG_HPP_INCLUDED #define CATCH_CONFIG_HPP_INCLUDED @@ -3945,6 +3508,7 @@ namespace Catch { #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED + #include <cassert> namespace Catch { @@ -3953,35 +3517,50 @@ namespace Catch { 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(): nullableValue( nullptr ) {} + ~Optional() { reset(); } + + Optional( T const& _value ): + nullableValue( new ( storage ) T( _value ) ) {} + Optional( T&& _value ): + nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} - ~Optional() { + Optional& operator=( T const& _value ) { + reset(); + nullableValue = new ( storage ) T( _value ); + return *this; + } + Optional& operator=( T&& _value ) { reset(); + nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); + return *this; } - Optional& operator= ( Optional const& _other ) { - if( &_other != this ) { + Optional( Optional const& _other ): + nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} + Optional( Optional&& _other ): + nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) + : nullptr ) {} + + Optional& operator=( Optional const& _other ) { + if ( &_other != this ) { reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); + if ( _other ) { nullableValue = new ( storage ) T( *_other ); } } return *this; } - Optional& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); + Optional& operator=( Optional&& _other ) { + if ( &_other != this ) { + reset(); + if ( _other ) { + nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); + } + } return *this; } void reset() { - if( nullableValue ) - nullableValue->~T(); + if ( nullableValue ) { nullableValue->~T(); } nullableValue = nullptr; } @@ -4025,177 +3604,42 @@ namespace Catch { } 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, - Skip = LightGrey, - - 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 ); + private: + T* nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; - ColourGuard( ColourGuard const& rhs ) = delete; - ColourGuard& operator=( ColourGuard const& rhs ) = delete; +} // end namespace Catch - ColourGuard( ColourGuard&& rhs ) noexcept; - ColourGuard& operator=( ColourGuard&& rhs ) noexcept; +#endif // CATCH_OPTIONAL_HPP_INCLUDED - //! 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 ) &&; +#ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED +#define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED - 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; - } +#include <cstdint> - void engageImpl( std::ostream& stream ); +namespace Catch { - }; + enum class GenerateFrom { + Time, + RandomDevice, + //! Currently equivalent to RandomDevice, but can change at any point + Default + }; - 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 ); + std::uint32_t generateRandomSeed(GenerateFrom from); - private: - virtual void use( Colour::Code colourCode ) const = 0; - }; +} // end namespace Catch - //! Provides ColourImpl based on global config and target compilation platform - Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection, - IStream* stream ); +#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED - //! Checks if specific colour impl has been compiled into the binary - bool isColourImplAvailable( ColourMode colourSelection ); -} // end namespace Catch +#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED +#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED -#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED #include <map> #include <string> @@ -4322,7 +3766,7 @@ namespace Catch { bool benchmarkNoAnalysis = false; unsigned int benchmarkSamples = 100; double benchmarkConfidenceInterval = 0.95; - unsigned int benchmarkResamples = 100000; + unsigned int benchmarkResamples = 100'000; std::chrono::milliseconds::rep benchmarkWarmupTime = 100; Verbosity verbosity = Verbosity::Normal; @@ -4424,6 +3868,29 @@ namespace Catch { + +/** \file + * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option + * + * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros + * by prepending CATCH_. This may not be desirable if the only clashes are with + * logger macros such as INFO and WARN. In this cases + * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset + * of relevant macros. + * + */ + +#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED +#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED + + +#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) + #define CATCH_CONFIG_PREFIX_MESSAGES +#endif + +#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED + + #ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED #define CATCH_STREAM_END_STOP_HPP_INCLUDED @@ -4435,10 +3902,10 @@ namespace Catch { // as well as // << stuff +StreamEndStop struct StreamEndStop { - StringRef operator+() const { return StringRef(); } + constexpr StringRef operator+() const { return StringRef(); } template <typename T> - friend T const& operator+( T const& value, StreamEndStop ) { + constexpr friend T const& operator+( T const& value, StreamEndStop ) { return value; } }; @@ -4447,12 +3914,47 @@ namespace Catch { #endif // CATCH_STREAM_END_STOP_HPP_INCLUDED + +#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED +#define CATCH_MESSAGE_INFO_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 + #include <string> #include <vector> namespace Catch { struct SourceLineInfo; + class IResultCapture; struct MessageStream { @@ -4493,7 +3995,7 @@ namespace Catch { class Capturer { std::vector<MessageInfo> m_messages; - IResultCapture& m_resultCapture = getResultCapture(); + IResultCapture& m_resultCapture; size_t m_captured = 0; public: Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); @@ -4544,28 +4046,28 @@ namespace Catch { 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) +#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !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) +#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && 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) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !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) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) (void)(0) #define UNSCOPED_INFO( msg ) (void)(0) @@ -4580,6 +4082,75 @@ namespace Catch { #endif // CATCH_MESSAGE_HPP_INCLUDED +#ifndef CATCH_SECTION_INFO_HPP_INCLUDED +#define CATCH_SECTION_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; + std::uint64_t skipped = 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_SESSION_HPP_INCLUDED #define CATCH_SESSION_HPP_INCLUDED @@ -4683,17 +4254,16 @@ namespace Catch { enum class TokenType { Option, Argument }; struct Token { TokenType type; - std::string token; + StringRef 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; + using Iterator = std::vector<StringRef>::const_iterator; Iterator it; Iterator itEnd; std::vector<Token> m_tokenBuffer; - void loadBuffer(); public: @@ -4745,12 +4315,17 @@ namespace Catch { ResultType m_type; }; - template <typename T> class ResultValueBase : public ResultBase { + template <typename T> + class ResultValueBase : public ResultBase { public: - auto value() const -> T const& { + T const& value() const& { enforceOk(); return m_value; } + T&& value() && { + enforceOk(); + return CATCH_MOVE( m_value ); + } protected: ResultValueBase( ResultType type ): ResultBase( type ) {} @@ -4760,13 +4335,23 @@ namespace Catch { if ( m_type == ResultType::Ok ) new ( &m_value ) T( other.m_value ); } + ResultValueBase( ResultValueBase&& other ): + ResultBase( other ) { + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + } - ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { + + ResultValueBase( ResultType, T const& value ): + ResultBase( ResultType::Ok ) { new ( &m_value ) T( value ); } + ResultValueBase( ResultType, T&& value ): + ResultBase( ResultType::Ok ) { + new ( &m_value ) T( CATCH_MOVE(value) ); + } - auto operator=( ResultValueBase const& other ) - -> ResultValueBase& { + ResultValueBase& operator=( ResultValueBase const& other ) { if ( m_type == ResultType::Ok ) m_value.~T(); ResultBase::operator=( other ); @@ -4774,6 +4359,14 @@ namespace Catch { new ( &m_value ) T( other.m_value ); return *this; } + ResultValueBase& operator=( ResultValueBase&& other ) { + if ( m_type == ResultType::Ok ) m_value.~T(); + ResultBase::operator=( other ); + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + return *this; + } + ~ResultValueBase() override { if ( m_type == ResultType::Ok ) @@ -4801,8 +4394,8 @@ namespace Catch { } template <typename U> - static auto ok( U const& value ) -> BasicResult { - return { ResultType::Ok, value }; + static auto ok( U&& value ) -> BasicResult { + return { ResultType::Ok, CATCH_FORWARD(value) }; } static auto ok() -> BasicResult { return { ResultType::Ok }; } static auto logicError( std::string&& message ) @@ -4849,12 +4442,15 @@ namespace Catch { class ParseState { public: ParseState( ParseResultType type, - TokenStream const& remainingTokens ); + TokenStream remainingTokens ); ParseResultType type() const { return m_type; } - TokenStream const& remainingTokens() const { + TokenStream const& remainingTokens() const& { return m_remainingTokens; } + TokenStream&& remainingTokens() && { + return CATCH_MOVE( m_remainingTokens ); + } private: ParseResultType m_type; @@ -4867,7 +4463,7 @@ namespace Catch { struct HelpColumns { std::string left; - std::string right; + StringRef descriptions; }; template <typename T> @@ -5027,7 +4623,7 @@ namespace Catch { virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } virtual auto parse( std::string const& exeName, - TokenStream const& tokens ) const + TokenStream tokens ) const -> InternalParseResult = 0; virtual size_t cardinality() const; @@ -5047,8 +4643,8 @@ namespace Catch { protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr<BoundRef> m_ref; - std::string m_hint; - std::string m_description; + StringRef m_hint; + StringRef m_description; explicit ParserRefImpl( std::shared_ptr<BoundRef> const& ref ): m_ref( ref ) {} @@ -5057,28 +4653,32 @@ namespace Catch { template <typename LambdaT> ParserRefImpl( accept_many_t, LambdaT const& ref, - std::string const& hint ): + StringRef 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 ): + ParserRefImpl( T& ref, StringRef 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 ): + ParserRefImpl( LambdaT const& ref, StringRef hint ): m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ), m_hint( hint ) {} - auto operator()( std::string const& description ) -> DerivedT& { + DerivedT& operator()( StringRef description ) & { m_description = description; return static_cast<DerivedT&>( *this ); } + DerivedT&& operator()( StringRef description ) && { + m_description = description; + return static_cast<DerivedT&&>( *this ); + } auto optional() -> DerivedT& { m_optionality = Optionality::Optional; @@ -5101,7 +4701,7 @@ namespace Catch { return 1; } - std::string const& hint() const { return m_hint; } + StringRef hint() const { return m_hint; } }; } // namespace detail @@ -5115,13 +4715,13 @@ namespace Catch { Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; // A parser for options class Opt : public Detail::ParserRefImpl<Opt> { protected: - std::vector<std::string> m_optNames; + std::vector<StringRef> m_optNames; public: template <typename LambdaT> @@ -5134,33 +4734,37 @@ namespace Catch { template <typename LambdaT, typename = typename std::enable_if_t< Detail::is_unary_function<LambdaT>::value>> - Opt( LambdaT const& ref, std::string const& hint ): + Opt( LambdaT const& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} template <typename LambdaT> - Opt( accept_many_t, LambdaT const& ref, std::string const& hint ): + Opt( accept_many_t, LambdaT const& ref, StringRef 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 ): + Opt( T& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} - auto operator[](std::string const& optName) -> Opt& { + Opt& operator[]( StringRef optName ) & { m_optNames.push_back(optName); return *this; } + Opt&& operator[]( StringRef optName ) && { + m_optNames.push_back( optName ); + return CATCH_MOVE(*this); + } - std::vector<Detail::HelpColumns> getHelpColumns() const; + Detail::HelpColumns getHelpColumns() const; - bool isMatch(std::string const& optToken) const; + bool isMatch(StringRef optToken) const; using ParserBase::parse; Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; Detail::Result validate() const override; }; @@ -5183,7 +4787,7 @@ namespace Catch { // handled specially Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; std::string const& name() const { return *m_name; } Detail::ParserResult set(std::string const& newName); @@ -5208,16 +4812,28 @@ namespace Catch { return *this; } - auto operator|=(Opt const& opt) -> Parser& { - m_options.push_back(opt); - return *this; + friend Parser& operator|=( Parser& p, Opt const& opt ) { + p.m_options.push_back( opt ); + return p; + } + friend Parser& operator|=( Parser& p, Opt&& opt ) { + p.m_options.push_back( CATCH_MOVE(opt) ); + return p; } Parser& operator|=(Parser const& other); template <typename T> - auto operator|(T const& other) const -> Parser { - return Parser(*this) |= other; + friend Parser operator|( Parser const& p, T&& rhs ) { + Parser temp( p ); + temp |= rhs; + return temp; + } + + template <typename T> + friend Parser operator|( Parser&& p, T&& rhs ) { + p |= CATCH_FORWARD(rhs); + return CATCH_MOVE(p); } std::vector<Detail::HelpColumns> getHelpColumns() const; @@ -5235,21 +4851,23 @@ namespace Catch { using ParserBase::parse; Detail::InternalParseResult parse(std::string const& exeName, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; - // Transport for raw args (copied from main args, or supplied via - // init list for testing) + /** + * Wrapper over argc + argv, assumes that the inputs outlive it + */ class Args { friend Detail::TokenStream; - std::string m_exeName; - std::vector<std::string> m_args; + StringRef m_exeName; + std::vector<StringRef> m_args; public: Args(int argc, char const* const* argv); - Args(std::initializer_list<std::string> args); + // Helper constructor for testing + Args(std::initializer_list<StringRef> args); - std::string const& exeName() const { return m_exeName; } + StringRef exeName() const { return m_exeName; } }; @@ -5855,8 +5473,6 @@ namespace Catch { namespace Catch { - class IResultCapture; - struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; @@ -5897,7 +5513,6 @@ namespace Catch { void handleUnexpectedInflightException(); void complete(); - void setCompleted(); // query auto allowThrows() const -> bool; @@ -5909,13 +5524,10 @@ 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) +#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr @@ -5923,6 +5535,16 @@ namespace Catch { #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr #endif +#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_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_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) /////////////////////////////////////////////////////////////////////////////// @@ -5934,7 +5556,7 @@ namespace Catch { #else // CATCH_CONFIG_FAST_COMPILE #define INTERNAL_CATCH_TRY try -#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } #endif @@ -5990,6 +5612,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast<void>(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6010,6 +5633,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast<void>(expr); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6036,6 +5660,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast<void>(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6051,12 +5676,40 @@ namespace Catch { #endif // CATCH_CONFIG_DISABLE -#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED +#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED + + +#ifndef CATCH_SECTION_HPP_INCLUDED +#define CATCH_SECTION_HPP_INCLUDED + + + + +/** \file + * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option + * + * Some of Catch2's macros can be defined differently to work better with + * static analysis tools, like clang-tidy or coverity. + * Currently the main use case is to show that `SECTION`s are executed + * exclusively, and not all in one run of a `TEST_CASE`. + */ + +#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + + +#if defined(__clang_analyzer__) || defined(__COVERITY__) + #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT +#endif +#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) +# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +#endif -#ifndef CATCH_SECTION_HPP_INCLUDED -#define CATCH_SECTION_HPP_INCLUDED +#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED #ifndef CATCH_TIMER_HPP_INCLUDED @@ -6103,17 +5756,63 @@ namespace Catch { } // 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::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) +# 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::Section( 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 + +#else + +// These section definitions imply that at most one section at one level +// will be intered (because only one section's __LINE__ can be equal to +// the dummy `catchInternalSectionHint` variable from `TEST_CASE`). + +namespace Catch { + namespace Detail { + // Intentionally without linkage, as it should only be used as a dummy + // symbol for static analysis. + int GetNewSectionHint(); + } // namespace Detail +} // namespace Catch + + +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#endif -#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 @@ -6123,42 +5822,20 @@ namespace Catch { -#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED - -#include <vector> +#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED namespace Catch { - class TestSpec; - struct TestCaseInfo; - class ITestInvoker { public: - virtual void invoke () const = 0; + 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 ); - -} +} // namespace Catch -#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED #ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED @@ -6230,6 +5907,9 @@ struct AutoReg : Detail::NonCopyable { void TestName::test() #endif + +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ @@ -6242,19 +5922,40 @@ struct AutoReg : Detail::NonCopyable { #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 { \ - const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ - Catch::makeTestInvoker( &QualifiedMethod ), \ - CATCH_INTERNAL_LINEINFO, \ - "&" #QualifiedMethod##_catch_sr, \ - Catch::NameAndTags{ __VA_ARGS__ } ); \ - } /* NOLINT */ \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT + + +// Dummy registrator for the dumy test case macros +namespace Catch { + namespace Detail { + struct DummyUse { + DummyUse( void ( * )( int ) ); + }; + } // namespace Detail +} // namespace Catch + +// Note that both the presence of the argument and its exact name are +// necessary for the section support. + +// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` +// tests can compile. The redefined `TEST_CASE` shadows this with param. +static int catchInternalSectionHint = 0; + +# define INTERNAL_CATCH_TESTCASE2( fname ) \ + static void fname( int ); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ + dummyUser )( &(fname) ); \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ) ) + + +#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ @@ -6276,6 +5977,22 @@ struct AutoReg : Detail::NonCopyable { #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_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &QualifiedMethod ), \ + CATCH_INTERNAL_LINEINFO, \ + "&" #QualifiedMethod##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ do { \ @@ -7194,6 +6911,7 @@ namespace Catch { }; class ITestInvoker; + struct NameAndTags; enum class TestCaseProperties : uint8_t { None = 0, @@ -7318,6 +7036,10 @@ namespace Catch { #include <exception> namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr<IExceptionTranslator>&& translator ); + } class ExceptionTranslatorRegistrar { template<typename T> @@ -7351,9 +7073,9 @@ namespace Catch { public: template<typename T> ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) { - getMutableRegistryHub().registerTranslator( - Detail::make_unique<ExceptionTranslator<T>>(translateFunction) - ); + Detail::registerTranslatorImpl( + Detail::make_unique<ExceptionTranslator<T>>( + translateFunction ) ); } }; @@ -7425,7 +7147,7 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 3 +#define CATCH_VERSION_MINOR 5 #define CATCH_VERSION_PATCH 2 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7584,12 +7306,6 @@ namespace Detail { } 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, @@ -8058,37 +7774,578 @@ namespace Catch { return static_cast<result_type>(-1); } - // Provide some default initial state for the default constructor - SimplePcg32():SimplePcg32(0xed743cc4U) {} + // 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 + + + +#ifndef CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED + + + + +#ifndef CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED + +#include <climits> +#include <cstddef> +#include <cstdint> +#include <type_traits> + +namespace Catch { + namespace Detail { + + template <std::size_t> + struct SizedUnsignedType; +#define SizedUnsignedTypeHelper( TYPE ) \ + template <> \ + struct SizedUnsignedType<sizeof( TYPE )> { \ + using type = TYPE; \ + } + + SizedUnsignedTypeHelper( std::uint8_t ); + SizedUnsignedTypeHelper( std::uint16_t ); + SizedUnsignedTypeHelper( std::uint32_t ); + SizedUnsignedTypeHelper( std::uint64_t ); +#undef SizedUnsignedTypeHelper + + template <std::size_t sz> + using SizedUnsignedType_t = typename SizedUnsignedType<sz>::type; + + template <typename T> + using DoubleWidthUnsignedType_t = SizedUnsignedType_t<2 * sizeof( T )>; + + template <typename T> + struct ExtendedMultResult { + T upper; + T lower; + friend bool operator==( ExtendedMultResult const& lhs, + ExtendedMultResult const& rhs ) { + return lhs.upper == rhs.upper && lhs.lower == rhs.lower; + } + }; + + // Returns 128 bit result of multiplying lhs and rhs + constexpr ExtendedMultResult<std::uint64_t> + extendedMult( std::uint64_t lhs, std::uint64_t rhs ) { + // We use the simple long multiplication approach for + // correctness, we can use platform specific builtins + // for performance later. + + // Split the lhs and rhs into two 32bit "digits", so that we can + // do 64 bit arithmetic to handle carry bits. + // 32b 32b 32b 32b + // lhs L1 L2 + // * rhs R1 R2 + // ------------------------ + // | R2 * L2 | + // | R2 * L1 | + // | R1 * L2 | + // | R1 * L1 | + // ------------------------- + // | a | b | c | d | + +#define CarryBits( x ) ( x >> 32 ) +#define Digits( x ) ( x & 0xFF'FF'FF'FF ) + + auto r2l2 = Digits( rhs ) * Digits( lhs ); + auto r2l1 = Digits( rhs ) * CarryBits( lhs ); + auto r1l2 = CarryBits( rhs ) * Digits( lhs ); + auto r1l1 = CarryBits( rhs ) * CarryBits( lhs ); + + // Sum to columns first + auto d = Digits( r2l2 ); + auto c = CarryBits( r2l2 ) + Digits( r2l1 ) + Digits( r1l2 ); + auto b = CarryBits( r2l1 ) + CarryBits( r1l2 ) + Digits( r1l1 ); + auto a = CarryBits( r1l1 ); + + // Propagate carries between columns + c += CarryBits( d ); + b += CarryBits( c ); + a += CarryBits( b ); + + // Remove the used carries + c = Digits( c ); + b = Digits( b ); + a = Digits( a ); + +#undef CarryBits +#undef Digits + + return { + a << 32 | b, // upper 64 bits + c << 32 | d // lower 64 bits + }; + } + + template <typename UInt> + constexpr ExtendedMultResult<UInt> extendedMult( UInt lhs, UInt rhs ) { + static_assert( std::is_unsigned<UInt>::value, + "extendedMult can only handle unsigned integers" ); + static_assert( sizeof( UInt ) < sizeof( std::uint64_t ), + "Generic extendedMult can only handle types smaller " + "than uint64_t" ); + using WideType = DoubleWidthUnsignedType_t<UInt>; + + auto result = WideType( lhs ) * WideType( rhs ); + return { + static_cast<UInt>( result >> ( CHAR_BIT * sizeof( UInt ) ) ), + static_cast<UInt>( result & UInt( -1 ) ) }; + } + + + template <typename TargetType, + typename Generator> + std::enable_if_t<sizeof(typename Generator::result_type) >= sizeof(TargetType), + TargetType> fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned<TargetType>::value, "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast<gresult_type>( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + // We want to return the top bits from a generator, as they are + // usually considered higher quality. + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + + return static_cast<TargetType>( gen() >> + ( generated_bits - return_bits) ); + } + + template <typename TargetType, + typename Generator> + std::enable_if_t<sizeof(typename Generator::result_type) < sizeof(TargetType), + TargetType> fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned<TargetType>::value, + "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast<gresult_type>( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + std::size_t filled_bits = 0; + TargetType ret = 0; + do { + ret <<= generated_bits; + ret |= gen(); + filled_bits += generated_bits; + } while ( filled_bits < return_bits ); + + return ret; + } + + /* + * Transposes numbers into unsigned type while keeping their ordering + * + * This means that signed types are changed so that the ordering is + * [INT_MIN, ..., -1, 0, ..., INT_MAX], rather than order we would + * get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1]) + */ + template <typename OriginalType, typename UnsignedType> + std::enable_if_t<std::is_signed<OriginalType>::value, UnsignedType> + transposeToNaturalOrder( UnsignedType in ) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned<UnsignedType>::value, + "Input type must be unsigned" ); + // Assuming 2s complement (standardized in current C++), the + // positive and negative numbers are already internally ordered, + // and their difference is in the top bit. Swapping it orders + // them the desired way. + constexpr auto highest_bit = + UnsignedType( 1 ) << ( sizeof( UnsignedType ) * CHAR_BIT - 1 ); + return static_cast<UnsignedType>( in ^ highest_bit ); + } + + + + template <typename OriginalType, + typename UnsignedType> + std::enable_if_t<std::is_unsigned<OriginalType>::value, UnsignedType> + transposeToNaturalOrder(UnsignedType in) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned<UnsignedType>::value, "Input type must be unsigned" ); + // No reordering is needed for unsigned -> unsigned + return in; + } + } // namespace Detail +} // namespace Catch + +#endif // CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED + +namespace Catch { + + namespace Detail { + // Indirection to enable make_unsigned<bool> behaviour. + template <typename T> + struct make_unsigned { + using type = std::make_unsigned_t<T>; + }; + + template <> + struct make_unsigned<bool> { + using type = uint8_t; + }; + + template <typename T> + using make_unsigned_t = typename make_unsigned<T>::type; + } + +/** + * Implementation of uniform distribution on integers. + * + * Unlike `std::uniform_int_distribution`, this implementation supports + * various 1 byte integral types, including bool (but you should not + * actually use it for bools). + * + * The underlying algorithm is based on the one described in "Fast Random + * Integer Generation in an Interval" by Daniel Lemire, but has been + * optimized under the assumption of reuse of the same distribution object. + */ +template <typename IntegerType> +class uniform_integer_distribution { + static_assert(std::is_integral<IntegerType>::value, "..."); + + using UnsignedIntegerType = Detail::make_unsigned_t<IntegerType>; + + // Only the left bound is stored, and we store it converted to its + // unsigned image. This avoids having to do the conversions inside + // the operator(), at the cost of having to do the conversion in + // the a() getter. The right bound is only needed in the b() getter, + // so we recompute it there from other stored data. + UnsignedIntegerType m_a; + + // How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type. + UnsignedIntegerType m_ab_distance; + + // We hoisted this out of the main generation function. Technically, + // this means that using this distribution will be slower than Lemire's + // algorithm if this distribution instance will be used only few times, + // but it will be faster if it is used many times. Since Catch2 uses + // distributions only to implement random generators, we assume that each + // distribution will be reused many times and this is an optimization. + UnsignedIntegerType m_rejection_threshold = 0; + + UnsignedIntegerType computeDistance(IntegerType a, IntegerType b) const { + // This overflows and returns 0 if a == 0 and b == TYPE_MAX. + // We handle that later when generating the number. + return transposeTo(b) - transposeTo(a) + 1; + } + + static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) { + // distance == 0 means that we will return all possible values from + // the type's range, and that we shouldn't reject anything. + if ( ab_distance == 0 ) { return 0; } + return ( ~ab_distance + 1 ) % ab_distance; + } + + static UnsignedIntegerType transposeTo(IntegerType in) { + return Detail::transposeToNaturalOrder<IntegerType>( + static_cast<UnsignedIntegerType>( in ) ); + } + static IntegerType transposeBack(UnsignedIntegerType in) { + return static_cast<IntegerType>( + Detail::transposeToNaturalOrder<IntegerType>(in) ); + } + +public: + using result_type = IntegerType; + + uniform_integer_distribution( IntegerType a, IntegerType b ): + m_a( transposeTo(a) ), + m_ab_distance( computeDistance(a, b) ), + m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) { + assert( a <= b ); + } + + template <typename Generator> + result_type operator()( Generator& g ) { + // All possible values of result_type are valid. + if ( m_ab_distance == 0 ) { + return transposeBack( Detail::fillBitsFrom<UnsignedIntegerType>( g ) ); + } + + auto random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g ); + auto emul = Detail::extendedMult( random_number, m_ab_distance ); + // Unlike Lemire's algorithm we skip the ab_distance check, since + // we precomputed the rejection threshold, which is always tighter. + while (emul.lower < m_rejection_threshold) { + random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g ); + emul = Detail::extendedMult( random_number, m_ab_distance ); + } + + return transposeBack(m_a + emul.upper); + } + + result_type a() const { return transposeBack(m_a); } + result_type b() const { return transposeBack(m_ab_distance + m_a - 1); } +}; + +} // end namespace Catch + +#endif // CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED + + + +#ifndef CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED + + + + +#ifndef CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_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); + + float nextafter(float x, float y); + double nextafter(double x, double y); + +} + +#endif // CATCH_POLYFILLS_HPP_INCLUDED + +#include <cassert> +#include <cmath> +#include <cstdint> +#include <limits> +#include <type_traits> + +namespace Catch { + + namespace Detail { + /** + * Returns the largest magnitude of 1-ULP distance inside the [a, b] range. + * + * Assumes `a < b`. + */ + template <typename FloatType> + FloatType gamma(FloatType a, FloatType b) { + static_assert( std::is_floating_point<FloatType>::value, + "gamma returns the largest ULP magnitude within " + "floating point range [a, b]. This only makes sense " + "for floating point types" ); + assert( a <= b ); + + const auto gamma_up = Catch::nextafter( a, std::numeric_limits<FloatType>::infinity() ) - a; + const auto gamma_down = b - Catch::nextafter( b, -std::numeric_limits<FloatType>::infinity() ); + + return gamma_up < gamma_down ? gamma_down : gamma_up; + } + + template <typename FloatingPoint> + struct DistanceTypePicker; + template <> + struct DistanceTypePicker<float> { + using type = std::uint32_t; + }; + template <> + struct DistanceTypePicker<double> { + using type = std::uint64_t; + }; + + template <typename T> + using DistanceType = typename DistanceTypePicker<T>::type; + +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + /** + * Computes the number of equi-distant floats in [a, b] + * + * Since not every range can be split into equidistant floats + * exactly, we actually compute ceil(b/distance - a/distance), + * because in those cases we want to overcount. + * + * Uses modified Dekker's FastTwoSum algorithm to handle rounding. + */ + template <typename FloatType> + DistanceType<FloatType> + count_equidistant_floats( FloatType a, FloatType b, FloatType distance ) { + assert( a <= b ); + // We get distance as gamma for our uniform float distribution, + // so this will round perfectly. + const auto ag = a / distance; + const auto bg = b / distance; + + const auto s = bg - ag; + const auto err = ( std::fabs( a ) <= std::fabs( b ) ) + ? -ag - ( s - bg ) + : bg - ( s + ag ); + const auto ceil_s = static_cast<DistanceType<FloatType>>( std::ceil( s ) ); + + return ( ceil_s != s ) ? ceil_s : ceil_s + ( err > 0 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } - explicit SimplePcg32(result_type seed_); +} // end namespace Catch - void seed(result_type seed_); - void discard(uint64_t skip); +#endif // CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED - result_type operator()(); +#include <cmath> +#include <type_traits> - private: - friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs); - friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs); +namespace Catch { - // In theory we also need operator<< and operator>> - // In practice we do not use them, so we will skip them for now + namespace Detail { +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + // The issue with overflow only happens with maximal ULP and HUGE + // distance, e.g. when generating numbers in [-inf, inf] for given + // type. So we only check for the largest possible ULP in the + // type, and return something that does not overflow to inf in 1 mult. + constexpr std::uint64_t calculate_max_steps_in_one_go(double gamma) { + if ( gamma == 1.99584030953472e+292 ) { return 9007199254740991; } + return static_cast<std::uint64_t>( -1 ); + } + constexpr std::uint32_t calculate_max_steps_in_one_go(float gamma) { + if ( gamma == 2.028241e+31f ) { return 16777215; } + return static_cast<std::uint32_t>( -1 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + } +/** + * Implementation of uniform distribution on floating point numbers. + * + * Note that we support only `float` and `double` types, because these + * usually mean the same thing across different platform. `long double` + * varies wildly by platform and thus we cannot provide reproducible + * implementation. Also note that we don't implement all parts of + * distribution per standard: this distribution is not serializable, nor + * can the range be arbitrarily reset. + * + * The implementation also uses different approach than the one taken by + * `std::uniform_real_distribution`, where instead of generating a number + * between [0, 1) and then multiplying the range bounds with it, we first + * split the [a, b] range into a set of equidistributed floating point + * numbers, and then use uniform int distribution to pick which one to + * return. + * + * This has the advantage of guaranteeing uniformity (the multiplication + * method loses uniformity due to rounding when multiplying floats), except + * for small non-uniformity at one side of the interval, where we have + * to deal with the fact that not every interval is splittable into + * equidistributed floats. + * + * Based on "Drawing random floating-point numbers from an interval" by + * Frederic Goualard. + */ +template <typename FloatType> +class uniform_floating_point_distribution { + static_assert(std::is_floating_point<FloatType>::value, "..."); + static_assert(!std::is_same<FloatType, long double>::value, + "We do not support long double due to inconsistent behaviour between platforms"); + + using WidthType = Detail::DistanceType<FloatType>; + + FloatType m_a, m_b; + FloatType m_ulp_magnitude; + WidthType m_floats_in_range; + uniform_integer_distribution<WidthType> m_int_dist; + + // In specific cases, we can overflow into `inf` when computing the + // `steps * g` offset. To avoid this, we don't offset by more than this + // in one multiply + addition. + WidthType m_max_steps_in_one_go; + // We don't want to do the magnitude check every call to `operator()` + bool m_a_has_leq_magnitude; - 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; - }; +public: + using result_type = FloatType; + + uniform_floating_point_distribution( FloatType a, FloatType b ): + m_a( a ), + m_b( b ), + m_ulp_magnitude( Detail::gamma( m_a, m_b ) ), + m_floats_in_range( Detail::count_equidistant_floats( m_a, m_b, m_ulp_magnitude ) ), + m_int_dist(0, m_floats_in_range), + m_max_steps_in_one_go( Detail::calculate_max_steps_in_one_go(m_ulp_magnitude)), + m_a_has_leq_magnitude(std::fabs(m_a) <= std::fabs(m_b)) + { + assert( a <= b ); + } -} // end namespace Catch + template <typename Generator> + result_type operator()( Generator& g ) { + WidthType steps = m_int_dist( g ); + if ( m_a_has_leq_magnitude ) { + if ( steps == m_floats_in_range ) { return m_a; } + auto b = m_b; + while (steps > m_max_steps_in_one_go) { + b -= m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return b - steps * m_ulp_magnitude; + } else { + if ( steps == m_floats_in_range ) { return m_b; } + auto a = m_a; + while (steps > m_max_steps_in_one_go) { + a += m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return a + steps * m_ulp_magnitude; + } + } -#endif // CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED + result_type a() const { return m_a; } + result_type b() const { return m_b; } +}; + +} // end namespace Catch -#include <random> +#endif // CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED namespace Catch { namespace Generators { @@ -8102,7 +8359,7 @@ namespace Detail { template <typename Float> class RandomFloatingGenerator final : public IGenerator<Float> { Catch::SimplePcg32 m_rng; - std::uniform_real_distribution<Float> m_dist; + Catch::uniform_floating_point_distribution<Float> m_dist; Float m_current_number; public: RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ): @@ -8120,10 +8377,27 @@ public: } }; +template <> +class RandomFloatingGenerator<long double> final : public IGenerator<long double> { + // We still rely on <random> for this specialization, but we don't + // want to drag it into the header. + struct PImpl; + Catch::Detail::unique_ptr<PImpl> m_pimpl; + long double m_current_number; + +public: + RandomFloatingGenerator( long double a, long double b, std::uint32_t seed ); + + long double const& get() const override { return m_current_number; } + bool next() override; + + ~RandomFloatingGenerator() override; // = default +}; + template <typename Integer> class RandomIntegerGenerator final : public IGenerator<Integer> { Catch::SimplePcg32 m_rng; - std::uniform_int_distribution<Integer> m_dist; + Catch::uniform_integer_distribution<Integer> m_dist; Integer m_current_number; public: RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ): @@ -8144,14 +8418,6 @@ public: 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()) ); @@ -8264,39 +8530,266 @@ 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())); +template <typename Container> +auto from_range(Container const& cnt) { + using std::begin; + using std::end; + return from_range( begin( cnt ), end( cnt ) ); } -} // namespace Generators -} // namespace Catch +} // 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_HPP_INCLUDED +#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED + + + +#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED +#define CATCH_TEST_RUN_INFO_HPP_INCLUDED + + +namespace Catch { + + struct TestRunInfo { + constexpr TestRunInfo(StringRef _name) : name(_name) {} + StringRef name; + }; + +} // end namespace Catch + +#endif // CATCH_TEST_RUN_INFO_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 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&& _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&& _stdOut, + std::string&& _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; + }; + + //! 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; -#endif // CATCH_GENERATORS_RANGE_HPP_INCLUDED + //! Called after assertion was fully evaluated + virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; -#endif // CATCH_GENERATORS_ALL_HPP_INCLUDED + //! 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. + * NOT called for test cases that are explicitly skipped using the `SKIP` macro. + * + * Deprecated - will be removed in the next major release. + */ + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; -/** \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. - */ + //! Called if a fatal error (signal/structured exception) occurred + 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>; -#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED -#define CATCH_INTERFACES_ALL_HPP_INCLUDED +} // end namespace Catch +#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED #ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED @@ -8337,89 +8830,79 @@ 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_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#include <string> +namespace Catch { -#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED -#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED + 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; -namespace Catch { - namespace Detail { - //! Provides case-insensitive `op<` semantics when called - struct CaseInsensitiveLess { - bool operator()( StringRef lhs, - StringRef rhs ) const; - }; + static ITagAliasRegistry const& get(); + }; - //! Provides case-insensitive `op==` semantics when called - struct CaseInsensitiveEqualTo { - bool operator()( StringRef lhs, - StringRef rhs ) const; - }; +} // end namespace Catch - } // namespace Detail -} // namespace Catch +#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED -#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED -#include <string> +#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED + #include <vector> -#include <map> namespace Catch { + struct TestCaseInfo; + class TestCaseHandle; class IConfig; - class IEventListener; - using IEventListenerPtr = Detail::unique_ptr<IEventListener>; - class IReporterFactory; - using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>; - struct ReporterConfig; - class EventListenerFactory; - - class IReporterRegistry { + class ITestCaseRegistry { 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; + 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; }; -} // 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 +#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#include <string> +#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED -namespace Catch { - struct TagAlias; +#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED +#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED - 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(); - }; +namespace Catch { + namespace Detail { + //! Provides case-insensitive `op<` semantics when called + struct CaseInsensitiveLess { + bool operator()( StringRef lhs, + StringRef rhs ) const; + }; -} // end namespace Catch + //! Provides case-insensitive `op==` semantics when called + struct CaseInsensitiveEqualTo { + bool operator()( StringRef lhs, + StringRef rhs ) const; + }; -#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED + } // namespace Detail +} // namespace Catch -#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED +#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED @@ -8446,46 +8929,182 @@ namespace Catch { # define CATCH_CONFIG_ANDROID_LOGWRITE #endif -#endif // CATCH_CONFIG_ANDROID_LOGWRITE_HPP_INCLUDED +#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_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, + Skip = LightGrey, + + 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; -/** \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. - */ + ColourGuard( ColourGuard&& rhs ) noexcept; + ColourGuard& operator=( ColourGuard&& rhs ) noexcept; -#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED -#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED + //! Removes colour _if_ the guard was engaged + ~ColourGuard(); -#if defined(_MSC_VER) -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif -#endif + /** + * 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; + } -#include <exception> + void engageImpl( std::ostream& stream ); -#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 + 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; + }; -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \ - && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ - && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + //! Provides ColourImpl based on global config and target compilation platform + Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection, + IStream* stream ); -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif + //! Checks if specific colour impl has been compiled into the binary + bool isColourImplAvailable( ColourMode colourSelection ); +} // end namespace Catch -#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED +#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED #ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED @@ -8751,7 +9370,6 @@ namespace Catch { ~ExceptionTranslatorRegistry() override; void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ); std::string translateActiveException() const override; - std::string tryTranslators() const; private: ExceptionTranslators m_translators; @@ -8764,7 +9382,6 @@ namespace Catch { #ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED #define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED - #include <cassert> namespace Catch { @@ -8827,17 +9444,6 @@ namespace Catch { #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> @@ -8850,6 +9456,11 @@ namespace Catch { uint32_t convertToBits(float f); uint64_t convertToBits(double d); + // Used when we know we want == comparison of two doubles + // to centralize warning suppression + bool directCompare( float lhs, float rhs ); + bool directCompare( double lhs, double rhs ); + } // end namespace Detail @@ -9126,6 +9737,119 @@ namespace Catch { #endif // CATCH_STREAM_HPP_INCLUDED +#ifndef CATCH_JSONWRITER_HPP_INCLUDED +#define CATCH_JSONWRITER_HPP_INCLUDED + + +#include <cstdint> +#include <sstream> + +namespace Catch { + class JsonObjectWriter; + class JsonArrayWriter; + + struct JsonUtils { + static void indent( std::ostream& os, std::uint64_t level ); + static void appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ); + }; + + class JsonValueWriter { + public: + JsonValueWriter( std::ostream& os ); + JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter writeObject() &&; + JsonArrayWriter writeArray() &&; + + template <typename T> + void write( T const& value ) && { + writeImpl( value, !std::is_arithmetic<T>::value ); + } + void write( StringRef value ) &&; + void write( bool value ) &&; + + private: + void writeImpl( StringRef value, bool quote ); + + // 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 + template <typename T, + typename = typename std::enable_if_t< + !std::is_convertible<T, StringRef>::value>> + void writeImpl( T const& value, bool quote_value ) { + m_sstream << value; + writeImpl( m_sstream.str(), quote_value ); + } + + std::ostream& m_os; + std::stringstream m_sstream; + std::uint64_t m_indent_level; + }; + + class JsonObjectWriter { + public: + JsonObjectWriter( std::ostream& os ); + JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter( JsonObjectWriter&& source ); + JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; + + ~JsonObjectWriter(); + + JsonValueWriter write( StringRef key ); + + private: + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + + class JsonArrayWriter { + public: + JsonArrayWriter( std::ostream& os ); + JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonArrayWriter( JsonArrayWriter&& source ); + JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; + + ~JsonArrayWriter(); + + JsonObjectWriter writeObject(); + JsonArrayWriter writeArray(); + + template <typename T> + JsonArrayWriter& write( T const& value ) { + return writeImpl( value ); + } + + JsonArrayWriter& write( bool value ); + + private: + template <typename T> + JsonArrayWriter& writeImpl( T const& value ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + JsonValueWriter{ m_os }.write( value ); + + return *this; + } + + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + +} // namespace Catch + +#endif // CATCH_JSONWRITER_HPP_INCLUDED + + #ifndef CATCH_LEAK_DETECTOR_HPP_INCLUDED #define CATCH_LEAK_DETECTOR_HPP_INCLUDED @@ -9312,28 +10036,45 @@ namespace Catch { #include <map> +#include <string> +#include <vector> namespace Catch { - class ReporterRegistry : public IReporterRegistry { - public: + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr<IEventListener>; + class IReporterFactory; + using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>; + struct ReporterConfig; + class EventListenerFactory; + + class ReporterRegistry { + struct ReporterRegistryImpl; + Detail::unique_ptr<ReporterRegistryImpl> m_impl; + public: ReporterRegistry(); - ~ReporterRegistry() override; // = default, out of line to allow fwd decl + ~ReporterRegistry(); // = default; - IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override; + IEventListenerPtr create( std::string const& name, + ReporterConfig&& config ) const; - void registerReporter( std::string const& name, IReporterFactoryPtr factory ); - void registerListener( Detail::unique_ptr<EventListenerFactory> factory ); + void registerReporter( std::string const& name, + IReporterFactoryPtr factory ); - FactoryMap const& getFactories() const override; - Listeners const& getListeners() const override; + void + registerListener( Detail::unique_ptr<EventListenerFactory> factory ); - private: - FactoryMap m_factories; - Listeners m_listeners; + std::map<std::string, + IReporterFactoryPtr, + Detail::CaseInsensitiveLess> const& + getFactories() const; + + std::vector<Detail::unique_ptr<EventListenerFactory>> const& + getListeners() const; }; -} + +} // end namespace Catch #endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED @@ -9448,7 +10189,7 @@ namespace TestCaseTracking { //! Returns true if tracker run to completion (successfully or not) virtual bool isComplete() const = 0; - //! Returns true if tracker run to completion succesfully + //! Returns true if tracker run to completion successfully bool isSuccessfullyCompleted() const { return m_runState == CompletedSuccessfully; } @@ -9582,13 +10323,14 @@ using TestCaseTracking::SectionTracker; namespace Catch { - class IMutableContext; class IGeneratorTracker; class IConfig; + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr<IEventListener>; /////////////////////////////////////////////////////////////////////////// - class RunContext : public IResultCapture { + class RunContext final : public IResultCapture { public: RunContext( RunContext const& ) = delete; @@ -9617,7 +10359,7 @@ namespace Catch { AssertionReaction& reaction ) override; void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) override; void handleIncomplete ( AssertionInfo const& info ) override; @@ -9626,6 +10368,7 @@ namespace Catch { ResultWas::OfType resultType, AssertionReaction &reaction ) override; + void notifyAssertionStarted( AssertionInfo const& info ) override; bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) override; @@ -9676,7 +10419,7 @@ namespace Catch { void resetAssertionInfo(); bool testForMissingAssertions( Counts& assertions ); - void assertionEnded( AssertionResult const& result ); + void assertionEnded( AssertionResult&& result ); void reportExpr ( AssertionInfo const &info, ResultWas::OfType resultType, @@ -9690,7 +10433,6 @@ namespace Catch { void handleUnfinishedSections(); TestRunInfo m_runInfo; - IMutableContext& m_context; TestCaseHandle const* m_activeTestCase = nullptr; ITracker* m_testCaseTracker = nullptr; Optional<AssertionResult> m_lastResult; @@ -9720,7 +10462,7 @@ namespace Catch { #ifndef CATCH_SHARDING_HPP_INCLUDED #define CATCH_SHARDING_HPP_INCLUDED - +#include <cassert> #include <cmath> #include <algorithm> @@ -9947,24 +10689,20 @@ namespace Catch { namespace Catch { - class TestCaseHandle; class IConfig; + class ITestInvoker; + class TestCaseHandle; 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; @@ -9985,18 +10723,6 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - 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 @@ -10082,6 +10808,7 @@ namespace Catch { #ifndef CATCH_TEXTFLOW_HPP_INCLUDED #define CATCH_TEXTFLOW_HPP_INCLUDED + #include <cassert> #include <string> #include <vector> @@ -10110,7 +10837,7 @@ namespace Catch { public: /** - * Iterates "lines" in `Column` and return sthem + * Iterates "lines" in `Column` and returns them */ class const_iterator { friend Column; @@ -10132,7 +10859,7 @@ namespace Catch { // Calculates the length of the current line void calcLength(); - // Returns current indention width + // Returns current indentation width size_t indentSize() const; // Creates an indented and (optionally) suffixed string from @@ -10164,20 +10891,35 @@ namespace Catch { using iterator = const_iterator; explicit Column( std::string const& text ): m_string( text ) {} + explicit Column( std::string&& text ): + m_string( CATCH_MOVE(text)) {} - Column& width( size_t newWidth ) { + Column& width( size_t newWidth ) & { assert( newWidth > 0 ); m_width = newWidth; return *this; } - Column& indent( size_t newIndent ) { + Column&& width( size_t newWidth ) && { + assert( newWidth > 0 ); + m_width = newWidth; + return CATCH_MOVE( *this ); + } + Column& indent( size_t newIndent ) & { m_indent = newIndent; return *this; } - Column& initialIndent( size_t newIndent ) { + Column&& indent( size_t newIndent ) && { + m_indent = newIndent; + return CATCH_MOVE( *this ); + } + Column& initialIndent( size_t newIndent ) & { m_initialIndent = newIndent; return *this; } + Column&& initialIndent( size_t newIndent ) && { + m_initialIndent = newIndent; + return CATCH_MOVE( *this ); + } size_t width() const { return m_width; } const_iterator begin() const { return const_iterator( *this ); } @@ -10186,7 +10928,8 @@ namespace Catch { friend std::ostream& operator<<( std::ostream& os, Column const& col ); - Columns operator+( Column const& other ); + friend Columns operator+( Column const& lhs, Column const& rhs ); + friend Columns operator+( Column&& lhs, Column&& rhs ); }; //! Creates a column that serves as an empty space of specific width @@ -10230,8 +10973,10 @@ namespace Catch { iterator begin() const { return iterator( *this ); } iterator end() const { return { *this, iterator::EndTag() }; } - Columns& operator+=( Column const& col ); - Columns operator+( Column const& col ); + friend Columns& operator+=( Columns& lhs, Column const& rhs ); + friend Columns& operator+=( Columns& lhs, Column&& rhs ); + friend Columns operator+( Columns const& lhs, Column const& rhs ); + friend Columns operator+( Columns&& lhs, Column&& rhs ); friend std::ostream& operator<<( std::ostream& os, Columns const& cols ); @@ -10445,6 +11190,8 @@ namespace Catch { #define CATCH_MATCHERS_IMPL_HPP_INCLUDED +#include <string> + namespace Catch { template<typename ArgT, typename MatcherT> @@ -11680,7 +12427,7 @@ namespace Catch { /** * Creates a matcher that checks if all elements in a range are equal - * to all elements in another range, in some permuation. + * to all elements in another range, in some permutation. * * Uses to provided predicate `predicate` to do the comparisons */ @@ -11850,11 +12597,10 @@ namespace Matchers { // - 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; + 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 { @@ -12358,7 +13104,7 @@ namespace Catch { void skipTest(TestCaseInfo const&) override {} protected: - //! Should the cumulative base store the assertion expansion for succesful assertions? + //! Should the cumulative base store the assertion expansion for successful assertions? bool m_shouldStoreSuccesfulAssertions = true; //! Should the cumulative base store the assertion expansion for failed assertions? bool m_shouldStoreFailedAssertions = true; @@ -12526,6 +13272,93 @@ namespace Catch { #endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED + +#ifndef CATCH_REPORTER_JSON_HPP_INCLUDED +#define CATCH_REPORTER_JSON_HPP_INCLUDED + + +#include <stack> + +namespace Catch { + class JsonReporter : public StreamingReporterBase { + public: + JsonReporter( ReporterConfig&& config ); + + ~JsonReporter() override; + + static std::string getDescription(); + + public: // StreamingReporterBase + void testRunStarting( TestRunInfo const& runInfo ) override; + void testRunEnded( TestRunStats const& runStats ) override; + + void testCaseStarting( TestCaseInfo const& tcInfo ) override; + void testCaseEnded( TestCaseStats const& tcStats ) override; + + void testCasePartialStarting( TestCaseInfo const& tcInfo, + uint64_t index ) override; + void testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t index ) override; + + void sectionStarting( SectionInfo const& sectionInfo ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + + void assertionStarting( AssertionInfo const& assertionInfo ) override; + void assertionEnded( AssertionStats const& assertionStats ) override; + + //void testRunEndedCumulative() 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; + enum class Writer { + Object, + Array + }; + + JsonArrayWriter& startArray(); + JsonArrayWriter& startArray( StringRef key ); + + JsonObjectWriter& startObject(); + JsonObjectWriter& startObject( StringRef key ); + + void endObject(); + void endArray(); + + bool isInside( Writer writer ); + + void startListing(); + void endListing(); + + // Invariant: + // When m_writers is not empty and its top element is + // - Writer::Object, then m_objectWriters is not be empty + // - Writer::Array, then m_arrayWriters shall not be empty + std::stack<JsonObjectWriter> m_objectWriters{}; + std::stack<JsonArrayWriter> m_arrayWriters{}; + std::stack<Writer> m_writers{}; + + bool m_startedListing = false; + + // std::size_t m_sectionDepth = 0; + // std::size_t m_sectionStarted = 0; + }; +} // namespace Catch + +#endif // CATCH_REPORTER_JSON_HPP_INCLUDED + + #ifndef CATCH_REPORTER_JUNIT_HPP_INCLUDED #define CATCH_REPORTER_JUNIT_HPP_INCLUDED @@ -12537,8 +13370,6 @@ namespace Catch { public: JunitReporter(ReporterConfig&& _config); - ~JunitReporter() override = default; - static std::string getDescription(); void testRunStarting(TestRunInfo const& runInfo) override; @@ -12665,7 +13496,8 @@ namespace Catch { //! independent on the reporter's concrete type void registerReporterImpl( std::string const& name, IReporterFactoryPtr reporterPtr ); - + //! Actually registers the factory, independent on listener's concrete type + void registerListenerImpl( Detail::unique_ptr<EventListenerFactory> listenerFactory ); } // namespace Detail class IEventListener; @@ -12726,7 +13558,7 @@ namespace Catch { public: ListenerRegistrar(StringRef listenerName) { - getMutableRegistryHub().registerListener( Detail::make_unique<TypedListenerFactory>(listenerName) ); + registerListenerImpl( Detail::make_unique<TypedListenerFactory>(listenerName) ); } }; } @@ -12778,8 +13610,6 @@ namespace Catch { 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; @@ -12826,7 +13656,6 @@ namespace Catch { StreamingReporterBase( CATCH_MOVE(config) ) { m_preferences.shouldReportAllAssertions = true; } - ~TAPReporter() override = default; static std::string getDescription() { using namespace std::string_literals; diff --git a/packages/Catch2/fuzzing/NullOStream.cpp b/packages/Catch2/fuzzing/NullOStream.cpp index 53e0893dc..e3a181e80 100644 --- a/packages/Catch2/fuzzing/NullOStream.cpp +++ b/packages/Catch2/fuzzing/NullOStream.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + #include "NullOStream.h" void NullOStream::avoidOutOfLineVirtualCompilerWarning() diff --git a/packages/Catch2/fuzzing/NullOStream.h b/packages/Catch2/fuzzing/NullOStream.h index e1fe15b08..abbec09c8 100644 --- a/packages/Catch2/fuzzing/NullOStream.h +++ b/packages/Catch2/fuzzing/NullOStream.h @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + #pragma once #include <ostream> diff --git a/packages/Catch2/fuzzing/fuzz_TestSpecParser.cpp b/packages/Catch2/fuzzing/fuzz_TestSpecParser.cpp index af4de4062..3aba8c840 100644 --- a/packages/Catch2/fuzzing/fuzz_TestSpecParser.cpp +++ b/packages/Catch2/fuzzing/fuzz_TestSpecParser.cpp @@ -1,4 +1,10 @@ -//License: Boost 1.0 + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 //By Paul Dreik 2020 #include <catch2/internal/catch_test_spec_parser.hpp> diff --git a/packages/Catch2/fuzzing/fuzz_XmlWriter.cpp b/packages/Catch2/fuzzing/fuzz_XmlWriter.cpp index f8e5a0d9a..70c4ed803 100644 --- a/packages/Catch2/fuzzing/fuzz_XmlWriter.cpp +++ b/packages/Catch2/fuzzing/fuzz_XmlWriter.cpp @@ -1,4 +1,10 @@ -//License: Boost 1.0 + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 //By Paul Dreik 2020 #include <catch2/internal/catch_xmlwriter.hpp> diff --git a/packages/Catch2/fuzzing/fuzz_textflow.cpp b/packages/Catch2/fuzzing/fuzz_textflow.cpp index eafe79feb..7000f420f 100644 --- a/packages/Catch2/fuzzing/fuzz_textflow.cpp +++ b/packages/Catch2/fuzzing/fuzz_textflow.cpp @@ -1,4 +1,10 @@ -//License: Boost 1.0 + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 //By Paul Dreik 2020 #include <catch2/internal/catch_textflow.hpp> diff --git a/packages/Catch2/meson.build b/packages/Catch2/meson.build index ed5033acd..0a897520d 100644 --- a/packages/Catch2/meson.build +++ b/packages/Catch2/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.3.2', # CML version placeholder, don't delete + version: '3.5.2', # CML version placeholder, don't delete license: 'BSL-1.0', meson_version: '>=0.54.1', ) diff --git a/packages/Catch2/src/CMakeLists.txt b/packages/Catch2/src/CMakeLists.txt index 0fdf931e6..eb805ddd0 100644 --- a/packages/Catch2/src/CMakeLists.txt +++ b/packages/Catch2/src/CMakeLists.txt @@ -33,6 +33,7 @@ set(BENCHMARK_HEADERS ) set(BENCHMARK_SOURCES ${SOURCES_DIR}/benchmark/catch_chronometer.cpp + ${SOURCES_DIR}/benchmark/detail/catch_analyse.cpp ${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.cpp ${SOURCES_DIR}/benchmark/detail/catch_run_for_at_least.cpp ${SOURCES_DIR}/benchmark/detail/catch_stats.cpp @@ -92,6 +93,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_getenv.hpp ${SOURCES_DIR}/internal/catch_istream.hpp ${SOURCES_DIR}/internal/catch_is_permutation.hpp + ${SOURCES_DIR}/internal/catch_jsonwriter.hpp ${SOURCES_DIR}/internal/catch_lazy_expr.hpp ${SOURCES_DIR}/internal/catch_leak_detector.hpp ${SOURCES_DIR}/internal/catch_list.hpp @@ -107,6 +109,8 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_polyfills.hpp ${SOURCES_DIR}/internal/catch_preprocessor.hpp ${SOURCES_DIR}/internal/catch_preprocessor_remove_parens.hpp + ${SOURCES_DIR}/internal/catch_random_floating_point_helpers.hpp + ${SOURCES_DIR}/internal/catch_random_integer_helpers.hpp ${SOURCES_DIR}/internal/catch_random_number_generator.hpp ${SOURCES_DIR}/internal/catch_random_seed_generation.hpp ${SOURCES_DIR}/internal/catch_reporter_registry.hpp @@ -136,6 +140,8 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_textflow.hpp ${SOURCES_DIR}/internal/catch_to_string.hpp ${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp + ${SOURCES_DIR}/internal/catch_uniform_floating_point_distribution.hpp + ${SOURCES_DIR}/internal/catch_uniform_integer_distribution.hpp ${SOURCES_DIR}/internal/catch_unique_name.hpp ${SOURCES_DIR}/internal/catch_unique_ptr.hpp ${SOURCES_DIR}/internal/catch_void_type.hpp @@ -176,6 +182,7 @@ set(IMPL_SOURCES ${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp ${SOURCES_DIR}/internal/catch_getenv.cpp ${SOURCES_DIR}/internal/catch_istream.cpp + ${SOURCES_DIR}/internal/catch_jsonwriter.cpp ${SOURCES_DIR}/internal/catch_lazy_expr.cpp ${SOURCES_DIR}/internal/catch_leak_detector.cpp ${SOURCES_DIR}/internal/catch_list.cpp @@ -288,6 +295,7 @@ set(REPORTER_HEADERS ${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.hpp ${SOURCES_DIR}/reporters/catch_reporter_event_listener.hpp ${SOURCES_DIR}/reporters/catch_reporter_helpers.hpp + ${SOURCES_DIR}/reporters/catch_reporter_json.hpp ${SOURCES_DIR}/reporters/catch_reporter_junit.hpp ${SOURCES_DIR}/reporters/catch_reporter_multi.hpp ${SOURCES_DIR}/reporters/catch_reporter_registrars.hpp @@ -306,6 +314,7 @@ set(REPORTER_SOURCES ${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.cpp ${SOURCES_DIR}/reporters/catch_reporter_event_listener.cpp ${SOURCES_DIR}/reporters/catch_reporter_helpers.cpp + ${SOURCES_DIR}/reporters/catch_reporter_json.cpp ${SOURCES_DIR}/reporters/catch_reporter_junit.cpp ${SOURCES_DIR}/reporters/catch_reporter_multi.cpp ${SOURCES_DIR}/reporters/catch_reporter_registrars.cpp @@ -339,7 +348,9 @@ source_group("generated headers" ) add_library(Catch2 ${ALL_FILES}) -add_build_reproducibility_settings(Catch2) +if (CATCH_ENABLE_REPRODUCIBLE_BUILD) + add_build_reproducibility_settings(Catch2) +endif() add_library(Catch2::Catch2 ALIAS Catch2) if (ANDROID) @@ -392,7 +403,9 @@ target_include_directories(Catch2 add_library(Catch2WithMain ${SOURCES_DIR}/internal/catch_main.cpp ) -add_build_reproducibility_settings(Catch2WithMain) +if (CATCH_ENABLE_REPRODUCIBLE_BUILD) + add_build_reproducibility_settings(Catch2WithMain) +endif() add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain) target_link_libraries(Catch2WithMain PUBLIC Catch2) set_target_properties(Catch2WithMain diff --git a/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp b/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp index 99d1c9df8..3db40bb04 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp @@ -31,9 +31,7 @@ #include <algorithm> #include <chrono> #include <exception> -#include <functional> #include <string> -#include <vector> #include <cmath> namespace Catch { @@ -47,16 +45,18 @@ namespace Catch { : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} template <typename Clock> - ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { + ExecutionPlan prepare(const IConfig &cfg, Environment 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); + auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(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 }; + return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; } template <typename Clock = default_clock> void run() { + static_assert( Clock::is_steady, + "Benchmarking clock should be steady" ); auto const* cfg = getCurrentContext().getConfig(); auto env = Detail::measure_environment<Clock>(); @@ -83,8 +83,8 @@ namespace Catch { return plan.template run<Clock>(*cfg, env); }); - auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); - BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; + auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); + BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); } CATCH_CATCH_ANON (TestFailureException const&) { getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); diff --git a/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp b/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp index c3f813060..95498e6be 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp @@ -32,7 +32,10 @@ namespace Catch { void start() override { started = Clock::now(); } void finish() override { finished = Clock::now(); } - ClockDuration<Clock> elapsed() const { return finished - started; } + IDuration elapsed() const { + return std::chrono::duration_cast<std::chrono::nanoseconds>( + finished - started ); + } TimePoint<Clock> started; TimePoint<Clock> finished; diff --git a/packages/Catch2/src/catch2/benchmark/catch_clock.hpp b/packages/Catch2/src/catch2/benchmark/catch_clock.hpp index cee46097d..4068c4d29 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_clock.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_clock.hpp @@ -11,28 +11,16 @@ #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>; + using IDuration = std::chrono::nanoseconds; + using FDuration = std::chrono::duration<double, std::nano>; 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 diff --git a/packages/Catch2/src/catch2/benchmark/catch_environment.hpp b/packages/Catch2/src/catch2/benchmark/catch_environment.hpp index de4d77df4..da3f2fa95 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_environment.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_environment.hpp @@ -15,21 +15,13 @@ namespace Catch { namespace Benchmark { - template <typename Duration> struct EnvironmentEstimate { - Duration mean; + FDuration 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; + EnvironmentEstimate clock_resolution; + EnvironmentEstimate clock_cost; }; } // namespace Benchmark } // namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/catch_estimate.hpp b/packages/Catch2/src/catch2/benchmark/catch_estimate.hpp index be594a189..64383a2e5 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_estimate.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_estimate.hpp @@ -12,17 +12,12 @@ namespace Catch { namespace Benchmark { - template <typename Duration> + template <typename Type> struct Estimate { - Duration point; - Duration lower_bound; - Duration upper_bound; + Type point; + Type lower_bound; + Type upper_bound; double confidence_interval; - - template <typename Duration2> - operator Estimate<Duration2>() const { - return { point, lower_bound, upper_bound, confidence_interval }; - } }; } // namespace Benchmark } // namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp b/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp index 4f60a6467..17ca589f5 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp @@ -21,33 +21,31 @@ namespace Catch { namespace Benchmark { - template <typename Duration> struct ExecutionPlan { int iterations_per_sample; - Duration estimated_duration; + FDuration estimated_duration; Detail::BenchmarkFunction benchmark; - Duration warmup_time; + FDuration 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 { + std::vector<FDuration> run(const IConfig &cfg, Environment 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>{})); + Detail::run_for_at_least<Clock>( + std::chrono::duration_cast<IDuration>( warmup_time ), + warmup_iterations, + Detail::repeat( []() { return Clock::now(); } ) + ); - std::vector<FloatDuration<Clock>> times; + std::vector<FDuration> times; const auto num_samples = cfg.benchmarkSamples(); times.reserve( num_samples ); for ( size_t i = 0; i < num_samples; ++i ) { 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(); + if ( sample_time < FDuration::zero() ) { + sample_time = FDuration::zero(); } times.push_back(sample_time / iterations_per_sample); } diff --git a/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp b/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp index 02cf2073e..61e6571f6 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp @@ -70,7 +70,7 @@ namespace Catch { 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)...); + CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...); } } // namespace Benchmark } // namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp b/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp index 97b8fe508..aeb87d05a 100644 --- a/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp +++ b/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp @@ -12,35 +12,18 @@ #include <catch2/benchmark/catch_estimate.hpp> #include <catch2/benchmark/catch_outlier_classification.hpp> -#include <catch2/internal/catch_move_and_forward.hpp> +#include <catch2/benchmark/catch_clock.hpp> #include <vector> namespace Catch { namespace Benchmark { - template <typename Duration> struct SampleAnalysis { - std::vector<Duration> samples; - Estimate<Duration> mean; - Estimate<Duration> standard_deviation; + std::vector<FDuration> samples; + Estimate<FDuration> mean; + Estimate<FDuration> standard_deviation; OutlierClassification outliers; double outlier_variance; - - template <typename Duration2> - operator SampleAnalysis<Duration2>() const { - std::vector<Duration2> samples2; - samples2.reserve(samples.size()); - for (auto const& d : samples) { - samples2.push_back(Duration2(d)); - } - return { - CATCH_MOVE(samples2), - mean, - standard_deviation, - outliers, - outlier_variance, - }; - } }; } // namespace Benchmark } // namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp b/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp new file mode 100644 index 000000000..7d27daf19 --- /dev/null +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp @@ -0,0 +1,85 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +// Adapted from donated nonius code. + +#include <catch2/benchmark/detail/catch_analyse.hpp> +#include <catch2/benchmark/catch_clock.hpp> +#include <catch2/benchmark/catch_sample_analysis.hpp> +#include <catch2/benchmark/detail/catch_stats.hpp> +#include <catch2/interfaces/catch_interfaces_config.hpp> +#include <catch2/internal/catch_move_and_forward.hpp> + +#include <vector> + +namespace Catch { + namespace Benchmark { + namespace Detail { + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector<double> samples; + samples.reserve(static_cast<size_t>(last - first)); + for (auto current = first; current != last; ++current) { + samples.push_back( current->count() ); + } + + auto analysis = Catch::Benchmark::Detail::analyse_samples( + cfg.benchmarkConfidenceInterval(), + cfg.benchmarkResamples(), + samples.data(), + samples.data() + samples.size() ); + auto outliers = Catch::Benchmark::Detail::classify_outliers( + samples.data(), samples.data() + samples.size() ); + + auto wrap_estimate = [](Estimate<double> e) { + return Estimate<FDuration> { + FDuration(e.point), + FDuration(e.lower_bound), + FDuration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector<FDuration> samples2; + samples2.reserve(samples.size()); + for (auto s : samples) { + samples2.push_back( FDuration( s ) ); + } + + return { + CATCH_MOVE(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector<FDuration> samples; + samples.reserve(static_cast<size_t>(last - first)); + + FDuration mean = FDuration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(FDuration(*it)); + mean += FDuration(*it); + } + mean /= i; + + return SampleAnalysis{ + CATCH_MOVE(samples), + Estimate<FDuration>{ mean, mean, mean, 0.0 }, + Estimate<FDuration>{ FDuration( 0 ), + FDuration( 0 ), + FDuration( 0 ), + 0.0 }, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp index c932ff26a..5e3f7b0f5 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp @@ -10,71 +10,16 @@ #ifndef CATCH_ANALYSE_HPP_INCLUDED #define CATCH_ANALYSE_HPP_INCLUDED -#include <catch2/benchmark/catch_environment.hpp> +#include <catch2/benchmark/catch_clock.hpp> #include <catch2/benchmark/catch_sample_analysis.hpp> -#include <catch2/benchmark/detail/catch_stats.hpp> -#include <catch2/interfaces/catch_interfaces_config.hpp> -#include <catch2/internal/catch_move_and_forward.hpp> -#include <vector> namespace Catch { + class IConfig; + 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)); - for (auto current = first; current != last; ++current) { - samples.push_back( current->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()); - for (auto s : samples) { - samples2.push_back( Duration( s ) ); - } - - 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 - }; - } - } + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); } // namespace Detail } // namespace Benchmark } // namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp index a8b3494e9..3633bc9f9 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp @@ -8,7 +8,6 @@ #ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_HPP_INCLUDED -#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/benchmark/catch_estimate.hpp> #include <catch2/benchmark/catch_outlier_classification.hpp> // The fwd decl & default specialization needs to be seen by VS2017 before @@ -30,32 +29,17 @@ namespace Catch { double clockCost; }; - template <class Duration> + // We need to keep template parameter for backwards compatibility, + // but we also do not want to use the template paraneter. + template <class Dummy> struct BenchmarkStats { BenchmarkInfo info; - std::vector<Duration> samples; - Benchmark::Estimate<Duration> mean; - Benchmark::Estimate<Duration> standardDeviation; + std::vector<Benchmark::FDuration> samples; + Benchmark::Estimate<Benchmark::FDuration> mean; + Benchmark::Estimate<Benchmark::FDuration> 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, - }; - } }; diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp index 607725613..2ccc25d58 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp @@ -8,14 +8,14 @@ #ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED -#include <chrono> +#include <catch2/benchmark/catch_clock.hpp> namespace Catch { // We cannot forward declare the type with default template argument // multiple times, so it is split out into a separate header so that // we can prevent multiple declarations in dependees - template <typename Duration = std::chrono::duration<double, std::nano>> + template <typename Duration = Benchmark::FDuration> struct BenchmarkStats; } // end namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp index 1e916ae4a..8e3552796 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp @@ -55,23 +55,23 @@ namespace Catch { template <typename Clock> int warmup() { - return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>) + return run_for_at_least<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>) + EnvironmentEstimate estimate_clock_resolution(int iterations) { + auto r = run_for_at_least<Clock>(clock_resolution_estimation_time, iterations, &resolution<Clock>) .result; return { - FloatDuration<Clock>(mean(r.begin(), r.end())), - classify_outliers(r.begin(), r.end()), + FDuration(mean(r.data(), r.data() + r.size())), + classify_outliers(r.data(), r.data() + r.size()), }; } template <typename Clock> - EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { + EnvironmentEstimate estimate_clock_cost(FDuration resolution) { auto time_limit = (std::min)( resolution * clock_cost_estimation_tick_limit, - FloatDuration<Clock>(clock_cost_estimation_time_limit)); + FDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure<Clock>([k] { for (int i = 0; i < k; ++i) { @@ -82,7 +82,7 @@ namespace Catch { }; 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); + auto&& r = run_for_at_least<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)); @@ -92,18 +92,18 @@ namespace Catch { .count() ) ); } return { - FloatDuration<Clock>(mean(times.begin(), times.end())), - classify_outliers(times.begin(), times.end()), + FDuration(mean(times.data(), times.data() + times.size())), + classify_outliers(times.data(), times.data() + times.size()), }; } template <typename Clock> - Environment<FloatDuration<Clock>> measure_environment() { + Environment 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; + static Catch::Detail::unique_ptr<Environment> env; #if defined(__clang__) # pragma clang diagnostic pop #endif @@ -115,7 +115,7 @@ namespace Catch { 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} ); + env = Catch::Detail::make_unique<Environment>( Environment{resolution, cost} ); return *env; } } // namespace Detail diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp index 1a30efabe..37494a68f 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp @@ -18,7 +18,7 @@ namespace Catch { namespace Benchmark { namespace Detail { template <typename Clock, typename Fun, typename... Args> - TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) { + TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) { auto start = Clock::now(); auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); auto end = Clock::now(); diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp index 976a4b243..4dfa8bbbb 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp @@ -24,11 +24,11 @@ namespace Catch { namespace Benchmark { namespace Detail { template <typename Clock, typename Fun> - TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) { + TimingOf<Fun, int> measure_one(Fun&& fun, int iters, std::false_type) { return Detail::measure<Clock>(fun, iters); } template <typename Clock, typename Fun> - TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) { + TimingOf<Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) { Detail::ChronometerModel<Clock> meter; auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); @@ -43,8 +43,8 @@ namespace Catch { 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, + TimingOf<Fun, run_for_at_least_argument_t<Clock, Fun>> + run_for_at_least(IDuration how_long, const int initial_iterations, Fun&& fun) { auto iters = initial_iterations; diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp index ea483a309..52cee4eea 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp @@ -10,8 +10,12 @@ #include <catch2/benchmark/detail/catch_stats.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp> +#include <catch2/internal/catch_floating_point_helpers.hpp> +#include <catch2/internal/catch_random_number_generator.hpp> +#include <algorithm> #include <cassert> +#include <cmath> #include <cstddef> #include <numeric> #include <random> @@ -30,28 +34,23 @@ namespace Catch { static sample resample( URng& rng, unsigned int resamples, - std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last, + double const* first, + double const* last, Estimator& estimator ) { auto n = static_cast<size_t>( last - first ); - std::uniform_int_distribution<decltype( n )> dist( 0, - n - 1 ); + std::uniform_int_distribution<size_t> dist( 0, n - 1 ); sample out; out.reserve( resamples ); - // We allocate the vector outside the loop to avoid realloc - // per resample std::vector<double> resampled; resampled.reserve( n ); for ( size_t i = 0; i < resamples; ++i ) { resampled.clear(); for ( size_t s = 0; s < n; ++s ) { - resampled.push_back( - first[static_cast<std::ptrdiff_t>( - dist( rng ) )] ); + resampled.push_back( first[dist( rng )] ); } const auto estimate = - estimator( resampled.begin(), resampled.end() ); + estimator( resampled.data(), resampled.data() + resampled.size() ); out.push_back( estimate ); } std::sort( out.begin(), out.end() ); @@ -168,8 +167,7 @@ namespace Catch { } static double - standard_deviation( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ) { + standard_deviation( double const* first, double const* last ) { auto m = Catch::Benchmark::Detail::mean( first, last ); double variance = std::accumulate( first, @@ -183,6 +181,23 @@ namespace Catch { return std::sqrt( variance ); } + static sample jackknife( double ( *estimator )( double const*, + double const* ), + double* first, + double* last ) { + const auto second = first + 1; + sample results; + results.reserve( static_cast<size_t>( last - first ) ); + + for ( auto it = first; it != last; ++it ) { + std::iter_swap( it, first ); + results.push_back( estimator( second, last ) ); + } + + return results; + } + + } // namespace } // namespace Detail } // namespace Benchmark @@ -192,23 +207,17 @@ 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) { + double weighted_average_quantile( int k, + int q, + double* first, + double* 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 ) ) { + if ( Catch::Detail::directCompare( g, 0 ) ) { return xj; } @@ -217,12 +226,11 @@ namespace Catch { } OutlierClassification - classify_outliers( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ) { + classify_outliers( double const* first, double const* 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 q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() ); + auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() ); auto iqr = q3 - q1; auto los = q1 - ( iqr * 3. ); auto lom = q1 - ( iqr * 1.5 ); @@ -246,8 +254,7 @@ namespace Catch { return o; } - double mean( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ) { + double mean( double const* first, double const* last ) { auto count = last - first; double sum = 0.; while (first != last) { @@ -257,6 +264,9 @@ namespace Catch { return sum / static_cast<double>(count); } + double normal_cdf( double x ) { + return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0; + } double erfc_inv(double x) { return erf_inv(1.0 - x); @@ -278,26 +288,77 @@ namespace Catch { return result; } - 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 + Estimate<double> + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ) { + 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.data(), jack.data() + jack.size() ); + double sum_squares = 0, sum_cubes = 0; + for ( double x : jack ) { + auto difference = jack_mean - x; + auto square = difference * difference; + auto cube = square * difference; + sum_squares += square; + sum_cubes += cube; + } - auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ + 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 ( Catch::Detail::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 }; + } + + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double* first, + double* last) { auto mean = &Detail::mean; auto stddev = &standard_deviation; #if defined(CATCH_CONFIG_USE_ASYNC) - auto Estimate = [=](double(*f)(std::vector<double>::const_iterator, - std::vector<double>::const_iterator)) { - auto seed = entropy(); + auto Estimate = [=](double(*f)(double const*, double const*)) { + std::random_device rd; + auto seed = rd(); return std::async(std::launch::async, [=] { - std::mt19937 rng(seed); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }); @@ -309,10 +370,10 @@ namespace Catch { auto mean_estimate = mean_future.get(); auto stddev_estimate = stddev_future.get(); #else - auto Estimate = [=](double(*f)(std::vector<double>::const_iterator, - std::vector<double>::const_iterator)) { - auto seed = entropy(); - std::mt19937 rng(seed); + auto Estimate = [=](double(*f)(double const* , double const*)) { + std::random_device rd; + auto seed = rd(); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }; @@ -321,6 +382,7 @@ namespace Catch { auto stddev_estimate = Estimate(stddev); #endif // CATCH_USE_ASYNC + auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); return { mean_estimate, stddev_estimate, outlier_variance }; diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp index c1ce56644..3bea612f9 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp @@ -13,100 +13,35 @@ #include <catch2/benchmark/catch_estimate.hpp> #include <catch2/benchmark/catch_outlier_classification.hpp> -#include <algorithm> #include <vector> -#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); + double weighted_average_quantile( int k, + int q, + double* first, + double* last ); OutlierClassification - classify_outliers( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ); - - double mean( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ); - - template <typename Estimator> - sample jackknife(Estimator&& estimator, - std::vector<double>::iterator first, - std::vector<double>::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; - } + classify_outliers( double const* first, double const* last ); + + double mean( double const* first, double const* last ); + + double normal_cdf( double x ); double erfc_inv(double x); double normal_quantile(double p); - template <typename Estimator> - Estimate<double> bootstrap( double confidence_level, - std::vector<double>::iterator first, - std::vector<double>::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 = 0, sum_cubes = 0; - for (double x : jack) { - auto difference = jack_mean - x; - auto square = difference * difference; - auto cube = square * difference; - sum_squares += square; sum_cubes += cube; - } - - 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 }; - } + Estimate<double> + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ); struct bootstrap_analysis { Estimate<double> mean; @@ -116,8 +51,8 @@ namespace Catch { bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, - std::vector<double>::iterator first, - std::vector<double>::iterator last); + double* first, + double* last); } // namespace Detail } // namespace Benchmark } // namespace Catch diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_timing.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_timing.hpp index f5c25571c..da5671908 100644 --- a/packages/Catch2/src/catch2/benchmark/detail/catch_timing.hpp +++ b/packages/Catch2/src/catch2/benchmark/detail/catch_timing.hpp @@ -17,14 +17,14 @@ namespace Catch { namespace Benchmark { - template <typename Duration, typename Result> + template <typename Result> struct Timing { - Duration elapsed; + IDuration elapsed; Result result; int iterations; }; - template <typename Clock, typename Func, typename... Args> - using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>; + template <typename Func, typename... Args> + using TimingOf = Timing<Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>; } // namespace Benchmark } // namespace Catch diff --git a/packages/Catch2/src/catch2/catch_all.hpp b/packages/Catch2/src/catch2/catch_all.hpp index 70ec402d6..f2cc85365 100644 --- a/packages/Catch2/src/catch2/catch_all.hpp +++ b/packages/Catch2/src/catch2/catch_all.hpp @@ -54,6 +54,7 @@ #include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_config_android_logwrite.hpp> #include <catch2/internal/catch_config_counter.hpp> +#include <catch2/internal/catch_config_prefix_messages.hpp> #include <catch2/internal/catch_config_static_analysis_support.hpp> #include <catch2/internal/catch_config_uncaught_exceptions.hpp> #include <catch2/internal/catch_config_wchar.hpp> @@ -73,6 +74,7 @@ #include <catch2/internal/catch_getenv.hpp> #include <catch2/internal/catch_is_permutation.hpp> #include <catch2/internal/catch_istream.hpp> +#include <catch2/internal/catch_jsonwriter.hpp> #include <catch2/internal/catch_lazy_expr.hpp> #include <catch2/internal/catch_leak_detector.hpp> #include <catch2/internal/catch_list.hpp> @@ -89,6 +91,8 @@ #include <catch2/internal/catch_preprocessor.hpp> #include <catch2/internal/catch_preprocessor_internal_stringify.hpp> #include <catch2/internal/catch_preprocessor_remove_parens.hpp> +#include <catch2/internal/catch_random_floating_point_helpers.hpp> +#include <catch2/internal/catch_random_integer_helpers.hpp> #include <catch2/internal/catch_random_number_generator.hpp> #include <catch2/internal/catch_random_seed_generation.hpp> #include <catch2/internal/catch_reporter_registry.hpp> @@ -118,6 +122,8 @@ #include <catch2/internal/catch_textflow.hpp> #include <catch2/internal/catch_to_string.hpp> #include <catch2/internal/catch_uncaught_exceptions.hpp> +#include <catch2/internal/catch_uniform_floating_point_distribution.hpp> +#include <catch2/internal/catch_uniform_integer_distribution.hpp> #include <catch2/internal/catch_unique_name.hpp> #include <catch2/internal/catch_unique_ptr.hpp> #include <catch2/internal/catch_void_type.hpp> diff --git a/packages/Catch2/src/catch2/catch_approx.cpp b/packages/Catch2/src/catch2/catch_approx.cpp index 407586d1d..9ad4ce3ee 100644 --- a/packages/Catch2/src/catch2/catch_approx.cpp +++ b/packages/Catch2/src/catch2/catch_approx.cpp @@ -70,10 +70,10 @@ namespace Catch { } namespace literals { - Approx operator "" _a(long double val) { + Approx operator ""_a(long double val) { return Approx(val); } - Approx operator "" _a(unsigned long long val) { + Approx operator ""_a(unsigned long long val) { return Approx(val); } } // end namespace literals diff --git a/packages/Catch2/src/catch2/catch_config.hpp b/packages/Catch2/src/catch2/catch_config.hpp index 784de4aa5..17e983e5c 100644 --- a/packages/Catch2/src/catch2/catch_config.hpp +++ b/packages/Catch2/src/catch2/catch_config.hpp @@ -69,7 +69,7 @@ namespace Catch { bool benchmarkNoAnalysis = false; unsigned int benchmarkSamples = 100; double benchmarkConfidenceInterval = 0.95; - unsigned int benchmarkResamples = 100000; + unsigned int benchmarkResamples = 100'000; std::chrono::milliseconds::rep benchmarkWarmupTime = 100; Verbosity verbosity = Verbosity::Normal; diff --git a/packages/Catch2/src/catch2/catch_message.hpp b/packages/Catch2/src/catch2/catch_message.hpp index b348ac870..05325ee8f 100644 --- a/packages/Catch2/src/catch2/catch_message.hpp +++ b/packages/Catch2/src/catch2/catch_message.hpp @@ -8,11 +8,13 @@ #ifndef CATCH_MESSAGE_HPP_INCLUDED #define CATCH_MESSAGE_HPP_INCLUDED +#include <catch2/internal/catch_config_prefix_messages.hpp> #include <catch2/internal/catch_result_type.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_stream_end_stop.hpp> #include <catch2/internal/catch_message_info.hpp> #include <catch2/catch_tostring.hpp> +#include <catch2/interfaces/catch_interfaces_capture.hpp> #include <string> #include <vector> @@ -112,28 +114,28 @@ namespace Catch { 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) +#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !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) +#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && 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) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !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) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) (void)(0) #define UNSCOPED_INFO( msg ) (void)(0) diff --git a/packages/Catch2/src/catch2/catch_test_case_info.cpp b/packages/Catch2/src/catch2/catch_test_case_info.cpp index e9dad5778..c38ee55ac 100644 --- a/packages/Catch2/src/catch2/catch_test_case_info.cpp +++ b/packages/Catch2/src/catch2/catch_test_case_info.cpp @@ -9,6 +9,7 @@ #include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_case_insensitive_comparisons.hpp> +#include <catch2/internal/catch_test_registry.hpp> #include <cassert> #include <cctype> diff --git a/packages/Catch2/src/catch2/catch_test_case_info.hpp b/packages/Catch2/src/catch2/catch_test_case_info.hpp index 5ff3e3e72..a2f4b43ec 100644 --- a/packages/Catch2/src/catch2/catch_test_case_info.hpp +++ b/packages/Catch2/src/catch2/catch_test_case_info.hpp @@ -8,10 +8,10 @@ #ifndef CATCH_TEST_CASE_INFO_HPP_INCLUDED #define CATCH_TEST_CASE_INFO_HPP_INCLUDED +#include <catch2/interfaces/catch_interfaces_test_invoker.hpp> #include <catch2/internal/catch_source_line_info.hpp> #include <catch2/internal/catch_noncopyable.hpp> #include <catch2/internal/catch_stringref.hpp> -#include <catch2/internal/catch_test_registry.hpp> #include <catch2/internal/catch_unique_ptr.hpp> @@ -44,6 +44,7 @@ namespace Catch { }; class ITestInvoker; + struct NameAndTags; enum class TestCaseProperties : uint8_t { None = 0, diff --git a/packages/Catch2/src/catch2/catch_tostring.hpp b/packages/Catch2/src/catch2/catch_tostring.hpp index 788d824c8..f3fb0beb7 100644 --- a/packages/Catch2/src/catch2/catch_tostring.hpp +++ b/packages/Catch2/src/catch2/catch_tostring.hpp @@ -398,6 +398,12 @@ namespace Catch { } } }; + template <> + struct StringMaker<std::nullopt_t> { + static std::string convert(const std::nullopt_t&) { + return "{ }"; + } + }; } #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER diff --git a/packages/Catch2/src/catch2/catch_user_config.hpp.in b/packages/Catch2/src/catch2/catch_user_config.hpp.in index 11ab5a6d1..10d61937f 100644 --- a/packages/Catch2/src/catch2/catch_user_config.hpp.in +++ b/packages/Catch2/src/catch2/catch_user_config.hpp.in @@ -198,6 +198,7 @@ #cmakedefine CATCH_CONFIG_FAST_COMPILE #cmakedefine CATCH_CONFIG_NOSTDOUT #cmakedefine CATCH_CONFIG_PREFIX_ALL +#cmakedefine CATCH_CONFIG_PREFIX_MESSAGES #cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG #cmakedefine CATCH_CONFIG_SHARED_LIBRARY diff --git a/packages/Catch2/src/catch2/catch_version.cpp b/packages/Catch2/src/catch2/catch_version.cpp index 19cab91b3..4e67d968c 100644 --- a/packages/Catch2/src/catch2/catch_version.cpp +++ b/packages/Catch2/src/catch2/catch_version.cpp @@ -36,7 +36,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 3, 2, "", 0 ); + static Version version( 3, 5, 2, "", 0 ); return version; } diff --git a/packages/Catch2/src/catch2/catch_version_macros.hpp b/packages/Catch2/src/catch2/catch_version_macros.hpp index 9ece85051..be2a04d2f 100644 --- a/packages/Catch2/src/catch2/catch_version_macros.hpp +++ b/packages/Catch2/src/catch2/catch_version_macros.hpp @@ -9,7 +9,7 @@ #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 3 +#define CATCH_VERSION_MINOR 5 #define CATCH_VERSION_PATCH 2 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/generators/catch_generators.hpp b/packages/Catch2/src/catch2/generators/catch_generators.hpp index 117f19019..0f35a9968 100644 --- a/packages/Catch2/src/catch2/generators/catch_generators.hpp +++ b/packages/Catch2/src/catch2/generators/catch_generators.hpp @@ -37,12 +37,6 @@ namespace Detail { } 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, diff --git a/packages/Catch2/src/catch2/generators/catch_generators_random.cpp b/packages/Catch2/src/catch2/generators/catch_generators_random.cpp index 2e3390fdf..00a8e634f 100644 --- a/packages/Catch2/src/catch2/generators/catch_generators_random.cpp +++ b/packages/Catch2/src/catch2/generators/catch_generators_random.cpp @@ -7,7 +7,35 @@ // SPDX-License-Identifier: BSL-1.0 #include <catch2/generators/catch_generators_random.hpp> - #include <catch2/internal/catch_context.hpp> -std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } +#include <random> + +namespace Catch { + namespace Generators { + namespace Detail { + std::uint32_t getSeed() { return sharedRng()(); } + } // namespace Detail + + struct RandomFloatingGenerator<long double>::PImpl { + PImpl( long double a, long double b, uint32_t seed ): + rng( seed ), dist( a, b ) {} + + Catch::SimplePcg32 rng; + std::uniform_real_distribution<long double> dist; + }; + + RandomFloatingGenerator<long double>::RandomFloatingGenerator( + long double a, long double b, std::uint32_t seed) : + m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) { + static_cast<void>( next() ); + } + + RandomFloatingGenerator<long double>::~RandomFloatingGenerator() = + default; + bool RandomFloatingGenerator<long double>::next() { + m_current_number = m_pimpl->dist( m_pimpl->rng ); + return true; + } + } // namespace Generators +} // namespace Catch diff --git a/packages/Catch2/src/catch2/generators/catch_generators_random.hpp b/packages/Catch2/src/catch2/generators/catch_generators_random.hpp index bcd4888dc..712835619 100644 --- a/packages/Catch2/src/catch2/generators/catch_generators_random.hpp +++ b/packages/Catch2/src/catch2/generators/catch_generators_random.hpp @@ -8,11 +8,11 @@ #ifndef CATCH_GENERATORS_RANDOM_HPP_INCLUDED #define CATCH_GENERATORS_RANDOM_HPP_INCLUDED -#include <catch2/internal/catch_context.hpp> #include <catch2/generators/catch_generators.hpp> #include <catch2/internal/catch_random_number_generator.hpp> - -#include <random> +#include <catch2/internal/catch_uniform_integer_distribution.hpp> +#include <catch2/internal/catch_uniform_floating_point_distribution.hpp> +#include <catch2/internal/catch_unique_ptr.hpp> namespace Catch { namespace Generators { @@ -26,7 +26,7 @@ namespace Detail { template <typename Float> class RandomFloatingGenerator final : public IGenerator<Float> { Catch::SimplePcg32 m_rng; - std::uniform_real_distribution<Float> m_dist; + Catch::uniform_floating_point_distribution<Float> m_dist; Float m_current_number; public: RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ): @@ -44,10 +44,27 @@ public: } }; +template <> +class RandomFloatingGenerator<long double> final : public IGenerator<long double> { + // We still rely on <random> for this specialization, but we don't + // want to drag it into the header. + struct PImpl; + Catch::Detail::unique_ptr<PImpl> m_pimpl; + long double m_current_number; + +public: + RandomFloatingGenerator( long double a, long double b, std::uint32_t seed ); + + long double const& get() const override { return m_current_number; } + bool next() override; + + ~RandomFloatingGenerator() override; // = default +}; + template <typename Integer> class RandomIntegerGenerator final : public IGenerator<Integer> { Catch::SimplePcg32 m_rng; - std::uniform_int_distribution<Integer> m_dist; + Catch::uniform_integer_distribution<Integer> m_dist; Integer m_current_number; public: RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ): @@ -68,14 +85,6 @@ public: 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()) ); diff --git a/packages/Catch2/src/catch2/generators/catch_generators_range.hpp b/packages/Catch2/src/catch2/generators/catch_generators_range.hpp index 495acb950..b67c1590e 100644 --- a/packages/Catch2/src/catch2/generators/catch_generators_range.hpp +++ b/packages/Catch2/src/catch2/generators/catch_generators_range.hpp @@ -96,10 +96,11 @@ 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())); +template <typename Container> +auto from_range(Container const& cnt) { + using std::begin; + using std::end; + return from_range( begin( cnt ), end( cnt ) ); } diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp index 3274bcf50..90536bb36 100644 --- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp +++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp @@ -7,19 +7,11 @@ // SPDX-License-Identifier: BSL-1.0 #include <catch2/interfaces/catch_interfaces_reporter.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp> -#include <catch2/internal/catch_console_colour.hpp> -#include <catch2/internal/catch_console_width.hpp> #include <catch2/catch_message.hpp> -#include <catch2/internal/catch_list.hpp> -#include <catch2/internal/catch_string_manip.hpp> -#include <catch2/catch_test_case_info.hpp> -#include <catch2/reporters/catch_reporter_helpers.hpp> #include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_istream.hpp> -#include <algorithm> #include <cassert> -#include <iomanip> namespace Catch { diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp index b40fce312..a052c5db1 100644 --- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp +++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp @@ -15,7 +15,6 @@ #include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_test_run_info.hpp> #include <catch2/internal/catch_unique_ptr.hpp> -#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/benchmark/detail/catch_benchmark_stats.hpp> #include <map> diff --git a/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp b/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp index e5232f70c..f650a7073 100644 --- a/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp +++ b/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp @@ -8,10 +8,8 @@ #include <catch2/internal/catch_assertion_handler.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/internal/catch_context.hpp> -#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_debugger.hpp> #include <catch2/internal/catch_test_failure_exception.hpp> -#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/matchers/catch_matchers_string.hpp> namespace Catch { diff --git a/packages/Catch2/src/catch2/internal/catch_clara.cpp b/packages/Catch2/src/catch2/internal/catch_clara.cpp index c9bc76959..c76089eea 100644 --- a/packages/Catch2/src/catch2/internal/catch_clara.cpp +++ b/packages/Catch2/src/catch2/internal/catch_clara.cpp @@ -11,6 +11,7 @@ #include <catch2/internal/catch_platform.hpp> #include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_textflow.hpp> +#include <catch2/internal/catch_reusable_string_stream.hpp> #include <algorithm> #include <ostream> @@ -24,13 +25,29 @@ namespace { ; } - std::string normaliseOpt( std::string const& optName ) { -#ifdef CATCH_PLATFORM_WINDOWS - if ( optName[0] == '/' ) - return "-" + optName.substr( 1 ); - else + Catch::StringRef normaliseOpt( Catch::StringRef optName ) { + if ( optName[0] == '-' +#if defined(CATCH_PLATFORM_WINDOWS) + || optName[0] == '/' #endif - return optName; + ) { + return optName.substr( 1, optName.size() ); + } + + return optName; + } + + static size_t find_first_separator(Catch::StringRef sr) { + auto is_separator = []( char c ) { + return c == ' ' || c == ':' || c == '='; + }; + size_t pos = 0; + while (pos < sr.size()) { + if (is_separator(sr[pos])) { return pos; } + ++pos; + } + + return Catch::StringRef::npos; } } // namespace @@ -48,23 +65,23 @@ namespace Catch { } if ( it != itEnd ) { - auto const& next = *it; + StringRef next = *it; if ( isOptPrefix( next[0] ) ) { - auto delimiterPos = next.find_first_of( " :=" ); - if ( delimiterPos != std::string::npos ) { + auto delimiterPos = find_first_separator(next); + if ( delimiterPos != StringRef::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); m_tokenBuffer.push_back( { TokenType::Argument, - next.substr( delimiterPos + 1 ) } ); + next.substr( delimiterPos + 1, next.size() ) } ); } else { if ( next[1] != '-' && next.size() > 2 ) { - std::string opt = "- "; + // Combined short args, e.g. "-ab" for "-a -b" for ( size_t i = 1; i < next.size(); ++i ) { - opt[1] = next[i]; m_tokenBuffer.push_back( - { TokenType::Option, opt } ); + { TokenType::Option, + next.substr( i, 1 ) } ); } } else { m_tokenBuffer.push_back( @@ -124,12 +141,12 @@ namespace Catch { size_t ParserBase::cardinality() const { return 1; } InternalParseResult ParserBase::parse( Args const& args ) const { - return parse( args.exeName(), TokenStream( args ) ); + return parse( static_cast<std::string>(args.exeName()), TokenStream( args ) ); } ParseState::ParseState( ParseResultType type, - TokenStream const& remainingTokens ): - m_type( type ), m_remainingTokens( remainingTokens ) {} + TokenStream remainingTokens ): + m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {} ParserResult BoundFlagRef::setFlag( bool flag ) { m_ref = flag; @@ -147,34 +164,34 @@ namespace Catch { } // namespace Detail Detail::InternalParseResult Arg::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { auto validationResult = validate(); if (!validationResult) return Detail::InternalParseResult(validationResult); - auto remainingTokens = tokens; - auto const& token = *remainingTokens; + auto token = *tokens; if (token.type != Detail::TokenType::Argument) return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::NoMatch, remainingTokens)); + ParseResultType::NoMatch, CATCH_MOVE(tokens))); 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); + auto result = valueRef->setValue(static_cast<std::string>(token.token)); + if ( !result ) + return Detail::InternalParseResult( result ); else - return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + return Detail::InternalParseResult::ok( + Detail::ParseState( ParseResultType::Matched, + CATCH_MOVE( ++tokens ) ) ); } Opt::Opt(bool& ref) : ParserRefImpl(std::make_shared<Detail::BoundFlagRef>(ref)) {} - std::vector<Detail::HelpColumns> Opt::getHelpColumns() const { - std::ostringstream oss; + Detail::HelpColumns Opt::getHelpColumns() const { + ReusableStringStream oss; bool first = true; for (auto const& opt : m_optNames) { if (first) @@ -185,10 +202,10 @@ namespace Catch { } if (!m_hint.empty()) oss << " <" << m_hint << '>'; - return { { oss.str(), m_description } }; + return { oss.str(), m_description }; } - bool Opt::isMatch(std::string const& optToken) const { + bool Opt::isMatch(StringRef optToken) const { auto normalisedToken = normaliseOpt(optToken); for (auto const& name : m_optNames) { if (normaliseOpt(name) == normalisedToken) @@ -198,15 +215,14 @@ namespace Catch { } Detail::InternalParseResult Opt::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream 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 (tokens && + tokens->type == Detail::TokenType::Option) { + auto const& token = *tokens; if (isMatch(token.token)) { if (m_ref->isFlag()) { auto flagRef = @@ -218,35 +234,35 @@ namespace Catch { if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } else { auto valueRef = static_cast<Detail::BoundValueRefBase*>( m_ref.get()); - ++remainingTokens; - if (!remainingTokens) + ++tokens; + if (!tokens) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - auto const& argToken = *remainingTokens; + auto const& argToken = *tokens; if (argToken.type != Detail::TokenType::Argument) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - const auto result = valueRef->setValue(argToken.token); + const auto result = valueRef->setValue(static_cast<std::string>(argToken.token)); if (!result) return Detail::InternalParseResult(result); if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + ParseResultType::Matched, CATCH_MOVE(++tokens))); } } return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, remainingTokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } Detail::Result Opt::validate() const { @@ -278,9 +294,9 @@ namespace Catch { Detail::InternalParseResult ExeName::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, tokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } ParserResult ExeName::set(std::string const& newName) { @@ -310,9 +326,9 @@ namespace Catch { std::vector<Detail::HelpColumns> Parser::getHelpColumns() const { std::vector<Detail::HelpColumns> cols; + cols.reserve( m_options.size() ); for ( auto const& o : m_options ) { - auto childCols = o.getHelpColumns(); - cols.insert( cols.end(), childCols.begin(), childCols.end() ); + cols.push_back(o.getHelpColumns()); } return cols; } @@ -350,12 +366,12 @@ namespace Catch { optWidth = ( std::min )( optWidth, consoleWidth / 2 ); - for ( auto const& cols : rows ) { - auto row = TextFlow::Column( cols.left ) + for ( auto& cols : rows ) { + auto row = TextFlow::Column( CATCH_MOVE(cols.left) ) .width( optWidth ) .indent( 2 ) + TextFlow::Spacer( 4 ) + - TextFlow::Column( cols.right ) + TextFlow::Column( static_cast<std::string>(cols.descriptions) ) .width( consoleWidth - 7 - optWidth ); os << row << '\n'; } @@ -377,7 +393,7 @@ namespace Catch { Detail::InternalParseResult Parser::parse( std::string const& exeName, - Detail::TokenStream const& tokens ) const { + Detail::TokenStream tokens ) const { struct ParserInfo { ParserBase const* parser = nullptr; @@ -395,7 +411,7 @@ namespace Catch { m_exeName.set( exeName ); auto result = Detail::InternalParseResult::ok( - Detail::ParseState( ParseResultType::NoMatch, tokens ) ); + Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) ); while ( result.value().remainingTokens() ) { bool tokenParsed = false; @@ -403,7 +419,7 @@ namespace Catch { if ( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { result = parseInfo.parser->parse( - exeName, result.value().remainingTokens() ); + exeName, CATCH_MOVE(result).value().remainingTokens() ); if ( !result ) return result; if ( result.value().type() != @@ -429,7 +445,7 @@ namespace Catch { 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) : + Args::Args(std::initializer_list<StringRef> args) : m_exeName(*args.begin()), m_args(args.begin() + 1, args.end()) {} diff --git a/packages/Catch2/src/catch2/internal/catch_clara.hpp b/packages/Catch2/src/catch2/internal/catch_clara.hpp index 9117b65e8..d869593bf 100644 --- a/packages/Catch2/src/catch2/internal/catch_clara.hpp +++ b/packages/Catch2/src/catch2/internal/catch_clara.hpp @@ -29,6 +29,7 @@ # endif #endif +#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_noncopyable.hpp> #include <catch2/internal/catch_void_type.hpp> @@ -101,17 +102,16 @@ namespace Catch { enum class TokenType { Option, Argument }; struct Token { TokenType type; - std::string token; + StringRef 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; + using Iterator = std::vector<StringRef>::const_iterator; Iterator it; Iterator itEnd; std::vector<Token> m_tokenBuffer; - void loadBuffer(); public: @@ -163,12 +163,17 @@ namespace Catch { ResultType m_type; }; - template <typename T> class ResultValueBase : public ResultBase { + template <typename T> + class ResultValueBase : public ResultBase { public: - auto value() const -> T const& { + T const& value() const& { enforceOk(); return m_value; } + T&& value() && { + enforceOk(); + return CATCH_MOVE( m_value ); + } protected: ResultValueBase( ResultType type ): ResultBase( type ) {} @@ -178,13 +183,23 @@ namespace Catch { if ( m_type == ResultType::Ok ) new ( &m_value ) T( other.m_value ); } + ResultValueBase( ResultValueBase&& other ): + ResultBase( other ) { + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + } + - ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { + ResultValueBase( ResultType, T const& value ): + ResultBase( ResultType::Ok ) { new ( &m_value ) T( value ); } + ResultValueBase( ResultType, T&& value ): + ResultBase( ResultType::Ok ) { + new ( &m_value ) T( CATCH_MOVE(value) ); + } - auto operator=( ResultValueBase const& other ) - -> ResultValueBase& { + ResultValueBase& operator=( ResultValueBase const& other ) { if ( m_type == ResultType::Ok ) m_value.~T(); ResultBase::operator=( other ); @@ -192,6 +207,14 @@ namespace Catch { new ( &m_value ) T( other.m_value ); return *this; } + ResultValueBase& operator=( ResultValueBase&& other ) { + if ( m_type == ResultType::Ok ) m_value.~T(); + ResultBase::operator=( other ); + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + return *this; + } + ~ResultValueBase() override { if ( m_type == ResultType::Ok ) @@ -219,8 +242,8 @@ namespace Catch { } template <typename U> - static auto ok( U const& value ) -> BasicResult { - return { ResultType::Ok, value }; + static auto ok( U&& value ) -> BasicResult { + return { ResultType::Ok, CATCH_FORWARD(value) }; } static auto ok() -> BasicResult { return { ResultType::Ok }; } static auto logicError( std::string&& message ) @@ -267,12 +290,15 @@ namespace Catch { class ParseState { public: ParseState( ParseResultType type, - TokenStream const& remainingTokens ); + TokenStream remainingTokens ); ParseResultType type() const { return m_type; } - TokenStream const& remainingTokens() const { + TokenStream const& remainingTokens() const& { return m_remainingTokens; } + TokenStream&& remainingTokens() && { + return CATCH_MOVE( m_remainingTokens ); + } private: ParseResultType m_type; @@ -285,7 +311,7 @@ namespace Catch { struct HelpColumns { std::string left; - std::string right; + StringRef descriptions; }; template <typename T> @@ -445,7 +471,7 @@ namespace Catch { virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } virtual auto parse( std::string const& exeName, - TokenStream const& tokens ) const + TokenStream tokens ) const -> InternalParseResult = 0; virtual size_t cardinality() const; @@ -465,8 +491,8 @@ namespace Catch { protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr<BoundRef> m_ref; - std::string m_hint; - std::string m_description; + StringRef m_hint; + StringRef m_description; explicit ParserRefImpl( std::shared_ptr<BoundRef> const& ref ): m_ref( ref ) {} @@ -475,28 +501,32 @@ namespace Catch { template <typename LambdaT> ParserRefImpl( accept_many_t, LambdaT const& ref, - std::string const& hint ): + StringRef 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 ): + ParserRefImpl( T& ref, StringRef 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 ): + ParserRefImpl( LambdaT const& ref, StringRef hint ): m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ), m_hint( hint ) {} - auto operator()( std::string const& description ) -> DerivedT& { + DerivedT& operator()( StringRef description ) & { m_description = description; return static_cast<DerivedT&>( *this ); } + DerivedT&& operator()( StringRef description ) && { + m_description = description; + return static_cast<DerivedT&&>( *this ); + } auto optional() -> DerivedT& { m_optionality = Optionality::Optional; @@ -519,7 +549,7 @@ namespace Catch { return 1; } - std::string const& hint() const { return m_hint; } + StringRef hint() const { return m_hint; } }; } // namespace detail @@ -533,13 +563,13 @@ namespace Catch { Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; // A parser for options class Opt : public Detail::ParserRefImpl<Opt> { protected: - std::vector<std::string> m_optNames; + std::vector<StringRef> m_optNames; public: template <typename LambdaT> @@ -552,33 +582,37 @@ namespace Catch { template <typename LambdaT, typename = typename std::enable_if_t< Detail::is_unary_function<LambdaT>::value>> - Opt( LambdaT const& ref, std::string const& hint ): + Opt( LambdaT const& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} template <typename LambdaT> - Opt( accept_many_t, LambdaT const& ref, std::string const& hint ): + Opt( accept_many_t, LambdaT const& ref, StringRef 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 ): + Opt( T& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} - auto operator[](std::string const& optName) -> Opt& { + Opt& operator[]( StringRef optName ) & { m_optNames.push_back(optName); return *this; } + Opt&& operator[]( StringRef optName ) && { + m_optNames.push_back( optName ); + return CATCH_MOVE(*this); + } - std::vector<Detail::HelpColumns> getHelpColumns() const; + Detail::HelpColumns getHelpColumns() const; - bool isMatch(std::string const& optToken) const; + bool isMatch(StringRef optToken) const; using ParserBase::parse; Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; Detail::Result validate() const override; }; @@ -601,7 +635,7 @@ namespace Catch { // handled specially Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; std::string const& name() const { return *m_name; } Detail::ParserResult set(std::string const& newName); @@ -626,16 +660,28 @@ namespace Catch { return *this; } - auto operator|=(Opt const& opt) -> Parser& { - m_options.push_back(opt); - return *this; + friend Parser& operator|=( Parser& p, Opt const& opt ) { + p.m_options.push_back( opt ); + return p; + } + friend Parser& operator|=( Parser& p, Opt&& opt ) { + p.m_options.push_back( CATCH_MOVE(opt) ); + return p; } Parser& operator|=(Parser const& other); template <typename T> - auto operator|(T const& other) const -> Parser { - return Parser(*this) |= other; + friend Parser operator|( Parser const& p, T&& rhs ) { + Parser temp( p ); + temp |= rhs; + return temp; + } + + template <typename T> + friend Parser operator|( Parser&& p, T&& rhs ) { + p |= CATCH_FORWARD(rhs); + return CATCH_MOVE(p); } std::vector<Detail::HelpColumns> getHelpColumns() const; @@ -653,21 +699,23 @@ namespace Catch { using ParserBase::parse; Detail::InternalParseResult parse(std::string const& exeName, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; - // Transport for raw args (copied from main args, or supplied via - // init list for testing) + /** + * Wrapper over argc + argv, assumes that the inputs outlive it + */ class Args { friend Detail::TokenStream; - std::string m_exeName; - std::vector<std::string> m_args; + StringRef m_exeName; + std::vector<StringRef> m_args; public: Args(int argc, char const* const* argv); - Args(std::initializer_list<std::string> args); + // Helper constructor for testing + Args(std::initializer_list<StringRef> args); - std::string const& exeName() const { return m_exeName; } + StringRef exeName() const { return m_exeName; } }; diff --git a/packages/Catch2/src/catch2/internal/catch_commandline.cpp b/packages/Catch2/src/catch2/internal/catch_commandline.cpp index 4ac1847b2..c29a801d3 100644 --- a/packages/Catch2/src/catch2/internal/catch_commandline.cpp +++ b/packages/Catch2/src/catch2/internal/catch_commandline.cpp @@ -9,6 +9,7 @@ #include <catch2/catch_config.hpp> #include <catch2/internal/catch_string_manip.hpp> +#include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_reporter_registry.hpp> #include <catch2/internal/catch_console_colour.hpp> @@ -300,8 +301,8 @@ namespace Catch { ( "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 ) + ( "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" ) diff --git a/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp b/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp index fc5d4f315..dacae01b7 100644 --- a/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp +++ b/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp @@ -156,7 +156,9 @@ //////////////////////////////////////////////////////////////////////////////// // Assume that some platforms do not support getenv. -#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION) +#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ + defined( CATCH_PLATFORM_PLAYSTATION ) || \ + defined( _GAMING_XBOX ) # define CATCH_INTERNAL_CONFIG_NO_GETENV #else # define CATCH_INTERNAL_CONFIG_GETENV diff --git a/packages/Catch2/src/catch2/internal/catch_config_prefix_messages.hpp b/packages/Catch2/src/catch2/internal/catch_config_prefix_messages.hpp new file mode 100644 index 000000000..be1e9a963 --- /dev/null +++ b/packages/Catch2/src/catch2/internal/catch_config_prefix_messages.hpp @@ -0,0 +1,29 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +/** \file + * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option + * + * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros + * by prepending CATCH_. This may not be desirable if the only clashes are with + * logger macros such as INFO and WARN. In this cases + * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset + * of relevant macros. + * + */ + +#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED +#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED + +#include <catch2/catch_user_config.hpp> + +#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) + #define CATCH_CONFIG_PREFIX_MESSAGES +#endif + +#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp b/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp index 7e8bf5e5e..a94b60881 100644 --- a/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp +++ b/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp @@ -39,7 +39,7 @@ namespace Catch { return parsed; } - EnumInfo::~EnumInfo() {} + EnumInfo::~EnumInfo() = default; StringRef EnumInfo::lookup( int value ) const { for( auto const& valueToName : m_values ) { diff --git a/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp b/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp index f3d47c0cd..1eb611473 100644 --- a/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp +++ b/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp @@ -15,6 +15,7 @@ namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) namespace { static std::string tryTranslators( std::vector< @@ -28,9 +29,9 @@ namespace Catch { } } +#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { - } + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) { m_translators.push_back( CATCH_MOVE( translator ) ); diff --git a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp index f9702b184..9ef5b2179 100644 --- a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp +++ b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp @@ -26,6 +26,7 @@ #include <catch2/internal/catch_fatal_condition_handler.hpp> +#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_enforce.hpp> #include <catch2/interfaces/catch_interfaces_capture.hpp> diff --git a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp index ce07f9b6a..81728b563 100644 --- a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp +++ b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp @@ -8,9 +8,6 @@ #ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED #define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED -#include <catch2/internal/catch_platform.hpp> -#include <catch2/internal/catch_compiler_capabilities.hpp> - #include <cassert> namespace Catch { diff --git a/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.cpp b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.cpp index e30ee4342..9631ed6d2 100644 --- a/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.cpp +++ b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.cpp @@ -27,6 +27,17 @@ namespace Catch { return i; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + bool directCompare( float lhs, float rhs ) { return lhs == rhs; } + bool directCompare( double lhs, double rhs ) { return lhs == rhs; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } // end namespace Detail } // end namespace Catch diff --git a/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.hpp b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.hpp index ca883c613..b2143726d 100644 --- a/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.hpp +++ b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.hpp @@ -22,6 +22,11 @@ namespace Catch { uint32_t convertToBits(float f); uint64_t convertToBits(double d); + // Used when we know we want == comparison of two doubles + // to centralize warning suppression + bool directCompare( float lhs, float rhs ); + bool directCompare( double lhs, double rhs ); + } // end namespace Detail diff --git a/packages/Catch2/src/catch2/internal/catch_istream.cpp b/packages/Catch2/src/catch2/internal/catch_istream.cpp index bf0f66e42..2867ce747 100644 --- a/packages/Catch2/src/catch2/internal/catch_istream.cpp +++ b/packages/Catch2/src/catch2/internal/catch_istream.cpp @@ -80,7 +80,6 @@ namespace Detail { 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; @@ -95,7 +94,6 @@ namespace Detail { // 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; } @@ -109,7 +107,6 @@ namespace Detail { // 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; } @@ -127,8 +124,6 @@ namespace Detail { m_os( m_streamBuf.get() ) {} - ~DebugOutStream() override = default; - public: // IStream std::ostream& stream() override { return m_os; } }; diff --git a/packages/Catch2/src/catch2/internal/catch_jsonwriter.cpp b/packages/Catch2/src/catch2/internal/catch_jsonwriter.cpp new file mode 100644 index 000000000..ff65a9d34 --- /dev/null +++ b/packages/Catch2/src/catch2/internal/catch_jsonwriter.cpp @@ -0,0 +1,148 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#include <catch2/internal/catch_enforce.hpp> +#include <catch2/internal/catch_jsonwriter.hpp> + +namespace Catch { + void JsonUtils::indent( std::ostream& os, std::uint64_t level ) { + for ( std::uint64_t i = 0; i < level; ++i ) { + os << " "; + } + } + void JsonUtils::appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ) { + if ( should_comma ) { os << ','; } + should_comma = true; + os << '\n'; + indent( os, level ); + } + + JsonObjectWriter::JsonObjectWriter( std::ostream& os ): + JsonObjectWriter{ os, 0 } {} + + JsonObjectWriter::JsonObjectWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '{'; + } + JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ): + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + + JsonObjectWriter::~JsonObjectWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << '}'; + } + + JsonValueWriter JsonObjectWriter::write( StringRef key ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + + m_os << '"' << key << "\": "; + return JsonValueWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter::JsonArrayWriter( std::ostream& os ): + JsonArrayWriter{ os, 0 } {} + JsonArrayWriter::JsonArrayWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '['; + } + JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ): + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + JsonArrayWriter::~JsonArrayWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << ']'; + } + + JsonObjectWriter JsonArrayWriter::writeObject() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonObjectWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter JsonArrayWriter::writeArray() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonArrayWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter& JsonArrayWriter::write( bool value ) { + return writeImpl( value ); + } + + JsonValueWriter::JsonValueWriter( std::ostream& os ): + JsonValueWriter{ os, 0 } {} + + JsonValueWriter::JsonValueWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } {} + + JsonObjectWriter JsonValueWriter::writeObject() && { + return JsonObjectWriter{ m_os, m_indent_level }; + } + + JsonArrayWriter JsonValueWriter::writeArray() && { + return JsonArrayWriter{ m_os, m_indent_level }; + } + + void JsonValueWriter::write( Catch::StringRef value ) && { + writeImpl( value, true ); + } + + void JsonValueWriter::write( bool value ) && { + writeImpl( value ? "true"_sr : "false"_sr, false ); + } + + void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { + if ( quote ) { m_os << '"'; } + for (char c : value) { + // Escape list taken from https://www.json.org/json-en.html, + // string definition. + // Note that while forward slash _can_ be escaped, it does + // not have to be, if JSON is not further embedded somewhere + // where forward slash is meaningful. + if ( c == '"' ) { + m_os << "\\\""; + } else if ( c == '\\' ) { + m_os << "\\\\"; + } else if ( c == '\b' ) { + m_os << "\\b"; + } else if ( c == '\f' ) { + m_os << "\\f"; + } else if ( c == '\n' ) { + m_os << "\\n"; + } else if ( c == '\r' ) { + m_os << "\\r"; + } else if ( c == '\t' ) { + m_os << "\\t"; + } else { + m_os << c; + } + } + if ( quote ) { m_os << '"'; } + } + +} // namespace Catch diff --git a/packages/Catch2/src/catch2/internal/catch_jsonwriter.hpp b/packages/Catch2/src/catch2/internal/catch_jsonwriter.hpp new file mode 100644 index 000000000..59c044e45 --- /dev/null +++ b/packages/Catch2/src/catch2/internal/catch_jsonwriter.hpp @@ -0,0 +1,120 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_JSONWRITER_HPP_INCLUDED +#define CATCH_JSONWRITER_HPP_INCLUDED + +#include <catch2/internal/catch_reusable_string_stream.hpp> +#include <catch2/internal/catch_stringref.hpp> + +#include <cstdint> +#include <sstream> + +namespace Catch { + class JsonObjectWriter; + class JsonArrayWriter; + + struct JsonUtils { + static void indent( std::ostream& os, std::uint64_t level ); + static void appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ); + }; + + class JsonValueWriter { + public: + JsonValueWriter( std::ostream& os ); + JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter writeObject() &&; + JsonArrayWriter writeArray() &&; + + template <typename T> + void write( T const& value ) && { + writeImpl( value, !std::is_arithmetic<T>::value ); + } + void write( StringRef value ) &&; + void write( bool value ) &&; + + private: + void writeImpl( StringRef value, bool quote ); + + // 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 + template <typename T, + typename = typename std::enable_if_t< + !std::is_convertible<T, StringRef>::value>> + void writeImpl( T const& value, bool quote_value ) { + m_sstream << value; + writeImpl( m_sstream.str(), quote_value ); + } + + std::ostream& m_os; + std::stringstream m_sstream; + std::uint64_t m_indent_level; + }; + + class JsonObjectWriter { + public: + JsonObjectWriter( std::ostream& os ); + JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter( JsonObjectWriter&& source ); + JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; + + ~JsonObjectWriter(); + + JsonValueWriter write( StringRef key ); + + private: + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + + class JsonArrayWriter { + public: + JsonArrayWriter( std::ostream& os ); + JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonArrayWriter( JsonArrayWriter&& source ); + JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; + + ~JsonArrayWriter(); + + JsonObjectWriter writeObject(); + JsonArrayWriter writeArray(); + + template <typename T> + JsonArrayWriter& write( T const& value ) { + return writeImpl( value ); + } + + JsonArrayWriter& write( bool value ); + + private: + template <typename T> + JsonArrayWriter& writeImpl( T const& value ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + JsonValueWriter{ m_os }.write( value ); + + return *this; + } + + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + +} // namespace Catch + +#endif // CATCH_JSONWRITER_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/internal/catch_leak_detector.cpp b/packages/Catch2/src/catch2/internal/catch_leak_detector.cpp index 7389eaf77..691bc772e 100644 --- a/packages/Catch2/src/catch2/internal/catch_leak_detector.cpp +++ b/packages/Catch2/src/catch2/internal/catch_leak_detector.cpp @@ -29,7 +29,7 @@ namespace Catch { #else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv - Catch::LeakDetector::LeakDetector() {} + Catch::LeakDetector::LeakDetector() = default; #endif // CATCH_CONFIG_WINDOWS_CRTDBG diff --git a/packages/Catch2/src/catch2/internal/catch_list.cpp b/packages/Catch2/src/catch2/internal/catch_list.cpp index 97e4c5931..5bd06a2ae 100644 --- a/packages/Catch2/src/catch2/internal/catch_list.cpp +++ b/packages/Catch2/src/catch2/internal/catch_list.cpp @@ -14,10 +14,7 @@ #include <catch2/internal/catch_reporter_registry.hpp> #include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_case_insensitive_comparisons.hpp> - -#include <catch2/internal/catch_context.hpp> #include <catch2/catch_config.hpp> -#include <catch2/catch_test_spec.hpp> #include <catch2/catch_test_case_info.hpp> namespace Catch { diff --git a/packages/Catch2/src/catch2/internal/catch_polyfills.cpp b/packages/Catch2/src/catch2/internal/catch_polyfills.cpp index 96efad5dd..776c22439 100644 --- a/packages/Catch2/src/catch2/internal/catch_polyfills.cpp +++ b/packages/Catch2/src/catch2/internal/catch_polyfills.cpp @@ -31,4 +31,12 @@ namespace Catch { } #endif +#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER ) + float nextafter( float x, float y ) { return std::nextafter( x, y ); } + double nextafter( double x, double y ) { return std::nextafter( x, y ); } +#else + float nextafter( float x, float y ) { return ::nextafterf( x, y ); } + double nextafter( double x, double y ) { return ::nextafter( x, y ); } +#endif + } // end namespace Catch diff --git a/packages/Catch2/src/catch2/internal/catch_polyfills.hpp b/packages/Catch2/src/catch2/internal/catch_polyfills.hpp index 23a9332bc..4503f8f2a 100644 --- a/packages/Catch2/src/catch2/internal/catch_polyfills.hpp +++ b/packages/Catch2/src/catch2/internal/catch_polyfills.hpp @@ -9,8 +9,13 @@ #define CATCH_POLYFILLS_HPP_INCLUDED namespace Catch { + bool isnan(float f); bool isnan(double d); + + float nextafter(float x, float y); + double nextafter(double x, double y); + } #endif // CATCH_POLYFILLS_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp b/packages/Catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp new file mode 100644 index 000000000..c59c05391 --- /dev/null +++ b/packages/Catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp @@ -0,0 +1,94 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED + +#include <catch2/internal/catch_polyfills.hpp> + +#include <cassert> +#include <cmath> +#include <cstdint> +#include <limits> +#include <type_traits> + +namespace Catch { + + namespace Detail { + /** + * Returns the largest magnitude of 1-ULP distance inside the [a, b] range. + * + * Assumes `a < b`. + */ + template <typename FloatType> + FloatType gamma(FloatType a, FloatType b) { + static_assert( std::is_floating_point<FloatType>::value, + "gamma returns the largest ULP magnitude within " + "floating point range [a, b]. This only makes sense " + "for floating point types" ); + assert( a <= b ); + + const auto gamma_up = Catch::nextafter( a, std::numeric_limits<FloatType>::infinity() ) - a; + const auto gamma_down = b - Catch::nextafter( b, -std::numeric_limits<FloatType>::infinity() ); + + return gamma_up < gamma_down ? gamma_down : gamma_up; + } + + template <typename FloatingPoint> + struct DistanceTypePicker; + template <> + struct DistanceTypePicker<float> { + using type = std::uint32_t; + }; + template <> + struct DistanceTypePicker<double> { + using type = std::uint64_t; + }; + + template <typename T> + using DistanceType = typename DistanceTypePicker<T>::type; + +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + /** + * Computes the number of equi-distant floats in [a, b] + * + * Since not every range can be split into equidistant floats + * exactly, we actually compute ceil(b/distance - a/distance), + * because in those cases we want to overcount. + * + * Uses modified Dekker's FastTwoSum algorithm to handle rounding. + */ + template <typename FloatType> + DistanceType<FloatType> + count_equidistant_floats( FloatType a, FloatType b, FloatType distance ) { + assert( a <= b ); + // We get distance as gamma for our uniform float distribution, + // so this will round perfectly. + const auto ag = a / distance; + const auto bg = b / distance; + + const auto s = bg - ag; + const auto err = ( std::fabs( a ) <= std::fabs( b ) ) + ? -ag - ( s - bg ) + : bg - ( s + ag ); + const auto ceil_s = static_cast<DistanceType<FloatType>>( std::ceil( s ) ); + + return ( ceil_s != s ) ? ceil_s : ceil_s + ( err > 0 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } + +} // end namespace Catch + +#endif // CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/internal/catch_random_integer_helpers.hpp b/packages/Catch2/src/catch2/internal/catch_random_integer_helpers.hpp new file mode 100644 index 000000000..1c450f05c --- /dev/null +++ b/packages/Catch2/src/catch2/internal/catch_random_integer_helpers.hpp @@ -0,0 +1,202 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED + +#include <climits> +#include <cstddef> +#include <cstdint> +#include <type_traits> + +namespace Catch { + namespace Detail { + + template <std::size_t> + struct SizedUnsignedType; +#define SizedUnsignedTypeHelper( TYPE ) \ + template <> \ + struct SizedUnsignedType<sizeof( TYPE )> { \ + using type = TYPE; \ + } + + SizedUnsignedTypeHelper( std::uint8_t ); + SizedUnsignedTypeHelper( std::uint16_t ); + SizedUnsignedTypeHelper( std::uint32_t ); + SizedUnsignedTypeHelper( std::uint64_t ); +#undef SizedUnsignedTypeHelper + + template <std::size_t sz> + using SizedUnsignedType_t = typename SizedUnsignedType<sz>::type; + + template <typename T> + using DoubleWidthUnsignedType_t = SizedUnsignedType_t<2 * sizeof( T )>; + + template <typename T> + struct ExtendedMultResult { + T upper; + T lower; + friend bool operator==( ExtendedMultResult const& lhs, + ExtendedMultResult const& rhs ) { + return lhs.upper == rhs.upper && lhs.lower == rhs.lower; + } + }; + + // Returns 128 bit result of multiplying lhs and rhs + constexpr ExtendedMultResult<std::uint64_t> + extendedMult( std::uint64_t lhs, std::uint64_t rhs ) { + // We use the simple long multiplication approach for + // correctness, we can use platform specific builtins + // for performance later. + + // Split the lhs and rhs into two 32bit "digits", so that we can + // do 64 bit arithmetic to handle carry bits. + // 32b 32b 32b 32b + // lhs L1 L2 + // * rhs R1 R2 + // ------------------------ + // | R2 * L2 | + // | R2 * L1 | + // | R1 * L2 | + // | R1 * L1 | + // ------------------------- + // | a | b | c | d | + +#define CarryBits( x ) ( x >> 32 ) +#define Digits( x ) ( x & 0xFF'FF'FF'FF ) + + auto r2l2 = Digits( rhs ) * Digits( lhs ); + auto r2l1 = Digits( rhs ) * CarryBits( lhs ); + auto r1l2 = CarryBits( rhs ) * Digits( lhs ); + auto r1l1 = CarryBits( rhs ) * CarryBits( lhs ); + + // Sum to columns first + auto d = Digits( r2l2 ); + auto c = CarryBits( r2l2 ) + Digits( r2l1 ) + Digits( r1l2 ); + auto b = CarryBits( r2l1 ) + CarryBits( r1l2 ) + Digits( r1l1 ); + auto a = CarryBits( r1l1 ); + + // Propagate carries between columns + c += CarryBits( d ); + b += CarryBits( c ); + a += CarryBits( b ); + + // Remove the used carries + c = Digits( c ); + b = Digits( b ); + a = Digits( a ); + +#undef CarryBits +#undef Digits + + return { + a << 32 | b, // upper 64 bits + c << 32 | d // lower 64 bits + }; + } + + template <typename UInt> + constexpr ExtendedMultResult<UInt> extendedMult( UInt lhs, UInt rhs ) { + static_assert( std::is_unsigned<UInt>::value, + "extendedMult can only handle unsigned integers" ); + static_assert( sizeof( UInt ) < sizeof( std::uint64_t ), + "Generic extendedMult can only handle types smaller " + "than uint64_t" ); + using WideType = DoubleWidthUnsignedType_t<UInt>; + + auto result = WideType( lhs ) * WideType( rhs ); + return { + static_cast<UInt>( result >> ( CHAR_BIT * sizeof( UInt ) ) ), + static_cast<UInt>( result & UInt( -1 ) ) }; + } + + + template <typename TargetType, + typename Generator> + std::enable_if_t<sizeof(typename Generator::result_type) >= sizeof(TargetType), + TargetType> fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned<TargetType>::value, "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast<gresult_type>( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + // We want to return the top bits from a generator, as they are + // usually considered higher quality. + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + + return static_cast<TargetType>( gen() >> + ( generated_bits - return_bits) ); + } + + template <typename TargetType, + typename Generator> + std::enable_if_t<sizeof(typename Generator::result_type) < sizeof(TargetType), + TargetType> fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned<TargetType>::value, + "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast<gresult_type>( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + std::size_t filled_bits = 0; + TargetType ret = 0; + do { + ret <<= generated_bits; + ret |= gen(); + filled_bits += generated_bits; + } while ( filled_bits < return_bits ); + + return ret; + } + + /* + * Transposes numbers into unsigned type while keeping their ordering + * + * This means that signed types are changed so that the ordering is + * [INT_MIN, ..., -1, 0, ..., INT_MAX], rather than order we would + * get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1]) + */ + template <typename OriginalType, typename UnsignedType> + std::enable_if_t<std::is_signed<OriginalType>::value, UnsignedType> + transposeToNaturalOrder( UnsignedType in ) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned<UnsignedType>::value, + "Input type must be unsigned" ); + // Assuming 2s complement (standardized in current C++), the + // positive and negative numbers are already internally ordered, + // and their difference is in the top bit. Swapping it orders + // them the desired way. + constexpr auto highest_bit = + UnsignedType( 1 ) << ( sizeof( UnsignedType ) * CHAR_BIT - 1 ); + return static_cast<UnsignedType>( in ^ highest_bit ); + } + + + + template <typename OriginalType, + typename UnsignedType> + std::enable_if_t<std::is_unsigned<OriginalType>::value, UnsignedType> + transposeToNaturalOrder(UnsignedType in) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned<UnsignedType>::value, "Input type must be unsigned" ); + // No reordering is needed for unsigned -> unsigned + return in; + } + } // namespace Detail +} // namespace Catch + +#endif // CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/internal/catch_random_seed_generation.cpp b/packages/Catch2/src/catch2/internal/catch_random_seed_generation.cpp index 40c468cb4..fdc3fa19e 100644 --- a/packages/Catch2/src/catch2/internal/catch_random_seed_generation.cpp +++ b/packages/Catch2/src/catch2/internal/catch_random_seed_generation.cpp @@ -9,6 +9,7 @@ #include <catch2/internal/catch_random_seed_generation.hpp> #include <catch2/internal/catch_enforce.hpp> +#include <catch2/internal/catch_random_integer_helpers.hpp> #include <ctime> #include <random> @@ -21,10 +22,10 @@ namespace Catch { 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{}() ); + case GenerateFrom::RandomDevice: { + std::random_device rd; + return Detail::fillBitsFrom<std::uint32_t>( rd ); + } default: CATCH_ERROR("Unknown generation method"); diff --git a/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp b/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp index b2b0cd074..cea8c4dc9 100644 --- a/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp +++ b/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp @@ -6,13 +6,14 @@ // SPDX-License-Identifier: BSL-1.0 -#include <catch2/internal/catch_reporter_registry.hpp> #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp> #include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_move_and_forward.hpp> +#include <catch2/internal/catch_reporter_registry.hpp> #include <catch2/reporters/catch_reporter_automake.hpp> #include <catch2/reporters/catch_reporter_compact.hpp> #include <catch2/reporters/catch_reporter_console.hpp> +#include <catch2/reporters/catch_reporter_json.hpp> #include <catch2/reporters/catch_reporter_junit.hpp> #include <catch2/reporters/catch_reporter_registrars.hpp> #include <catch2/reporters/catch_reporter_sonarqube.hpp> @@ -47,6 +48,8 @@ namespace Catch { Detail::make_unique<ReporterFactory<TeamCityReporter>>(); m_impl->factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>(); + m_impl->factories["JSON"] = + Detail::make_unique<ReporterFactory<JsonReporter>>(); } ReporterRegistry::~ReporterRegistry() = default; diff --git a/packages/Catch2/src/catch2/internal/catch_reporter_spec_parser.hpp b/packages/Catch2/src/catch2/internal/catch_reporter_spec_parser.hpp index d446ce98b..9f447ee2f 100644 --- a/packages/Catch2/src/catch2/internal/catch_reporter_spec_parser.hpp +++ b/packages/Catch2/src/catch2/internal/catch_reporter_spec_parser.hpp @@ -8,7 +8,7 @@ #ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED #define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED -#include <catch2/internal/catch_console_colour.hpp> +#include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/internal/catch_optional.hpp> #include <catch2/internal/catch_stringref.hpp> diff --git a/packages/Catch2/src/catch2/internal/catch_run_context.cpp b/packages/Catch2/src/catch2/internal/catch_run_context.cpp index 6f15cfb1a..77b476d82 100644 --- a/packages/Catch2/src/catch2/internal/catch_run_context.cpp +++ b/packages/Catch2/src/catch2/internal/catch_run_context.cpp @@ -20,6 +20,7 @@ #include <catch2/internal/catch_output_redirect.hpp> #include <catch2/internal/catch_assertion_handler.hpp> #include <catch2/internal/catch_test_failure_exception.hpp> +#include <catch2/internal/catch_result_type.hpp> #include <cassert> #include <algorithm> @@ -37,7 +38,6 @@ namespace Catch { TrackerContext& ctx, ITracker* parent ): TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} - ~GeneratorTracker() override = default; static GeneratorTracker* acquire( TrackerContext& ctx, @@ -293,13 +293,14 @@ namespace Catch { m_messageScopes.clear(); } - // Reset working state - resetAssertionInfo(); + // Reset working state. assertion info will be reset after + // populateReaction is run if it is needed m_lastResult = CATCH_MOVE( result ); } void RunContext::resetAssertionInfo() { m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal; } void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { @@ -447,6 +448,7 @@ namespace Catch { AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); assertionEnded(CATCH_MOVE(result) ); + resetAssertionInfo(); handleUnfinishedSections(); @@ -583,6 +585,7 @@ namespace Catch { reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); populateReaction( reaction ); } + resetAssertionInfo(); } void RunContext::reportExpr( AssertionInfo const &info, @@ -621,6 +624,7 @@ namespace Catch { // considered "OK" reaction.shouldSkip = true; } + resetAssertionInfo(); } void RunContext::handleUnexpectedExceptionNotThrown( AssertionInfo const& info, @@ -641,6 +645,7 @@ namespace Catch { AssertionResult assertionResult{ info, CATCH_MOVE(data) }; assertionEnded( CATCH_MOVE(assertionResult) ); populateReaction( reaction ); + resetAssertionInfo(); } void RunContext::populateReaction( AssertionReaction& reaction ) { @@ -658,6 +663,7 @@ namespace Catch { data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionEnded( CATCH_MOVE(assertionResult) ); + resetAssertionInfo(); } void RunContext::handleNonExpr( AssertionInfo const &info, @@ -672,6 +678,7 @@ namespace Catch { const auto isOk = assertionResult.isOk(); assertionEnded( CATCH_MOVE(assertionResult) ); if ( !isOk ) { populateReaction( reaction ); } + resetAssertionInfo(); } diff --git a/packages/Catch2/src/catch2/internal/catch_section.cpp b/packages/Catch2/src/catch2/internal/catch_section.cpp index 061732b1d..677c2164c 100644 --- a/packages/Catch2/src/catch2/internal/catch_section.cpp +++ b/packages/Catch2/src/catch2/internal/catch_section.cpp @@ -6,7 +6,7 @@ // SPDX-License-Identifier: BSL-1.0 #include <catch2/internal/catch_section.hpp> -#include <catch2/internal/catch_run_context.hpp> +#include <catch2/interfaces/catch_interfaces_capture.hpp> #include <catch2/internal/catch_uncaught_exceptions.hpp> #include <catch2/internal/catch_move_and_forward.hpp> diff --git a/packages/Catch2/src/catch2/internal/catch_section.hpp b/packages/Catch2/src/catch2/internal/catch_section.hpp index bd92bdf46..8c894eeb8 100644 --- a/packages/Catch2/src/catch2/internal/catch_section.hpp +++ b/packages/Catch2/src/catch2/internal/catch_section.hpp @@ -78,7 +78,7 @@ namespace Catch { CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ - if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ catchInternalSectionHint, \ catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ catchInternalPreviousSectionHint == __LINE__ ) \ @@ -88,7 +88,7 @@ namespace Catch { CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ - if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ catchInternalSectionHint, \ catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ catchInternalPreviousSectionHint == __LINE__ ) \ diff --git a/packages/Catch2/src/catch2/internal/catch_sharding.hpp b/packages/Catch2/src/catch2/internal/catch_sharding.hpp index d0e4cfa13..22561f4bf 100644 --- a/packages/Catch2/src/catch2/internal/catch_sharding.hpp +++ b/packages/Catch2/src/catch2/internal/catch_sharding.hpp @@ -8,8 +8,7 @@ #ifndef CATCH_SHARDING_HPP_INCLUDED #define CATCH_SHARDING_HPP_INCLUDED -#include <catch2/catch_session.hpp> - +#include <cassert> #include <cmath> #include <algorithm> diff --git a/packages/Catch2/src/catch2/internal/catch_stringref.hpp b/packages/Catch2/src/catch2/internal/catch_stringref.hpp index 99bb9a986..4b9212bfa 100644 --- a/packages/Catch2/src/catch2/internal/catch_stringref.hpp +++ b/packages/Catch2/src/catch2/internal/catch_stringref.hpp @@ -25,6 +25,8 @@ namespace Catch { using size_type = std::size_t; using const_iterator = const char*; + static constexpr size_type npos{ static_cast<size_type>( -1 ) }; + private: static constexpr char const* const s_empty = ""; @@ -75,7 +77,7 @@ namespace Catch { } // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, start + size()). + // If start + length > size(), then the substring is [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) { diff --git a/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp b/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp index b7c6b9ec7..510df167f 100644 --- a/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp +++ b/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp @@ -6,14 +6,13 @@ // SPDX-License-Identifier: BSL-1.0 #include <catch2/internal/catch_tag_alias_registry.hpp> -#include <catch2/internal/catch_console_colour.hpp> #include <catch2/internal/catch_enforce.hpp> #include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_string_manip.hpp> namespace Catch { - TagAliasRegistry::~TagAliasRegistry() {} + TagAliasRegistry::~TagAliasRegistry() = default; TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); diff --git a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp index f1702979e..c2b052daf 100644 --- a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp +++ b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp @@ -7,12 +7,9 @@ // SPDX-License-Identifier: BSL-1.0 #include <catch2/internal/catch_test_case_registry_impl.hpp> -#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_enforce.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/interfaces/catch_interfaces_registry_hub.hpp> -#include <catch2/internal/catch_random_number_generator.hpp> -#include <catch2/internal/catch_run_context.hpp> #include <catch2/internal/catch_sharding.hpp> #include <catch2/catch_test_case_info.hpp> #include <catch2/catch_test_spec.hpp> @@ -73,7 +70,6 @@ namespace Catch { return sorted; } case TestRunOrder::Randomized: { - seedRng(config); using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>; TestCaseInfoHasher h{ config.rngSeed() }; diff --git a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp index a4a27ed12..99a38498f 100644 --- a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp +++ b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp @@ -30,8 +30,6 @@ namespace Catch { 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; diff --git a/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp b/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp index 39366023f..0d95650fb 100644 --- a/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp +++ b/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp @@ -34,7 +34,7 @@ #else // CATCH_CONFIG_FAST_COMPILE #define INTERNAL_CATCH_TRY try -#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } #endif @@ -49,7 +49,7 @@ INTERNAL_CATCH_TRY { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ diff --git a/packages/Catch2/src/catch2/internal/catch_test_registry.hpp b/packages/Catch2/src/catch2/internal/catch_test_registry.hpp index d248d3cf9..7766fe111 100644 --- a/packages/Catch2/src/catch2/internal/catch_test_registry.hpp +++ b/packages/Catch2/src/catch2/internal/catch_test_registry.hpp @@ -113,7 +113,7 @@ static int catchInternalSectionHint = 0; CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ - dummyUser )( &fname ); \ + dummyUser )( &(fname) ); \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION diff --git a/packages/Catch2/src/catch2/internal/catch_textflow.cpp b/packages/Catch2/src/catch2/internal/catch_textflow.cpp index 7eac97325..857fd2b9f 100644 --- a/packages/Catch2/src/catch2/internal/catch_textflow.cpp +++ b/packages/Catch2/src/catch2/internal/catch_textflow.cpp @@ -233,23 +233,36 @@ namespace Catch { return os; } - Columns Column::operator+( Column const& other ) { + Columns operator+(Column const& lhs, Column const& rhs) { Columns cols; - cols += *this; - cols += other; + cols += lhs; + cols += rhs; return cols; } - - Columns& Columns::operator+=( Column const& col ) { - m_columns.push_back( col ); - return *this; + Columns operator+(Column&& lhs, Column&& rhs) { + Columns cols; + cols += CATCH_MOVE( lhs ); + cols += CATCH_MOVE( rhs ); + return cols; } - Columns Columns::operator+( Column const& col ) { - Columns combined = *this; - combined += col; + Columns& operator+=(Columns& lhs, Column const& rhs) { + lhs.m_columns.push_back( rhs ); + return lhs; + } + Columns& operator+=(Columns& lhs, Column&& rhs) { + lhs.m_columns.push_back( CATCH_MOVE(rhs) ); + return lhs; + } + Columns operator+( Columns const& lhs, Column const& rhs ) { + auto combined( lhs ); + combined += rhs; return combined; } + Columns operator+( Columns&& lhs, Column&& rhs ) { + lhs += CATCH_MOVE( rhs ); + return CATCH_MOVE( lhs ); + } } // namespace TextFlow } // namespace Catch diff --git a/packages/Catch2/src/catch2/internal/catch_textflow.hpp b/packages/Catch2/src/catch2/internal/catch_textflow.hpp index 0776ab922..a78451d55 100644 --- a/packages/Catch2/src/catch2/internal/catch_textflow.hpp +++ b/packages/Catch2/src/catch2/internal/catch_textflow.hpp @@ -8,8 +8,10 @@ #ifndef CATCH_TEXTFLOW_HPP_INCLUDED #define CATCH_TEXTFLOW_HPP_INCLUDED -#include <cassert> #include <catch2/internal/catch_console_width.hpp> +#include <catch2/internal/catch_move_and_forward.hpp> + +#include <cassert> #include <string> #include <vector> @@ -37,7 +39,7 @@ namespace Catch { public: /** - * Iterates "lines" in `Column` and return sthem + * Iterates "lines" in `Column` and returns them */ class const_iterator { friend Column; @@ -91,20 +93,35 @@ namespace Catch { using iterator = const_iterator; explicit Column( std::string const& text ): m_string( text ) {} + explicit Column( std::string&& text ): + m_string( CATCH_MOVE(text)) {} - Column& width( size_t newWidth ) { + Column& width( size_t newWidth ) & { assert( newWidth > 0 ); m_width = newWidth; return *this; } - Column& indent( size_t newIndent ) { + Column&& width( size_t newWidth ) && { + assert( newWidth > 0 ); + m_width = newWidth; + return CATCH_MOVE( *this ); + } + Column& indent( size_t newIndent ) & { m_indent = newIndent; return *this; } - Column& initialIndent( size_t newIndent ) { + Column&& indent( size_t newIndent ) && { + m_indent = newIndent; + return CATCH_MOVE( *this ); + } + Column& initialIndent( size_t newIndent ) & { m_initialIndent = newIndent; return *this; } + Column&& initialIndent( size_t newIndent ) && { + m_initialIndent = newIndent; + return CATCH_MOVE( *this ); + } size_t width() const { return m_width; } const_iterator begin() const { return const_iterator( *this ); } @@ -113,7 +130,8 @@ namespace Catch { friend std::ostream& operator<<( std::ostream& os, Column const& col ); - Columns operator+( Column const& other ); + friend Columns operator+( Column const& lhs, Column const& rhs ); + friend Columns operator+( Column&& lhs, Column&& rhs ); }; //! Creates a column that serves as an empty space of specific width @@ -157,8 +175,10 @@ namespace Catch { iterator begin() const { return iterator( *this ); } iterator end() const { return { *this, iterator::EndTag() }; } - Columns& operator+=( Column const& col ); - Columns operator+( Column const& col ); + friend Columns& operator+=( Columns& lhs, Column const& rhs ); + friend Columns& operator+=( Columns& lhs, Column&& rhs ); + friend Columns operator+( Columns const& lhs, Column const& rhs ); + friend Columns operator+( Columns&& lhs, Column&& rhs ); friend std::ostream& operator<<( std::ostream& os, Columns const& cols ); diff --git a/packages/Catch2/src/catch2/internal/catch_uncaught_exceptions.cpp b/packages/Catch2/src/catch2/internal/catch_uncaught_exceptions.cpp index 704d6e1ca..8cfabc0f8 100644 --- a/packages/Catch2/src/catch2/internal/catch_uncaught_exceptions.cpp +++ b/packages/Catch2/src/catch2/internal/catch_uncaught_exceptions.cpp @@ -7,7 +7,6 @@ // SPDX-License-Identifier: BSL-1.0 #include <catch2/internal/catch_uncaught_exceptions.hpp> -#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_config_uncaught_exceptions.hpp> #include <catch2/catch_user_config.hpp> diff --git a/packages/Catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp b/packages/Catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp new file mode 100644 index 000000000..23d03b43c --- /dev/null +++ b/packages/Catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp @@ -0,0 +1,131 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED + +#include <catch2/internal/catch_random_floating_point_helpers.hpp> +#include <catch2/internal/catch_uniform_integer_distribution.hpp> + +#include <cmath> +#include <type_traits> + +namespace Catch { + + namespace Detail { +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + // The issue with overflow only happens with maximal ULP and HUGE + // distance, e.g. when generating numbers in [-inf, inf] for given + // type. So we only check for the largest possible ULP in the + // type, and return something that does not overflow to inf in 1 mult. + constexpr std::uint64_t calculate_max_steps_in_one_go(double gamma) { + if ( gamma == 1.99584030953472e+292 ) { return 9007199254740991; } + return static_cast<std::uint64_t>( -1 ); + } + constexpr std::uint32_t calculate_max_steps_in_one_go(float gamma) { + if ( gamma == 2.028241e+31f ) { return 16777215; } + return static_cast<std::uint32_t>( -1 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + } + +/** + * Implementation of uniform distribution on floating point numbers. + * + * Note that we support only `float` and `double` types, because these + * usually mean the same thing across different platform. `long double` + * varies wildly by platform and thus we cannot provide reproducible + * implementation. Also note that we don't implement all parts of + * distribution per standard: this distribution is not serializable, nor + * can the range be arbitrarily reset. + * + * The implementation also uses different approach than the one taken by + * `std::uniform_real_distribution`, where instead of generating a number + * between [0, 1) and then multiplying the range bounds with it, we first + * split the [a, b] range into a set of equidistributed floating point + * numbers, and then use uniform int distribution to pick which one to + * return. + * + * This has the advantage of guaranteeing uniformity (the multiplication + * method loses uniformity due to rounding when multiplying floats), except + * for small non-uniformity at one side of the interval, where we have + * to deal with the fact that not every interval is splittable into + * equidistributed floats. + * + * Based on "Drawing random floating-point numbers from an interval" by + * Frederic Goualard. + */ +template <typename FloatType> +class uniform_floating_point_distribution { + static_assert(std::is_floating_point<FloatType>::value, "..."); + static_assert(!std::is_same<FloatType, long double>::value, + "We do not support long double due to inconsistent behaviour between platforms"); + + using WidthType = Detail::DistanceType<FloatType>; + + FloatType m_a, m_b; + FloatType m_ulp_magnitude; + WidthType m_floats_in_range; + uniform_integer_distribution<WidthType> m_int_dist; + + // In specific cases, we can overflow into `inf` when computing the + // `steps * g` offset. To avoid this, we don't offset by more than this + // in one multiply + addition. + WidthType m_max_steps_in_one_go; + // We don't want to do the magnitude check every call to `operator()` + bool m_a_has_leq_magnitude; + +public: + using result_type = FloatType; + + uniform_floating_point_distribution( FloatType a, FloatType b ): + m_a( a ), + m_b( b ), + m_ulp_magnitude( Detail::gamma( m_a, m_b ) ), + m_floats_in_range( Detail::count_equidistant_floats( m_a, m_b, m_ulp_magnitude ) ), + m_int_dist(0, m_floats_in_range), + m_max_steps_in_one_go( Detail::calculate_max_steps_in_one_go(m_ulp_magnitude)), + m_a_has_leq_magnitude(std::fabs(m_a) <= std::fabs(m_b)) + { + assert( a <= b ); + } + + template <typename Generator> + result_type operator()( Generator& g ) { + WidthType steps = m_int_dist( g ); + if ( m_a_has_leq_magnitude ) { + if ( steps == m_floats_in_range ) { return m_a; } + auto b = m_b; + while (steps > m_max_steps_in_one_go) { + b -= m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return b - steps * m_ulp_magnitude; + } else { + if ( steps == m_floats_in_range ) { return m_b; } + auto a = m_a; + while (steps > m_max_steps_in_one_go) { + a += m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return a + steps * m_ulp_magnitude; + } + } + + result_type a() const { return m_a; } + result_type b() const { return m_b; } +}; + +} // end namespace Catch + +#endif // CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp b/packages/Catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp new file mode 100644 index 000000000..afa2015d9 --- /dev/null +++ b/packages/Catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp @@ -0,0 +1,124 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED + +#include <catch2/internal/catch_random_integer_helpers.hpp> + +namespace Catch { + + namespace Detail { + // Indirection to enable make_unsigned<bool> behaviour. + template <typename T> + struct make_unsigned { + using type = std::make_unsigned_t<T>; + }; + + template <> + struct make_unsigned<bool> { + using type = uint8_t; + }; + + template <typename T> + using make_unsigned_t = typename make_unsigned<T>::type; + } + +/** + * Implementation of uniform distribution on integers. + * + * Unlike `std::uniform_int_distribution`, this implementation supports + * various 1 byte integral types, including bool (but you should not + * actually use it for bools). + * + * The underlying algorithm is based on the one described in "Fast Random + * Integer Generation in an Interval" by Daniel Lemire, but has been + * optimized under the assumption of reuse of the same distribution object. + */ +template <typename IntegerType> +class uniform_integer_distribution { + static_assert(std::is_integral<IntegerType>::value, "..."); + + using UnsignedIntegerType = Detail::make_unsigned_t<IntegerType>; + + // Only the left bound is stored, and we store it converted to its + // unsigned image. This avoids having to do the conversions inside + // the operator(), at the cost of having to do the conversion in + // the a() getter. The right bound is only needed in the b() getter, + // so we recompute it there from other stored data. + UnsignedIntegerType m_a; + + // How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type. + UnsignedIntegerType m_ab_distance; + + // We hoisted this out of the main generation function. Technically, + // this means that using this distribution will be slower than Lemire's + // algorithm if this distribution instance will be used only few times, + // but it will be faster if it is used many times. Since Catch2 uses + // distributions only to implement random generators, we assume that each + // distribution will be reused many times and this is an optimization. + UnsignedIntegerType m_rejection_threshold = 0; + + UnsignedIntegerType computeDistance(IntegerType a, IntegerType b) const { + // This overflows and returns 0 if a == 0 and b == TYPE_MAX. + // We handle that later when generating the number. + return transposeTo(b) - transposeTo(a) + 1; + } + + static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) { + // distance == 0 means that we will return all possible values from + // the type's range, and that we shouldn't reject anything. + if ( ab_distance == 0 ) { return 0; } + return ( ~ab_distance + 1 ) % ab_distance; + } + + static UnsignedIntegerType transposeTo(IntegerType in) { + return Detail::transposeToNaturalOrder<IntegerType>( + static_cast<UnsignedIntegerType>( in ) ); + } + static IntegerType transposeBack(UnsignedIntegerType in) { + return static_cast<IntegerType>( + Detail::transposeToNaturalOrder<IntegerType>(in) ); + } + +public: + using result_type = IntegerType; + + uniform_integer_distribution( IntegerType a, IntegerType b ): + m_a( transposeTo(a) ), + m_ab_distance( computeDistance(a, b) ), + m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) { + assert( a <= b ); + } + + template <typename Generator> + result_type operator()( Generator& g ) { + // All possible values of result_type are valid. + if ( m_ab_distance == 0 ) { + return transposeBack( Detail::fillBitsFrom<UnsignedIntegerType>( g ) ); + } + + auto random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g ); + auto emul = Detail::extendedMult( random_number, m_ab_distance ); + // Unlike Lemire's algorithm we skip the ab_distance check, since + // we precomputed the rejection threshold, which is always tighter. + while (emul.lower < m_rejection_threshold) { + random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g ); + emul = Detail::extendedMult( random_number, m_ab_distance ); + } + + return transposeBack(m_a + emul.upper); + } + + result_type a() const { return transposeBack(m_a); } + result_type b() const { return transposeBack(m_ab_distance + m_a - 1); } +}; + +} // end namespace Catch + +#endif // CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp b/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp index 6e596466e..206332ef7 100644 --- a/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp +++ b/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp @@ -38,26 +38,11 @@ namespace { 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; } diff --git a/packages/Catch2/src/catch2/meson.build b/packages/Catch2/src/catch2/meson.build index 2e9469d84..cc45e6419 100644 --- a/packages/Catch2/src/catch2/meson.build +++ b/packages/Catch2/src/catch2/meson.build @@ -18,6 +18,8 @@ configure_file( configuration: conf_data, ) +fs = import('fs') + benchmark_headers = [ 'benchmark/catch_benchmark.hpp', 'benchmark/catch_benchmark_all.hpp', @@ -45,6 +47,7 @@ benchmark_headers = [ benchmark_sources = files( 'benchmark/catch_chronometer.cpp', + 'benchmark/detail/catch_analyse.cpp', 'benchmark/detail/catch_benchmark_function.cpp', 'benchmark/detail/catch_run_for_at_least.cpp', 'benchmark/detail/catch_stats.cpp', @@ -97,6 +100,7 @@ internal_headers = [ 'internal/catch_getenv.hpp', 'internal/catch_istream.hpp', 'internal/catch_is_permutation.hpp', + 'internal/catch_jsonwriter.hpp', 'internal/catch_lazy_expr.hpp', 'internal/catch_leak_detector.hpp', 'internal/catch_list.hpp', @@ -113,6 +117,8 @@ internal_headers = [ 'internal/catch_preprocessor.hpp', 'internal/catch_preprocessor_internal_stringify.hpp', 'internal/catch_preprocessor_remove_parens.hpp', + 'internal/catch_random_floating_point_helpers.hpp', + 'internal/catch_random_integer_helpers.hpp', 'internal/catch_random_number_generator.hpp', 'internal/catch_random_seed_generation.hpp', 'internal/catch_reporter_registry.hpp', @@ -142,6 +148,8 @@ internal_headers = [ 'internal/catch_textflow.hpp', 'internal/catch_to_string.hpp', 'internal/catch_uncaught_exceptions.hpp', + 'internal/catch_uniform_floating_point_distribution.hpp', + 'internal/catch_uniform_integer_distribution.hpp', 'internal/catch_unique_name.hpp', 'internal/catch_unique_ptr.hpp', 'internal/catch_void_type.hpp', @@ -156,6 +164,7 @@ internal_headers = [ 'matchers/catch_matchers_floating_point.hpp', 'matchers/catch_matchers_predicate.hpp', 'matchers/catch_matchers_quantifiers.hpp', + 'matchers/catch_matchers_range_equals.hpp', 'matchers/catch_matchers_string.hpp', 'matchers/catch_matchers_templated.hpp', 'matchers/catch_matchers_vector.hpp', @@ -212,6 +221,7 @@ internal_sources = files( 'internal/catch_floating_point_helpers.cpp', 'internal/catch_getenv.cpp', 'internal/catch_istream.cpp', + 'internal/catch_jsonwriter.cpp', 'internal/catch_lazy_expr.cpp', 'internal/catch_leak_detector.cpp', 'internal/catch_list.cpp', @@ -278,6 +288,7 @@ reporter_headers = [ 'reporters/catch_reporter_cumulative_base.hpp', 'reporters/catch_reporter_event_listener.hpp', 'reporters/catch_reporter_helpers.hpp', + 'reporters/catch_reporter_json.hpp', 'reporters/catch_reporter_junit.hpp', 'reporters/catch_reporter_multi.hpp', 'reporters/catch_reporter_registrars.hpp', @@ -297,6 +308,7 @@ reporter_sources = files( 'reporters/catch_reporter_cumulative_base.cpp', 'reporters/catch_reporter_event_listener.cpp', 'reporters/catch_reporter_helpers.cpp', + 'reporters/catch_reporter_json.cpp', 'reporters/catch_reporter_junit.cpp', 'reporters/catch_reporter_multi.cpp', 'reporters/catch_reporter_registrars.cpp', @@ -330,9 +342,19 @@ foreach file : headers install_headers(file, subdir: join_paths(include_subdir, folder)) endforeach +catch2_dependencies = [] +# Check if this is an Android NDK build. +if ((host_machine.system() == 'android') or + # Check if this is an Android Termux build. + (host_machine.system() == 'linux' and fs.is_dir('/data/data/com.termux'))) + log_dep = meson.get_compiler('cpp').find_library('log') + catch2_dependencies += log_dep +endif + catch2 = static_library( 'Catch2', sources, + dependencies: catch2_dependencies, include_directories: '..', install: true, ) diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_automake.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_automake.cpp index 993b594b8..5e506a6bc 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_automake.cpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_automake.cpp @@ -12,7 +12,7 @@ namespace Catch { - AutomakeReporter::~AutomakeReporter() {} + AutomakeReporter::~AutomakeReporter() = default; void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp index 88acb6a46..0f855944e 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp @@ -249,6 +249,6 @@ private: StreamingReporterBase::testRunEnded( _testRunStats ); } - CompactReporter::~CompactReporter() {} + CompactReporter::~CompactReporter() = default; } // end namespace Catch diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp index a46b22cf0..f3b8b5b14 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp @@ -209,15 +209,9 @@ findMax( std::size_t& i, std::size_t& j, std::size_t& k, std::size_t& l ) { return l; } -enum class Justification { Left, Right }; - -struct ColumnInfo { - std::string name; - std::size_t width; - Justification justification; -}; struct ColumnBreak {}; struct RowBreak {}; +struct OutputFlush {}; class Duration { enum class Unit { @@ -292,6 +286,14 @@ public: }; } // end anon namespace +enum class Justification { Left, Right }; + +struct ColumnInfo { + std::string name; + std::size_t width; + Justification justification; +}; + class TablePrinter { std::ostream& m_os; std::vector<ColumnInfo> m_columnInfos; @@ -314,11 +316,10 @@ public: *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; + headerCols += TextFlow::Spacer( 2 ); } m_os << headerCols << '\n'; @@ -334,12 +335,12 @@ public: } template<typename T> - friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + friend TablePrinter& operator<< (TablePrinter& tp, T const& value) { tp.m_oss << value; return tp; } - friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) { auto colStr = tp.m_oss.str(); const auto strSize = colStr.size(); tp.m_oss.str(""); @@ -361,13 +362,18 @@ public: return tp; } - friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, RowBreak) { if (tp.m_currentColumn > 0) { tp.m_os << '\n'; tp.m_currentColumn = -1; } return tp; } + + friend TablePrinter& operator<<(TablePrinter& tp, OutputFlush) { + tp.m_os << std::flush; + return tp; + } }; ConsoleReporter::ConsoleReporter(ReporterConfig&& config): @@ -389,7 +395,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config): { "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 } + { "est run time high mean high std dev", 14, Justification::Right } }; } }())) {} @@ -473,8 +479,11 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { (*m_tablePrinter) << info.samples << ColumnBreak() << info.iterations << ColumnBreak(); - if (!m_config->benchmarkNoAnalysis()) - (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); + if ( !m_config->benchmarkNoAnalysis() ) { + ( *m_tablePrinter ) + << Duration( info.estimatedDuration ) << ColumnBreak(); + } + ( *m_tablePrinter ) << OutputFlush{}; } void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { if (m_config->benchmarkNoAnalysis()) diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_json.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_json.cpp new file mode 100644 index 000000000..1f0db8b0d --- /dev/null +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_json.cpp @@ -0,0 +1,372 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +// +#include <catch2/catch_test_case_info.hpp> +#include <catch2/catch_test_spec.hpp> +#include <catch2/catch_version.hpp> +#include <catch2/interfaces/catch_interfaces_config.hpp> +#include <catch2/internal/catch_list.hpp> +#include <catch2/internal/catch_string_manip.hpp> +#include <catch2/reporters/catch_reporter_json.hpp> + +namespace Catch { + namespace { + void writeSourceInfo( JsonObjectWriter& writer, + SourceLineInfo const& sourceInfo ) { + auto source_location_writer = + writer.write( "source-location"_sr ).writeObject(); + source_location_writer.write( "filename"_sr ) + .write( sourceInfo.file ); + source_location_writer.write( "line"_sr ).write( sourceInfo.line ); + } + + void writeTags( JsonArrayWriter writer, std::vector<Tag> const& tags ) { + for ( auto const& tag : tags ) { + writer.write( tag.original ); + } + } + + void writeProperties( JsonArrayWriter writer, + TestCaseInfo const& info ) { + if ( info.isHidden() ) { writer.write( "is-hidden"_sr ); } + if ( info.okToFail() ) { writer.write( "ok-to-fail"_sr ); } + if ( info.expectedToFail() ) { + writer.write( "expected-to-fail"_sr ); + } + if ( info.throws() ) { writer.write( "throws"_sr ); } + } + + } // namespace + + JsonReporter::JsonReporter( ReporterConfig&& config ): + StreamingReporterBase{ CATCH_MOVE( config ) } { + + m_preferences.shouldRedirectStdOut = true; + // TBD: Do we want to report all assertions? XML reporter does + // not, but for machine-parseable reporters I think the answer + // should be yes. + m_preferences.shouldReportAllAssertions = true; + + m_objectWriters.emplace( m_stream ); + m_writers.emplace( Writer::Object ); + auto& writer = m_objectWriters.top(); + + writer.write( "version"_sr ).write( 1 ); + + { + auto metadata_writer = writer.write( "metadata"_sr ).writeObject(); + metadata_writer.write( "name"_sr ).write( m_config->name() ); + metadata_writer.write( "rng-seed"_sr ).write( m_config->rngSeed() ); + metadata_writer.write( "catch2-version"_sr ) + .write( libraryVersion() ); + if ( m_config->testSpec().hasFilters() ) { + metadata_writer.write( "filters"_sr ) + .write( m_config->testSpec() ); + } + } + } + + JsonReporter::~JsonReporter() { + endListing(); + // TODO: Ensure this closes the top level object, add asserts + assert( m_writers.size() == 1 && "Only the top level object should be open" ); + assert( m_writers.top() == Writer::Object ); + endObject(); + m_stream << '\n' << std::flush; + assert( m_writers.empty() ); + } + + JsonArrayWriter& JsonReporter::startArray() { + m_arrayWriters.emplace( m_arrayWriters.top().writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + JsonArrayWriter& JsonReporter::startArray( StringRef key ) { + m_arrayWriters.emplace( + m_objectWriters.top().write( key ).writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + + JsonObjectWriter& JsonReporter::startObject() { + m_objectWriters.emplace( m_arrayWriters.top().writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + JsonObjectWriter& JsonReporter::startObject( StringRef key ) { + m_objectWriters.emplace( + m_objectWriters.top().write( key ).writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + + void JsonReporter::endObject() { + assert( isInside( Writer::Object ) ); + m_objectWriters.pop(); + m_writers.pop(); + } + void JsonReporter::endArray() { + assert( isInside( Writer::Array ) ); + m_arrayWriters.pop(); + m_writers.pop(); + } + + bool JsonReporter::isInside( Writer writer ) { + return !m_writers.empty() && m_writers.top() == writer; + } + + void JsonReporter::startListing() { + if ( !m_startedListing ) { startObject( "listings"_sr ); } + m_startedListing = true; + } + void JsonReporter::endListing() { + if ( m_startedListing ) { endObject(); } + m_startedListing = false; + } + + std::string JsonReporter::getDescription() { + return "Outputs listings as JSON. Test listing is Work-in-Progress!"; + } + + void JsonReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + endListing(); + + assert( isInside( Writer::Object ) ); + startObject( "test-run"_sr ); + startArray( "test-cases"_sr ); + } + + static void writeCounts( JsonObjectWriter&& writer, Counts const& counts ) { + writer.write( "passed"_sr ).write( counts.passed ); + writer.write( "failed"_sr ).write( counts.failed ); + writer.write( "fail-but-ok"_sr ).write( counts.failedButOk ); + writer.write( "skipped"_sr ).write( counts.skipped ); + } + + void JsonReporter::testRunEnded(TestRunStats const& runStats) { + assert( isInside( Writer::Array ) ); + // End "test-cases" + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + runStats.totals.assertions ); + writeCounts( totals.write( "test-cases"_sr ).writeObject(), + runStats.totals.testCases ); + } + + // End the "test-run" object + endObject(); + } + + void JsonReporter::testCaseStarting( TestCaseInfo const& tcInfo ) { + StreamingReporterBase::testCaseStarting( tcInfo ); + + assert( isInside( Writer::Array ) && + "We should be in the 'test-cases' array" ); + startObject(); + // "test-info" prelude + { + auto testInfo = + m_objectWriters.top().write( "test-info"_sr ).writeObject(); + // TODO: handle testName vs className!! + testInfo.write( "name"_sr ).write( tcInfo.name ); + writeSourceInfo(testInfo, tcInfo.lineInfo); + writeTags( testInfo.write( "tags"_sr ).writeArray(), tcInfo.tags ); + writeProperties( testInfo.write( "properties"_sr ).writeArray(), + tcInfo ); + } + + + // Start the array for individual test runs (testCasePartial pairs) + startArray( "runs"_sr ); + } + + void JsonReporter::testCaseEnded( TestCaseStats const& tcStats ) { + StreamingReporterBase::testCaseEnded( tcStats ); + + // We need to close the 'runs' array before finishing the test case + assert( isInside( Writer::Array ) ); + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in partial result? + } + // We do not write out stderr/stdout, because we instead wrote those out in partial runs + + // TODO: aborting? + + // And we also close this test case's object + assert( isInside( Writer::Object ) ); + endObject(); + } + + void JsonReporter::testCasePartialStarting( TestCaseInfo const& /*tcInfo*/, + uint64_t index ) { + startObject(); + m_objectWriters.top().write( "run-idx"_sr ).write( index ); + startArray( "path"_sr ); + // TODO: we want to delay most of the printing to the 'root' section + // TODO: childSection key name? + } + + void JsonReporter::testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t /*index*/ ) { + // Fixme: the top level section handles this. + //// path object + endArray(); + if ( !tcStats.stdOut.empty() ) { + m_objectWriters.top() + .write( "captured-stdout"_sr ) + .write( tcStats.stdOut ); + } + if ( !tcStats.stdErr.empty() ) { + m_objectWriters.top() + .write( "captured-stderr"_sr ) + .write( tcStats.stdErr ); + } + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will + // always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in + // partial result? + } + // TODO: aborting? + // run object + endObject(); + } + + void JsonReporter::sectionStarting( SectionInfo const& sectionInfo ) { + assert( isInside( Writer::Array ) && + "Section should always start inside an object" ); + // We want to nest top level sections, even though it shares name + // and source loc with the TEST_CASE + auto& sectionObject = startObject(); + sectionObject.write( "kind"_sr ).write( "section"_sr ); + sectionObject.write( "name"_sr ).write( sectionInfo.name ); + writeSourceInfo( m_objectWriters.top(), sectionInfo.lineInfo ); + + + // TBD: Do we want to create this event lazily? It would become + // rather complex, but we could do it, and it would look + // better for empty sections. OTOH, empty sections should + // be rare. + startArray( "path"_sr ); + } + void JsonReporter::sectionEnded( SectionStats const& /*sectionStats */) { + // End the subpath array + endArray(); + // TODO: metadata + // TODO: what info do we have here? + + // End the section object + endObject(); + } + + void JsonReporter::assertionStarting( AssertionInfo const& /*assertionInfo*/ ) {} + void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) { + // TODO: There is lot of different things to handle here, but + // we can fill it in later, after we show that the basic + // outline and streaming reporter impl works well enough. + //if ( !m_config->includeSuccessfulResults() + // && assertionStats.assertionResult.isOk() ) { + // return; + //} + assert( isInside( Writer::Array ) ); + auto assertionObject = m_arrayWriters.top().writeObject(); + + assertionObject.write( "kind"_sr ).write( "assertion"_sr ); + writeSourceInfo( assertionObject, + assertionStats.assertionResult.getSourceInfo() ); + assertionObject.write( "status"_sr ) + .write( assertionStats.assertionResult.isOk() ); + // TODO: handling of result. + // TODO: messages + // TODO: totals? + } + + + void JsonReporter::benchmarkPreparing( StringRef name ) { (void)name; } + void JsonReporter::benchmarkStarting( BenchmarkInfo const& ) {} + void JsonReporter::benchmarkEnded( BenchmarkStats<> const& ) {} + void JsonReporter::benchmarkFailed( StringRef error ) { (void)error; } + + void JsonReporter::listReporters( + std::vector<ReporterDescription> const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "reporters"_sr ).writeArray(); + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listListeners( + std::vector<ListenerDescription> const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "listeners"_sr ).writeArray(); + + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listTests( std::vector<TestCaseHandle> const& tests ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tests"_sr ).writeArray(); + + for ( auto const& test : tests ) { + auto desc_writer = writer.writeObject(); + auto const& info = test.getTestCaseInfo(); + + desc_writer.write( "name"_sr ).write( info.name ); + desc_writer.write( "class-name"_sr ).write( info.className ); + { + auto tag_writer = desc_writer.write( "tags"_sr ).writeArray(); + for ( auto const& tag : info.tags ) { + tag_writer.write( tag.original ); + } + } + writeSourceInfo( desc_writer, info.lineInfo ); + } + } + void JsonReporter::listTags( std::vector<TagInfo> const& tags ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tags"_sr ).writeArray(); + for ( auto const& tag : tags ) { + auto tag_writer = writer.writeObject(); + { + auto aliases_writer = + tag_writer.write( "aliases"_sr ).writeArray(); + for ( auto alias : tag.spellings ) { + aliases_writer.write( alias ); + } + } + tag_writer.write( "count"_sr ).write( tag.count ); + } + } +} // namespace Catch diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_json.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_json.hpp new file mode 100644 index 000000000..c938ca394 --- /dev/null +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_json.hpp @@ -0,0 +1,95 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_REPORTER_JSON_HPP_INCLUDED +#define CATCH_REPORTER_JSON_HPP_INCLUDED + +#include <catch2/catch_timer.hpp> +#include <catch2/internal/catch_jsonwriter.hpp> +#include <catch2/reporters/catch_reporter_streaming_base.hpp> + +#include <stack> + +namespace Catch { + class JsonReporter : public StreamingReporterBase { + public: + JsonReporter( ReporterConfig&& config ); + + ~JsonReporter() override; + + static std::string getDescription(); + + public: // StreamingReporterBase + void testRunStarting( TestRunInfo const& runInfo ) override; + void testRunEnded( TestRunStats const& runStats ) override; + + void testCaseStarting( TestCaseInfo const& tcInfo ) override; + void testCaseEnded( TestCaseStats const& tcStats ) override; + + void testCasePartialStarting( TestCaseInfo const& tcInfo, + uint64_t index ) override; + void testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t index ) override; + + void sectionStarting( SectionInfo const& sectionInfo ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + + void assertionStarting( AssertionInfo const& assertionInfo ) override; + void assertionEnded( AssertionStats const& assertionStats ) override; + + //void testRunEndedCumulative() 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; + enum class Writer { + Object, + Array + }; + + JsonArrayWriter& startArray(); + JsonArrayWriter& startArray( StringRef key ); + + JsonObjectWriter& startObject(); + JsonObjectWriter& startObject( StringRef key ); + + void endObject(); + void endArray(); + + bool isInside( Writer writer ); + + void startListing(); + void endListing(); + + // Invariant: + // When m_writers is not empty and its top element is + // - Writer::Object, then m_objectWriters is not be empty + // - Writer::Array, then m_arrayWriters shall not be empty + std::stack<JsonObjectWriter> m_objectWriters{}; + std::stack<JsonArrayWriter> m_arrayWriters{}; + std::stack<Writer> m_writers{}; + + bool m_startedListing = false; + + // std::size_t m_sectionDepth = 0; + // std::size_t m_sectionStarted = 0; + }; +} // namespace Catch + +#endif // CATCH_REPORTER_JSON_HPP_INCLUDED diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp index 87c7c5679..7cb53c25b 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp @@ -19,8 +19,6 @@ namespace Catch { public: JunitReporter(ReporterConfig&& _config); - ~JunitReporter() override = default; - static std::string getDescription(); void testRunStarting(TestRunInfo const& runInfo) override; diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp index cad6deec8..d26af62e8 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp @@ -25,8 +25,6 @@ namespace Catch { 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; diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp index fe45df63e..e6889bb11 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp @@ -19,7 +19,6 @@ namespace Catch { StreamingReporterBase( CATCH_MOVE(config) ) { m_preferences.shouldReportAllAssertions = true; } - ~TAPReporter() override = default; static std::string getDescription() { using namespace std::string_literals; diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp index 320728007..38aa55a65 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp @@ -45,7 +45,7 @@ namespace Catch { } // end anonymous namespace - TeamCityReporter::~TeamCityReporter() {} + TeamCityReporter::~TeamCityReporter() = default; void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name ) diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp index f80cd2470..35a3028ee 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp @@ -234,26 +234,23 @@ namespace Catch { } void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { - m_xml.startElement("mean") + m_xml.scopedElement("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") + m_xml.scopedElement("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") + m_xml.scopedElement("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) { diff --git a/packages/Catch2/src/catch2/reporters/catch_reporters_all.hpp b/packages/Catch2/src/catch2/reporters/catch_reporters_all.hpp index 16f7bd70c..5c713fe14 100644 --- a/packages/Catch2/src/catch2/reporters/catch_reporters_all.hpp +++ b/packages/Catch2/src/catch2/reporters/catch_reporters_all.hpp @@ -28,6 +28,7 @@ #include <catch2/reporters/catch_reporter_cumulative_base.hpp> #include <catch2/reporters/catch_reporter_event_listener.hpp> #include <catch2/reporters/catch_reporter_helpers.hpp> +#include <catch2/reporters/catch_reporter_json.hpp> #include <catch2/reporters/catch_reporter_junit.hpp> #include <catch2/reporters/catch_reporter_multi.hpp> #include <catch2/reporters/catch_reporter_registrars.hpp> diff --git a/packages/Catch2/tests/CMakeLists.txt b/packages/Catch2/tests/CMakeLists.txt index 7be57abec..d3ab14a7f 100644 --- a/packages/Catch2/tests/CMakeLists.txt +++ b/packages/Catch2/tests/CMakeLists.txt @@ -78,6 +78,7 @@ endif(MSVC) #Temporary workaround set(TEST_SOURCES ${SELF_TEST_DIR}/TestRegistrations.cpp ${SELF_TEST_DIR}/IntrospectiveTests/Algorithms.tests.cpp + ${SELF_TEST_DIR}/IntrospectiveTests/AssertionHandler.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/Clara.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/CmdLine.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/CmdLineHelpers.tests.cpp @@ -85,7 +86,9 @@ set(TEST_SOURCES ${SELF_TEST_DIR}/IntrospectiveTests/Details.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/FloatingPoint.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/GeneratorsImpl.tests.cpp + ${SELF_TEST_DIR}/IntrospectiveTests/Integer.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/InternalBenchmark.tests.cpp + ${SELF_TEST_DIR}/IntrospectiveTests/Json.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/Parse.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/PartTracker.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/RandomNumberGeneration.tests.cpp @@ -622,6 +625,18 @@ if (CATCH_ENABLE_CONFIGURE_TESTS) endforeach() endif() +if (CATCH_ENABLE_CMAKE_HELPER_TESTS) + add_test(NAME "CMakeHelper::DiscoverTests" + COMMAND + "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/TestScripts/DiscoverTests/VerifyRegistration.py" "${CATCH_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" + ) + set_tests_properties("CMakeHelper::DiscoverTests" + PROPERTIES + COST 240 + LABELS "uses-python" + ) +endif() + foreach (reporterName # "Automake" - the simple .trs format does not support any kind of comments/metadata "compact" "console" @@ -629,7 +644,8 @@ foreach (reporterName # "Automake" - the simple .trs format does not support any "SonarQube" "TAP" # "TeamCity" - does not seem to support test suite-level metadata/comments - "XML") + "XML" + "JSON") add_test(NAME "Reporters:Filters:${reporterName}" COMMAND @@ -639,6 +655,8 @@ foreach (reporterName # "Automake" - the simple .trs format does not support any # Different regex for these two reporters, because the commas end up xml-escaped if (reporterName MATCHES "JUnit|XML") set(testCaseNameFormat ""CaseInsensitiveLess is case insensitive"") + elseif(reporterName MATCHES "JSON") + set(testCaseNameFormat "\\\\\"CaseInsensitiveLess is case insensitive\\\\\"") else() set(testCaseNameFormat "\"CaseInsensitiveLess is case insensitive\"") endif() diff --git a/packages/Catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp b/packages/Catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp index ef5b46b95..6f44bf691 100644 --- a/packages/Catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp +++ b/packages/Catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp @@ -7,11 +7,10 @@ // SPDX-License-Identifier: BSL-1.0 /**\file - * TODO: FIXES Registers custom reporter that reports testCase* events + * Registers an event listener to increments counter of assertionStarting events. * - * The resulting executable can then be used by an external Python script - * to verify that testCase{Starting,Ended} and testCasePartial{Starting,Ended} - * events are properly nested. + * Different assertion macros then check that the counter is at expected + * value when they are evaluated. */ #include <catch2/catch_test_macros.hpp> @@ -23,9 +22,6 @@ namespace { static size_t assertion_starting_events_seen = 0; - // TODO: custom matcher to check that "assertion_starting_events_seen" has - // the right number of checks - class AssertionStartingListener : public Catch::EventListenerBase { public: AssertionStartingListener( Catch::IConfig const* config ): diff --git a/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt index 6b5938a67..88c23e173 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt @@ -154,6 +154,7 @@ Nor would this :test-result: PASS Filter generator throws exception for empty generator :test-result: PASS Floating point matchers: double :test-result: PASS Floating point matchers: float +:test-result: PASS GENERATE can combine literals and generators :test-result: PASS Generators -- adapters :test-result: PASS Generators -- simple :test-result: PASS Generators internals @@ -162,12 +163,16 @@ Nor would this :test-result: PASS Hashers with same seed produce same hash :test-result: PASS Hashing different test cases produces different result :test-result: PASS Hashing test case produces same hash across multiple calls +:test-result: FAIL INFO and UNSCOPED_INFO can stream multiple arguments :test-result: FAIL INFO and WARN do not abort tests :test-result: FAIL INFO gets logged on failure :test-result: FAIL INFO gets logged on failure, even if captured before successful assertions :test-result: FAIL INFO is reset for each loop +:test-result: XFAIL Incomplete AssertionHandler :test-result: XFAIL Inequality checks that should fail :test-result: PASS Inequality checks that should succeed +:test-result: PASS JsonWriter +:test-result: PASS JsonWriter escapes charaters in strings properly :test-result: PASS Lambdas in assertions :test-result: PASS Less-than inequalities with different epsilons :test-result: PASS ManuallyRegistered @@ -265,6 +270,8 @@ Message from section two :test-result: PASS Testing checked-if :test-result: XFAIL Testing checked-if 2 :test-result: XFAIL Testing checked-if 3 +:test-result: XFAIL Testing checked-if 4 +:test-result: XFAIL Testing checked-if 5 :test-result: FAIL The NO_FAIL macro reports a failure but does not fail the test :test-result: PASS The default listing implementation write to provided stream :test-result: FAIL This test 'should' fail but doesn't @@ -408,6 +415,7 @@ b1! :test-result: PASS tuple<string,string> :test-result: PASS tuple<tuple<int>,tuple<>,float> :test-result: PASS uniform samples +:test-result: PASS uniform_integer_distribution can return the bounds :test-result: PASS unique_ptr reimplementation: basic functionality :test-result: PASS vec<vec<string,alloc>> -> toString :test-result: PASS vector<bool> -> toString diff --git a/packages/Catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt index cd56e6487..a37b1a2b5 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt @@ -152,6 +152,7 @@ :test-result: PASS Filter generator throws exception for empty generator :test-result: PASS Floating point matchers: double :test-result: PASS Floating point matchers: float +:test-result: PASS GENERATE can combine literals and generators :test-result: PASS Generators -- adapters :test-result: PASS Generators -- simple :test-result: PASS Generators internals @@ -160,12 +161,16 @@ :test-result: PASS Hashers with same seed produce same hash :test-result: PASS Hashing different test cases produces different result :test-result: PASS Hashing test case produces same hash across multiple calls +:test-result: FAIL INFO and UNSCOPED_INFO can stream multiple arguments :test-result: FAIL INFO and WARN do not abort tests :test-result: FAIL INFO gets logged on failure :test-result: FAIL INFO gets logged on failure, even if captured before successful assertions :test-result: FAIL INFO is reset for each loop +:test-result: XFAIL Incomplete AssertionHandler :test-result: XFAIL Inequality checks that should fail :test-result: PASS Inequality checks that should succeed +:test-result: PASS JsonWriter +:test-result: PASS JsonWriter escapes charaters in strings properly :test-result: PASS Lambdas in assertions :test-result: PASS Less-than inequalities with different epsilons :test-result: PASS ManuallyRegistered @@ -258,6 +263,8 @@ :test-result: PASS Testing checked-if :test-result: XFAIL Testing checked-if 2 :test-result: XFAIL Testing checked-if 3 +:test-result: XFAIL Testing checked-if 4 +:test-result: XFAIL Testing checked-if 5 :test-result: FAIL The NO_FAIL macro reports a failure but does not fail the test :test-result: PASS The default listing implementation write to provided stream :test-result: FAIL This test 'should' fail but doesn't @@ -397,6 +404,7 @@ :test-result: PASS tuple<string,string> :test-result: PASS tuple<tuple<int>,tuple<>,float> :test-result: PASS uniform samples +:test-result: PASS uniform_integer_distribution can return the bounds :test-result: PASS unique_ptr reimplementation: basic functionality :test-result: PASS vec<vec<string,alloc>> -> toString :test-result: PASS vector<bool> -> toString diff --git a/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt index be7a41203..0669fdbbb 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt @@ -331,7 +331,7 @@ MatchersRanges.tests.cpp:<line number>: passed: inner_lists_are_empty.front(), I MatchersRanges.tests.cpp:<line number>: passed: has_empty{}, !IsEmpty() for: {?} not is empty MatchersRanges.tests.cpp:<line number>: passed: unrelated::ADL_empty{}, IsEmpty() for: {?} is empty Message.tests.cpp:<line number>: passed: with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' -Message.tests.cpp:<line number>: passed: with 7 messages: 'std::vector<int>{1, 2, 3}[0, 1, 2] := 3' and 'std::vector<int>{1, 2, 3}[(0, 1)] := 2' and 'std::vector<int>{1, 2, 3}[0] := 1' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +Message.tests.cpp:<line number>: passed: with 7 messages: 'custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op<int>{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op<int>{1, 2, 3}[0] := 0' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' Message.tests.cpp:<line number>: passed: with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' ToStringGeneral.tests.cpp:<line number>: passed: true with 1 message: 'i := 2' ToStringGeneral.tests.cpp:<line number>: passed: true with 1 message: '3' @@ -666,6 +666,10 @@ Matchers.tests.cpp:<line number>: passed: 1., !IsNaN() for: 1.0 not is NaN Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: filter([] (int) {return false; }, value(1)), Catch::GeneratorException Generators.tests.cpp:<line number>: passed: i < 4 for: 1 < 4 Generators.tests.cpp:<line number>: passed: i < 4 for: 2 < 4 @@ -944,6 +948,7 @@ TestCaseInfoHasher.tests.cpp:<line number>: passed: h( dummy1 ) != h( dummy2 ) f TestCaseInfoHasher.tests.cpp:<line number>: passed: h( dummy ) == h( dummy ) for: 3422778688 (0x<hex digits>) == 3422778688 (0x<hex digits>) +Message.tests.cpp:<line number>: failed: explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' Message.tests.cpp:<line number>: warning: 'this is a message' with 1 message: 'this is a warning' Message.tests.cpp:<line number>: failed: a == 1 for: 2 == 1 with 2 messages: 'this message should be logged' and 'so should this' Message.tests.cpp:<line number>: passed: a == 2 for: 2 == 2 with 1 message: 'this message may be logged later' @@ -961,6 +966,7 @@ Message.tests.cpp:<line number>: passed: i < 10 for: 7 < 10 with 2 messages: 'cu Message.tests.cpp:<line number>: passed: i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and 'i := 8' Message.tests.cpp:<line number>: passed: i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' Message.tests.cpp:<line number>: failed: i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy Condition.tests.cpp:<line number>: failed: data.int_seven != 7 for: 7 != 7 Condition.tests.cpp:<line number>: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) Condition.tests.cpp:<line number>: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) @@ -977,6 +983,91 @@ Condition.tests.cpp:<line number>: passed: data.str_hello != "goodbye" for: "hel Condition.tests.cpp:<line number>: passed: data.str_hello != "hell" for: "hello" != "hell" Condition.tests.cpp:<line number>: passed: data.str_hello != "hello1" for: "hello" != "hello1" Condition.tests.cpp:<line number>: passed: data.str_hello.size() != 6 for: 5 != 6 +Json.tests.cpp:<line number>: passed: stream.str() == "" for: "" == "" +Json.tests.cpp:<line number>: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:<line number>: passed: stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) +Json.tests.cpp:<line number>: passed: stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) +Json.tests.cpp:<line number>: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:<line number>: passed: stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +Json.tests.cpp:<line number>: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:<line number>: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:<line number>: passed: stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"/\"" for: ""/"" == ""/"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" Compilation.tests.cpp:<line number>: passed: []() { return true; }() for: true Approx.tests.cpp:<line number>: passed: d <= Approx( 1.24 ) for: 1.23 <= Approx( 1.24 ) Approx.tests.cpp:<line number>: passed: d <= Approx( 1.23 ) for: 1.23 <= Approx( 1.23 ) @@ -1341,6 +1432,60 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fa " ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: console' Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fakeTag"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fake reporter"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fakeTag"s) for: "<?xml version="1.0" encoding="UTF-8"?> All available tags: 1 [fakeTag] @@ -1750,6 +1895,10 @@ Misc.tests.cpp:<line number>: passed: true Misc.tests.cpp:<line number>: failed: explicitly Misc.tests.cpp:<line number>: failed - but was ok: false Misc.tests.cpp:<line number>: failed: explicitly +Misc.tests.cpp:<line number>: passed: true +Misc.tests.cpp:<line number>: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +Misc.tests.cpp:<line number>: failed - but was ok: false +Misc.tests.cpp:<line number>: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} Message.tests.cpp:<line number>: failed - but was ok: 1 == 2 Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("[fakeTag]"s) for: "All available tags: 1 [fakeTag] @@ -2473,6 +2622,8 @@ InternalBenchmark.tests.cpp:<line number>: passed: e.point == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:<line number>: passed: e.upper_bound == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:<line number>: passed: e.lower_bound == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:<line number>: passed: e.confidence_interval == 0.95 for: 0.95 == 0.95 +RandomNumberGeneration.tests.cpp:<line number>: passed: dist.a() == -10 for: -10 == -10 +RandomNumberGeneration.tests.cpp:<line number>: passed: dist.b() == 10 for: 10 == 10 UniquePtr.tests.cpp:<line number>: passed: !(ptr) for: !{?} UniquePtr.tests.cpp:<line number>: passed: ptr.get() == 0 for: 0 == 0 UniquePtr.tests.cpp:<line number>: passed: ptr for: {?} @@ -2538,7 +2689,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0 InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0 Misc.tests.cpp:<line number>: passed: Misc.tests.cpp:<line number>: passed: -test cases: 409 | 308 passed | 84 failed | 6 skipped | 11 failed as expected -assertions: 2225 | 2048 passed | 145 failed | 32 failed as expected +test cases: 417 | 312 passed | 85 failed | 6 skipped | 14 failed as expected +assertions: 2260 | 2079 passed | 146 failed | 35 failed as expected diff --git a/packages/Catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt index 6c48ab917..214fef74b 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt @@ -329,7 +329,7 @@ MatchersRanges.tests.cpp:<line number>: passed: inner_lists_are_empty.front(), I MatchersRanges.tests.cpp:<line number>: passed: has_empty{}, !IsEmpty() for: {?} not is empty MatchersRanges.tests.cpp:<line number>: passed: unrelated::ADL_empty{}, IsEmpty() for: {?} is empty Message.tests.cpp:<line number>: passed: with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' -Message.tests.cpp:<line number>: passed: with 7 messages: 'std::vector<int>{1, 2, 3}[0, 1, 2] := 3' and 'std::vector<int>{1, 2, 3}[(0, 1)] := 2' and 'std::vector<int>{1, 2, 3}[0] := 1' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +Message.tests.cpp:<line number>: passed: with 7 messages: 'custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op<int>{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op<int>{1, 2, 3}[0] := 0' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' Message.tests.cpp:<line number>: passed: with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' ToStringGeneral.tests.cpp:<line number>: passed: true with 1 message: 'i := 2' ToStringGeneral.tests.cpp:<line number>: passed: true with 1 message: '3' @@ -664,6 +664,10 @@ Matchers.tests.cpp:<line number>: passed: 1., !IsNaN() for: 1.0 not is NaN Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:<line number>: passed: filter([] (int) {return false; }, value(1)), Catch::GeneratorException Generators.tests.cpp:<line number>: passed: i < 4 for: 1 < 4 Generators.tests.cpp:<line number>: passed: i < 4 for: 2 < 4 @@ -942,6 +946,7 @@ TestCaseInfoHasher.tests.cpp:<line number>: passed: h( dummy1 ) != h( dummy2 ) f TestCaseInfoHasher.tests.cpp:<line number>: passed: h( dummy ) == h( dummy ) for: 3422778688 (0x<hex digits>) == 3422778688 (0x<hex digits>) +Message.tests.cpp:<line number>: failed: explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' Message.tests.cpp:<line number>: warning: 'this is a message' with 1 message: 'this is a warning' Message.tests.cpp:<line number>: failed: a == 1 for: 2 == 1 with 2 messages: 'this message should be logged' and 'so should this' Message.tests.cpp:<line number>: passed: a == 2 for: 2 == 2 with 1 message: 'this message may be logged later' @@ -959,6 +964,7 @@ Message.tests.cpp:<line number>: passed: i < 10 for: 7 < 10 with 2 messages: 'cu Message.tests.cpp:<line number>: passed: i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and 'i := 8' Message.tests.cpp:<line number>: passed: i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' Message.tests.cpp:<line number>: failed: i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy Condition.tests.cpp:<line number>: failed: data.int_seven != 7 for: 7 != 7 Condition.tests.cpp:<line number>: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) Condition.tests.cpp:<line number>: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) @@ -975,6 +981,91 @@ Condition.tests.cpp:<line number>: passed: data.str_hello != "goodbye" for: "hel Condition.tests.cpp:<line number>: passed: data.str_hello != "hell" for: "hello" != "hell" Condition.tests.cpp:<line number>: passed: data.str_hello != "hello1" for: "hello" != "hello1" Condition.tests.cpp:<line number>: passed: data.str_hello.size() != 6 for: 5 != 6 +Json.tests.cpp:<line number>: passed: stream.str() == "" for: "" == "" +Json.tests.cpp:<line number>: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:<line number>: passed: stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) +Json.tests.cpp:<line number>: passed: stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) +Json.tests.cpp:<line number>: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:<line number>: passed: stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +Json.tests.cpp:<line number>: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:<line number>: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:<line number>: passed: stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"/\"" for: ""/"" == ""/"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" Compilation.tests.cpp:<line number>: passed: []() { return true; }() for: true Approx.tests.cpp:<line number>: passed: d <= Approx( 1.24 ) for: 1.23 <= Approx( 1.24 ) Approx.tests.cpp:<line number>: passed: d <= Approx( 1.23 ) for: 1.23 <= Approx( 1.23 ) @@ -1339,6 +1430,60 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fa " ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: console' Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fakeTag"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fake reporter"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fakeTag"s) for: "<?xml version="1.0" encoding="UTF-8"?> All available tags: 1 [fakeTag] @@ -1743,6 +1888,10 @@ Misc.tests.cpp:<line number>: passed: true Misc.tests.cpp:<line number>: failed: explicitly Misc.tests.cpp:<line number>: failed - but was ok: false Misc.tests.cpp:<line number>: failed: explicitly +Misc.tests.cpp:<line number>: passed: true +Misc.tests.cpp:<line number>: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +Misc.tests.cpp:<line number>: failed - but was ok: false +Misc.tests.cpp:<line number>: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} Message.tests.cpp:<line number>: failed - but was ok: 1 == 2 Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("[fakeTag]"s) for: "All available tags: 1 [fakeTag] @@ -2462,6 +2611,8 @@ InternalBenchmark.tests.cpp:<line number>: passed: e.point == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:<line number>: passed: e.upper_bound == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:<line number>: passed: e.lower_bound == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:<line number>: passed: e.confidence_interval == 0.95 for: 0.95 == 0.95 +RandomNumberGeneration.tests.cpp:<line number>: passed: dist.a() == -10 for: -10 == -10 +RandomNumberGeneration.tests.cpp:<line number>: passed: dist.b() == 10 for: 10 == 10 UniquePtr.tests.cpp:<line number>: passed: !(ptr) for: !{?} UniquePtr.tests.cpp:<line number>: passed: ptr.get() == 0 for: 0 == 0 UniquePtr.tests.cpp:<line number>: passed: ptr for: {?} @@ -2527,7 +2678,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0 InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0 Misc.tests.cpp:<line number>: passed: Misc.tests.cpp:<line number>: passed: -test cases: 409 | 308 passed | 84 failed | 6 skipped | 11 failed as expected -assertions: 2225 | 2048 passed | 145 failed | 32 failed as expected +test cases: 417 | 312 passed | 85 failed | 6 skipped | 14 failed as expected +assertions: 2260 | 2079 passed | 146 failed | 35 failed as expected diff --git a/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt index 0945f0dfb..254262565 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt @@ -599,6 +599,18 @@ explicitly with message: Message.tests.cpp:<line number>: warning: This message appears in the output +------------------------------------------------------------------------------- +INFO and UNSCOPED_INFO can stream multiple arguments +------------------------------------------------------------------------------- +Message.tests.cpp:<line number> +............................................................................... + +Message.tests.cpp:<line number>: FAILED: +explicitly with messages: + This info has multiple parts. + This unscoped info has multiple parts. + Show infos! + ------------------------------------------------------------------------------- INFO and WARN do not abort tests ------------------------------------------------------------------------------- @@ -659,6 +671,17 @@ with messages: current counter 10 i := 10 +------------------------------------------------------------------------------- +Incomplete AssertionHandler +------------------------------------------------------------------------------- +AssertionHandler.tests.cpp:<line number> +............................................................................... + +AssertionHandler.tests.cpp:<line number>: FAILED: + REQUIRE( Dummy ) +due to unexpected exception with message: + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -997,6 +1020,28 @@ Misc.tests.cpp:<line number> Misc.tests.cpp:<line number>: FAILED: +------------------------------------------------------------------------------- +Testing checked-if 4 +------------------------------------------------------------------------------- +Misc.tests.cpp:<line number> +............................................................................... + +Misc.tests.cpp:<line number>: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + +------------------------------------------------------------------------------- +Testing checked-if 5 +------------------------------------------------------------------------------- +Misc.tests.cpp:<line number> +............................................................................... + +Misc.tests.cpp:<line number>: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + ------------------------------------------------------------------------------- Thrown string literals are translated ------------------------------------------------------------------------------- @@ -1543,6 +1588,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 409 | 322 passed | 69 failed | 7 skipped | 11 failed as expected -assertions: 2208 | 2048 passed | 128 failed | 32 failed as expected +test cases: 417 | 326 passed | 70 failed | 7 skipped | 14 failed as expected +assertions: 2243 | 2079 passed | 129 failed | 35 failed as expected diff --git a/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt index 150980e82..077b7bf75 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt @@ -2740,9 +2740,9 @@ Message.tests.cpp:<line number> Message.tests.cpp:<line number>: PASSED: with messages: - std::vector<int>{1, 2, 3}[0, 1, 2] := 3 - std::vector<int>{1, 2, 3}[(0, 1)] := 2 - std::vector<int>{1, 2, 3}[0] := 1 + custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0 + custom_index_op<int>{1, 2, 3}[(0, 1)] := 0 + custom_index_op<int>{1, 2, 3}[0] := 0 (helper_1436<int, int>{12, -12}) := { 12, -12 } (helper_1436<int, int>(-12, 12)) := { -12, 12 } (1, 2) := 2 @@ -4889,6 +4889,50 @@ Matchers.tests.cpp:<line number>: PASSED: with expansion: 1.0 not is NaN +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- Generators -- adapters Filtering by predicate @@ -6982,6 +7026,18 @@ with expansion: == 3422778688 (0x<hex digits>) +------------------------------------------------------------------------------- +INFO and UNSCOPED_INFO can stream multiple arguments +------------------------------------------------------------------------------- +Message.tests.cpp:<line number> +............................................................................... + +Message.tests.cpp:<line number>: FAILED: +explicitly with messages: + This info has multiple parts. + This unscoped info has multiple parts. + Show infos! + ------------------------------------------------------------------------------- INFO and WARN do not abort tests ------------------------------------------------------------------------------- @@ -7143,6 +7199,17 @@ with messages: current counter 10 i := 10 +------------------------------------------------------------------------------- +Incomplete AssertionHandler +------------------------------------------------------------------------------- +AssertionHandler.tests.cpp:<line number> +............................................................................... + +AssertionHandler.tests.cpp:<line number>: FAILED: + REQUIRE( Dummy ) +due to unexpected exception with message: + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -7235,6 +7302,291 @@ Condition.tests.cpp:<line number>: PASSED: with expansion: 5 != 6 +------------------------------------------------------------------------------- +JsonWriter + Newly constructed JsonWriter does nothing +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "" ) +with expansion: + "" == "" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject with key will create an object to write the value +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) ) +with expansion: + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] + }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: + ""true": true," and contains: ""false": false," and contains: ""string": + "this is a string"," and contains: ""array": [ + 1, + 2 + ] + }" ) + +------------------------------------------------------------------------------- +JsonWriter + nesting objects +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) ) +with expansion: + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } + }" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray creates array to write the values to +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" ) +with expansion: + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + == + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonObjectWriter shall not insert superfluous brace +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonArrayWriter shall not insert superfluous bracket +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Custom class shall be quoted +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "\"custom\"" ) +with expansion: + ""custom"" == ""custom"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Quote in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\\"\"" ) +with expansion: + ""\""" == ""\""" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backslash in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\\\\"" ) +with expansion: + ""\\"" == ""\\"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Forward slash in a string is **not** escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"/\"" ) +with expansion: + ""/"" == ""/"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backspace in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\b\"" ) +with expansion: + ""\b"" == ""\b"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Formfeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\f\"" ) +with expansion: + ""\f"" == ""\f"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + linefeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\n\"" ) +with expansion: + ""\n"" == ""\n"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + carriage return in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\r\"" ) +with expansion: + ""\r"" == ""\r"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + tab in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\t\"" ) +with expansion: + ""\t"" == ""\t"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + combination of characters is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\\\/\\t\\r\\n\"" ) +with expansion: + ""\\/\t\r\n"" == ""\\/\t\r\n"" + ------------------------------------------------------------------------------- Lambdas in assertions ------------------------------------------------------------------------------- @@ -9733,6 +10085,129 @@ Reporter's write listings to provided stream Reporters.tests.cpp:<line number> ............................................................................... +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tags +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fakeTag"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists reporters +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fake reporter"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tests +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + Reporters.tests.cpp:<line number>: PASSED: REQUIRE_FALSE( factories.empty() ) with expansion: @@ -12522,6 +12997,34 @@ Misc.tests.cpp:<line number>: FAILED - but was ok: Misc.tests.cpp:<line number>: FAILED: +------------------------------------------------------------------------------- +Testing checked-if 4 +------------------------------------------------------------------------------- +Misc.tests.cpp:<line number> +............................................................................... + +Misc.tests.cpp:<line number>: PASSED: + CHECKED_ELSE( true ) + +Misc.tests.cpp:<line number>: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + +------------------------------------------------------------------------------- +Testing checked-if 5 +------------------------------------------------------------------------------- +Misc.tests.cpp:<line number> +............................................................................... + +Misc.tests.cpp:<line number>: FAILED - but was ok: + CHECKED_ELSE( false ) + +Misc.tests.cpp:<line number>: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + ------------------------------------------------------------------------------- The NO_FAIL macro reports a failure but does not fail the test ------------------------------------------------------------------------------- @@ -17745,6 +18248,22 @@ InternalBenchmark.tests.cpp:<line number>: PASSED: with expansion: 0.95 == 0.95 +------------------------------------------------------------------------------- +uniform_integer_distribution can return the bounds +------------------------------------------------------------------------------- +RandomNumberGeneration.tests.cpp:<line number> +............................................................................... + +RandomNumberGeneration.tests.cpp:<line number>: PASSED: + REQUIRE( dist.a() == -10 ) +with expansion: + -10 == -10 + +RandomNumberGeneration.tests.cpp:<line number>: PASSED: + REQUIRE( dist.b() == 10 ) +with expansion: + 10 == 10 + ------------------------------------------------------------------------------- unique_ptr reimplementation: basic functionality Default constructed unique_ptr is empty @@ -18232,6 +18751,6 @@ Misc.tests.cpp:<line number> Misc.tests.cpp:<line number>: PASSED: =============================================================================== -test cases: 409 | 308 passed | 84 failed | 6 skipped | 11 failed as expected -assertions: 2225 | 2048 passed | 145 failed | 32 failed as expected +test cases: 417 | 312 passed | 85 failed | 6 skipped | 14 failed as expected +assertions: 2260 | 2079 passed | 146 failed | 35 failed as expected diff --git a/packages/Catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt index 4cc942dd4..5d204990c 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt @@ -2738,9 +2738,9 @@ Message.tests.cpp:<line number> Message.tests.cpp:<line number>: PASSED: with messages: - std::vector<int>{1, 2, 3}[0, 1, 2] := 3 - std::vector<int>{1, 2, 3}[(0, 1)] := 2 - std::vector<int>{1, 2, 3}[0] := 1 + custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0 + custom_index_op<int>{1, 2, 3}[(0, 1)] := 0 + custom_index_op<int>{1, 2, 3}[0] := 0 (helper_1436<int, int>{12, -12}) := { 12, -12 } (helper_1436<int, int>(-12, 12)) := { -12, 12 } (1, 2) := 2 @@ -4887,6 +4887,50 @@ Matchers.tests.cpp:<line number>: PASSED: with expansion: 1.0 not is NaN +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp:<line number> +............................................................................... + +Generators.tests.cpp:<line number>: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- Generators -- adapters Filtering by predicate @@ -6980,6 +7024,18 @@ with expansion: == 3422778688 (0x<hex digits>) +------------------------------------------------------------------------------- +INFO and UNSCOPED_INFO can stream multiple arguments +------------------------------------------------------------------------------- +Message.tests.cpp:<line number> +............................................................................... + +Message.tests.cpp:<line number>: FAILED: +explicitly with messages: + This info has multiple parts. + This unscoped info has multiple parts. + Show infos! + ------------------------------------------------------------------------------- INFO and WARN do not abort tests ------------------------------------------------------------------------------- @@ -7141,6 +7197,17 @@ with messages: current counter 10 i := 10 +------------------------------------------------------------------------------- +Incomplete AssertionHandler +------------------------------------------------------------------------------- +AssertionHandler.tests.cpp:<line number> +............................................................................... + +AssertionHandler.tests.cpp:<line number>: FAILED: + REQUIRE( Dummy ) +due to unexpected exception with message: + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -7233,6 +7300,291 @@ Condition.tests.cpp:<line number>: PASSED: with expansion: 5 != 6 +------------------------------------------------------------------------------- +JsonWriter + Newly constructed JsonWriter does nothing +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "" ) +with expansion: + "" == "" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject with key will create an object to write the value +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) ) +with expansion: + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] + }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: + ""true": true," and contains: ""false": false," and contains: ""string": + "this is a string"," and contains: ""array": [ + 1, + 2 + ] + }" ) + +------------------------------------------------------------------------------- +JsonWriter + nesting objects +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) ) +with expansion: + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } + }" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray creates array to write the values to +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" ) +with expansion: + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + == + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonObjectWriter shall not insert superfluous brace +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonArrayWriter shall not insert superfluous bracket +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Custom class shall be quoted +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( stream.str() == "\"custom\"" ) +with expansion: + ""custom"" == ""custom"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Quote in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\\"\"" ) +with expansion: + ""\""" == ""\""" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backslash in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\\\\"" ) +with expansion: + ""\\"" == ""\\"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Forward slash in a string is **not** escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"/\"" ) +with expansion: + ""/"" == ""/"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backspace in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\b\"" ) +with expansion: + ""\b"" == ""\b"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Formfeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\f\"" ) +with expansion: + ""\f"" == ""\f"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + linefeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\n\"" ) +with expansion: + ""\n"" == ""\n"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + carriage return in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\r\"" ) +with expansion: + ""\r"" == ""\r"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + tab in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\t\"" ) +with expansion: + ""\t"" == ""\t"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + combination of characters is escaped +------------------------------------------------------------------------------- +Json.tests.cpp:<line number> +............................................................................... + +Json.tests.cpp:<line number>: PASSED: + REQUIRE( sstream.str() == "\"\\\\/\\t\\r\\n\"" ) +with expansion: + ""\\/\t\r\n"" == ""\\/\t\r\n"" + ------------------------------------------------------------------------------- Lambdas in assertions ------------------------------------------------------------------------------- @@ -9731,6 +10083,129 @@ Reporter's write listings to provided stream Reporters.tests.cpp:<line number> ............................................................................... +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tags +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fakeTag"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists reporters +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fake reporter"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tests +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + +Reporters.tests.cpp:<line number>: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp:<line number> +............................................................................... + Reporters.tests.cpp:<line number>: PASSED: REQUIRE_FALSE( factories.empty() ) with expansion: @@ -12515,6 +12990,34 @@ Misc.tests.cpp:<line number>: FAILED - but was ok: Misc.tests.cpp:<line number>: FAILED: +------------------------------------------------------------------------------- +Testing checked-if 4 +------------------------------------------------------------------------------- +Misc.tests.cpp:<line number> +............................................................................... + +Misc.tests.cpp:<line number>: PASSED: + CHECKED_ELSE( true ) + +Misc.tests.cpp:<line number>: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + +------------------------------------------------------------------------------- +Testing checked-if 5 +------------------------------------------------------------------------------- +Misc.tests.cpp:<line number> +............................................................................... + +Misc.tests.cpp:<line number>: FAILED - but was ok: + CHECKED_ELSE( false ) + +Misc.tests.cpp:<line number>: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + ------------------------------------------------------------------------------- The NO_FAIL macro reports a failure but does not fail the test ------------------------------------------------------------------------------- @@ -17734,6 +18237,22 @@ InternalBenchmark.tests.cpp:<line number>: PASSED: with expansion: 0.95 == 0.95 +------------------------------------------------------------------------------- +uniform_integer_distribution can return the bounds +------------------------------------------------------------------------------- +RandomNumberGeneration.tests.cpp:<line number> +............................................................................... + +RandomNumberGeneration.tests.cpp:<line number>: PASSED: + REQUIRE( dist.a() == -10 ) +with expansion: + -10 == -10 + +RandomNumberGeneration.tests.cpp:<line number>: PASSED: + REQUIRE( dist.b() == 10 ) +with expansion: + 10 == 10 + ------------------------------------------------------------------------------- unique_ptr reimplementation: basic functionality Default constructed unique_ptr is empty @@ -18221,6 +18740,6 @@ Misc.tests.cpp:<line number> Misc.tests.cpp:<line number>: PASSED: =============================================================================== -test cases: 409 | 308 passed | 84 failed | 6 skipped | 11 failed as expected -assertions: 2225 | 2048 passed | 145 failed | 32 failed as expected +test cases: 417 | 312 passed | 85 failed | 6 skipped | 14 failed as expected +assertions: 2260 | 2079 passed | 146 failed | 35 failed as expected diff --git a/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt index c992154c4..48eccfc3d 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <testsuitesloose text artifact > - <testsuite name="<exe-name>" errors="17" failures="128" skipped="12" tests="2237" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> + <testsuite name="<exe-name>" errors="17" failures="129" skipped="12" tests="2272" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <properties> <property name="random-seed" value="1"/> <property name="filters" value=""*" ~[!nonportable] ~[!benchmark] ~[approvals]"/> @@ -708,6 +708,7 @@ at Message.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="Floating point matchers: float/Composed" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Floating point matchers: float/Constructor validation" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Floating point matchers: float/IsNaN" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="GENERATE can combine literals and generators" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Filtering by predicate/Basic usage" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Filtering by predicate/Throws if there are no matching values" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Shortening a range" time="{duration}" status="run"/> @@ -752,6 +753,15 @@ at Message.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="Hashing different test cases produces different result/Different classname" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Hashing different test cases produces different result/Different tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Hashing test case produces same hash across multiple calls" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="INFO and UNSCOPED_INFO can stream multiple arguments" time="{duration}" status="run"> + <failure type="FAIL"> +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. +at Message.tests.cpp:<line number> + </failure> + </testcase> <testcase classname="<exe-name>.global" name="INFO and WARN do not abort tests" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="INFO gets logged on failure" time="{duration}" status="run"> <failure message="a == 1" type="REQUIRE"> @@ -796,6 +806,15 @@ i := 10 at Message.tests.cpp:<line number> </failure> </testcase> + <testcase classname="<exe-name>.global" name="Incomplete AssertionHandler" time="{duration}" status="run"> + <skipped message="TEST_CASE tagged with !mayfail"/> + <error message="Dummy" type="REQUIRE"> +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp:<line number> + </error> + </testcase> <testcase classname="<exe-name>.global" name="Inequality checks that should fail" time="{duration}" status="run"> <skipped message="TEST_CASE tagged with !mayfail"/> <failure message="data.int_seven != 7" type="CHECK"> @@ -835,6 +854,24 @@ at Condition.tests.cpp:<line number> </failure> </testcase> <testcase classname="<exe-name>.global" name="Inequality checks that should succeed" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Newly constructed JsonWriter does nothing" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeObject will create an empty pair of braces" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeObject with key will create an object to write the value" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/nesting objects" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeArray will create an empty pair of braces" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeArray creates array to write the values to" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Moved from JsonObjectWriter shall not insert superfluous brace" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Moved from JsonArrayWriter shall not insert superfluous bracket" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Custom class shall be quoted" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Quote in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Backslash in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Forward slash in a string is **not** escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Backspace in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Formfeed in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/linefeed in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/carriage return in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/tab in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/combination of characters is escaped" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Lambdas in assertions" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Less-than inequalities with different epsilons" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="ManuallyRegistered" time="{duration}" status="run"/> @@ -1172,6 +1209,9 @@ at Matchers.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/console reporter lists tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/console reporter lists reporters" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/console reporter lists tests" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JSON reporter lists tags" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JSON reporter lists reporters" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JSON reporter lists tests" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JUnit reporter lists tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JUnit reporter lists reporters" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JUnit reporter lists tests" time="{duration}" status="run"/> @@ -1360,6 +1400,24 @@ FAILED: at Misc.tests.cpp:<line number> </failure> </testcase> + <testcase classname="<exe-name>.global" name="Testing checked-if 4" time="{duration}" status="run"> + <skipped message="TEST_CASE tagged with !mayfail"/> + <error message="{Unknown expression after the reported line}"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp:<line number> + </error> + </testcase> + <testcase classname="<exe-name>.global" name="Testing checked-if 5" time="{duration}" status="run"> + <skipped message="TEST_CASE tagged with !mayfail"/> + <error message="{Unknown expression after the reported line}"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp:<line number> + </error> + </testcase> <testcase classname="<exe-name>.global" name="The NO_FAIL macro reports a failure but does not fail the test" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="The default listing implementation write to provided stream/Listing tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="The default listing implementation write to provided stream/Listing reporters" time="{duration}" status="run"/> @@ -2017,6 +2075,7 @@ at Exception.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="tuple<string,string>" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="tuple<tuple<int>,tuple<>,float>" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="uniform samples" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="uniform_integer_distribution can return the bounds" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="unique_ptr reimplementation: basic functionality/Default constructed unique_ptr is empty" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="unique_ptr reimplementation: basic functionality/Take ownership of allocation" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="unique_ptr reimplementation: basic functionality/Take ownership of allocation/Plain reset deallocates" time="{duration}" status="run"/> diff --git a/packages/Catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt index 79c323650..d270c88fb 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <testsuites> - <testsuite name="<exe-name>" errors="17" failures="128" skipped="12" tests="2237" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> + <testsuite name="<exe-name>" errors="17" failures="129" skipped="12" tests="2272" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <properties> <property name="random-seed" value="1"/> <property name="filters" value=""*" ~[!nonportable] ~[!benchmark] ~[approvals]"/> @@ -707,6 +707,7 @@ at Message.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="Floating point matchers: float/Composed" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Floating point matchers: float/Constructor validation" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Floating point matchers: float/IsNaN" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="GENERATE can combine literals and generators" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Filtering by predicate/Basic usage" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Filtering by predicate/Throws if there are no matching values" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Shortening a range" time="{duration}" status="run"/> @@ -751,6 +752,15 @@ at Message.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="Hashing different test cases produces different result/Different classname" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Hashing different test cases produces different result/Different tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Hashing test case produces same hash across multiple calls" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="INFO and UNSCOPED_INFO can stream multiple arguments" time="{duration}" status="run"> + <failure type="FAIL"> +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. +at Message.tests.cpp:<line number> + </failure> + </testcase> <testcase classname="<exe-name>.global" name="INFO and WARN do not abort tests" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="INFO gets logged on failure" time="{duration}" status="run"> <failure message="a == 1" type="REQUIRE"> @@ -795,6 +805,15 @@ i := 10 at Message.tests.cpp:<line number> </failure> </testcase> + <testcase classname="<exe-name>.global" name="Incomplete AssertionHandler" time="{duration}" status="run"> + <skipped message="TEST_CASE tagged with !mayfail"/> + <error message="Dummy" type="REQUIRE"> +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp:<line number> + </error> + </testcase> <testcase classname="<exe-name>.global" name="Inequality checks that should fail" time="{duration}" status="run"> <skipped message="TEST_CASE tagged with !mayfail"/> <failure message="data.int_seven != 7" type="CHECK"> @@ -834,6 +853,24 @@ at Condition.tests.cpp:<line number> </failure> </testcase> <testcase classname="<exe-name>.global" name="Inequality checks that should succeed" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Newly constructed JsonWriter does nothing" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeObject will create an empty pair of braces" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeObject with key will create an object to write the value" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/nesting objects" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeArray will create an empty pair of braces" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Calling writeArray creates array to write the values to" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Moved from JsonObjectWriter shall not insert superfluous brace" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Moved from JsonArrayWriter shall not insert superfluous bracket" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter/Custom class shall be quoted" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Quote in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Backslash in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Forward slash in a string is **not** escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Backspace in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/Formfeed in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/linefeed in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/carriage return in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/tab in a string is escaped" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="JsonWriter escapes charaters in strings properly/combination of characters is escaped" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Lambdas in assertions" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Less-than inequalities with different epsilons" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="ManuallyRegistered" time="{duration}" status="run"/> @@ -1171,6 +1208,9 @@ at Matchers.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/console reporter lists tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/console reporter lists reporters" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/console reporter lists tests" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JSON reporter lists tags" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JSON reporter lists reporters" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JSON reporter lists tests" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JUnit reporter lists tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JUnit reporter lists reporters" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="Reporter's write listings to provided stream/JUnit reporter lists tests" time="{duration}" status="run"/> @@ -1359,6 +1399,24 @@ FAILED: at Misc.tests.cpp:<line number> </failure> </testcase> + <testcase classname="<exe-name>.global" name="Testing checked-if 4" time="{duration}" status="run"> + <skipped message="TEST_CASE tagged with !mayfail"/> + <error message="{Unknown expression after the reported line}"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp:<line number> + </error> + </testcase> + <testcase classname="<exe-name>.global" name="Testing checked-if 5" time="{duration}" status="run"> + <skipped message="TEST_CASE tagged with !mayfail"/> + <error message="{Unknown expression after the reported line}"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp:<line number> + </error> + </testcase> <testcase classname="<exe-name>.global" name="The NO_FAIL macro reports a failure but does not fail the test" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="The default listing implementation write to provided stream/Listing tags" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="The default listing implementation write to provided stream/Listing reporters" time="{duration}" status="run"/> @@ -2016,6 +2074,7 @@ at Exception.tests.cpp:<line number> <testcase classname="<exe-name>.global" name="tuple<string,string>" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="tuple<tuple<int>,tuple<>,float>" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="uniform samples" time="{duration}" status="run"/> + <testcase classname="<exe-name>.global" name="uniform_integer_distribution can return the bounds" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="unique_ptr reimplementation: basic functionality/Default constructed unique_ptr is empty" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="unique_ptr reimplementation: basic functionality/Take ownership of allocation" time="{duration}" status="run"/> <testcase classname="<exe-name>.global" name="unique_ptr reimplementation: basic functionality/Take ownership of allocation/Plain reset deallocates" time="{duration}" status="run"/> diff --git a/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index 592887f9c..36b05e54d 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt @@ -2,6 +2,16 @@ <!-- filters='"*" ~[!nonportable] ~[!benchmark] ~[approvals]' rng-seed=1 --> <testExecutions version="1"loose text artifact > + <file path="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp"> + <testCase name="Incomplete AssertionHandler" duration="{duration}"> + <skipped message="REQUIRE(Dummy)"> +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp:<line number> + </skipped> + </testCase> + </file> <file path="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp"> <testCase name="Clara::Arg supports single-arg parse the way Opt does" duration="{duration}"/> <testCase name="Clara::Opt supports accept-many lambdas/Parsing fails on multiple options without accept_many" duration="{duration}"/> @@ -120,6 +130,26 @@ <testCase name="warmup" duration="{duration}"/> <testCase name="weighted_average_quantile" duration="{duration}"/> </file> + <file path="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp"> + <testCase name="JsonWriter/Newly constructed JsonWriter does nothing" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeObject will create an empty pair of braces" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeObject with key will create an object to write the value" duration="{duration}"/> + <testCase name="JsonWriter/nesting objects" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeArray will create an empty pair of braces" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeArray creates array to write the values to" duration="{duration}"/> + <testCase name="JsonWriter/Moved from JsonObjectWriter shall not insert superfluous brace" duration="{duration}"/> + <testCase name="JsonWriter/Moved from JsonArrayWriter shall not insert superfluous bracket" duration="{duration}"/> + <testCase name="JsonWriter/Custom class shall be quoted" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Quote in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Backslash in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Forward slash in a string is **not** escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Backspace in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Formfeed in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/linefeed in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/carriage return in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/tab in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/combination of characters is escaped" duration="{duration}"/> + </file> <file path="tests/<exe-name>/IntrospectiveTests/Parse.tests.cpp"> <testCase name="Parse uints/proper inputs" duration="{duration}"/> <testCase name="Parse uints/Bad inputs" duration="{duration}"/> @@ -151,6 +181,7 @@ <testCase name="Our PCG implementation provides expected results for known seeds/Specific seed" duration="{duration}"/> <testCase name="Random seed generation accepts known methods" duration="{duration}"/> <testCase name="Random seed generation reports unknown methods" duration="{duration}"/> + <testCase name="uniform_integer_distribution can return the bounds" duration="{duration}"/> </file> <file path="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp"> <testCase name="Multireporter calls reporters and listeners in correct order" duration="{duration}"/> @@ -168,6 +199,9 @@ <testCase name="Reporter's write listings to provided stream/console reporter lists tags" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/console reporter lists reporters" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/console reporter lists tests" duration="{duration}"/> + <testCase name="Reporter's write listings to provided stream/JSON reporter lists tags" duration="{duration}"/> + <testCase name="Reporter's write listings to provided stream/JSON reporter lists reporters" duration="{duration}"/> + <testCase name="Reporter's write listings to provided stream/JSON reporter lists tests" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/JUnit reporter lists tags" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/JUnit reporter lists reporters" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/JUnit reporter lists tests" duration="{duration}"/> @@ -1038,6 +1072,7 @@ at Generators.tests.cpp:<line number> <testCase name="Copy and then generate a range/from var and iterators" duration="{duration}"/> <testCase name="Copy and then generate a range/From a temporary container" duration="{duration}"/> <testCase name="Copy and then generate a range/Final validation" duration="{duration}"/> + <testCase name="GENERATE can combine literals and generators" duration="{duration}"/> <testCase name="Generators -- adapters/Filtering by predicate/Basic usage" duration="{duration}"/> <testCase name="Generators -- adapters/Filtering by predicate/Throws if there are no matching values" duration="{duration}"/> <testCase name="Generators -- adapters/Shortening a range" duration="{duration}"/> @@ -1467,6 +1502,15 @@ at Message.tests.cpp:<line number> <failure message="FAIL_CHECK()"> FAILED: This is a failure +at Message.tests.cpp:<line number> + </failure> + </testCase> + <testCase name="INFO and UNSCOPED_INFO can stream multiple arguments" duration="{duration}"> + <failure message="FAIL()"> +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. at Message.tests.cpp:<line number> </failure> </testCase> @@ -1727,6 +1771,22 @@ at Misc.tests.cpp:<line number> <testCase name="Testing checked-if 3" duration="{duration}"> <skipped message="FAIL()"> FAILED: +at Misc.tests.cpp:<line number> + </skipped> + </testCase> + <testCase name="Testing checked-if 4" duration="{duration}"> + <skipped message="({Unknown expression after the reported line})"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp:<line number> + </skipped> + </testCase> + <testCase name="Testing checked-if 5" duration="{duration}"> + <skipped message="({Unknown expression after the reported line})"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! at Misc.tests.cpp:<line number> </skipped> </testCase> diff --git a/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt index 3509287f7..c9d3d205b 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt @@ -1,6 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- filters='"*" ~[!nonportable] ~[!benchmark] ~[approvals]' rng-seed=1 --> <testExecutions version="1"> + <file path="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp"> + <testCase name="Incomplete AssertionHandler" duration="{duration}"> + <skipped message="REQUIRE(Dummy)"> +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp:<line number> + </skipped> + </testCase> + </file> <file path="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp"> <testCase name="Clara::Arg supports single-arg parse the way Opt does" duration="{duration}"/> <testCase name="Clara::Opt supports accept-many lambdas/Parsing fails on multiple options without accept_many" duration="{duration}"/> @@ -119,6 +129,26 @@ <testCase name="warmup" duration="{duration}"/> <testCase name="weighted_average_quantile" duration="{duration}"/> </file> + <file path="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp"> + <testCase name="JsonWriter/Newly constructed JsonWriter does nothing" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeObject will create an empty pair of braces" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeObject with key will create an object to write the value" duration="{duration}"/> + <testCase name="JsonWriter/nesting objects" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeArray will create an empty pair of braces" duration="{duration}"/> + <testCase name="JsonWriter/Calling writeArray creates array to write the values to" duration="{duration}"/> + <testCase name="JsonWriter/Moved from JsonObjectWriter shall not insert superfluous brace" duration="{duration}"/> + <testCase name="JsonWriter/Moved from JsonArrayWriter shall not insert superfluous bracket" duration="{duration}"/> + <testCase name="JsonWriter/Custom class shall be quoted" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Quote in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Backslash in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Forward slash in a string is **not** escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Backspace in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/Formfeed in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/linefeed in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/carriage return in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/tab in a string is escaped" duration="{duration}"/> + <testCase name="JsonWriter escapes charaters in strings properly/combination of characters is escaped" duration="{duration}"/> + </file> <file path="tests/<exe-name>/IntrospectiveTests/Parse.tests.cpp"> <testCase name="Parse uints/proper inputs" duration="{duration}"/> <testCase name="Parse uints/Bad inputs" duration="{duration}"/> @@ -150,6 +180,7 @@ <testCase name="Our PCG implementation provides expected results for known seeds/Specific seed" duration="{duration}"/> <testCase name="Random seed generation accepts known methods" duration="{duration}"/> <testCase name="Random seed generation reports unknown methods" duration="{duration}"/> + <testCase name="uniform_integer_distribution can return the bounds" duration="{duration}"/> </file> <file path="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp"> <testCase name="Multireporter calls reporters and listeners in correct order" duration="{duration}"/> @@ -167,6 +198,9 @@ <testCase name="Reporter's write listings to provided stream/console reporter lists tags" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/console reporter lists reporters" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/console reporter lists tests" duration="{duration}"/> + <testCase name="Reporter's write listings to provided stream/JSON reporter lists tags" duration="{duration}"/> + <testCase name="Reporter's write listings to provided stream/JSON reporter lists reporters" duration="{duration}"/> + <testCase name="Reporter's write listings to provided stream/JSON reporter lists tests" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/JUnit reporter lists tags" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/JUnit reporter lists reporters" duration="{duration}"/> <testCase name="Reporter's write listings to provided stream/JUnit reporter lists tests" duration="{duration}"/> @@ -1037,6 +1071,7 @@ at Generators.tests.cpp:<line number> <testCase name="Copy and then generate a range/from var and iterators" duration="{duration}"/> <testCase name="Copy and then generate a range/From a temporary container" duration="{duration}"/> <testCase name="Copy and then generate a range/Final validation" duration="{duration}"/> + <testCase name="GENERATE can combine literals and generators" duration="{duration}"/> <testCase name="Generators -- adapters/Filtering by predicate/Basic usage" duration="{duration}"/> <testCase name="Generators -- adapters/Filtering by predicate/Throws if there are no matching values" duration="{duration}"/> <testCase name="Generators -- adapters/Shortening a range" duration="{duration}"/> @@ -1466,6 +1501,15 @@ at Message.tests.cpp:<line number> <failure message="FAIL_CHECK()"> FAILED: This is a failure +at Message.tests.cpp:<line number> + </failure> + </testCase> + <testCase name="INFO and UNSCOPED_INFO can stream multiple arguments" duration="{duration}"> + <failure message="FAIL()"> +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. at Message.tests.cpp:<line number> </failure> </testCase> @@ -1726,6 +1770,22 @@ at Misc.tests.cpp:<line number> <testCase name="Testing checked-if 3" duration="{duration}"> <skipped message="FAIL()"> FAILED: +at Misc.tests.cpp:<line number> + </skipped> + </testCase> + <testCase name="Testing checked-if 4" duration="{duration}"> + <skipped message="({Unknown expression after the reported line})"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp:<line number> + </skipped> + </testCase> + <testCase name="Testing checked-if 5" duration="{duration}"> + <skipped message="({Unknown expression after the reported line})"> +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! at Misc.tests.cpp:<line number> </skipped> </testCase> diff --git a/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt index acd0a1c14..a02dbd954 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt @@ -659,7 +659,7 @@ ok {test-number} - unrelated::ADL_empty{}, IsEmpty() for: {?} is empty # CAPTURE can deal with complex expressions ok {test-number} - with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' # CAPTURE can deal with complex expressions involving commas -ok {test-number} - with 7 messages: 'std::vector<int>{1, 2, 3}[0, 1, 2] := 3' and 'std::vector<int>{1, 2, 3}[(0, 1)] := 2' and 'std::vector<int>{1, 2, 3}[0] := 1' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +ok {test-number} - with 7 messages: 'custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op<int>{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op<int>{1, 2, 3}[0] := 0' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' # CAPTURE parses string and character constants ok {test-number} - with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' # Capture and info messages @@ -1258,6 +1258,14 @@ ok {test-number} - WithinRel( 1.f, -0.2f ), std::domain_error ok {test-number} - WithinRel( 1.f, 1.f ), std::domain_error # Floating point matchers: float ok {test-number} - 1., !IsNaN() for: 1.0 not is NaN +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters @@ -1796,6 +1804,8 @@ ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2673152918 (0x<hex digits>) ! ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2074929312 (0x<hex digits>) != 3429949824 (0x<hex digits>) # Hashing test case produces same hash across multiple calls ok {test-number} - h( dummy ) == h( dummy ) for: 3422778688 (0x<hex digits>) == 3422778688 (0x<hex digits>) +# INFO and UNSCOPED_INFO can stream multiple arguments +not ok {test-number} - explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' # INFO and WARN do not abort tests warning {test-number} - 'this is a message' with 1 message: 'this is a warning' # INFO gets logged on failure @@ -1830,6 +1840,8 @@ ok {test-number} - i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and ' ok {test-number} - i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' # INFO is reset for each loop not ok {test-number} - i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +# Incomplete AssertionHandler +not ok {test-number} - unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy # Inequality checks that should fail not ok {test-number} - data.int_seven != 7 for: 7 != 7 # Inequality checks that should fail @@ -1862,6 +1874,42 @@ ok {test-number} - data.str_hello != "hell" for: "hello" != "hell" ok {test-number} - data.str_hello != "hello1" for: "hello" != "hello1" # Inequality checks that should succeed ok {test-number} - data.str_hello.size() != 6 for: 5 != 6 +# JsonWriter +ok {test-number} - stream.str() == "" for: "" == "" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ "int": 1, "double": 1.5, "true": true, "false": false, "string": "this is a string", "array": [ 1, 2 ] }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ 1, 2 ] }" ) +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ "empty_object": { }, "fully_object": { "key": 1 } }" ( contains: ""empty_object": { }," and contains: ""fully_object": { "key": 1 }" ) +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" == "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"/\"" for: ""/"" == ""/"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" # Lambdas in assertions ok {test-number} - []() { return true; }() for: true # Less-than inequalities with different epsilons @@ -2455,6 +2503,18 @@ ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && Cont # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false # Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tests": [ { "name": "fake test name", "class-name": "", "tags": [ "fakeTestTag" ], "source-location": { "filename": "fake-file.cpp", "line": 123456789 } } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "<?xml version="1.0" encoding="UTF-8"?> All available tags: 1 [fakeTag] 1 tag " contains: "fakeTag" with 1 message: 'Tested reporter: JUnit' # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false @@ -3067,6 +3127,14 @@ not ok {test-number} - explicitly ok {test-number} - false # TODO # Testing checked-if 3 not ok {test-number} - explicitly +# Testing checked-if 4 +ok {test-number} - true +# Testing checked-if 4 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +# Testing checked-if 5 +ok {test-number} - false # TODO +# Testing checked-if 5 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} # The NO_FAIL macro reports a failure but does not fail the test ok {test-number} - 1 == 2 # TODO # The default listing implementation write to provided stream @@ -4355,6 +4423,10 @@ ok {test-number} - e.upper_bound == 23 for: 23.0 == 23 ok {test-number} - e.lower_bound == 23 for: 23.0 == 23 # uniform samples ok {test-number} - e.confidence_interval == 0.95 for: 0.95 == 0.95 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.a() == -10 for: -10 == -10 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.b() == 10 for: 10 == 10 # unique_ptr reimplementation: basic functionality ok {test-number} - !(ptr) for: !{?} # unique_ptr reimplementation: basic functionality @@ -4477,5 +4549,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..2237 +1..2272 diff --git a/packages/Catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt index 033290497..13449bd40 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt @@ -657,7 +657,7 @@ ok {test-number} - unrelated::ADL_empty{}, IsEmpty() for: {?} is empty # CAPTURE can deal with complex expressions ok {test-number} - with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' # CAPTURE can deal with complex expressions involving commas -ok {test-number} - with 7 messages: 'std::vector<int>{1, 2, 3}[0, 1, 2] := 3' and 'std::vector<int>{1, 2, 3}[(0, 1)] := 2' and 'std::vector<int>{1, 2, 3}[0] := 1' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +ok {test-number} - with 7 messages: 'custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op<int>{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op<int>{1, 2, 3}[0] := 0' and '(helper_1436<int, int>{12, -12}) := { 12, -12 }' and '(helper_1436<int, int>(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' # CAPTURE parses string and character constants ok {test-number} - with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' # Capture and info messages @@ -1256,6 +1256,14 @@ ok {test-number} - WithinRel( 1.f, -0.2f ), std::domain_error ok {test-number} - WithinRel( 1.f, 1.f ), std::domain_error # Floating point matchers: float ok {test-number} - 1., !IsNaN() for: 1.0 not is NaN +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters @@ -1794,6 +1802,8 @@ ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2673152918 (0x<hex digits>) ! ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2074929312 (0x<hex digits>) != 3429949824 (0x<hex digits>) # Hashing test case produces same hash across multiple calls ok {test-number} - h( dummy ) == h( dummy ) for: 3422778688 (0x<hex digits>) == 3422778688 (0x<hex digits>) +# INFO and UNSCOPED_INFO can stream multiple arguments +not ok {test-number} - explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' # INFO and WARN do not abort tests warning {test-number} - 'this is a message' with 1 message: 'this is a warning' # INFO gets logged on failure @@ -1828,6 +1838,8 @@ ok {test-number} - i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and ' ok {test-number} - i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' # INFO is reset for each loop not ok {test-number} - i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +# Incomplete AssertionHandler +not ok {test-number} - unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy # Inequality checks that should fail not ok {test-number} - data.int_seven != 7 for: 7 != 7 # Inequality checks that should fail @@ -1860,6 +1872,42 @@ ok {test-number} - data.str_hello != "hell" for: "hello" != "hell" ok {test-number} - data.str_hello != "hello1" for: "hello" != "hello1" # Inequality checks that should succeed ok {test-number} - data.str_hello.size() != 6 for: 5 != 6 +# JsonWriter +ok {test-number} - stream.str() == "" for: "" == "" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ "int": 1, "double": 1.5, "true": true, "false": false, "string": "this is a string", "array": [ 1, 2 ] }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ 1, 2 ] }" ) +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ "empty_object": { }, "fully_object": { "key": 1 } }" ( contains: ""empty_object": { }," and contains: ""fully_object": { "key": 1 }" ) +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" == "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"/\"" for: ""/"" == ""/"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" # Lambdas in assertions ok {test-number} - []() { return true; }() for: true # Less-than inequalities with different epsilons @@ -2453,6 +2501,18 @@ ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && Cont # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false # Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tests": [ { "name": "fake test name", "class-name": "", "tags": [ "fakeTestTag" ], "source-location": { "filename": "fake-file.cpp", "line": 123456789 } } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "<?xml version="1.0" encoding="UTF-8"?> All available tags: 1 [fakeTag] 1 tag " contains: "fakeTag" with 1 message: 'Tested reporter: JUnit' # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false @@ -3060,6 +3120,14 @@ not ok {test-number} - explicitly ok {test-number} - false # TODO # Testing checked-if 3 not ok {test-number} - explicitly +# Testing checked-if 4 +ok {test-number} - true +# Testing checked-if 4 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +# Testing checked-if 5 +ok {test-number} - false # TODO +# Testing checked-if 5 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} # The NO_FAIL macro reports a failure but does not fail the test ok {test-number} - 1 == 2 # TODO # The default listing implementation write to provided stream @@ -4344,6 +4412,10 @@ ok {test-number} - e.upper_bound == 23 for: 23.0 == 23 ok {test-number} - e.lower_bound == 23 for: 23.0 == 23 # uniform samples ok {test-number} - e.confidence_interval == 0.95 for: 0.95 == 0.95 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.a() == -10 for: -10 == -10 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.b() == 10 for: 10 == 10 # unique_ptr reimplementation: basic functionality ok {test-number} - !(ptr) for: !{?} # unique_ptr reimplementation: basic functionality @@ -4466,5 +4538,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..2237 +1..2272 diff --git a/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt index a298633a1..2a2c40cfc 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt @@ -377,6 +377,8 @@ ##teamcity[testFinished name='Floating point matchers: double' duration="{duration}"] ##teamcity[testStarted name='Floating point matchers: float'] ##teamcity[testFinished name='Floating point matchers: float' duration="{duration}"] +##teamcity[testStarted name='GENERATE can combine literals and generators'] +##teamcity[testFinished name='GENERATE can combine literals and generators' duration="{duration}"] ##teamcity[testStarted name='Generators -- adapters'] ##teamcity[testFinished name='Generators -- adapters' duration="{duration}"] ##teamcity[testStarted name='Generators -- simple'] @@ -393,6 +395,9 @@ ##teamcity[testFinished name='Hashing different test cases produces different result' duration="{duration}"] ##teamcity[testStarted name='Hashing test case produces same hash across multiple calls'] ##teamcity[testFinished name='Hashing test case produces same hash across multiple calls' duration="{duration}"] +##teamcity[testStarted name='INFO and UNSCOPED_INFO can stream multiple arguments'] +##teamcity[testFailed name='INFO and UNSCOPED_INFO can stream multiple arguments' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexplicit failure with messages:|n "This info has multiple parts."|n "This unscoped info has multiple parts."|n "Show infos!"'] +##teamcity[testFinished name='INFO and UNSCOPED_INFO can stream multiple arguments' duration="{duration}"] ##teamcity[testStarted name='INFO and WARN do not abort tests'] ##teamcity[testFinished name='INFO and WARN do not abort tests' duration="{duration}"] ##teamcity[testStarted name='INFO gets logged on failure'] @@ -405,6 +410,9 @@ ##teamcity[testStarted name='INFO is reset for each loop'] ##teamcity[testFailed name='INFO is reset for each loop' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexpression failed with messages:|n "current counter 10"|n "i := 10"|n REQUIRE( i < 10 )|nwith expansion:|n 10 < 10|n'] ##teamcity[testFinished name='INFO is reset for each loop' duration="{duration}"] +##teamcity[testStarted name='Incomplete AssertionHandler'] +##teamcity[testIgnored name='Incomplete AssertionHandler' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"|n REQUIRE( Dummy )|nwith expansion:|n Dummy|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Incomplete AssertionHandler' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should fail'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:<line number>|n...............................................................................|n|nCondition.tests.cpp:<line number>|nexpression failed|n CHECK( data.int_seven != 7 )|nwith expansion:|n 7 != 7|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:<line number>|nexpression failed|n CHECK( data.float_nine_point_one != Approx( 9.1f ) )|nwith expansion:|n 9.1f != Approx( 9.1000003815 )|n- failure ignore as test marked as |'ok to fail|'|n'] @@ -414,6 +422,10 @@ ##teamcity[testFinished name='Inequality checks that should fail' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should succeed'] ##teamcity[testFinished name='Inequality checks that should succeed' duration="{duration}"] +##teamcity[testStarted name='JsonWriter'] +##teamcity[testFinished name='JsonWriter' duration="{duration}"] +##teamcity[testStarted name='JsonWriter escapes charaters in strings properly'] +##teamcity[testFinished name='JsonWriter escapes charaters in strings properly' duration="{duration}"] ##teamcity[testStarted name='Lambdas in assertions'] ##teamcity[testFinished name='Lambdas in assertions' duration="{duration}"] ##teamcity[testStarted name='Less-than inequalities with different epsilons'] @@ -639,6 +651,12 @@ ##teamcity[testStarted name='Testing checked-if 3'] ##teamcity[testIgnored name='Testing checked-if 3' message='Misc.tests.cpp:<line number>|n...............................................................................|n|nMisc.tests.cpp:<line number>|nexplicit failure- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Testing checked-if 3' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 4'] +##teamcity[testIgnored name='Testing checked-if 4' message='Misc.tests.cpp:<line number>|n...............................................................................|n|nMisc.tests.cpp:<line number>|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 4' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 5'] +##teamcity[testIgnored name='Testing checked-if 5' message='Misc.tests.cpp:<line number>|n...............................................................................|n|nMisc.tests.cpp:<line number>|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 5' duration="{duration}"] ##teamcity[testStarted name='The NO_FAIL macro reports a failure but does not fail the test'] ##teamcity[testFinished name='The NO_FAIL macro reports a failure but does not fail the test' duration="{duration}"] ##teamcity[testStarted name='The default listing implementation write to provided stream'] @@ -976,6 +994,8 @@ loose text artifact ##teamcity[testFinished name='tuple<tuple<int>,tuple<>,float>' duration="{duration}"] ##teamcity[testStarted name='uniform samples'] ##teamcity[testFinished name='uniform samples' duration="{duration}"] +##teamcity[testStarted name='uniform_integer_distribution can return the bounds'] +##teamcity[testFinished name='uniform_integer_distribution can return the bounds' duration="{duration}"] ##teamcity[testStarted name='unique_ptr reimplementation: basic functionality'] ##teamcity[testFinished name='unique_ptr reimplementation: basic functionality' duration="{duration}"] ##teamcity[testStarted name='vec<vec<string,alloc>> -> toString'] diff --git a/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt index 861d64715..24ed5d988 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt @@ -377,6 +377,8 @@ ##teamcity[testFinished name='Floating point matchers: double' duration="{duration}"] ##teamcity[testStarted name='Floating point matchers: float'] ##teamcity[testFinished name='Floating point matchers: float' duration="{duration}"] +##teamcity[testStarted name='GENERATE can combine literals and generators'] +##teamcity[testFinished name='GENERATE can combine literals and generators' duration="{duration}"] ##teamcity[testStarted name='Generators -- adapters'] ##teamcity[testFinished name='Generators -- adapters' duration="{duration}"] ##teamcity[testStarted name='Generators -- simple'] @@ -393,6 +395,9 @@ ##teamcity[testFinished name='Hashing different test cases produces different result' duration="{duration}"] ##teamcity[testStarted name='Hashing test case produces same hash across multiple calls'] ##teamcity[testFinished name='Hashing test case produces same hash across multiple calls' duration="{duration}"] +##teamcity[testStarted name='INFO and UNSCOPED_INFO can stream multiple arguments'] +##teamcity[testFailed name='INFO and UNSCOPED_INFO can stream multiple arguments' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexplicit failure with messages:|n "This info has multiple parts."|n "This unscoped info has multiple parts."|n "Show infos!"'] +##teamcity[testFinished name='INFO and UNSCOPED_INFO can stream multiple arguments' duration="{duration}"] ##teamcity[testStarted name='INFO and WARN do not abort tests'] ##teamcity[testFinished name='INFO and WARN do not abort tests' duration="{duration}"] ##teamcity[testStarted name='INFO gets logged on failure'] @@ -405,6 +410,9 @@ ##teamcity[testStarted name='INFO is reset for each loop'] ##teamcity[testFailed name='INFO is reset for each loop' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexpression failed with messages:|n "current counter 10"|n "i := 10"|n REQUIRE( i < 10 )|nwith expansion:|n 10 < 10|n'] ##teamcity[testFinished name='INFO is reset for each loop' duration="{duration}"] +##teamcity[testStarted name='Incomplete AssertionHandler'] +##teamcity[testIgnored name='Incomplete AssertionHandler' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"|n REQUIRE( Dummy )|nwith expansion:|n Dummy|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Incomplete AssertionHandler' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should fail'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:<line number>|n...............................................................................|n|nCondition.tests.cpp:<line number>|nexpression failed|n CHECK( data.int_seven != 7 )|nwith expansion:|n 7 != 7|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:<line number>|nexpression failed|n CHECK( data.float_nine_point_one != Approx( 9.1f ) )|nwith expansion:|n 9.1f != Approx( 9.1000003815 )|n- failure ignore as test marked as |'ok to fail|'|n'] @@ -414,6 +422,10 @@ ##teamcity[testFinished name='Inequality checks that should fail' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should succeed'] ##teamcity[testFinished name='Inequality checks that should succeed' duration="{duration}"] +##teamcity[testStarted name='JsonWriter'] +##teamcity[testFinished name='JsonWriter' duration="{duration}"] +##teamcity[testStarted name='JsonWriter escapes charaters in strings properly'] +##teamcity[testFinished name='JsonWriter escapes charaters in strings properly' duration="{duration}"] ##teamcity[testStarted name='Lambdas in assertions'] ##teamcity[testFinished name='Lambdas in assertions' duration="{duration}"] ##teamcity[testStarted name='Less-than inequalities with different epsilons'] @@ -639,6 +651,12 @@ ##teamcity[testStarted name='Testing checked-if 3'] ##teamcity[testIgnored name='Testing checked-if 3' message='Misc.tests.cpp:<line number>|n...............................................................................|n|nMisc.tests.cpp:<line number>|nexplicit failure- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Testing checked-if 3' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 4'] +##teamcity[testIgnored name='Testing checked-if 4' message='Misc.tests.cpp:<line number>|n...............................................................................|n|nMisc.tests.cpp:<line number>|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 4' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 5'] +##teamcity[testIgnored name='Testing checked-if 5' message='Misc.tests.cpp:<line number>|n...............................................................................|n|nMisc.tests.cpp:<line number>|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 5' duration="{duration}"] ##teamcity[testStarted name='The NO_FAIL macro reports a failure but does not fail the test'] ##teamcity[testFinished name='The NO_FAIL macro reports a failure but does not fail the test' duration="{duration}"] ##teamcity[testStarted name='The default listing implementation write to provided stream'] @@ -975,6 +993,8 @@ ##teamcity[testFinished name='tuple<tuple<int>,tuple<>,float>' duration="{duration}"] ##teamcity[testStarted name='uniform samples'] ##teamcity[testFinished name='uniform samples' duration="{duration}"] +##teamcity[testStarted name='uniform_integer_distribution can return the bounds'] +##teamcity[testFinished name='uniform_integer_distribution can return the bounds' duration="{duration}"] ##teamcity[testStarted name='unique_ptr reimplementation: basic functionality'] ##teamcity[testFinished name='unique_ptr reimplementation: basic functionality' duration="{duration}"] ##teamcity[testStarted name='vec<vec<string,alloc>> -> toString'] diff --git a/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt index bf9cf2053..be57798bf 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt @@ -667,7 +667,7 @@ Nor would this </Expression> <OverallResult success="true" skips="0"/> </TestCase> - <TestCase name="#2615 - Throwing in constructor generator fails test case but does not abort" tags="[!shouldfail]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <TestCase name="#2615 - Throwing in constructor generator fails test case but does not abort" tags="[!shouldfail][generators][regression]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > <Exception filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > failure to init </Exception> @@ -2911,13 +2911,13 @@ Nor would this </TestCase> <TestCase name="CAPTURE can deal with complex expressions involving commas" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > - std::vector<int>{1, 2, 3}[0, 1, 2] := 3 + custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0 </Info> <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > - std::vector<int>{1, 2, 3}[(0, 1)] := 2 + custom_index_op<int>{1, 2, 3}[(0, 1)] := 0 </Info> <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > - std::vector<int>{1, 2, 3}[0] := 1 + custom_index_op<int>{1, 2, 3}[0] := 0 </Info> <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > (helper_1436<int, int>{12, -12}) := { 12, -12 } @@ -5583,6 +5583,41 @@ C </Section> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="GENERATE can combine literals and generators" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="Generators -- adapters" tags="[generators][generic]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > @@ -8371,6 +8406,18 @@ C </Expression> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="INFO and UNSCOPED_INFO can stream multiple arguments" tags="[.][failing][info][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + This info has multiple parts. + </Info> + <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + This unscoped info has multiple parts. + </Info> + <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + Show infos! + </Failure> + <OverallResult success="false" skips="0"/> + </TestCase> <TestCase name="INFO and WARN do not abort tests" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > this is a message @@ -8619,6 +8666,20 @@ C </Expression> <OverallResult success="false" skips="0"/> </TestCase> + <TestCase name="Incomplete AssertionHandler" tags="[!shouldfail][assertion-handler]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" > + <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" > + <Original> + Dummy + </Original> + <Expanded> + Dummy + </Expanded> + <Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" > + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + </Exception> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="Inequality checks that should fail" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" > <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" > <Original> @@ -8753,6 +8814,277 @@ C </Expression> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="JsonWriter" tags="[JSON][JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Section name="Newly constructed JsonWriter does nothing" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "" + </Original> + <Expanded> + "" == "" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeObject will create an empty pair of braces" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "{\n}" + </Original> + <Expanded> + "{ +}" +== +"{ +}" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeObject with key will create an object to write the value" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) + </Original> + <Expanded> + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="nesting objects" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) + </Original> + <Expanded> + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeArray will create an empty pair of braces" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "[\n]" + </Original> + <Expanded> + "[ +]" +== +"[ +]" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeArray creates array to write the values to" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" + </Original> + <Expanded> + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Moved from JsonObjectWriter shall not insert superfluous brace" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "{\n}" + </Original> + <Expanded> + "{ +}" +== +"{ +}" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Moved from JsonArrayWriter shall not insert superfluous bracket" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "[\n]" + </Original> + <Expanded> + "[ +]" +== +"[ +]" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Custom class shall be quoted" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "\"custom\"" + </Original> + <Expanded> + ""custom"" == ""custom"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <OverallResult success="true" skips="0"/> + </TestCase> + <TestCase name="JsonWriter escapes charaters in strings properly" tags="[JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Section name="Quote in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\\"\"" + </Original> + <Expanded> + ""\""" == ""\""" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Backslash in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\\\\"" + </Original> + <Expanded> + ""\\"" == ""\\"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Forward slash in a string is **not** escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"/\"" + </Original> + <Expanded> + ""/"" == ""/"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Backspace in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\b\"" + </Original> + <Expanded> + ""\b"" == ""\b"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Formfeed in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\f\"" + </Original> + <Expanded> + ""\f"" == ""\f"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="linefeed in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\n\"" + </Original> + <Expanded> + ""\n"" == ""\n"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="carriage return in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\r\"" + </Original> + <Expanded> + ""\r"" == ""\r"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="tab in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\t\"" + </Original> + <Expanded> + ""\t"" == ""\t"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="combination of characters is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\\\/\\t\\r\\n\"" + </Original> + <Expanded> + ""\\/\t\r\n"" == ""\\/\t\r\n"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="Lambdas in assertions" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" > <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" > <Original> @@ -11669,6 +12001,120 @@ C !false </Expanded> </Expression> + <Section name="JSON reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + Tested reporter: JSON + </Info> + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + listingString, ContainsSubstring("fakeTag"s) + </Original> + <Expanded> + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + !(factories.empty()) + </Original> + <Expanded> + !false + </Expanded> + </Expression> + <Section name="JSON reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + Tested reporter: JSON + </Info> + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + listingString, ContainsSubstring("fake reporter"s) + </Original> + <Expanded> + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + !(factories.empty()) + </Original> + <Expanded> + !false + </Expanded> + </Expression> + <Section name="JSON reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + Tested reporter: JSON + </Info> + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) + </Original> + <Expanded> + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + !(factories.empty()) + </Original> + <Expanded> + !false + </Expanded> + </Expression> <Section name="JUnit reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > Tested reporter: JUnit @@ -14547,6 +14993,50 @@ Message from section two <Failure filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" /> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="Testing checked-if 4" tags="[!shouldfail][checked-if]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Expression success="true" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + true + </Original> + <Expanded> + true + </Expanded> + </Expression> + <Expression success="false" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + {Unknown expression after the reported line} + </Original> + <Expanded> + {Unknown expression after the reported line} + </Expanded> + <Exception filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + Uncaught exception should fail! + </Exception> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> + <TestCase name="Testing checked-if 5" tags="[!shouldfail][checked-if]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Expression success="false" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + false + </Original> + <Expanded> + false + </Expanded> + </Expression> + <Expression success="false" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + {Unknown expression after the reported line} + </Original> + <Expanded> + {Unknown expression after the reported line} + </Expanded> + <Exception filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + Uncaught exception should fail! + </Exception> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="The NO_FAIL macro reports a failure but does not fail the test" tags="[messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Expression success="false" type="CHECK_NOFAIL" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Original> @@ -20644,6 +21134,25 @@ b1! </Expression> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="uniform_integer_distribution can return the bounds" tags="[distribution][rng]" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" > + <Original> + dist.a() == -10 + </Original> + <Expanded> + -10 == -10 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" > + <Original> + dist.b() == 10 + </Original> + <Expanded> + 10 == 10 + </Expanded> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="unique_ptr reimplementation: basic functionality" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" > <Section name="Default constructed unique_ptr is empty" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" > <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" > @@ -21198,6 +21707,6 @@ b1! </Section> <OverallResult success="true" skips="0"/> </TestCase> - <OverallResults successes="2048" failures="145" expectedFailures="32" skips="12"/> - <OverallResultsCases successes="308" failures="84" expectedFailures="11" skips="6"/> + <OverallResults successes="2079" failures="146" expectedFailures="35" skips="12"/> + <OverallResultsCases successes="312" failures="85" expectedFailures="14" skips="6"/> </Catch2TestRun> diff --git a/packages/Catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt index 41dc8cb31..08ff6c437 100644 --- a/packages/Catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt +++ b/packages/Catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt @@ -667,7 +667,7 @@ Nor would this </Expression> <OverallResult success="true" skips="0"/> </TestCase> - <TestCase name="#2615 - Throwing in constructor generator fails test case but does not abort" tags="[!shouldfail]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <TestCase name="#2615 - Throwing in constructor generator fails test case but does not abort" tags="[!shouldfail][generators][regression]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > <Exception filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > failure to init </Exception> @@ -2911,13 +2911,13 @@ Nor would this </TestCase> <TestCase name="CAPTURE can deal with complex expressions involving commas" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > - std::vector<int>{1, 2, 3}[0, 1, 2] := 3 + custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0 </Info> <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > - std::vector<int>{1, 2, 3}[(0, 1)] := 2 + custom_index_op<int>{1, 2, 3}[(0, 1)] := 0 </Info> <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > - std::vector<int>{1, 2, 3}[0] := 1 + custom_index_op<int>{1, 2, 3}[0] := 0 </Info> <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > (helper_1436<int, int>{12, -12}) := { 12, -12 } @@ -5583,6 +5583,41 @@ C </Section> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="GENERATE can combine literals and generators" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > + <Original> + i % 2 == 0 + </Original> + <Expanded> + 0 == 0 + </Expanded> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="Generators -- adapters" tags="[generators][generic]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" > @@ -8371,6 +8406,18 @@ C </Expression> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="INFO and UNSCOPED_INFO can stream multiple arguments" tags="[.][failing][info][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + This info has multiple parts. + </Info> + <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + This unscoped info has multiple parts. + </Info> + <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > + Show infos! + </Failure> + <OverallResult success="false" skips="0"/> + </TestCase> <TestCase name="INFO and WARN do not abort tests" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > this is a message @@ -8619,6 +8666,20 @@ C </Expression> <OverallResult success="false" skips="0"/> </TestCase> + <TestCase name="Incomplete AssertionHandler" tags="[!shouldfail][assertion-handler]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" > + <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" > + <Original> + Dummy + </Original> + <Expanded> + Dummy + </Expanded> + <Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" > + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + </Exception> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="Inequality checks that should fail" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" > <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" > <Original> @@ -8753,6 +8814,277 @@ C </Expression> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="JsonWriter" tags="[JSON][JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Section name="Newly constructed JsonWriter does nothing" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "" + </Original> + <Expanded> + "" == "" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeObject will create an empty pair of braces" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "{\n}" + </Original> + <Expanded> + "{ +}" +== +"{ +}" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeObject with key will create an object to write the value" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) + </Original> + <Expanded> + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="nesting objects" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) + </Original> + <Expanded> + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeArray will create an empty pair of braces" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "[\n]" + </Original> + <Expanded> + "[ +]" +== +"[ +]" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Calling writeArray creates array to write the values to" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" + </Original> + <Expanded> + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Moved from JsonObjectWriter shall not insert superfluous brace" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "{\n}" + </Original> + <Expanded> + "{ +}" +== +"{ +}" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Moved from JsonArrayWriter shall not insert superfluous bracket" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "[\n]" + </Original> + <Expanded> + "[ +]" +== +"[ +]" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Custom class shall be quoted" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + stream.str() == "\"custom\"" + </Original> + <Expanded> + ""custom"" == ""custom"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <OverallResult success="true" skips="0"/> + </TestCase> + <TestCase name="JsonWriter escapes charaters in strings properly" tags="[JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Section name="Quote in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\\"\"" + </Original> + <Expanded> + ""\""" == ""\""" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Backslash in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\\\\"" + </Original> + <Expanded> + ""\\"" == ""\\"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Forward slash in a string is **not** escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"/\"" + </Original> + <Expanded> + ""/"" == ""/"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Backspace in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\b\"" + </Original> + <Expanded> + ""\b"" == ""\b"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="Formfeed in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\f\"" + </Original> + <Expanded> + ""\f"" == ""\f"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="linefeed in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\n\"" + </Original> + <Expanded> + ""\n"" == ""\n"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="carriage return in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\r\"" + </Original> + <Expanded> + ""\r"" == ""\r"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="tab in a string is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\t\"" + </Original> + <Expanded> + ""\t"" == ""\t"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Section name="combination of characters is escaped" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" > + <Original> + sstream.str() == "\"\\\\/\\t\\r\\n\"" + </Original> + <Expanded> + ""\\/\t\r\n"" == ""\\/\t\r\n"" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="Lambdas in assertions" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" > <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" > <Original> @@ -11669,6 +12001,120 @@ C !false </Expanded> </Expression> + <Section name="JSON reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + Tested reporter: JSON + </Info> + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + listingString, ContainsSubstring("fakeTag"s) + </Original> + <Expanded> + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + !(factories.empty()) + </Original> + <Expanded> + !false + </Expanded> + </Expression> + <Section name="JSON reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + Tested reporter: JSON + </Info> + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + listingString, ContainsSubstring("fake reporter"s) + </Original> + <Expanded> + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + !(factories.empty()) + </Original> + <Expanded> + !false + </Expanded> + </Expression> + <Section name="JSON reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + Tested reporter: JSON + </Info> + <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) + </Original> + <Expanded> + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "<version>" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) + </Expanded> + </Expression> + <OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/> + </Section> + <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > + <Original> + !(factories.empty()) + </Original> + <Expanded> + !false + </Expanded> + </Expression> <Section name="JUnit reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > <Info filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" > Tested reporter: JUnit @@ -14547,6 +14993,50 @@ Message from section two <Failure filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" /> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="Testing checked-if 4" tags="[!shouldfail][checked-if]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Expression success="true" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + true + </Original> + <Expanded> + true + </Expanded> + </Expression> + <Expression success="false" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + {Unknown expression after the reported line} + </Original> + <Expanded> + {Unknown expression after the reported line} + </Expanded> + <Exception filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + Uncaught exception should fail! + </Exception> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> + <TestCase name="Testing checked-if 5" tags="[!shouldfail][checked-if]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Expression success="false" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + false + </Original> + <Expanded> + false + </Expanded> + </Expression> + <Expression success="false" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + <Original> + {Unknown expression after the reported line} + </Original> + <Expanded> + {Unknown expression after the reported line} + </Expanded> + <Exception filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" > + Uncaught exception should fail! + </Exception> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="The NO_FAIL macro reports a failure but does not fail the test" tags="[messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Expression success="false" type="CHECK_NOFAIL" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" > <Original> @@ -20643,6 +21133,25 @@ b1! </Expression> <OverallResult success="true" skips="0"/> </TestCase> + <TestCase name="uniform_integer_distribution can return the bounds" tags="[distribution][rng]" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" > + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" > + <Original> + dist.a() == -10 + </Original> + <Expanded> + -10 == -10 + </Expanded> + </Expression> + <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" > + <Original> + dist.b() == 10 + </Original> + <Expanded> + 10 == 10 + </Expanded> + </Expression> + <OverallResult success="true" skips="0"/> + </TestCase> <TestCase name="unique_ptr reimplementation: basic functionality" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" > <Section name="Default constructed unique_ptr is empty" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" > <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" > @@ -21197,6 +21706,6 @@ b1! </Section> <OverallResult success="true" skips="0"/> </TestCase> - <OverallResults successes="2048" failures="145" expectedFailures="32" skips="12"/> - <OverallResultsCases successes="308" failures="84" expectedFailures="11" skips="6"/> + <OverallResults successes="2079" failures="146" expectedFailures="35" skips="12"/> + <OverallResultsCases successes="312" failures="85" expectedFailures="14" skips="6"/> </Catch2TestRun> diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp new file mode 100644 index 000000000..ab0960745 --- /dev/null +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp @@ -0,0 +1,17 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include <catch2/catch_test_macros.hpp> + +TEST_CASE( "Incomplete AssertionHandler", "[assertion-handler][!shouldfail]" ) { + Catch::AssertionHandler catchAssertionHandler( + "REQUIRE"_catch_sr, + CATCH_INTERNAL_LINEINFO, + "Dummy", + Catch::ResultDisposition::Normal ); +} diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp index 08a579c9d..d2181702d 100644 --- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp @@ -9,7 +9,9 @@ #include <catch2/catch_test_macros.hpp> #include <catch2/catch_template_test_macros.hpp> #include <catch2/internal/catch_floating_point_helpers.hpp> +#include <catch2/internal/catch_random_floating_point_helpers.hpp> +#include <limits> TEST_CASE("convertToBits", "[floating-point][conversion]") { using Catch::Detail::convertToBits; @@ -72,3 +74,66 @@ TEST_CASE("UlpDistance", "[floating-point][ulp][approvals]") { CHECK( ulpDistance( 1.f, 2.f ) == 0x80'00'00 ); CHECK( ulpDistance( -2.f, 2.f ) == 0x80'00'00'00 ); } + + + +TEMPLATE_TEST_CASE("gamma", "[approvals][floating-point][ulp][gamma]", float, double) { + using Catch::Detail::gamma; + using Catch::Detail::directCompare; + + // We need to butcher the equal tests with the directCompare helper, + // because the Wfloat-equal triggers in decomposer rather than here, + // so we cannot locally disable it. Goddamn GCC. + CHECK( directCompare( gamma( TestType( -1. ), TestType( 1. ) ), + gamma( TestType( 0.2332 ), TestType( 1.0 ) ) ) ); + CHECK( directCompare( gamma( TestType( -2. ), TestType( 0 ) ), + gamma( TestType( 1. ), TestType( 1.5 ) ) ) ); + CHECK( gamma( TestType( 0. ), TestType( 1.0 ) ) < + gamma( TestType( 1.0 ), TestType( 1.5 ) ) ); + CHECK( gamma( TestType( 0 ), TestType( 1. ) ) < + std::numeric_limits<TestType>::epsilon() ); + CHECK( gamma( TestType( -1. ), TestType( -0. ) ) < + std::numeric_limits<TestType>::epsilon() ); + CHECK( directCompare( gamma( TestType( 1. ), TestType( 2. ) ), + std::numeric_limits<TestType>::epsilon() ) ); + CHECK( directCompare( gamma( TestType( -2. ), TestType( -1. ) ), + std::numeric_limits<TestType>::epsilon() ) ); +} + +TEMPLATE_TEST_CASE("count_equidistant_floats", + "[approvals][floating-point][distance]", + float, + double) { + using Catch::Detail::count_equidistant_floats; + auto count_steps = []( TestType a, TestType b ) { + return count_equidistant_floats( a, b, Catch::Detail::gamma( a, b ) ); + }; + + CHECK( count_steps( TestType( -1. ), TestType( 1. ) ) == + 2 * count_steps( TestType( 0. ), TestType( 1. ) ) ); +} + +TEST_CASE( "count_equidistant_floats", + "[approvals][floating-point][distance]" ) { + using Catch::Detail::count_equidistant_floats; + auto count_floats_with_scaled_ulp = []( auto a, auto b ) { + return count_equidistant_floats( a, b, Catch::Detail::gamma( a, b ) ); + }; + + CHECK( count_floats_with_scaled_ulp( 1., 1.5 ) == 1ull << 51 ); + CHECK( count_floats_with_scaled_ulp( 1.25, 1.5 ) == 1ull << 50 ); + CHECK( count_floats_with_scaled_ulp( 1.f, 1.5f ) == 1 << 22 ); + CHECK( count_floats_with_scaled_ulp( -std::numeric_limits<float>::max(), + std::numeric_limits<float>::max() ) == + 33554430 ); // (1 << 25) - 2 due to not including infinities + CHECK( count_floats_with_scaled_ulp( -std::numeric_limits<double>::max(), + std::numeric_limits<double>::max() ) == + 18014398509481982 ); // (1 << 54) - 2 due to not including infinities + + STATIC_REQUIRE( std::is_same<std::uint64_t, + decltype( count_floats_with_scaled_ulp( + 0., 1. ) )>::value ); + STATIC_REQUIRE( std::is_same<std::uint32_t, + decltype( count_floats_with_scaled_ulp( + 0.f, 1.f ) )>::value ); +} diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp index f7b7c57cc..acfeebed0 100644 --- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp @@ -10,6 +10,8 @@ # pragma GCC diagnostic ignored "-Wfloat-equal" #endif +#include <helpers/range_test_helpers.hpp> + #include <catch2/catch_approx.hpp> #include <catch2/catch_test_macros.hpp> #include <catch2/generators/catch_generator_exception.hpp> @@ -545,3 +547,30 @@ TEST_CASE("Filter generator throws exception for empty generator", filter( []( int ) { return false; }, value( 3 ) ), Catch::GeneratorException ); } + +TEST_CASE("from_range(container) supports ADL begin/end and arrays", "[generators][from-range][approvals]") { + using namespace Catch::Generators; + + SECTION("C array") { + int arr[3]{ 5, 6, 7 }; + auto gen = from_range( arr ); + REQUIRE( gen.get() == 5 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 6 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 7 ); + REQUIRE_FALSE( gen.next() ); + } + + SECTION( "ADL range" ) { + unrelated::needs_ADL_begin<int> range{ 1, 2, 3 }; + auto gen = from_range( range ); + REQUIRE( gen.get() == 1 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 2 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 3 ); + REQUIRE_FALSE( gen.next() ); + } + +} diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp new file mode 100644 index 000000000..fd620ebbf --- /dev/null +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp @@ -0,0 +1,150 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include <catch2/catch_test_macros.hpp> +#include <catch2/internal/catch_random_integer_helpers.hpp> + +namespace { + template <typename Int> + static void + CommutativeMultCheck( Int a, Int b, Int upper_result, Int lower_result ) { + using Catch::Detail::extendedMult; + using Catch::Detail::ExtendedMultResult; + CHECK( extendedMult( a, b ) == + ExtendedMultResult<Int>{ upper_result, lower_result } ); + CHECK( extendedMult( b, a ) == + ExtendedMultResult<Int>{ upper_result, lower_result } ); + } +} // namespace + +TEST_CASE( "extendedMult 64x64", "[Integer][approvals]" ) { + // a x 0 == 0 + CommutativeMultCheck<uint64_t>( 0x1234'5678'9ABC'DEFF, 0, 0, 0 ); + + // bit carried from low half to upper half + CommutativeMultCheck<uint64_t>( uint64_t( 1 ) << 63, 2, 1, 0 ); + + // bits in upper half on one side, bits in lower half on other side + CommutativeMultCheck<uint64_t>( 0xcdcd'dcdc'0000'0000, + 0x0000'0000'aeae'aeae, + 0x0000'0000'8c6e'5a77, + 0x7391'a588'0000'0000 ); + + // Some input numbers without interesting patterns + CommutativeMultCheck<uint64_t>( 0xaaaa'aaaa'aaaa'aaaa, + 0xbbbb'bbbb'bbbb'bbbb, + 0x7d27'd27d'27d2'7d26, + 0xd82d'82d8'2d82'd82e ); + + CommutativeMultCheck<uint64_t>( 0x7d27'd27d'27d2'7d26, + 0xd82d'82d8'2d82'd82e, + 0x69af'd991'8256'b953, + 0x8724'8909'fcb6'8cd4 ); + + CommutativeMultCheck<uint64_t>( 0xdead'beef'dead'beef, + 0xfeed'feed'feed'feef, + 0xddbf'680b'2b0c'b558, + 0x7a36'b06f'2ce9'6321 ); + + CommutativeMultCheck<uint64_t>( 0xddbf'680b'2b0c'b558, + 0x7a36'b06f'2ce9'6321, + 0x69dc'96c9'294b'fc7f, + 0xd038'39fa'a3dc'6858 ); + + CommutativeMultCheck<uint64_t>( 0x61c8'8646'80b5'83eb, + 0x61c8'8646'80b5'83eb, + 0x2559'92d3'8220'8bbe, + 0xdf44'2d22'ce48'59b9 ); +} + +TEST_CASE( "SizedUnsignedType helpers", "[integer][approvals]" ) { + using Catch::Detail::SizedUnsignedType_t; + using Catch::Detail::DoubleWidthUnsignedType_t; + + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<1> ) == 1 ); + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<2> ) == 2 ); + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<4> ) == 4 ); + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<8> ) == 8 ); + + STATIC_REQUIRE( sizeof( DoubleWidthUnsignedType_t<std::uint8_t> ) == 2 ); + STATIC_REQUIRE( std::is_unsigned<DoubleWidthUnsignedType_t<std::uint8_t>>::value ); + STATIC_REQUIRE( sizeof( DoubleWidthUnsignedType_t<std::uint16_t> ) == 4 ); + STATIC_REQUIRE( std::is_unsigned<DoubleWidthUnsignedType_t<std::uint16_t>>::value ); + STATIC_REQUIRE( sizeof( DoubleWidthUnsignedType_t<std::uint32_t> ) == 8 ); + STATIC_REQUIRE( std::is_unsigned<DoubleWidthUnsignedType_t<std::uint32_t>>::value ); +} + +TEST_CASE( "extendedMult 32x32", "[integer][approvals]" ) { + // a x 0 == 0 + CommutativeMultCheck<uint32_t>( 0x1234'5678, 0, 0, 0 ); + + // bit carried from low half to upper half + CommutativeMultCheck<uint32_t>( uint32_t(1) << 31, 2, 1, 0 ); + + // bits in upper half on one side, bits in lower half on other side + CommutativeMultCheck<uint32_t>( 0xdcdc'0000, 0x0000'aabb, 0x0000'934b, 0x6cb4'0000 ); + + // Some input numbers without interesting patterns + CommutativeMultCheck<uint32_t>( + 0xaaaa'aaaa, 0xbbbb'bbbb, 0x7d27'd27c, 0x2d82'd82e ); + + CommutativeMultCheck<uint32_t>( + 0x7d27'd27c, 0x2d82'd82e, 0x163f'f7e8, 0xc5b8'7248 ); + + CommutativeMultCheck<uint32_t>( + 0xdead'beef, 0xfeed'feed, 0xddbf'6809, 0x6f8d'e543 ); + + CommutativeMultCheck<uint32_t>( + 0xddbf'6809, 0x6f8d'e543, 0x60a0'e71e, 0x751d'475b ); +} + +TEST_CASE( "extendedMult 8x8", "[integer][approvals]" ) { + // a x 0 == 0 + CommutativeMultCheck<uint8_t>( 0xcd, 0, 0, 0 ); + + // bit carried from low half to upper half + CommutativeMultCheck<uint8_t>( uint8_t( 1 ) << 7, 2, 1, 0 ); + + // bits in upper half on one side, bits in lower half on other side + CommutativeMultCheck<uint8_t>( 0x80, 0x03, 0x01, 0x80 ); + + // Some input numbers without interesting patterns + CommutativeMultCheck<uint8_t>( 0xaa, 0xbb, 0x7c, 0x2e ); + CommutativeMultCheck<uint8_t>( 0x7c, 0x2e, 0x16, 0x48 ); + CommutativeMultCheck<uint8_t>( 0xdc, 0xcd, 0xb0, 0x2c ); + CommutativeMultCheck<uint8_t>( 0xb0, 0x2c, 0x1e, 0x40 ); +} + + +TEST_CASE( "negative and positive signed integers keep their order after transposeToNaturalOrder", + "[integer][approvals]") { + using Catch::Detail::transposeToNaturalOrder; + int32_t negative( -1 ); + int32_t positive( 1 ); + uint32_t adjusted_negative = + transposeToNaturalOrder<int32_t>( static_cast<uint32_t>( negative ) ); + uint32_t adjusted_positive = + transposeToNaturalOrder<int32_t>( static_cast<uint32_t>( positive ) ); + REQUIRE( adjusted_negative < adjusted_positive ); + REQUIRE( adjusted_positive - adjusted_negative == 2 ); + + // Conversion has to be reversible + REQUIRE( negative == static_cast<int32_t>( transposeToNaturalOrder<int32_t>( + adjusted_negative ) ) ); + REQUIRE( positive == static_cast<int32_t>( transposeToNaturalOrder<int32_t>( + adjusted_positive ) ) ); +} + +TEST_CASE( "unsigned integers are unchanged by transposeToNaturalOrder", + "[integer][approvals]") { + using Catch::Detail::transposeToNaturalOrder; + uint32_t max = std::numeric_limits<uint32_t>::max(); + uint32_t zero = 0; + REQUIRE( max == transposeToNaturalOrder<uint32_t>( max ) ); + REQUIRE( zero == transposeToNaturalOrder<uint32_t>( zero ) ); +} diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp index 24bfe68cd..bc8d715b4 100644 --- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp @@ -156,8 +156,12 @@ TEST_CASE("uniform samples", "[benchmark]") { std::vector<double> samples(100); std::fill(samples.begin(), samples.end(), 23); - using it = std::vector<double>::iterator; - auto e = Catch::Benchmark::Detail::bootstrap(0.95, samples.begin(), samples.end(), samples, [](it a, it b) { + auto e = Catch::Benchmark::Detail::bootstrap( + 0.95, + samples.data(), + samples.data() + samples.size(), + samples, + []( double const* a, double const* b ) { auto sum = std::accumulate(a, b, 0.); return sum / (b - a); }); @@ -198,7 +202,7 @@ TEST_CASE("normal_quantile", "[benchmark]") { TEST_CASE("mean", "[benchmark]") { std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; - auto m = Catch::Benchmark::Detail::mean(x.begin(), x.end()); + auto m = Catch::Benchmark::Detail::mean(x.data(), x.data() + x.size()); REQUIRE(m == 19.); } @@ -206,9 +210,9 @@ TEST_CASE("mean", "[benchmark]") { TEST_CASE("weighted_average_quantile", "[benchmark]") { std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; - auto q1 = Catch::Benchmark::Detail::weighted_average_quantile(1, 4, x.begin(), x.end()); - auto med = Catch::Benchmark::Detail::weighted_average_quantile(1, 2, x.begin(), x.end()); - auto q3 = Catch::Benchmark::Detail::weighted_average_quantile(3, 4, x.begin(), x.end()); + auto q1 = Catch::Benchmark::Detail::weighted_average_quantile(1, 4, x.data(), x.data() + x.size()); + auto med = Catch::Benchmark::Detail::weighted_average_quantile(1, 2, x.data(), x.data() + x.size()); + auto q3 = Catch::Benchmark::Detail::weighted_average_quantile(3, 4, x.data(), x.data() + x.size()); REQUIRE(q1 == 14.5); REQUIRE(med == 18.); @@ -227,7 +231,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("none") { std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast<int>(x.size())); require_outliers(o, 0, 0, 0, 0); @@ -235,7 +240,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("low severe") { std::vector<double> x{ -12., 20., 14., 16., 30., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast<int>(x.size())); require_outliers(o, 1, 0, 0, 0); @@ -243,7 +249,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("low mild") { std::vector<double> x{ 1., 20., 14., 16., 30., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast<int>(x.size())); require_outliers(o, 0, 1, 0, 0); @@ -251,7 +258,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("high mild") { std::vector<double> x{ 10., 20., 14., 16., 36., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast<int>(x.size())); require_outliers(o, 0, 0, 1, 0); @@ -259,7 +267,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("high severe") { std::vector<double> x{ 10., 20., 14., 16., 49., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast<int>(x.size())); require_outliers(o, 0, 0, 0, 1); @@ -267,7 +276,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("mixed") { std::vector<double> x{ -20., 20., 14., 16., 39., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast<int>(x.size())); require_outliers(o, 1, 0, 1, 0); @@ -282,15 +292,13 @@ TEST_CASE("analyse", "[approvals][benchmark]") { data.benchmarkSamples = 99; Catch::Config config{data}; - using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>; - - Catch::Benchmark::Environment<Duration> env; - std::vector<Duration> samples(99); + using FDuration = Catch::Benchmark::FDuration; + std::vector<FDuration> samples(99); for (size_t i = 0; i < samples.size(); ++i) { - samples[i] = Duration(23 + (i % 3 - 1)); + samples[i] = FDuration(23 + (i % 3 - 1)); } - auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); + auto analysis = Catch::Benchmark::Detail::analyse(config, samples.data(), samples.data() + samples.size()); CHECK( analysis.mean.point.count() == 23 ); CHECK( analysis.mean.lower_bound.count() < 23 ); CHECK(analysis.mean.lower_bound.count() > 22); @@ -323,15 +331,13 @@ TEST_CASE("analyse no analysis", "[benchmark]") { data.benchmarkSamples = 99; Catch::Config config{ data }; - using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>; - - Catch::Benchmark::Environment<Duration> env; - std::vector<Duration> samples(99); + using FDuration = Catch::Benchmark::FDuration; + std::vector<FDuration> samples(99); for (size_t i = 0; i < samples.size(); ++i) { - samples[i] = Duration(23 + (i % 3 - 1)); + samples[i] = FDuration(23 + (i % 3 - 1)); } - auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); + auto analysis = Catch::Benchmark::Detail::analyse(config, samples.data(), samples.data() + samples.size()); CHECK(analysis.mean.point.count() == 23); CHECK(analysis.mean.lower_bound.count() == 23); CHECK(analysis.mean.upper_bound.count() == 23); diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp new file mode 100644 index 000000000..8204e3c4b --- /dev/null +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp @@ -0,0 +1,152 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include <catch2/catch_test_macros.hpp> +#include <catch2/internal/catch_jsonwriter.hpp> +#include <catch2/matchers/catch_matchers_string.hpp> + +#include <sstream> + +namespace { + struct Custom {}; + static std::ostream& operator<<( std::ostream& os, Custom const& ) { + return os << "custom"; + } +} // namespace + +TEST_CASE( "JsonWriter", "[JSON][JsonWriter]" ) { + + std::stringstream stream; + SECTION( "Newly constructed JsonWriter does nothing" ) { + Catch::JsonValueWriter writer{ stream }; + REQUIRE( stream.str() == "" ); + } + + SECTION( "Calling writeObject will create an empty pair of braces" ) { + { auto writer = Catch::JsonValueWriter{ stream }.writeObject(); } + REQUIRE( stream.str() == "{\n}" ); + } + + SECTION( "Calling writeObject with key will create an object to write the " + "value" ) { + using Catch::Matchers::ContainsSubstring; + { + auto writer = Catch::JsonValueWriter{ stream }.writeObject(); + writer.write( "int" ).write( 1 ); + writer.write( "double" ).write( 1.5 ); + writer.write( "true" ).write( true ); + writer.write( "false" ).write( false ); + writer.write( "string" ).write( "this is a string" ); + writer.write( "array" ).writeArray().write( 1 ).write( 2 ); + } + REQUIRE_THAT( + stream.str(), + ContainsSubstring( "\"int\": 1," ) && + ContainsSubstring( "\"double\": 1.5," ) && + ContainsSubstring( "\"true\": true," ) && + ContainsSubstring( "\"false\": false," ) && + ContainsSubstring( "\"string\": \"this is a string\"," ) && + ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) ); + } + + SECTION( "nesting objects" ) { + using Catch::Matchers::ContainsSubstring; + { + auto writer = Catch::JsonValueWriter{ stream }.writeObject(); + writer.write( "empty_object" ).writeObject(); + writer.write( "fully_object" ) + .writeObject() + .write( "key" ) + .write( 1 ); + } + REQUIRE_THAT( stream.str(), + ContainsSubstring( "\"empty_object\": {\n }," ) && + ContainsSubstring( + "\"fully_object\": {\n \"key\": 1\n }" ) ); + } + + SECTION( "Calling writeArray will create an empty pair of braces" ) { + { auto writer = Catch::JsonValueWriter{ stream }.writeArray(); } + REQUIRE( stream.str() == "[\n]" ); + } + + SECTION( "Calling writeArray creates array to write the values to" ) { + { + auto writer = Catch::JsonValueWriter{ stream }.writeArray(); + writer.write( 1 ); + writer.write( 1.5 ); + writer.write( true ); + writer.write( false ); + writer.write( "this is a string" ); + writer.writeObject().write( "object" ).write( 42 ); + writer.writeArray().write( "array" ).write( 42.5 ); + } + REQUIRE( stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" ); + } + + SECTION( + "Moved from JsonObjectWriter shall not insert superfluous brace" ) { + { + auto writer = Catch::JsonObjectWriter{ stream }; + auto another_writer = std::move( writer ); + } + REQUIRE( stream.str() == "{\n}" ); + } + SECTION( + "Moved from JsonArrayWriter shall not insert superfluous bracket" ) { + { + auto writer = Catch::JsonArrayWriter{ stream }; + auto another_writer = std::move( writer ); + } + REQUIRE( stream.str() == "[\n]" ); + } + SECTION( "Custom class shall be quoted" ) { + Catch::JsonValueWriter{ stream }.write( Custom{} ); + REQUIRE( stream.str() == "\"custom\"" ); + } +} + +TEST_CASE( "JsonWriter escapes charaters in strings properly", "[JsonWriter]" ) { + std::stringstream sstream; + SECTION( "Quote in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\"" ); + REQUIRE( sstream.str() == "\"\\\"\"" ); + } + SECTION("Backslash in a string is escaped") { + Catch::JsonValueWriter{ sstream }.write( "\\" ); + REQUIRE( sstream.str() == "\"\\\\\"" ); + } + SECTION( "Forward slash in a string is **not** escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "/" ); + REQUIRE( sstream.str() == "\"/\"" ); + } + SECTION( "Backspace in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\b" ); + REQUIRE( sstream.str() == "\"\\b\"" ); + } + SECTION( "Formfeed in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\f" ); + REQUIRE( sstream.str() == "\"\\f\"" ); + } + SECTION( "linefeed in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\n" ); + REQUIRE( sstream.str() == "\"\\n\"" ); + } + SECTION( "carriage return in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\r" ); + REQUIRE( sstream.str() == "\"\\r\"" ); + } + SECTION( "tab in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\t" ); + REQUIRE( sstream.str() == "\"\\t\"" ); + } + SECTION( "combination of characters is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\\/\t\r\n" ); + REQUIRE( sstream.str() == "\"\\\\/\\t\\r\\n\"" ); + } +} diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp index 8018b7eb0..03be6c9ca 100644 --- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp @@ -7,9 +7,17 @@ // SPDX-License-Identifier: BSL-1.0 #include <catch2/catch_test_macros.hpp> +#include <catch2/catch_template_test_macros.hpp> +#include <catch2/internal/catch_floating_point_helpers.hpp> +#include <catch2/internal/catch_random_integer_helpers.hpp> #include <catch2/internal/catch_random_number_generator.hpp> #include <catch2/internal/catch_random_seed_generation.hpp> +#include <catch2/internal/catch_uniform_floating_point_distribution.hpp> +#include <catch2/internal/catch_uniform_integer_distribution.hpp> #include <catch2/generators/catch_generators.hpp> +#include <catch2/matchers/catch_matchers_range_equals.hpp> + +#include <random> TEST_CASE("Our PCG implementation provides expected results for known seeds", "[rng]") { Catch::SimplePcg32 rng; @@ -60,3 +68,523 @@ TEST_CASE("Random seed generation accepts known methods", "[rng][seed]") { REQUIRE_NOTHROW(Catch::generateRandomSeed(method)); } + +TEMPLATE_TEST_CASE("uniform_floating_point_distribution never returns infs from finite range", + "[rng][distribution][floating-point][approvals]", float, double) { + std::random_device rd{}; + Catch::SimplePcg32 pcg( rd() ); + Catch::uniform_floating_point_distribution<TestType> dist( + -std::numeric_limits<TestType>::max(), + std::numeric_limits<TestType>::max() ); + + for (size_t i = 0; i < 10'000; ++i) { + auto ret = dist( pcg ); + REQUIRE_FALSE( std::isinf( ret ) ); + REQUIRE_FALSE( std::isnan( ret ) ); + } +} + +TEST_CASE( "fillBitsFrom - shortening and stretching", "[rng][approvals]" ) { + using Catch::Detail::fillBitsFrom; + + // The seed is not important, but the numbers below have to be repeatable. + // They should also exhibit the same general pattern of being prefixes + Catch::SimplePcg32 pcg( 0xaabb'ccdd ); + + SECTION( "Shorten to 8 bits" ) { + // We cast the result to avoid dealing with char-like type in uint8_t + auto shortened = static_cast<uint32_t>( fillBitsFrom<uint8_t>( pcg ) ); + REQUIRE( shortened == 0xcc ); + } + SECTION( "Shorten to 16 bits" ) { + auto shortened = fillBitsFrom<uint16_t>( pcg ); + REQUIRE( shortened == 0xccbe ); + } + SECTION( "Keep at 32 bits" ) { + auto n = fillBitsFrom<uint32_t>( pcg ); + REQUIRE( n == 0xccbe'5f04 ); + } + SECTION( "Stretch to 64 bits" ) { + auto stretched = fillBitsFrom<uint64_t>( pcg ); + REQUIRE( stretched == 0xccbe'5f04'a424'a486 ); + } +} + +TEST_CASE("uniform_integer_distribution can return the bounds", "[rng][distribution]") { + Catch::uniform_integer_distribution<int32_t> dist( -10, 10 ); + REQUIRE( dist.a() == -10 ); + REQUIRE( dist.b() == 10 ); +} + +namespace { + template <typename T> + static void CheckReturnValue(Catch::uniform_integer_distribution<T>& dist, + Catch::SimplePcg32& rng, + T target) { + REQUIRE( dist.a() == dist.b() ); + for (int i = 0; i < 1'000; ++i) { + REQUIRE( dist( rng ) == target ); + } + } +} + +TEMPLATE_TEST_CASE( "uniform_integer_distribution can handle unit ranges", + "[rng][distribution][approvals]", + unsigned char, + signed char, + char, + uint8_t, + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t ) { + // We want random seed to sample different parts of the rng state, + // the output is predetermined anyway + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + // We check unitary ranges of 3 different values, min for type, max for type, + // some value inbetween just to make sure + SECTION("lowest value") { + constexpr auto lowest = std::numeric_limits<TestType>::min(); + Catch::uniform_integer_distribution<TestType> dist( lowest, lowest ); + CheckReturnValue( dist, pcg, lowest ); + } + SECTION( "highest value" ) { + constexpr auto highest = std::numeric_limits<TestType>::max(); + Catch::uniform_integer_distribution<TestType> dist( highest, highest ); + CheckReturnValue( dist, pcg, highest ); + } + SECTION( "some value" ) { + constexpr auto some = TestType( 42 ); + Catch::uniform_integer_distribution<TestType> dist( some, some ); + CheckReturnValue( dist, pcg, some ); + } +} + +// Bool needs its own test because it doesn't have a valid "third" value +TEST_CASE( "uniform_integer_distribution can handle boolean unit ranges", + "[rng][distribution][approvals]" ) { + // We want random seed to sample different parts of the rng state, + // the output is predetermined anyway + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + // We check unitary ranges of 3 different values, min for type, max for + // type, some value inbetween just to make sure + SECTION( "lowest value" ) { + Catch::uniform_integer_distribution<bool> dist( false, false ); + CheckReturnValue( dist, pcg, false ); + } + SECTION( "highest value" ) { + Catch::uniform_integer_distribution<bool> dist( true, true ); + CheckReturnValue( dist, pcg, true ); + } +} + +TEMPLATE_TEST_CASE( "uniform_integer_distribution can handle full width ranges", + "[rng][distribution][approvals]", + unsigned char, + signed char, + char, + uint8_t, + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t ) { + // We want random seed to sample different parts of the rng state, + // the output is predetermined anyway + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + constexpr auto lowest = std::numeric_limits<TestType>::min(); + constexpr auto highest = std::numeric_limits<TestType>::max(); + Catch::uniform_integer_distribution<TestType> dist( lowest, highest ); + STATIC_REQUIRE( std::is_same<TestType, decltype( dist( pcg ) )>::value ); + + // We need to do bit operations on the results, so we will have to + // cast them to unsigned type. + using BitType = std::make_unsigned_t<TestType>; + BitType ORs = 0; + BitType ANDs = BitType(-1); + for (int i = 0; i < 100; ++i) { + auto bits = static_cast<BitType>( dist( pcg ) ); + ORs |= bits; + ANDs &= bits; + } + // Assuming both our RNG and distribution are unbiased, asking for + // the full range should essentially give us random bit generator. + // Over long run, OR of all the generated values should have all + // bits set to 1, while AND should have all bits set to 0. + // The chance of this test failing for unbiased pipeline is + // 1 / 2**iters, which for 100 iterations is astronomical. + REQUIRE( ORs == BitType( -1 ) ); + REQUIRE( ANDs == 0 ); +} + +namespace { + template <typename T> + struct uniform_integer_test_params; + + template <> + struct uniform_integer_test_params<bool> { + static constexpr bool lowest = false; + static constexpr bool highest = true; + // This seems weird, but it is an artifact of the specific seed + static constexpr bool expected[] = { true, + true, + true, + true, + true, + true, + false, + true, + true, + true, + true, + true, + false, + true, + true }; + }; + + template <> + struct uniform_integer_test_params<char> { + static constexpr char lowest = 32; + static constexpr char highest = 126; + static constexpr char expected[] = { 'k', + '\\', + 'Z', + 'X', + '`', + 'Q', + ';', + 'o', + ']', + 'T', + 'v', + 'p', + ':', + 'S', + 't' }; + }; + + template <> + struct uniform_integer_test_params<uint8_t> { + static constexpr uint8_t lowest = 3; + static constexpr uint8_t highest = 123; + static constexpr uint8_t expected[] = { 'c', + 'P', + 'M', + 'J', + 'U', + 'A', + '%', + 'h', + 'Q', + 'F', + 'q', + 'i', + '$', + 'E', + 'o' }; + }; + + template <> + struct uniform_integer_test_params<int8_t> { + static constexpr int8_t lowest = -27; + static constexpr int8_t highest = 73; + static constexpr int8_t expected[] = { '5', + '%', + '#', + ' ', + '*', + 25, + 2, + '9', + '&', + 29, + 'A', + ':', + 1, + 28, + '?' }; + }; + + template <> + struct uniform_integer_test_params<uint16_t> { + static constexpr uint16_t lowest = 123; + static constexpr uint16_t highest = 33333; + static constexpr uint16_t expected[] = { 26684, + 21417, + 20658, + 19791, + 22896, + 17433, + 9806, + 27948, + 21767, + 18588, + 30556, + 28244, + 9439, + 18293, + 29949 }; + }; + + template <> + struct uniform_integer_test_params<int16_t> { + static constexpr int16_t lowest = -17222; + static constexpr int16_t highest = 17222; + static constexpr int16_t expected[] = { 10326, + 4863, + 4076, + 3177, + 6397, + 731, + -7179, + 11637, + 5226, + 1929, + 14342, + 11944, + -7560, + 1623, + 13712 }; + }; + + template <> + struct uniform_integer_test_params<uint32_t> { + static constexpr uint32_t lowest = 17222; + static constexpr uint32_t highest = 234234; + static constexpr uint32_t expected[] = { 190784, + 156367, + 151409, + 145743, + 166032, + 130337, + 80501, + 199046, + 158654, + 137883, + 216091, + 200981, + 78099, + 135954, + 212120 }; + }; + + template <> + struct uniform_integer_test_params<int32_t> { + static constexpr int32_t lowest = -237272; + static constexpr int32_t highest = 234234; + static constexpr int32_t expected[] = { 139829, + 65050, + 54278, + 41969, + 86051, + 8494, + -99785, + 157781, + 70021, + 24890, + 194815, + 161985, + -105004, + 20699, + 186186 }; + }; + + template <> + struct uniform_integer_test_params<uint64_t> { + static constexpr uint64_t lowest = 1234; + static constexpr uint64_t highest = 1234567890; + static constexpr uint64_t expected[] = { 987382749, + 763380386, + 846572137, + 359990258, + 804599765, + 1131353566, + 346324913, + 1108760730, + 1141693933, + 856999148, + 879390623, + 1149485521, + 900556586, + 952385958, + 807916408 }; + }; + + template <> + struct uniform_integer_test_params<int64_t> { + static constexpr int64_t lowest = -1234567890; + static constexpr int64_t highest = 1234567890; + static constexpr int64_t expected[] = { 740197113, + 292191940, + 458575608, + -514589122, + 374630781, + 1028139036, + -541919840, + 982953318, + 1048819790, + 479429651, + 524212647, + 1064402981, + 566544615, + 670203462, + 381264073 }; + }; + + // We need these definitions for C++14 and earlier, but + // GCC will complain about them in newer C++ standards +#if __cplusplus <= 201402L + constexpr bool uniform_integer_test_params<bool>::expected[]; + constexpr char uniform_integer_test_params<char>::expected[]; + constexpr uint8_t uniform_integer_test_params<uint8_t>::expected[]; + constexpr int8_t uniform_integer_test_params<int8_t>::expected[]; + constexpr uint16_t uniform_integer_test_params<uint16_t>::expected[]; + constexpr int16_t uniform_integer_test_params<int16_t>::expected[]; + constexpr uint32_t uniform_integer_test_params<uint32_t>::expected[]; + constexpr int32_t uniform_integer_test_params<int32_t>::expected[]; + constexpr uint64_t uniform_integer_test_params<uint64_t>::expected[]; + constexpr int64_t uniform_integer_test_params<int64_t>::expected[]; +#endif + +} + +TEMPLATE_TEST_CASE( "uniform_integer_distribution is reproducible", + "[rng][distribution][approvals]", + bool, + char, + uint8_t, + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t) { + Catch::SimplePcg32 pcg( 0xaabb'ccdd ); + + constexpr auto lowest = uniform_integer_test_params<TestType>::lowest; + constexpr auto highest = uniform_integer_test_params<TestType>::highest; + Catch::uniform_integer_distribution<TestType> dist(lowest, highest); + + constexpr auto iters = 15; + std::array<TestType, iters> generated; + for (int i = 0; i < iters; ++i) { + generated[i] = dist( pcg ); + } + + REQUIRE_THAT(generated, Catch::Matchers::RangeEquals(uniform_integer_test_params<TestType>::expected)); +} + + +namespace { + template <typename T> + struct uniform_fp_test_params; + + template<> + struct uniform_fp_test_params<float> { + // These are exactly representable + static constexpr float lowest = -256.125f; + static constexpr float highest = 385.125f; + // These are just round-trip formatted + static constexpr float expected[] = { 92.56961f, + -23.170044f, + 310.81833f, + -53.023132f, + 105.03287f, + 198.77591f, + -172.72931f, + 51.805176f, + -241.10156f, + 64.66101f, + 212.12509f, + -49.24292f, + -177.1399f, + 245.23679f, + 173.22421f }; + }; + template <> + struct uniform_fp_test_params<double> { + // These are exactly representable + static constexpr double lowest = -234582.9921875; + static constexpr double highest = 261238.015625; + // These are just round-trip formatted + static constexpr double expected[] = { 35031.207052832615, + 203783.3401838024, + 44667.940405848756, + -170100.5877224467, + -222966.7418051684, + 127472.72630072923, + -173510.88209096913, + 97394.16172239158, + 119123.6921592663, + 22595.741022785165, + 8988.68409120926, + 136906.86520606978, + 33369.19104222473, + 60912.7615841752, + -149060.05936760217 }; + }; + +// We need these definitions for C++14 and earlier, but +// GCC will complain about them in newer C++ standards +#if __cplusplus <= 201402L + constexpr float uniform_fp_test_params<float>::expected[]; + constexpr double uniform_fp_test_params<double>::expected[]; +#endif +} // namespace + +TEMPLATE_TEST_CASE( "uniform_floating_point_distribution is reproducible", + "[rng][distribution][floating-point][approvals]", + float, + double ) { + Catch::SimplePcg32 pcg( 0xaabb'aabb ); + + const auto lowest = uniform_fp_test_params<TestType>::lowest; + const auto highest = uniform_fp_test_params<TestType>::highest; + Catch::uniform_floating_point_distribution<TestType> dist( lowest, highest ); + + constexpr auto iters = 15; + std::array<TestType, iters> generated; + for ( int i = 0; i < iters; ++i ) { + generated[i] = dist( pcg ); + } + + REQUIRE_THAT( generated, Catch::Matchers::RangeEquals( uniform_fp_test_params<TestType>::expected ) ); +} + +TEMPLATE_TEST_CASE( "uniform_floating_point_distribution can handle unitary ranges", + "[rng][distribution][floating-point][approvals]", + float, + double ) { + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + const auto highest = uniform_fp_test_params<TestType>::highest; + Catch::uniform_floating_point_distribution<TestType> dist( highest, + highest ); + + constexpr auto iters = 20; + for (int i = 0; i < iters; ++i) { + REQUIRE( Catch::Detail::directCompare( dist( pcg ), highest ) ); + } +} diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp index 1568c9516..e5a65bda5 100644 --- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp @@ -109,7 +109,9 @@ TEST_CASE( "Reporter's write listings to provided stream", "[reporters]" ) { auto sstream = Catch::Detail::make_unique<StringIStream>(); auto& sstreamRef = *sstream.get(); - Catch::Config config( Catch::ConfigData{} ); + Catch::ConfigData cfg_data; + cfg_data.rngSeed = 1234; + Catch::Config config( cfg_data ); auto reporter = factory.second->create( Catch::ReporterConfig{ &config, CATCH_MOVE( sstream ), Catch::ColourMode::None, {} } ); diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp index 7a0b3b4ab..43c58b49b 100644 --- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp +++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp @@ -177,7 +177,7 @@ TEST_CASE("StringRef at compilation time", "[Strings][StringRef][constexpr]") { STATIC_REQUIRE_FALSE(sr1.empty()); STATIC_REQUIRE(sr1.size() == 3); - using Catch::operator"" _sr; + using Catch::operator""_sr; constexpr auto sr2 = ""_sr; STATIC_REQUIRE(sr2.empty()); STATIC_REQUIRE(sr2.size() == 0); diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp index 8e2c387a3..f04cf4f09 100644 --- a/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp +++ b/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp @@ -305,9 +305,19 @@ namespace { } // namespace -TEST_CASE( "#2615 - Throwing in constructor generator fails test case but does not abort", "[!shouldfail]" ) { +TEST_CASE( "#2615 - Throwing in constructor generator fails test case but does not abort", + "[!shouldfail][regression][generators]" ) { // this should fail the test case, but not abort the application auto sample = GENERATE( make_test_generator() ); // this assertion shouldn't trigger REQUIRE( sample == 0 ); } + +TEST_CASE( "GENERATE can combine literals and generators", "[generators]" ) { + auto i = GENERATE( 2, + 4, + take( 2, + filter( []( int val ) { return val % 2 == 0; }, + random( -100, 100 ) ) ) ); + REQUIRE( i % 2 == 0 ); +} diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Message.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Message.tests.cpp index a5e695825..6367bf591 100644 --- a/packages/Catch2/tests/SelfTest/UsageTests/Message.tests.cpp +++ b/packages/Catch2/tests/SelfTest/UsageTests/Message.tests.cpp @@ -255,10 +255,24 @@ std::ostream& operator<<(std::ostream& out, helper_1436<T1, T2> const& helper) { #pragma clang diagnostic ignored "-Wunused-value" #endif +namespace { + template <typename T> + struct custom_index_op { + constexpr custom_index_op( std::initializer_list<T> ) {} + constexpr T operator[]( size_t ) { return T{}; } +#if defined( __cpp_multidimensional_subscript ) && \ + __cpp_multidimensional_subscript >= 202110L + constexpr T operator[]( size_t, size_t, size_t ) const noexcept { + return T{}; + } +#endif + }; +} + TEST_CASE("CAPTURE can deal with complex expressions involving commas", "[messages][capture]") { - CAPTURE(std::vector<int>{1, 2, 3}[0, 1, 2], - std::vector<int>{1, 2, 3}[(0, 1)], - std::vector<int>{1, 2, 3}[0]); + CAPTURE(custom_index_op<int>{1, 2, 3}[0, 1, 2], + custom_index_op<int>{1, 2, 3}[(0, 1)], + custom_index_op<int>{1, 2, 3}[0]); CAPTURE((helper_1436<int, int>{12, -12}), (helper_1436<int, int>(-12, 12))); CAPTURE( (1, 2), (2, 3) ); @@ -285,3 +299,14 @@ TEST_CASE("CAPTURE parses string and character constants", "[messages][capture]" #ifdef _MSC_VER #pragma warning(pop) #endif + +TEST_CASE( "INFO and UNSCOPED_INFO can stream multiple arguments", + "[messages][info][.failing]" ) { + INFO( "This info" + << " has multiple" + << " parts." ); + UNSCOPED_INFO( "This unscoped info" + << " has multiple" + << " parts." ); + FAIL( "Show infos!" ); +} diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp index 6c1fd68f4..7f06704b4 100644 --- a/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp +++ b/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp @@ -217,6 +217,18 @@ TEST_CASE("Testing checked-if 3", "[checked-if][!shouldfail]") { SUCCEED(); } +[[noreturn]] +TEST_CASE("Testing checked-if 4", "[checked-if][!shouldfail]") { + CHECKED_ELSE(true) {} + throw std::runtime_error("Uncaught exception should fail!"); +} + +[[noreturn]] +TEST_CASE("Testing checked-if 5", "[checked-if][!shouldfail]") { + CHECKED_ELSE(false) {} + throw std::runtime_error("Uncaught exception should fail!"); +} + TEST_CASE( "xmlentitycheck" ) { SECTION( "embedded xml: <test>it should be possible to embed xml characters, such as <, \" or &, or even whole <xml>documents</xml> within an attribute</test>" ) { SUCCEED(); // We need this here to stop it failing due to no tests diff --git a/packages/Catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp index 9fd9d6b45..3671771a7 100644 --- a/packages/Catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp +++ b/packages/Catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp @@ -28,4 +28,8 @@ TEST_CASE( "std::vector<std::optional<int> > -> toString", "[toString][optional] REQUIRE( "{ 0, { }, 2 }" == ::Catch::Detail::stringify( type{ 0, {}, 2 } ) ); } +TEST_CASE( "std::nullopt -> toString", "[toString][optional][approvals]" ) { + REQUIRE( "{ }" == ::Catch::Detail::stringify( std::nullopt ) ); +} + #endif // CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL diff --git a/packages/Catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt b/packages/Catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt new file mode 100644 index 000000000..d19f2f889 --- /dev/null +++ b/packages/Catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.10) + +project(discover-tests-test + LANGUAGES CXX +) + +add_executable(tests + register-tests.cpp +) + +add_subdirectory(${CATCH2_PATH} catch2-build) +target_link_libraries(tests PRIVATE Catch2::Catch2WithMain) + +include(CTest) +include(Catch) +catch_discover_tests(tests) diff --git a/packages/Catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py b/packages/Catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py new file mode 100644 index 000000000..9ec42f24c --- /dev/null +++ b/packages/Catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +# Copyright Catch2 Authors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.boost.org/LICENSE_1_0.txt) + +# SPDX-License-Identifier: BSL-1.0 + +import os +import subprocess +import sys + + +def build_project(sources_dir, output_base_path, catch2_path): + build_dir = os.path.join(output_base_path, 'ctest-registration-test') + config_cmd = ['cmake', + '-B', build_dir, + '-S', sources_dir, + f'-DCATCH2_PATH={catch2_path}', + '-DCMAKE_BUILD_TYPE=Debug'] + + build_cmd = ['cmake', + '--build', build_dir, + '--config', 'Debug'] + + try: + subprocess.run(config_cmd, + capture_output = True, + check = True, + text = True) + subprocess.run(build_cmd, + capture_output = True, + check = True, + text = True) + except subprocess.CalledProcessError as err: + print('Error when building the test project') + print(f'cmd: {err.cmd}') + print(f'stderr: {err.stderr}') + print(f'stdout: {err.stdout}') + exit(3) + + return build_dir + + + +def get_test_names(build_path): + # For now we assume that Windows builds are done using MSBuild under + # Debug configuration. This means that we need to add "Debug" folder + # to the path when constructing it. On Linux, we don't add anything. + config_path = "Debug" if os.name == 'nt' else "" + full_path = os.path.join(build_path, config_path, 'tests') + + + cmd = [full_path, '--reporter', 'xml', '--list-tests'] + result = subprocess.run(cmd, + capture_output = True, + check = True, + text = True) + + import xml.etree.ElementTree as ET + root = ET.fromstring(result.stdout) + return [tc.text for tc in root.findall('TestCase/Name')] + + +def list_ctest_tests(build_path): + old_path = os.getcwd() + os.chdir(build_path) + + cmd = ['ctest', '-C', 'debug', '--show-only=json-v1'] + result = subprocess.run(cmd, + capture_output = True, + check = True, + text = True) + os.chdir(old_path) + + import json + + ctest_response = json.loads(result.stdout) + tests = ctest_response['tests'] + test_names = [] + for test in tests: + test_command = test['command'] + # First part of the command is the binary, second is the filter. + # If there are less, registration has failed. If there are more, + # registration has changed and the script needs updating. + assert len(test_command) == 2 + test_names.append(test_command[1]) + test_name = test_command[1] + + return test_names + +def escape_catch2_test_name(name): + for char in ('\\', ',', '[', ']'): + name = name.replace(char, f"\\{char}") + return name + +if __name__ == '__main__': + if len(sys.argv) != 3: + print(f'Usage: {sys.argv[0]} path-to-catch2-cml output-path') + exit(2) + catch2_path = sys.argv[1] + output_base_path = sys.argv[2] + sources_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + + build_path = build_project(sources_dir, output_base_path, catch2_path) + + catch_test_names = [escape_catch2_test_name(name) for name in get_test_names(build_path)] + ctest_test_names = list_ctest_tests(build_path) + + mismatched = 0 + for catch_test in catch_test_names: + if catch_test not in ctest_test_names: + print(f"Catch2 test '{catch_test}' not found in CTest") + mismatched += 1 + for ctest_test in ctest_test_names: + if ctest_test not in catch_test_names: + print(f"CTest test '{ctest_test}' not found in Catch2") + mismatched += 1 + + if mismatched: + print(f"Found {mismatched} mismatched tests catch test names and ctest test commands!") + exit(1) diff --git a/packages/Catch2/tests/TestScripts/DiscoverTests/register-tests.cpp b/packages/Catch2/tests/TestScripts/DiscoverTests/register-tests.cpp new file mode 100644 index 000000000..aa603df1a --- /dev/null +++ b/packages/Catch2/tests/TestScripts/DiscoverTests/register-tests.cpp @@ -0,0 +1,16 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include <catch2/catch_test_macros.hpp> + +TEST_CASE("@Script[C:\\EPM1A]=x;\"SCALA_ZERO:\"", "[script regressions]"){} +TEST_CASE("Some test") {} +TEST_CASE( "Let's have a test case with a long name. Longer. No, even longer. " + "Really looooooooooooong. Even longer than that. Multiple lines " + "worth of test name. Yep, like this." ) {} +TEST_CASE( "And now a test case with weird tags.", "[tl;dr][tl;dw][foo,bar]" ) {} diff --git a/packages/Catch2/tests/meson.build b/packages/Catch2/tests/meson.build index f525f0412..58302b7aa 100644 --- a/packages/Catch2/tests/meson.build +++ b/packages/Catch2/tests/meson.build @@ -17,6 +17,7 @@ self_test_sources = files( 'SelfTest/IntrospectiveTests/Details.tests.cpp', 'SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp', 'SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp', + 'SelfTest/IntrospectiveTests/Integer.tests.cpp', 'SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp', 'SelfTest/IntrospectiveTests/Parse.tests.cpp', 'SelfTest/IntrospectiveTests/PartTracker.tests.cpp', diff --git a/packages/Catch2/tools/scripts/checkLicense.py b/packages/Catch2/tools/scripts/checkLicense.py index 9a9497692..7078d3ec2 100755 --- a/packages/Catch2/tools/scripts/checkLicense.py +++ b/packages/Catch2/tools/scripts/checkLicense.py @@ -33,7 +33,8 @@ def check_licences_in_path(path: str) -> int: def check_licences(): failed = 0 - roots = ['src/catch2', 'tests'] + # Add 'extras' after the amalgamted files are regenerated with the new script (past 3.4.0) + roots = ['src/catch2', 'tests', 'examples', 'fuzzing'] for root in roots: failed += check_licences_in_path(root) diff --git a/packages/Catch2/tools/scripts/generateAmalgamatedFiles.py b/packages/Catch2/tools/scripts/generateAmalgamatedFiles.py index 99fc446bf..e3e86aab9 100755 --- a/packages/Catch2/tools/scripts/generateAmalgamatedFiles.py +++ b/packages/Catch2/tools/scripts/generateAmalgamatedFiles.py @@ -1,4 +1,9 @@ #!/usr/bin/env python3 +# Copyright Catch2 Authors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.boost.org/LICENSE_1_0.txt) +# SPDX-License-Identifier: BSL-1.0 import os import re @@ -12,6 +17,8 @@ starting_header = os.path.join(root_path, 'catch2', 'catch_all.hpp') output_header = os.path.join(catchPath, 'extras', 'catch_amalgamated.hpp') output_cpp = os.path.join(catchPath, 'extras', 'catch_amalgamated.cpp') +# REUSE-IgnoreStart + # These are the copyright comments in each file, we want to ignore them copyright_lines = [ '// Copyright Catch2 Authors\n', @@ -24,6 +31,7 @@ copyright_lines = [ # The header of the amalgamated file: copyright information + explanation # what this file is. file_header = '''\ + // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -39,6 +47,8 @@ file_header = '''\ // ---------------------------------------------------------- ''' +# REUSE-IgnoreEnd + # Returns file header with proper version string and generation time def formatted_file_header(version): return file_header.format(version_string=version.getVersionString(), diff --git a/packages/Catch2/tools/scripts/releaseCommon.py b/packages/Catch2/tools/scripts/releaseCommon.py index 0d995eaf7..1ff4af291 100644 --- a/packages/Catch2/tools/scripts/releaseCommon.py +++ b/packages/Catch2/tools/scripts/releaseCommon.py @@ -114,8 +114,8 @@ def updateVersionDefine(version): def updateVersionPlaceholder(filename, version): with open(filename, 'rb') as file: lines = file.readlines() - placeholderRegex = re.compile(b'in Catch[0-9]? X.Y.Z') - replacement = 'in Catch2 {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii') + placeholderRegex = re.compile(b'Catch[0-9]? X.Y.Z') + replacement = 'Catch2 {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii') with open(filename, 'wb') as file: for line in lines: file.write(placeholderRegex.sub(replacement, line)) -- GitLab