From e52119b54a9db3806de071d6cb620a09b5dbd42f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 04:58:06 +0000 Subject: [PATCH 1/3] Add clang-22 to CI matrix, drop clang-19 clang-19 was the oldest compiler tested; clang-22 keeps the matrix's two-versions-apart coverage window alongside the clang-21 default. --- .github/workflows/pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 531f58ac9..0a707b5d2 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -28,8 +28,8 @@ jobs: cxx: 'g++-14' flags: '-DENABLE_TRACING=OFF -DENABLE_LOGGING=OFF' - os: 'ubuntu-24.04' - cc: 'clang-19' - cxx: 'clang++-19' + cc: 'clang-22' + cxx: 'clang++-22' flags: '' - os: 'ubuntu-24.04' cc: 'clang-21' @@ -108,7 +108,7 @@ jobs: run: | ctest --test-dir build/plugins/simd/test --output-on-failure - name: test inlining plugin - if: startsWith(matrix.cc, 'clang-19') || startsWith(matrix.cc, 'clang-20') || startsWith(matrix.cc, 'clang-21') + if: startsWith(matrix.cc, 'clang-20') || startsWith(matrix.cc, 'clang-21') || startsWith(matrix.cc, 'clang-22') shell: bash run: | ctest --test-dir build/plugins/inlining/test --output-on-failure From 8efe41b26d1a930fbb476f7380cdb005391a2c63 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 05:10:29 +0000 Subject: [PATCH 2/3] Update vendored Catch2 from 3.11.0 to 3.15.1 Fixes clang-22 CI failures: __COUNTER__ (used internally by SECTION) was promoted to a C2y extension in clang 22 and rejected under -pedantic-errors. Catch2 3.14.0 added a suppression for this. --- third_party/Catch2/BUILD.bazel | 4 +- .../Catch2/CMake/CatchConfigOptions.cmake | 2 +- third_party/Catch2/CMake/llvm-cov-wrapper | 0 third_party/Catch2/CMakeLists.txt | 2 +- third_party/Catch2/Doxyfile | 698 ++++++---- third_party/Catch2/MODULE.bazel | 4 +- third_party/Catch2/README.md | 13 +- third_party/Catch2/benchmarks/CMakeLists.txt | 7 +- .../Catch2/benchmarks/assertion_listener.cpp | 2 + .../Catch2/benchmarks/only_include.cpp | 16 + third_party/Catch2/benchmarks/readme.md | 81 ++ .../benchmarks/runtime_assertion_benches.cpp | 45 +- third_party/Catch2/conanfile.py | 0 third_party/Catch2/extras/CatchAddTests.cmake | 1 + .../Catch2/extras/catch_amalgamated.cpp | 818 +++++++---- .../Catch2/extras/catch_amalgamated.hpp | 1206 +++++++++++------ third_party/Catch2/fuzzing/build_fuzzers.sh | 0 .../Catch2/fuzzing/fuzz_TestSpecParser.cpp | 1 + third_party/Catch2/fuzzing/fuzz_textflow.cpp | 1 + third_party/Catch2/meson.build | 2 +- third_party/Catch2/src/CMakeLists.txt | 10 +- .../catch2/benchmark/detail/catch_analyse.cpp | 10 +- .../detail/catch_benchmark_function.hpp | 2 +- .../detail/catch_benchmark_stats.hpp | 1 + .../detail/catch_benchmark_stats_fwd.hpp | 10 +- .../detail/catch_run_for_at_least.cpp | 2 +- third_party/Catch2/src/catch2/catch_all.hpp | 5 +- .../Catch2/src/catch2/catch_config.cpp | 13 +- .../Catch2/src/catch2/catch_config.hpp | 8 +- .../Catch2/src/catch2/catch_message.cpp | 22 +- .../Catch2/src/catch2/catch_message.hpp | 56 +- .../Catch2/src/catch2/catch_registry_hub.cpp | 5 - .../Catch2/src/catch2/catch_session.cpp | 1 + .../Catch2/src/catch2/catch_tostring.cpp | 30 + .../Catch2/src/catch2/catch_tostring.hpp | 117 +- .../src/catch2/catch_translate_exception.hpp | 2 +- .../src/catch2/catch_user_config.hpp.in | 10 +- .../Catch2/src/catch2/catch_version.cpp | 2 +- .../src/catch2/catch_version_macros.hpp | 4 +- .../catch2/generators/catch_generators.cpp | 10 - .../catch2/generators/catch_generators.hpp | 33 +- .../generators/catch_generators_adapters.hpp | 97 +- .../generators/catch_generators_all.hpp | 1 + .../generators/catch_generators_random.cpp | 5 + .../generators/catch_generators_random.hpp | 3 + .../generators/catch_generators_range.hpp | 4 + .../generators/catch_generators_throw.cpp | 24 + .../generators/catch_generators_throw.hpp | 23 + .../interfaces/catch_interfaces_all.hpp | 1 - .../interfaces/catch_interfaces_capture.hpp | 9 - .../interfaces/catch_interfaces_config.hpp | 10 +- .../catch_interfaces_enum_values_registry.hpp | 47 - .../catch_interfaces_generatortracker.cpp | 29 + .../catch_interfaces_generatortracker.hpp | 27 +- .../catch_interfaces_registry_hub.hpp | 1 - .../internal/catch_assertion_handler.cpp | 9 +- .../internal/catch_assertion_handler.hpp | 16 +- .../src/catch2/internal/catch_commandline.cpp | 65 +- .../internal/catch_compiler_capabilities.hpp | 41 +- .../catch2/internal/catch_console_colour.cpp | 4 +- .../src/catch2/internal/catch_decomposer.hpp | 4 +- .../internal/catch_deprecation_macro.hpp | 4 +- ...alues_registry.cpp => catch_enum_info.cpp} | 28 +- .../src/catch2/internal/catch_enum_info.hpp | 48 + .../internal/catch_enum_values_registry.hpp | 36 - .../catch_exception_translator_registry.hpp | 2 +- .../src/catch2/internal/catch_jsonwriter.hpp | 13 +- .../catch2/internal/catch_lifetimebound.hpp | 24 + .../catch2/internal/catch_message_info.cpp | 14 +- .../catch2/internal/catch_message_info.hpp | 6 +- .../catch2/internal/catch_output_redirect.cpp | 6 +- .../src/catch2/internal/catch_path_filter.hpp | 33 + .../src/catch2/internal/catch_run_context.cpp | 293 ++-- .../src/catch2/internal/catch_run_context.hpp | 4 +- .../src/catch2/internal/catch_singletons.hpp | 2 +- .../catch2/internal/catch_string_manip.hpp | 7 +- .../src/catch2/internal/catch_stringref.hpp | 13 +- .../internal/catch_tag_alias_registry.cpp | 7 +- .../internal/catch_tag_alias_registry.hpp | 2 +- .../internal/catch_template_test_registry.hpp | 124 +- .../catch_test_case_registry_impl.hpp | 2 +- .../internal/catch_test_case_tracker.cpp | 62 +- .../internal/catch_test_case_tracker.hpp | 39 +- .../internal/catch_test_failure_exception.cpp | 4 +- .../catch2/internal/catch_test_macro_impl.hpp | 12 +- .../catch2/internal/catch_test_registry.hpp | 6 +- .../src/catch2/internal/catch_textflow.cpp | 19 + .../catch2/internal/catch_thread_local.hpp | 19 + .../catch2/internal/catch_thread_support.hpp | 12 +- .../src/catch2/internal/catch_unique_name.hpp | 13 + .../src/catch2/internal/catch_xmlwriter.hpp | 7 +- .../src/catch2/matchers/catch_matchers.cpp | 10 +- .../src/catch2/matchers/catch_matchers.hpp | 76 +- .../catch_matchers_container_properties.cpp | 8 - .../catch_matchers_container_properties.hpp | 18 +- .../matchers/catch_matchers_contains.hpp | 14 +- .../matchers/catch_matchers_quantifiers.cpp | 6 +- .../matchers/catch_matchers_quantifiers.hpp | 45 +- .../matchers/catch_matchers_templated.cpp | 6 +- .../matchers/catch_matchers_templated.hpp | 149 +- .../matchers/internal/catch_matchers_impl.hpp | 2 +- third_party/Catch2/src/catch2/meson.build | 10 +- .../reporters/catch_reporter_common_base.cpp | 6 +- .../catch_reporter_cumulative_base.cpp | 5 +- .../reporters/catch_reporter_helpers.cpp | 25 +- .../reporters/catch_reporter_helpers.hpp | 8 +- .../catch2/reporters/catch_reporter_junit.cpp | 113 +- .../catch2/reporters/catch_reporter_junit.hpp | 2 +- .../Catch2/tools/scripts/approvalTests.py | 2 +- third_party/Catch2/tools/scripts/approve.py | 0 .../Catch2/tools/scripts/buildAndTest.sh | 0 .../tools/scripts/checkConvenienceHeaders.py | 0 .../tools/scripts/checkDuplicateFilenames.py | 0 .../Catch2/tools/scripts/checkLicense.py | 0 .../Catch2/tools/scripts/developBuild.py | 0 .../Catch2/tools/scripts/fixWhitespace.py | 0 .../tools/scripts/generateAmalgamatedFiles.py | 0 .../Catch2/tools/scripts/majorRelease.py | 0 .../Catch2/tools/scripts/minorRelease.py | 0 .../Catch2/tools/scripts/patchRelease.py | 0 .../tools/scripts/updateDocumentSnippets.py | 0 .../Catch2/tools/scripts/updateDocumentToC.py | 0 122 files changed, 3309 insertions(+), 1733 deletions(-) mode change 100755 => 100644 third_party/Catch2/CMake/llvm-cov-wrapper create mode 100644 third_party/Catch2/benchmarks/only_include.cpp create mode 100644 third_party/Catch2/benchmarks/readme.md mode change 100755 => 100644 third_party/Catch2/conanfile.py mode change 100755 => 100644 third_party/Catch2/fuzzing/build_fuzzers.sh create mode 100644 third_party/Catch2/src/catch2/generators/catch_generators_throw.cpp create mode 100644 third_party/Catch2/src/catch2/generators/catch_generators_throw.hpp delete mode 100644 third_party/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp rename third_party/Catch2/src/catch2/internal/{catch_enum_values_registry.cpp => catch_enum_info.cpp} (65%) create mode 100644 third_party/Catch2/src/catch2/internal/catch_enum_info.hpp delete mode 100644 third_party/Catch2/src/catch2/internal/catch_enum_values_registry.hpp create mode 100644 third_party/Catch2/src/catch2/internal/catch_lifetimebound.hpp create mode 100644 third_party/Catch2/src/catch2/internal/catch_path_filter.hpp create mode 100644 third_party/Catch2/src/catch2/internal/catch_thread_local.hpp mode change 100755 => 100644 third_party/Catch2/tools/scripts/approvalTests.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/approve.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/buildAndTest.sh mode change 100755 => 100644 third_party/Catch2/tools/scripts/checkConvenienceHeaders.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/checkDuplicateFilenames.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/checkLicense.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/developBuild.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/fixWhitespace.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/generateAmalgamatedFiles.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/majorRelease.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/minorRelease.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/patchRelease.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/updateDocumentSnippets.py mode change 100755 => 100644 third_party/Catch2/tools/scripts/updateDocumentToC.py diff --git a/third_party/Catch2/BUILD.bazel b/third_party/Catch2/BUILD.bazel index eb484e409..a318d1908 100644 --- a/third_party/Catch2/BUILD.bazel +++ b/third_party/Catch2/BUILD.bazel @@ -76,8 +76,8 @@ expand_template( "#cmakedefine CATCH_CONFIG_WINDOWS_SEH": "", "#cmakedefine CATCH_CONFIG_USE_BUILTIN_CONSTANT_P": "", "#cmakedefine CATCH_CONFIG_NO_USE_BUILTIN_CONSTANT_P": "", - "#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "", - "#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "", + "#cmakedefine CATCH_CONFIG_THREAD_SAFE_ASSERTIONS": "", + "#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS": "", }, template = "src/catch2/catch_user_config.hpp.in", ) diff --git a/third_party/Catch2/CMake/CatchConfigOptions.cmake b/third_party/Catch2/CMake/CatchConfigOptions.cmake index 3621fac0b..967250666 100644 --- a/third_party/Catch2/CMake/CatchConfigOptions.cmake +++ b/third_party/Catch2/CMake/CatchConfigOptions.cmake @@ -45,7 +45,7 @@ set(_OverridableOptions "EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT" "USE_BUILTIN_CONSTANT_P" "DEPRECATION_ANNOTATIONS" - "EXPERIMENTAL_THREAD_SAFE_ASSERTIONS" + "THREAD_SAFE_ASSERTIONS" ) foreach(OptionName ${_OverridableOptions}) diff --git a/third_party/Catch2/CMake/llvm-cov-wrapper b/third_party/Catch2/CMake/llvm-cov-wrapper old mode 100755 new mode 100644 diff --git a/third_party/Catch2/CMakeLists.txt b/third_party/Catch2/CMakeLists.txt index 918927c43..49d7879b8 100644 --- a/third_party/Catch2/CMakeLists.txt +++ b/third_party/Catch2/CMakeLists.txt @@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.11.0 # CML version placeholder, don't delete + VERSION 3.15.1 # CML version placeholder, don't delete LANGUAGES CXX HOMEPAGE_URL "https://github.com/catchorg/Catch2" DESCRIPTION "A modern, C++-native, unit test framework." diff --git a/third_party/Catch2/Doxyfile b/third_party/Catch2/Doxyfile index 914e59848..83b4f2157 100644 --- a/third_party/Catch2/Doxyfile +++ b/third_party/Catch2/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.9.1 +# Doxyfile 1.9.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -60,16 +70,28 @@ PROJECT_LOGO = OUTPUT_DIRECTORY = docs/doxygen -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,26 +103,18 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -248,16 +262,16 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = "complexity=@par Complexity:" \ noexcept=**Noexcept** @@ -303,8 +317,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # 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, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, 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). For instance to make doxygen treat .inc files @@ -340,6 +354,17 @@ MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 5 +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -451,19 +476,27 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of 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 +# which effectively 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 +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = DATETIME + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -545,7 +578,8 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO @@ -576,14 +610,15 @@ INTERNAL_DOCS = NO # 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 +# are not case sensitive the option should 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. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO @@ -601,6 +636,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -758,7 +799,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -804,27 +846,50 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = YES +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # 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. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO @@ -835,13 +900,27 @@ WARN_AS_ERROR = NO # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = doxygen.errors @@ -862,10 +941,21 @@ INPUT = src/catch2 # 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. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. @@ -877,57 +967,23 @@ INPUT_ENCODING = UTF-8 # 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 (to be provided as doxygen C comment), -# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, -# *.ucf, *.qsf and *.ice. +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, +# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.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 @@ -965,10 +1021,7 @@ EXCLUDE_PATTERNS = */lib/* # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = @@ -1013,6 +1066,11 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. @@ -1054,6 +1112,15 @@ FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- @@ -1151,9 +1218,11 @@ VERBATIM_HEADERS = YES 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. +# If the CLANG_ASSISTED_PARSING tag is set to YES 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. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_ADD_INC_PATHS = YES @@ -1189,10 +1258,11 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = @@ -1271,7 +1341,12 @@ HTML_STYLESHEET = # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = @@ -1286,9 +1361,22 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1298,7 +1386,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1316,15 +1404,6 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1344,6 +1423,13 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1380,6 +1466,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1405,8 +1498,12 @@ 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. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # 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 @@ -1463,6 +1560,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1565,16 +1672,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1599,6 +1718,13 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # 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 @@ -1619,17 +1745,6 @@ HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # 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. @@ -1647,11 +1762,29 @@ FORMULA_MACROFILE = USE_MATHJAX = YES +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_3 + # 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/v2.7-latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1664,15 +1797,21 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = TeX/AMSmath \ @@ -1853,29 +1992,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1918,10 +2059,16 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1934,16 +2081,6 @@ LATEX_BATCHMODE = YES LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1952,14 +2089,6 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -2024,16 +2153,6 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -2130,27 +2249,44 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3 +# database with symbols found by doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each doxygen run. If set to NO, doxygen +# will warn if an a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2225,7 +2361,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2292,15 +2429,15 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2314,25 +2451,9 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = NO - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2341,7 +2462,7 @@ HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://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: YES. @@ -2358,49 +2479,73 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. -CLASS_GRAPH = NO +CLASS_GRAPH = YES # If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2460,7 +2605,9 @@ TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2469,7 +2616,10 @@ INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2509,28 +2659,37 @@ GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -DIRECTORY_GRAPH = NO +DIRECTORY_GRAPH = YES + +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). +# https://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, 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 +# Possible values are: png, 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. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_IMAGE_FORMAT = png +DOT_IMAGE_FORMAT = svg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. @@ -2557,11 +2716,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2570,10 +2730,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2611,18 +2771,6 @@ DOT_GRAPH_MAX_NODES = 100 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2635,6 +2783,8 @@ DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2643,8 +2793,24 @@ GENERATE_LEGEND = YES # 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. +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. DOT_CLEANUP = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/third_party/Catch2/MODULE.bazel b/third_party/Catch2/MODULE.bazel index 8ee125ba5..ad4c2c4ef 100644 --- a/third_party/Catch2/MODULE.bazel +++ b/third_party/Catch2/MODULE.bazel @@ -1,5 +1,5 @@ module(name = "catch2") -bazel_dep(name = "bazel_skylib", version = "1.7.1") -bazel_dep(name = "rules_cc", version = "0.1.1") +bazel_dep(name = "bazel_skylib", version = "1.9.0") +bazel_dep(name = "rules_cc", version = "0.2.16") bazel_dep(name = "rules_license", version = "1.0.0") diff --git a/third_party/Catch2/README.md b/third_party/Catch2/README.md index ebd6bfd9f..03dc7d7cc 100644 --- a/third_party/Catch2/README.md +++ b/third_party/Catch2/README.md @@ -1,16 +1,5 @@ - - - - - - -
-
-
Special thanks to:
- -
-
+![Catch2 logo](data/artwork/catch2-logo-full-with-background.svg) [![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases) [![Linux build status](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml) diff --git a/third_party/Catch2/benchmarks/CMakeLists.txt b/third_party/Catch2/benchmarks/CMakeLists.txt index 688377833..b5819c79a 100644 --- a/third_party/Catch2/benchmarks/CMakeLists.txt +++ b/third_party/Catch2/benchmarks/CMakeLists.txt @@ -9,8 +9,13 @@ add_executable(AssertionsSlowPath assertion_listener.cpp ) +add_executable(EmptyExecutable + only_include.cpp +) + target_link_libraries(AssertionsFastPath PRIVATE Catch2::Catch2WithMain) target_link_libraries(AssertionsSlowPath PRIVATE Catch2::Catch2WithMain) +target_link_libraries(EmptyExecutable PRIVATE Catch2::Catch2WithMain) -list(APPEND CATCH_TEST_TARGETS AssertionsFastPath AssertionsSlowPath) +list(APPEND CATCH_TEST_TARGETS AssertionsFastPath AssertionsSlowPath EmptyExecutable) set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE) diff --git a/third_party/Catch2/benchmarks/assertion_listener.cpp b/third_party/Catch2/benchmarks/assertion_listener.cpp index 2aa65b45f..38399568b 100644 --- a/third_party/Catch2/benchmarks/assertion_listener.cpp +++ b/third_party/Catch2/benchmarks/assertion_listener.cpp @@ -12,6 +12,7 @@ /** * Event listener that listens to all assertions, forcing assertion slow path */ +namespace { class AssertionSlowPathListener : public Catch::EventListenerBase { public: static std::string getDescription() { @@ -26,3 +27,4 @@ class AssertionSlowPathListener : public Catch::EventListenerBase { }; CATCH_REGISTER_LISTENER( AssertionSlowPathListener ) +} diff --git a/third_party/Catch2/benchmarks/only_include.cpp b/third_party/Catch2/benchmarks/only_include.cpp new file mode 100644 index 000000000..d82cbcc3f --- /dev/null +++ b/third_party/Catch2/benchmarks/only_include.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 + +/**\file + * "empty" include of catch_test_macros.hpp + * + * This file does only one thing: include `catch2/catch_test_macros.hpp`, + * so that we can measure the compilation cost of the header. + */ + +#include diff --git a/third_party/Catch2/benchmarks/readme.md b/third_party/Catch2/benchmarks/readme.md new file mode 100644 index 000000000..9ef825323 --- /dev/null +++ b/third_party/Catch2/benchmarks/readme.md @@ -0,0 +1,81 @@ + + +# Benchmarks of Catch2 + +This folder holds benchmarks for Catch2. It should not be built for the +small(er) test sets, as it is only relevant for running (mainly runtime) +benchmarks. + +Below you will find some practical examples using +[hyperfine](https://github.com/sharkdp/hyperfine) to determine the +performance of various scenarios. They assume two parallel checkouts, one +named `Catch2-old` and the other `Catch2-new`. You will need to change +the paths to work on your own machine. + + +## Runtime benchmarks + +The runtime benchmarks currently consist of few different `TEST_CASE`s, +each with a simple loop over different assertion type. They are compiled +into two binaries, one for assertion slow path and one for assertion fast +path. + + +### Assumptions and notes about real-world usage + +The basic assertion macro, `REQUIRE` (and `CHECK`) is the most common one, +by far. Thus, it is the most important one to run quickly. It is probably +followed by the matcher macro, `REQUIRE_THAT`. + +The most common result of an assertion is that it passes. Even if it is +stringified and reported, that is most likely due to some listener/reporter +that wants the string representation, not because it failed. + +The performance of both Debug and Release builds are important; users +will run tests in both. LTO runtime performance is not too important, +but compile time perf is. + + +### Examples + +**Compare performance of `REQUIRE` in slow path, debug build** +```text +hyperfine --warmup 2 --shell none --parameter-list version old,new '/home/xarn/benches/Catch2-{version}/build-debug/benchmarks/AssertionsSlowPath -o /dev/null "REQUIRE"' +``` + +**Compare performance of `REQUIRE_THAT` in fast path, release build** +```text +hyperfine --warmup 2 --shell none --parameter-list version old,new '/home/xarn/benches/Catch2-{version}/build-release/benchmarks/AssertionsFastPath -o /dev/null "REQUIRE_THAT"' +``` + +**Compare performance of `REQUIRE` with stringification enabled, release build** +```text +hyperfine --warmup 2 --shell none --parameter-list version old,new '/home/xarn/benches/Catch2-{version}/build-release/benchmarks/AssertionsFastPath -s -o /dev/null "REQUIRE"' +``` +_Note that we redirect the output to `/dev/null` to reduce the overhead of the actual output printing, to see just the impact of stringification._ + +TODO: + * Start empty binary (set up cost base) + * Start binary with X (100/1k/10k) tests (test registration cost) + * Section tracking + +## Compilation benchmarks + +As tests are often iterated upon and relinked, the compilation cost of +Catch2 is also important. + + +### Examples + +**Compare overhead of including `catch_test_macros.hpp`** +```text +hyperfine --warmup 2 --shell none --parameter-list version old,new '/usr/bin/c++ -I/home/xarn/benches/Catch2-{version}/src/catch2/.. -I/home/xarn/benches/Catch2-{version}/build-debug/generated-includes -g -o /dev/null -c /home/xarn/benches/Catch2-{version}/benchmarks/only_include.cpp' +``` + +**Compare build time of Catch2's `SelfTest` test suite, Debug build** +```text +hyperfine --warmup 2 --parameter-list version old,vas --prepare 'find ~/benches/Catch2-{version}/tests/SelfTest -type f -name "*.cpp" -exec touch {} +' 'ninja -j 1 -C ~/benches/Catch2-{version}/build-debug' +``` + +TODO: + * Link-only recipe diff --git a/third_party/Catch2/benchmarks/runtime_assertion_benches.cpp b/third_party/Catch2/benchmarks/runtime_assertion_benches.cpp index 6a28e3fa2..1202b0cde 100644 --- a/third_party/Catch2/benchmarks/runtime_assertion_benches.cpp +++ b/third_party/Catch2/benchmarks/runtime_assertion_benches.cpp @@ -7,21 +7,48 @@ // SPDX-License-Identifier: BSL-1.0 #include +#include -TEST_CASE("Simple REQUIRE - 10M") { - for (size_t i = 0; i < 10'000'000; ++i) { - REQUIRE(true); +namespace { + +class MatchAllMatcher final : public Catch::Matchers::MatcherGenericBase { +public: + template + bool match( Any&& ) const { + return true; + } + + std::string describe() const override { + using namespace std::string_literals; + return "Long string that does not fit into SSO"s; + } +}; + +MatchAllMatcher MatchAll() { return MatchAllMatcher(); } + + +TEST_CASE( "REQUIRE", "[assertions]" ) { + for ( size_t i = 0; i < 10'000'000; ++i ) { + REQUIRE( true ); } } -TEST_CASE("Simple NOTHROW - 10M") { - for (size_t i = 0; i < 10'000'000; ++i) { - REQUIRE_NOTHROW([](){}()); +TEST_CASE( "REQUIRE_THAT", "[assertions][matchers]" ) { + for ( size_t i = 0; i < 10'000'000; ++i ) { + REQUIRE_THAT( 1, MatchAll() ); } } -TEST_CASE("Simple THROWS - 10M") { - for (size_t i = 0; i < 10'000'000; ++i) { - REQUIRE_THROWS([]() { throw 1; }()); +TEST_CASE( "REQUIRE_NOTHROW", "[assertions][exceptions]" ) { + for ( size_t i = 0; i < 10'000'000; ++i ) { + REQUIRE_NOTHROW( []() {}() ); } } + +TEST_CASE( "REQUIRE_THROWS", "[assertions][exceptions]" ) { + for ( size_t i = 0; i < 10'000'000; ++i ) { + REQUIRE_THROWS( []() { throw 1; }() ); + } +} + +} // namespace diff --git a/third_party/Catch2/conanfile.py b/third_party/Catch2/conanfile.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/extras/CatchAddTests.cmake b/third_party/Catch2/extras/CatchAddTests.cmake index 90fbd5e31..4c27f479e 100644 --- a/third_party/Catch2/extras/CatchAddTests.cmake +++ b/third_party/Catch2/extras/CatchAddTests.cmake @@ -146,6 +146,7 @@ function(catch_discover_tests_impl) # Exit early if no tests are detected if(num_tests STREQUAL "0") + file(WRITE "${_CTEST_FILE}" "") return() endif() diff --git a/third_party/Catch2/extras/catch_amalgamated.cpp b/third_party/Catch2/extras/catch_amalgamated.cpp index 892d637d6..baf527f09 100644 --- a/third_party/Catch2/extras/catch_amalgamated.cpp +++ b/third_party/Catch2/extras/catch_amalgamated.cpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.11.0 -// Generated: 2025-09-30 10:49:12.549018 +// Catch v3.15.1 +// Generated: 2026-06-14 10:51:56.053498 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -74,11 +74,11 @@ namespace Catch { samples.data(), samples.data() + samples.size() ); auto wrap_estimate = [](Estimate e) { - return Estimate { - FDuration(e.point), - FDuration(e.lower_bound), - FDuration(e.upper_bound), - e.confidence_interval, + return Estimate{ + FDuration( e.point ), + FDuration( e.lower_bound ), + FDuration( e.upper_bound ), + e.confidence_interval, }; }; std::vector samples2; @@ -147,7 +147,7 @@ namespace Catch { namespace Catch { namespace Benchmark { namespace Detail { - struct optimized_away_error : std::exception { + struct optimized_away_error final : std::exception { const char* what() const noexcept override; }; @@ -798,6 +798,10 @@ namespace Catch { lhs.customOptions == rhs.customOptions; } + bool operator==( PathFilter const& lhs, PathFilter const& rhs ) { + return lhs.type == rhs.type && lhs.filter == rhs.filter; + } + Config::Config( ConfigData const& data ): m_data( data ) { // We need to trim filter specs to avoid trouble with superfluous @@ -807,9 +811,6 @@ namespace Catch { for (auto& elem : m_data.testsOrTags) { elem = trim(elem); } - for (auto& elem : m_data.sectionsToRun) { - elem = trim(elem); - } // Insert the default reporter if user hasn't asked for a specific one if ( m_data.reporterSpecifications.empty() ) { @@ -875,7 +876,8 @@ namespace Catch { bool Config::listListeners() const { return m_data.listListeners; } std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } - std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + std::vector const& Config::getPathFilters() const { return m_data.pathFilters; } + bool Config::useNewFilterBehaviour() const { return m_data.useNewPathFilteringBehaviour; } std::vector const& Config::getReporterSpecs() const { return m_data.reporterSpecifications; @@ -903,6 +905,9 @@ namespace Catch { bool Config::warnAboutUnmatchedTestSpecs() const { return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec ); } + bool Config::warnAboutInfiniteGenerators() const { + return !!( m_data.warnings & WarnAbout::InfiniteGenerator ); + } bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; } ShowDurations Config::showDurations() const { return m_data.showDurations; } double Config::minDuration() const { return m_data.minDuration; } @@ -990,6 +995,7 @@ namespace Catch { + #include #include @@ -1002,7 +1008,7 @@ namespace Catch { m_messageId( builder.m_info.sequence ) { MessageInfo info( CATCH_MOVE( builder.m_info ) ); info.message = builder.m_stream.str(); - IResultCapture::pushScopedMessage( CATCH_MOVE( info ) ); + Detail::pushScopedMessage( CATCH_MOVE( info ) ); } ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept: @@ -1011,14 +1017,16 @@ namespace Catch { } ScopedMessage::~ScopedMessage() { - if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); } + if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); } } Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, - StringRef names ) { + StringRef names, + bool isScoped): + m_isScoped(isScoped) { auto trimmed = [&] (size_t start, size_t end) { while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; @@ -1079,15 +1087,21 @@ namespace Catch { } Capturer::~Capturer() { assert( m_captured == m_messages.size() ); - for (auto const& message : m_messages) { - IResultCapture::popScopedMessage( message.sequence ); + if ( m_isScoped ) { + for ( auto const& message : m_messages ) { + Detail::popScopedMessage( message.sequence ); + } } } void Capturer::captureValue( size_t index, std::string const& value ) { assert( index < m_messages.size() ); m_messages[index].message += value; - IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) ); + if ( m_isScoped ) { + Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) ); + } else { + Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) ); + } m_captured++; } @@ -1147,9 +1161,6 @@ namespace Catch { CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); #endif } - IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { - return m_enumValuesRegistry; - } private: TestRegistry m_testCaseRegistry; @@ -1157,7 +1168,6 @@ namespace Catch { ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; TagAliasRegistry m_tagAliasRegistry; StartupExceptionRegistry m_exceptionRegistry; - Detail::EnumValuesRegistry m_enumValuesRegistry; }; } @@ -2045,6 +2055,36 @@ namespace Detail { } } // end unnamed namespace + std::size_t catch_strnlen( const char* str, std::size_t n ) { + auto ret = std::char_traits::find( str, n, '\0' ); + if ( ret != nullptr ) { return static_cast( ret - str ); } + return n; + } + + std::string formatTimeT(std::time_t time) { +#ifdef _MSC_VER + std::tm timeInfo = {}; + const auto err = gmtime_s( &timeInfo, &time ); + if ( err ) { + return "gmtime from provided timepoint has failed. This " + "happens e.g. with pre-1970 dates using Microsoft libc"; + } +#else + std::tm* timeInfo = std::gmtime( &time ); +#endif + + auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" ); + char timeStamp[timeStampSize]; + const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime( timeStamp, timeStampSize, fmt, &timeInfo ); +#else + std::strftime( timeStamp, timeStampSize, fmt, timeInfo ); +#endif + return std::string( timeStamp, timeStampSize - 1 ); + } + std::string convertIntoString(StringRef string, bool escapeInvisibles) { std::string ret; // This is enough for the "don't escape invisibles" case, and a good @@ -2354,7 +2394,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 11, 0, "", 0 ); + static Version version( 3, 15, 1, "", 0 ); return version; } @@ -2380,14 +2420,6 @@ namespace Catch { namespace Generators { -namespace Detail { - - [[noreturn]] - void throw_generator_exception(char const* msg) { - Catch::throw_exception(GeneratorException{ msg }); - } -} // end namespace Detail - GeneratorUntypedBase::~GeneratorUntypedBase() = default; IGeneratorTracker* acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) { @@ -2435,6 +2467,27 @@ namespace Catch { m_current_number = m_pimpl->dist( m_pimpl->rng ); return true; } + + bool RandomFloatingGenerator::isFinite() const { + return false; + } + + } // namespace Generators +} // namespace Catch + + + + +namespace Catch { + namespace Generators { + namespace Detail { + + [[noreturn]] + void throw_generator_exception( char const* msg ) { + Catch::throw_exception( GeneratorException{ msg } ); + } + + } // namespace Detail } // namespace Generators } // namespace Catch @@ -2468,6 +2521,7 @@ namespace Catch { + #include namespace Catch { @@ -2482,6 +2536,31 @@ namespace Catch { return ret; } + void GeneratorUntypedBase::skipToNthElementImpl( std::size_t n ) { + for ( size_t i = m_currentElementIndex; i < n; ++i ) { + bool isValid = next(); + if ( !isValid ) { + Detail::throw_generator_exception( + "Coud not jump to Nth element: not enough elements" ); + } + } + } + + void GeneratorUntypedBase::skipToNthElement( std::size_t n ) { + if ( n < m_currentElementIndex ) { + Detail::throw_generator_exception( + "Tried to jump generator backwards" ); + } + if ( n == m_currentElementIndex ) { return; } + + skipToNthElementImpl(n); + // Fixup tracking after moving the generator forward + // * Ensure that the correct element index is set after skipping + // * Invalidate cache + m_currentElementIndex = n; + m_stringReprCache.clear(); + } + StringRef GeneratorUntypedBase::currentElementAsString() const { if ( m_stringReprCache.empty() ) { m_stringReprCache = stringifyImpl(); @@ -2489,6 +2568,8 @@ namespace Catch { return m_stringReprCache; } + bool GeneratorUntypedBase::isFinite() const { return true; } + } // namespace Generators } // namespace Catch @@ -2602,13 +2683,17 @@ namespace Catch { namespace Catch { + void AssertionHandler::finishIncomplete() { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + AssertionHandler::AssertionHandler ( StringRef macroName, SourceLineInfo const& lineInfo, StringRef capturedExpression, ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, - m_resultCapture( getResultCapture() ) + m_resultCapture( static_cast(getResultCapture()) ) { m_resultCapture.notifyAssertionStarted( m_assertionInfo ); } @@ -3169,6 +3254,9 @@ namespace Catch { } else if ( warning == "UnmatchedTestSpec" ) { config.warnings = static_cast(config.warnings | WarnAbout::UnmatchedTestSpec); return ParserResult::ok( ParseResultType::Matched ); + } else if ( warning == "InfiniteGenerators" ) { + config.warnings = static_cast(config.warnings | WarnAbout::InfiniteGenerator); + return ParserResult::ok( ParseResultType::Matched ); } return ParserResult ::runtimeError( @@ -3326,6 +3414,19 @@ namespace Catch { config.shardCount = *parsedCount; return ParserResult::ok( ParseResultType::Matched ); }; + auto const setBenchmarkSamples = [&]( std::string const& samples ) { + auto parsedSamples = parseUInt( samples ); + if ( !parsedSamples ) { + return ParserResult::runtimeError( + "Could not parse '" + samples + "' as benchmark samples" ); + } + if ( *parsedSamples == 0 ) { + return ParserResult::runtimeError( + "Benchmark samples must be greater than 0" ); + } + config.benchmarkSamples = *parsedSamples; + return ParserResult::ok( ParseResultType::Matched ); + }; auto const setShardIndex = [&](std::string const& shardIndex) { auto parsedIndex = parseUInt( shardIndex ); @@ -3337,6 +3438,43 @@ namespace Catch { return ParserResult::ok( ParseResultType::Matched ); }; + auto const setSectionFilter = [&]( std::string const& sectionFilter ) { + config.pathFilters.emplace_back( PathFilter::For::Section, trim(sectionFilter) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setGeneratorFilter = [&]( std::string const& generatorFilter ) { + if (generatorFilter != "*") { + // TODO: avoid re-parsing the index? + auto parsedIndex = parseUInt( generatorFilter ); + if ( !parsedIndex ) { + return ParserResult::runtimeError( "Could not parse '" + + generatorFilter + + "' as generator index" ); + } + } + config.useNewPathFilteringBehaviour = true; + config.pathFilters.emplace_back( PathFilter::For::Generator, trim(generatorFilter) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + // Copy-capturing other `setFoo` functions enables calling them later, + // as the config ref remains valid, but the local lambda vars won't. + auto const setPathFilter = [=, &config]( std::string const& pathFilter ) { + config.useNewPathFilteringBehaviour = true; + if ( pathFilter.size() < 3 ) { + return ParserResult::runtimeError( + "Path filter '" + pathFilter + "' is too short" ); + } + if ( startsWith( pathFilter, "g:" ) ) { + return setGeneratorFilter( pathFilter.substr( 2 ) ); + } + if ( startsWith( pathFilter, "c:" ) ) { + return setSectionFilter( pathFilter.substr( 2 ) ); + } + return ParserResult::runtimeError( "Path filter '" + pathFilter + + "' has unknown type prefix" ); + }; + + auto cli = ExeName( config.processName ) | Help( config.showHelp ) @@ -3382,9 +3520,15 @@ namespace Catch { | Opt( config.filenamesAsTags ) ["-#"]["--filenames-as-tags"] ( "adds a tag for the filename" ) - | Opt( config.sectionsToRun, "section name" ) + | Opt( accept_many, setSectionFilter, "section name" ) ["-c"]["--section"] ( "specify section to run" ) + | Opt( accept_many, setGeneratorFilter, "index spec" ) + ["-g"]["--generator-index"] + ( "specify generator elements to try" ) + | Opt( accept_many, setPathFilter, "path filter spec" ) + ["-p"]["--path-filter"] + ( "qualified path filter" ) | Opt( setVerbosity, "quiet|normal|high" ) ["-v"]["--verbosity"] ( "set output verbosity" ) @@ -3402,7 +3546,7 @@ namespace Catch { ( "list all listeners" ) | Opt( setTestOrder, "decl|lex|rand" ) ["--order"] - ( "test case order (defaults to decl)" ) + ( "test case order (defaults to rand)" ) | Opt( setRngSeed, "'time'|'random-device'|number" ) ["--rng-seed"] ( "set a specific seed for random numbers" ) @@ -3418,7 +3562,7 @@ namespace Catch { | Opt( config.skipBenchmarks) ["--skip-benchmarks"] ( "disable running benchmarks") - | Opt( config.benchmarkSamples, "samples" ) + | Opt( setBenchmarkSamples, "samples" ) ["--benchmark-samples"] ( "number of samples to collect (default: 100)" ) | Opt( config.benchmarkResamples, "resamples" ) @@ -3602,7 +3746,9 @@ namespace { #if defined( CATCH_PLATFORM_LINUX ) \ || defined( CATCH_PLATFORM_MAC ) \ || defined( __GLIBC__ ) \ - || defined( __FreeBSD__ ) \ + || (defined( __FreeBSD__ ) \ + /* PlayStation platform does not have `isatty()` */ \ + && !defined(CATCH_PLATFORM_PLAYSTATION)) \ || defined( CATCH_PLATFORM_QNX ) # define CATCH_INTERNAL_HAS_ISATTY # include @@ -3943,12 +4089,11 @@ namespace Catch { + #include namespace Catch { - IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default; - namespace Detail { namespace { @@ -3974,9 +4119,7 @@ namespace Catch { return parsed; } - EnumInfo::~EnumInfo() = default; - - StringRef EnumInfo::lookup( int value ) const { + StringRef EnumInfo::lookup( int64_t value ) const { for( auto const& valueToName : m_values ) { if( valueToName.first == value ) return valueToName.second; @@ -3984,25 +4127,20 @@ namespace Catch { return "{** unexpected enum value **}"_sr; } - Catch::Detail::unique_ptr makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ) { - auto enumInfo = Catch::Detail::make_unique(); - enumInfo->m_name = enumName; - enumInfo->m_values.reserve( values.size() ); + EnumInfo makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ) { + EnumInfo enumInfo; + enumInfo.m_name = enumName; + enumInfo.m_values.reserve( values.size() ); const auto valueNames = Catch::Detail::parseEnums( allValueNames ); assert( valueNames.size() == values.size() ); - std::size_t i = 0; - for( auto value : values ) - enumInfo->m_values.emplace_back(value, valueNames[i++]); + for (size_t i = 0; i < values.size(); ++i) { + enumInfo.m_values.emplace_back( values[i], valueNames[i] ); + } return enumInfo; } - EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values ) { - m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); - return *m_enumInfos.back(); - } - } // Detail } // Catch @@ -4886,19 +5024,22 @@ int main (int argc, char * argv[]) { namespace Catch { + namespace { + // Messages are owned by their individual threads, so the counter should + // be thread-local as well. Alternative consideration: atomic counter, + // so threads don't share IDs and things are easier to debug. + static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0; + } + MessageInfo::MessageInfo( StringRef _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), lineInfo( _lineInfo ), type( _type ), - sequence( ++globalCount ) + sequence( ++messageIDCounter ) {} - // Messages are owned by their individual threads, so the counter should be thread-local as well. - // Alternative consideration: atomic, so threads don't share IDs and things are easier to debug. - thread_local unsigned int MessageInfo::globalCount = 0; - } // end namespace Catch @@ -4924,7 +5065,7 @@ namespace Catch { namespace { //! A no-op implementation, used if no reporter wants output //! redirection. - class NoopRedirect : public OutputRedirect { + class NoopRedirect final : public OutputRedirect { void activateImpl() override {} void deactivateImpl() override {} std::string getStdout() override { return {}; } @@ -4961,7 +5102,7 @@ namespace Catch { * Redirects the `std::cout`, `std::cerr`, `std::clog` streams, * but does not touch the actual `stdout`/`stderr` file descriptors. */ - class StreamRedirect : public OutputRedirect { + class StreamRedirect final : public OutputRedirect { ReusableStringStream m_redirectedOut, m_redirectedErr; RedirectedStreamNew m_cout, m_cerr, m_clog; @@ -5072,7 +5213,7 @@ namespace Catch { * Works by replacing the file descriptors numbered 1 and 2 * with an open temporary file. */ - class FileRedirect : public OutputRedirect { + class FileRedirect final : public OutputRedirect { TempFile m_outFile, m_errFile; int m_originalOut = -1; int m_originalErr = -1; @@ -5717,12 +5858,51 @@ namespace Catch { struct GeneratorTracker final : TestCaseTracking::TrackerBase, IGeneratorTracker { GeneratorBasePtr m_generator; + // Filtered generator has moved to specific index due to + // a filter, it needs special handling of `countedNext()` + bool m_isFiltered = false; GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, - ITracker* parent ): - TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} + ITracker* parent, + GeneratorBasePtr&& generator ): + TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ), + m_generator( CATCH_MOVE( generator ) ) { + assert( m_generator && + "Cannot create tracker without generator" ); + + // Handle potential filter and move forward here... + // Old style filters do not affect generators at all + if (m_newStyleFilters && m_allTrackerDepth < m_filterRef->size()) { + auto const& filter = + ( *m_filterRef )[m_allTrackerDepth]; + // Generator cannot be un-entered the way a section + // can be, so the tracker has to throw for a wrong + // filter to stop the execution flow. + if (filter.type == PathFilter::For::Section) { + // We want the semantics of `SKIP()`, but we inline it + // to avoid issues with conditionally prefixed macros + INTERNAL_CATCH_MSG( + "SKIP", + Catch::ResultWas::ExplicitSkip, + Catch::ResultDisposition::Normal, + "" ); + Catch::Detail::Unreachable(); + } + // '*' is the wildcard for "all elements in generator" + // used for filtering sections below the generator, but + // not the generator itself. + if ( filter.filter != "*" ) { + m_isFiltered = true; + // TBD: We assume that the filter was validated as + // number during parsing. We should pass it + // as number from the CLI parser. + size_t targetIndex = std::stoul( filter.filter ); + m_generator->skipToNthElement( targetIndex ); + } + } + } static GeneratorTracker* acquire( TrackerContext& ctx, @@ -5766,9 +5946,6 @@ namespace Catch { // 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) @@ -5797,29 +5974,24 @@ namespace Catch { // _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( *parent ); - auto const& filters = parentSection.getFilters(); - // No filters -> no restrictions on running sections - if ( filters.empty() ) { return true; } + // No filters left -> no restrictions on running sections + size_t childDepth = 1 + (m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth); + if ( childDepth >= m_filterRef->size() ) { + return true; + } + // If we are using the new style filters, we need to check + // whether the successive filter is for section or a generator. + if ( m_newStyleFilters + && (*m_filterRef)[childDepth].type != PathFilter::For::Section ) { + return false; + } + // Look for any child section that could match the remaining filters for ( auto const& child : m_children ) { if ( child->isSectionTracker() && - std::find( filters.begin(), - filters.end(), - static_cast( - *child ) - .trimmedName() ) != - filters.end() ) { + static_cast( *child ) + .trimmedName() == StringRef((*m_filterRef)[childDepth].filter) ) { return true; } } @@ -5831,9 +6003,10 @@ namespace Catch { // 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() ) ) { + if ( should_wait_for_child + || ( m_runState == CompletedSuccessfully + && !m_isFiltered // filtered generators cannot meaningfully move forward, as they would get past the filter + && m_generator->countedNext() ) ) { m_children.clear(); m_runState = Executing; } @@ -5843,9 +6016,6 @@ namespace Catch { auto getGenerator() const -> GeneratorBasePtr const& override { return m_generator; } - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = CATCH_MOVE( generator ); - } }; } // namespace } @@ -5862,29 +6032,122 @@ namespace Catch { // should also be thread local. For now we just use naked globals // below, in the future we will want to allocate piece of memory // from heap, to avoid consuming too much thread-local storage. + // + // Note that we also don't want non-trivial the thread-local variables + // below be initialized for every thread, only for those that touch + // Catch2. To make this work with both GCC/Clang and MSVC, we have to + // make them thread-local magic statics. (Class-level statics have the + // desired semantics on GCC, but not on MSVC). // This is used for the "if" part of CHECKED_IF/CHECKED_ELSE - static thread_local bool g_lastAssertionPassed = false; + static CATCH_INTERNAL_THREAD_LOCAL bool g_lastAssertionPassed = false; // This is the source location for last encountered macro. It is // used to provide the users with more precise location of error // when an unexpected exception/fatal error happens. - static thread_local SourceLineInfo g_lastKnownLineInfo("DummyLocation", static_cast(-1)); + static CATCH_INTERNAL_THREAD_LOCAL SourceLineInfo + g_lastKnownLineInfo( "DummyLocation", static_cast( -1 ) ); // Should we clear message scopes before sending off the messages to // reporter? Set in `assertionPassedFastPath` to avoid doing the full // clear there for performance reasons. - static thread_local bool g_clearMessageScopes = false; + static CATCH_INTERNAL_THREAD_LOCAL bool g_clearMessageScopes = false; + + + // Holds the data for both scoped and unscoped messages together, + // to avoid issues where their lifetimes start in wrong order, + // and then are destroyed in wrong order. + class MessageHolder { + // The actual message vector passed to the reporters + std::vector messages; + // IDs of messages from UNSCOPED_X macros, which we have to + // remove manually. + std::vector unscoped_ids; + + public: + // We do not need to special-case the unscoped messages when + // we only keep around the raw msg ids. + ~MessageHolder() = default; + + void addUnscopedMessage( MessageInfo&& info ) { + repairUnscopedMessageInvariant(); + unscoped_ids.push_back( info.sequence ); + messages.push_back( CATCH_MOVE( info ) ); + } + + void addUnscopedMessage(MessageBuilder&& builder) { + MessageInfo info( CATCH_MOVE( builder.m_info ) ); + info.message = builder.m_stream.str(); + addUnscopedMessage( CATCH_MOVE( info ) ); + } + + void addScopedMessage(MessageInfo&& info) { + messages.push_back( CATCH_MOVE( info ) ); + } + + std::vector const& getMessages() const { + return messages; + } + + void removeMessage( unsigned int messageId ) { + // Note: On average, it would probably be better to look for + // the message backwards. However, we do not expect to have + // to deal with more messages than low single digits, so + // the improvement is tiny, and we would have to hand-write + // the loop to avoid terrible codegen of reverse iterators + // in debug mode. + auto iter = + std::find_if( messages.begin(), + messages.end(), + [messageId]( MessageInfo const& msg ) { + return msg.sequence == messageId; + } ); + assert( iter != messages.end() && + "Trying to remove non-existent message." ); + messages.erase( iter ); + } + + void removeUnscopedMessages() { + for ( const auto messageId : unscoped_ids ) { + removeMessage( messageId ); + } + unscoped_ids.clear(); + g_clearMessageScopes = false; + } + + void repairUnscopedMessageInvariant() { + if ( g_clearMessageScopes ) { removeUnscopedMessages(); } + g_clearMessageScopes = false; + } + }; CATCH_INTERNAL_START_WARNINGS_SUPPRESSION CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - // Actual messages to be provided to the reporter - static thread_local std::vector g_messages; - - // Owners for the UNSCOPED_X information macro - static thread_local std::vector g_messageScopes; + static MessageHolder& g_messageHolder() { + static CATCH_INTERNAL_THREAD_LOCAL MessageHolder value; + return value; + } CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + void pushScopedMessage( MessageInfo&& message ) { + Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) ); + } + + void popScopedMessage( unsigned int messageId ) { + Detail::g_messageHolder().removeMessage( messageId ); + } + + void emplaceUnscopedMessage( MessageBuilder&& builder ) { + Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) ); + } + + void addUnscopedMessage( MessageInfo&& message ) { + Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) ); + } + + bool lastAssertionPassed() { return Detail::g_lastAssertionPassed; } + } // namespace Detail RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) @@ -5899,6 +6162,13 @@ namespace Catch { { getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); + + // TODO: HACK! + // We need to make sure the underlying cache is initialized + // while we are guaranteed to be running in a single thread, + // because the initialization is not thread-safe. + ReusableStringStream rss; + (void)rss; } RunContext::~RunContext() { @@ -5918,7 +6188,8 @@ namespace Catch { ITracker& rootTracker = m_trackerContext.startRun(); assert(rootTracker.isSectionTracker()); - static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + rootTracker.setFilters( &m_config->getPathFilters(), + m_config->useNewFilterBehaviour() ); // We intentionally only seed the internal RNG once per test case, // before it is first invoked. The reason for that is a complex @@ -6010,32 +6281,29 @@ namespace Catch { Detail::g_lastAssertionPassed = true; } else if (!result.succeeded()) { Detail::g_lastAssertionPassed = false; - if (result.isOk()) { - } - else if( m_activeTestCase->getTestCaseInfo().okToFail() ) // Read from a shared state established before the threads could start, this is fine + if (result.isOk()) {} + else if( m_activeTestCase->getTestCaseInfo().okToFail() ) { // Read from a shared state established before the threads could start, this is fine m_atomicAssertionCount.failedButOk++; - else + } else { m_atomicAssertionCount.failed++; - } - else { + } + } else { Detail::g_lastAssertionPassed = true; } - if ( Detail::g_clearMessageScopes ) { - Detail::g_messageScopes.clear(); - Detail::g_clearMessageScopes = false; - } + auto& msgHolder = Detail::g_messageHolder(); + msgHolder.repairUnscopedMessageInvariant(); // From here, we are touching shared state and need mutex. Detail::LockGuard lock( m_assertionMutex ); { auto _ = scopedDeactivate( *m_outputRedirect ); updateTotalsFromAtomics(); - m_reporter->assertionEnded( AssertionStats( result, Detail::g_messages, m_totals ) ); + m_reporter->assertionEnded( AssertionStats( result, msgHolder.getMessages(), m_totals ) ); } if ( result.getResultType() != ResultWas::Warning ) { - Detail::g_messageScopes.clear(); + msgHolder.removeUnscopedMessages(); } // Reset working state. assertion info will be reset after @@ -6092,18 +6360,32 @@ namespace Catch { SourceLineInfo lineInfo, Generators::GeneratorBasePtr&& generator ) { + // TBD: Do we want to avoid the warning if the generator is filtered? + if ( m_config->warnAboutInfiniteGenerators() && + !generator->isFinite() ) { + // We want the semantics of `FAIL()`, but we inline it + // to avoid issues with conditionally prefixed macros + INTERNAL_CATCH_MSG( "FAIL", + Catch::ResultWas::ExplicitFailure, + Catch::ResultDisposition::Normal, + "GENERATE() would run infinitely" ); + } + auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast( generatorName ), lineInfo ); auto& currentTracker = m_trackerContext.currentTracker(); assert( currentTracker.nameAndLocation() != nameAndLoc && "Trying to create tracker for a generator that already has one" ); - auto newTracker = Catch::Detail::make_unique( - CATCH_MOVE(nameAndLoc), m_trackerContext, ¤tTracker ); + auto newTracker = + Catch::Detail::make_unique( + CATCH_MOVE( nameAndLoc ), + m_trackerContext, + ¤tTracker, + CATCH_MOVE( generator ) ); auto ret = newTracker.get(); currentTracker.addChild( CATCH_MOVE( newTracker ) ); - ret->setGenerator( CATCH_MOVE( generator ) ); ret->open(); return ret; } @@ -6187,7 +6469,7 @@ namespace Catch { // and since IResultCapture::getLastResult is deprecated, // we will leave it as is, until it is finally removed. Detail::LockGuard _( m_assertionMutex ); - return &(*m_lastResult); + return &*m_lastResult; } void RunContext::exceptionEarlyReported() { @@ -6261,10 +6543,6 @@ namespace Catch { m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); } - bool RunContext::lastAssertionPassed() { - return Detail::g_lastAssertionPassed; - } - void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) { // We want to save the line info for better experience with unexpected assertions Detail::g_lastKnownLineInfo = lineInfo; @@ -6324,10 +6602,10 @@ namespace Catch { m_testCaseTracker->close(); handleUnfinishedSections(); - Detail::g_messageScopes.clear(); - // TBD: At this point, m_messages should be empty. Do we want to - // assert that this is true, or keep the defensive clear call? - Detail::g_messages.clear(); + auto& msgHolder = Detail::g_messageHolder(); + msgHolder.removeUnscopedMessages(); + assert( msgHolder.getMessages().empty() && + "There should be no leftover messages after the test ends" ); SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions); m_reporter->sectionEnded(testCaseSectionStats); @@ -6443,7 +6721,7 @@ namespace Catch { } void RunContext::populateReaction( AssertionReaction& reaction, - bool has_normal_disposition ) { + bool has_normal_disposition ) const { reaction.shouldDebugBreak = m_shouldDebugBreak; reaction.shouldThrow = aborting() || has_normal_disposition; } @@ -6494,28 +6772,6 @@ namespace Catch { } } - void IResultCapture::pushScopedMessage( MessageInfo&& message ) { - Detail::g_messages.push_back( CATCH_MOVE( message ) ); - } - - void IResultCapture::popScopedMessage( unsigned int messageId ) { - // Note: On average, it would probably be better to look for the message - // backwards. However, we do not expect to have to deal with more - // messages than low single digits, so the optimization is tiny, - // and we would have to hand-write the loop to avoid terrible - // codegen of reverse iterators in debug mode. - Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(), - Detail::g_messages.end(), - [=]( MessageInfo const& msg ) { - return msg.sequence == - messageId; - } ) ); - } - - void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) { - Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) ); - } - void seedRng(IConfig const& config) { sharedRng().seed(config.rngSeed()); } @@ -6847,10 +7103,11 @@ namespace Catch { TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); - if( it != m_registry.end() ) - return &(it->second); - else + if ( it != m_registry.end() ) { + return &it->second; + } else { return nullptr; + } } std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { @@ -7074,6 +7331,17 @@ namespace TestCaseTracking { location( _location ) {} + ITracker::ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ): + m_nameAndLocation( CATCH_MOVE( nameAndLoc ) ), m_parent( parent ) { + if ( m_parent ) { + m_allTrackerDepth = m_parent->m_allTrackerDepth + 1; + // We leave section trackers to bump themselves up, as + // we cannot use `isSectionTracker` in constructor + m_sectionOnlyDepth = m_parent->m_sectionOnlyDepth; + m_filterRef = m_parent->m_filterRef; + m_newStyleFilters = m_parent->m_newStyleFilters; + } + } ITracker::~ITracker() = default; @@ -7206,25 +7474,32 @@ namespace TestCaseTracking { : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) { - if( parent ) { - while ( !parent->isSectionTracker() ) { - parent = parent->parent(); - } - - SectionTracker& parentSection = static_cast( *parent ); - addNextFilters( parentSection.m_filters ); + if( m_parent ) { + ++m_sectionOnlyDepth; } } bool SectionTracker::isComplete() const { - bool complete = true; - - if (m_filters.empty() - || m_filters[0].empty() - || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { - complete = TrackerBase::isComplete(); + // If there are active filters AND we do not pass them, + // the section is always "completed" + const size_t filterIndex = + m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth; + if ( filterIndex < m_filterRef->size() ) { + // There is active filter, check it + // 1) New style filter must explicitly target section + if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type != + PathFilter::For::Section ) { + return true; + } + // 2) Both style filters must match the trimmed name exactly + if ( m_trimmed_name != + StringRef( ( *m_filterRef )[filterIndex].filter ) ) { + return true; + } } - return complete; + + // Otherwise we delegate to the generic processing + return TrackerBase::isComplete(); } bool SectionTracker::isSectionTracker() const { return true; } @@ -7260,19 +7535,6 @@ namespace TestCaseTracking { open(); } - void SectionTracker::addInitialFilters( std::vector const& filters ) { - if( !filters.empty() ) { - m_filters.reserve( m_filters.size() + filters.size() + 2 ); - m_filters.emplace_back(StringRef{}); // Root - should never be consulted - m_filters.emplace_back(StringRef{}); // Test Case - not a section filter - m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); - } - } - void SectionTracker::addNextFilters( std::vector const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); - } - StringRef SectionTracker::trimmedName() const { return m_trimmed_name; } @@ -7292,7 +7554,7 @@ namespace Catch { void throw_test_failure_exception() { #if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw TestFailureException{}; + throw TestFailureException{}; //NOLINT(bugprone-std-exception-baseclass) #else CATCH_ERROR( "Test failure requires aborting test!" ); #endif @@ -7300,7 +7562,7 @@ namespace Catch { void throw_test_skip_exception() { #if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); + throw Catch::TestSkipException(); //NOLINT(bugprone-std-exception-baseclass) #else CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); #endif @@ -7632,6 +7894,10 @@ namespace { return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; } + bool isUtf8ContinuationByte( char c ) { + return ( static_cast( c ) & 0xC0 ) == 0x80; + } + } // namespace namespace Catch { @@ -7658,6 +7924,11 @@ namespace Catch { if ( it != m_string.end() ) { ++m_size; ++it; + // Skip UTF-8 continuation bytes + while ( it != m_string.end() && + isUtf8ContinuationByte( *it ) ) { + ++it; + } } } } @@ -7720,6 +7991,11 @@ namespace Catch { void AnsiSkippingString::const_iterator::advance() { assert( m_it != m_string->end() ); m_it++; + // Skip UTF-8 continuation bytes + while ( m_it != m_string->end() && + isUtf8ContinuationByte( *m_it ) ) { + m_it++; + } tryParseAnsiEscapes(); } @@ -7739,6 +8015,11 @@ namespace Catch { assert( *m_it == '\033' ); m_it--; } + // Skip back over UTF-8 continuation bytes to the leading byte + while ( m_it != m_string->begin() && + isUtf8ContinuationByte( *m_it ) ) { + m_it--; + } } static bool isBoundary( AnsiSkippingString const& line, @@ -8385,13 +8666,13 @@ namespace Catch { namespace Matchers { std::string MatcherUntypedBase::toString() const { - if (m_cachedToString.empty()) { - m_cachedToString = describe(); - } - return m_cachedToString; + return describe(); } - MatcherUntypedBase::~MatcherUntypedBase() = default; + std::string MatcherUntypedBase::describe() const { + using namespace std::string_literals; + return "Undescribed matcher"s; + } } // namespace Matchers } // namespace Catch @@ -8412,14 +8693,6 @@ namespace Matchers { return sstr.str(); } - IsEmptyMatcher IsEmpty() { - return {}; - } - - HasSizeMatcher SizeIs(std::size_t sz) { - return HasSizeMatcher{ sz }; - } - } // end namespace Matchers } // end namespace Catch @@ -8673,17 +8946,13 @@ std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc namespace Catch { namespace Matchers { - std::string AllTrueMatcher::describe() const { return "contains only true"; } - AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } + std::string AllTrueMatcher::describe() const { return "contains only true"; } std::string NoneTrueMatcher::describe() const { return "contains no true"; } - NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } - std::string AnyTrueMatcher::describe() const { return "contains at least one true"; } - AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } } // namespace Matchers } // namespace Catch @@ -8796,7 +9065,11 @@ namespace Matchers { namespace Catch { namespace Matchers { - MatcherGenericBase::~MatcherGenericBase() = default; + + std::string MatcherGenericBase::describe() const { + using namespace std::string_literals; + return "Undescribed generic matcher"s; + } namespace Detail { @@ -8891,12 +9164,12 @@ namespace Catch { void ReporterBase::listReporters( std::vector const& descriptions ) { - defaultListReporters(m_stream, descriptions, m_config->verbosity()); + defaultListReporters( m_stream, descriptions, m_config->verbosity() ); } void ReporterBase::listListeners( std::vector const& descriptions ) { - defaultListListeners( m_stream, descriptions ); + defaultListListeners( m_stream, descriptions, m_config->verbosity() ); } void ReporterBase::listTests(std::vector const& tests) { @@ -8908,7 +9181,7 @@ namespace Catch { } void ReporterBase::listTags(std::vector const& tags) { - defaultListTags( m_stream, tags, m_config->hasTestFilters() ); + defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_config->verbosity() ); } } // namespace Catch @@ -9819,9 +10092,8 @@ namespace Catch { bool operator()( Detail::unique_ptr const& node ) const { - return ( - ( node->stats.sectionInfo.name == m_other.name ) && - ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) ); + return node->stats.sectionInfo.name == m_other.name + && node->stats.sectionInfo.lineInfo == m_other.lineInfo; } void operator=( BySectionInfo const& ) = delete; @@ -10119,7 +10391,15 @@ namespace Catch { } void defaultListListeners( std::ostream& out, - std::vector const& descriptions ) { + std::vector const& descriptions, + Verbosity verbosity ) { + if ( verbosity == Verbosity::Quiet ) { + for ( auto const& desc : descriptions ) { + out << desc.name << '\n'; + } + return; + } + out << "Registered listeners:\n"; if(descriptions.empty()) { @@ -10152,7 +10432,14 @@ namespace Catch { void defaultListTags( std::ostream& out, std::vector const& tags, - bool isFiltered ) { + bool isFiltered, + Verbosity verbosity ) { + if (verbosity == Verbosity::Quiet) { + for (auto const& tagCount : tags) { + out << tagCount.all() << '\n'; + } + return; + } if ( isFiltered ) { out << "Tags for matching test cases:\n"; } else { @@ -10160,7 +10447,7 @@ namespace Catch { } // minimum whitespace to pad tag counts, possibly overwritten below - size_t maxTagCountLen = 2; + int maxTagCountLen = 2; // determine necessary padding for tag count column if ( ! tags.empty() ) { @@ -10171,11 +10458,11 @@ namespace Catch { return lhs.count < rhs.count; } ) ->count; - + // more padding necessary for 3+ digits if (maxTagCount >= 100) { auto numDigits = 1 + std::floor( std::log10( maxTagCount ) ); - maxTagCountLen = static_cast( numDigits ); + maxTagCountLen = static_cast( numDigits ); } } @@ -10928,70 +11215,77 @@ namespace Catch { void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { if (assertionOrBenchmark.isAssertion()) { - writeAssertion(assertionOrBenchmark.asAssertion()); + // JUnit XML format supports only 1 error/failure/skip + // assertion elements per test case + if (writeAssertion(assertionOrBenchmark.asAssertion())) { + break; + } } } } - void JunitReporter::writeAssertion( AssertionStats const& stats ) { + bool JunitReporter::writeAssertion( AssertionStats const& stats ) { AssertionResult const& result = stats.assertionResult; - if ( !result.isOk() || - result.getResultType() == ResultWas::ExplicitSkip ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - case ResultWas::ExpressionFailed: - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - case ResultWas::ExplicitSkip: - elementName = "skipped"; - break; - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } + if ( result.isOk() && + result.getResultType() != ResultWas::ExplicitSkip ) { + return false; + } + std::string elementName; + switch ( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + case ResultWas::ExplicitSkip: + elementName = "skipped"; + break; + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - xml.writeAttribute( "message"_sr, result.getExpression() ); - xml.writeAttribute( "type"_sr, result.getTestMacroName() ); + xml.writeAttribute( "message"_sr, result.getExpression() ); + xml.writeAttribute( "type"_sr, result.getTestMacroName() ); - ReusableStringStream rss; - if ( result.getResultType() == ResultWas::ExplicitSkip ) { - rss << "SKIPPED\n"; - } else { - rss << "FAILED" << ":\n"; - if (result.hasExpression()) { - rss << " "; - rss << result.getExpressionInMacro(); - rss << '\n'; - } - if (result.hasExpandedExpression()) { - rss << "with expansion:\n"; - rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n'; - } + ReusableStringStream rss; + if ( result.getResultType() == ResultWas::ExplicitSkip ) { + rss << "SKIPPED\n"; + } else { + rss << "FAILED:\n"; + if ( result.hasExpression() ) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; + } + if ( result.hasExpandedExpression() ) { + rss << "with expansion:\n"; + rss << TextFlow::Column( result.getExpandedExpression() ) + .indent( 2 ) + << '\n'; } + } - if( result.hasMessage() ) - rss << result.getMessage() << '\n'; - for( auto const& msg : stats.infoMessages ) - if( msg.type == ResultWas::Info ) - rss << msg.message << '\n'; - - rss << "at " << result.getSourceInfo(); - xml.writeText( rss.str(), XmlFormatting::Newline ); + if ( result.hasMessage() ) { rss << result.getMessage() << '\n'; } + for ( auto const& msg : stats.infoMessages ) { + if ( msg.type == ResultWas::Info ) { rss << msg.message << '\n'; } } + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), XmlFormatting::Newline ); + return true; } } // end namespace Catch diff --git a/third_party/Catch2/extras/catch_amalgamated.hpp b/third_party/Catch2/extras/catch_amalgamated.hpp index d5eb1bb75..fc71645c2 100644 --- a/third_party/Catch2/extras/catch_amalgamated.hpp +++ b/third_party/Catch2/extras/catch_amalgamated.hpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.11.0 -// Generated: 2025-09-30 10:49:11.225746 +// Catch v3.15.1 +// Generated: 2026-06-14 10:51:55.600632 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -118,16 +118,24 @@ #endif // CATCH_PLATFORM_HPP_INCLUDED -#ifdef __cplusplus - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif +#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +#endif -# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) -# define CATCH_CPP20_OR_GREATER -# endif +#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +# define CATCH_CPP20_OR_GREATER +#endif +// Matchers are only constexpr-able in C++20 +#if defined( CATCH_CPP20_OR_GREATER ) && \ + defined( __cpp_constexpr_dynamic_alloc ) && \ + __cpp_constexpr_dynamic_alloc >= 201907L && \ + /* GCC < 13 define the feature macro, but compiler bugs stop us from using it */ \ + ( !defined( __GNUC__ ) || __GNUC__ >= 13 || defined(__clang__) ) +# define CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED +# define CATCH_DESTRUCTOR_CONSTEXPR constexpr +#else +# define CATCH_DESTRUCTOR_CONSTEXPR #endif // Only GCC compiler should be used in this block, so other compilers trying to @@ -201,10 +209,15 @@ # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wc++20-extensions\"" ) # else -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) # endif +# if ( __clang_major__ >= 22 ) +# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wc2y-extensions\"" ) +# endif + # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) @@ -299,7 +312,7 @@ //////////////////////////////////////////////////////////////////////////////// // Visual C++ -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) // We want to defer to nvcc-specific warning suppression if we are compiled // with nvcc masquerading for MSVC. @@ -310,6 +323,11 @@ __pragma( warning( pop ) ) # endif +// Suppress MSVC C++ Core Guidelines checker warning 26426: +// "Global initializer calls a non-constexpr function (i.22)" +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + __pragma( warning( disable : 26426 ) ) + // Universal Windows platform does not support SEH # if !defined(CATCH_PLATFORM_WINDOWS_UWP) # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH @@ -515,6 +533,9 @@ #if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) # define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS #endif +#if !defined( CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS +#endif #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS @@ -673,8 +694,20 @@ namespace Catch { #endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED + +// Fixme: Clang 22 has an annoying bug where the localized suppression +// below does not actually suppress the extension warning from +// using __COUNTER__, so we have to leak the suppression for the +// whole TU. Hopefully Clang 23 fixes this before full release. +// As AppleClang does its own thing version-wise, we ignore it +// completely. +#if defined( __clang__ ) && ( __clang_major__ >= 22 ) && !defined( __APPLE__ ) +CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS +#endif + #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 @@ -694,11 +727,30 @@ namespace Catch { #ifndef CATCH_STRINGREF_HPP_INCLUDED #define CATCH_STRINGREF_HPP_INCLUDED + + + +#ifndef CATCH_LIFETIMEBOUND_HPP_INCLUDED +#define CATCH_LIFETIMEBOUND_HPP_INCLUDED + +#if !defined( __has_cpp_attribute ) +# define CATCH_ATTR_LIFETIMEBOUND +#elif __has_cpp_attribute( msvc::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]] +#elif __has_cpp_attribute( clang::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] +#elif __has_cpp_attribute( lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]] +#else +# define CATCH_ATTR_LIFETIMEBOUND +#endif + +#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED + #include #include #include #include - #include namespace Catch { @@ -722,14 +774,16 @@ namespace Catch { public: // construction constexpr StringRef() noexcept = default; - StringRef( char const* rawChars ) noexcept; + StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept; - constexpr StringRef( char const* rawChars, size_type size ) noexcept + constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND, + size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} - StringRef( std::string const& stdString ) noexcept + StringRef( + std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} @@ -775,7 +829,7 @@ namespace Catch { } // Returns the current start pointer. May not be null-terminated. - constexpr char const* data() const noexcept { + constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND { return m_start; } @@ -985,35 +1039,16 @@ namespace Detail { #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 - namespace Catch { - namespace Benchmark { - using IDuration = std::chrono::nanoseconds; - using FDuration = std::chrono::duration; - - template - using TimePoint = typename Clock::time_point; - - using default_clock = std::chrono::steady_clock; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_CLOCK_HPP_INCLUDED -namespace Catch { + namespace Detail { + struct DummyTemplateArgPlaceholder; + } // 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 + // we can prevent multiple declarations in dependencies + template struct BenchmarkStats; } // end namespace Catch @@ -1026,8 +1061,6 @@ namespace Catch { struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; - struct MessageInfo; - struct MessageBuilder; struct Counts; struct AssertionReaction; struct SourceLineInfo; @@ -1067,10 +1100,6 @@ namespace Catch { virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; virtual void benchmarkFailed( StringRef error ) = 0; - static void pushScopedMessage( MessageInfo&& message ); - static void popScopedMessage( unsigned int messageId ); - static void emplaceUnscopedMessage( MessageBuilder&& builder ); - virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual void handleExpr @@ -1096,9 +1125,6 @@ namespace Catch { ResultWas::OfType resultType, AssertionReaction &reaction ) = 0; - - virtual bool lastAssertionPassed() = 0; - // Deprecated, do not use: virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; @@ -1168,6 +1194,8 @@ namespace Catch { NoAssertions = 0x01, //! A command line test spec matched no test cases UnmatchedTestSpec = 0x02, + //! The resulting generator in GENERATE is infinite + InfiniteGenerator = 0x04, }; }; enum class ShowDurations { @@ -1199,8 +1227,9 @@ namespace Catch { class TestSpec; class IStream; + struct PathFilter; - class IConfig : public Detail::NonCopyable { + class IConfig : Detail::NonCopyable { public: virtual ~IConfig(); @@ -1210,6 +1239,7 @@ namespace Catch { virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual bool warnAboutUnmatchedTestSpecs() const = 0; + virtual bool warnAboutInfiniteGenerators() const = 0; virtual bool zeroTestsCountAsSuccess() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; @@ -1223,7 +1253,9 @@ namespace Catch { virtual unsigned int shardCount() const = 0; virtual unsigned int shardIndex() const = 0; virtual ColourMode defaultColourMode() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; + virtual std::vector const& getPathFilters() const = 0; + virtual bool useNewFilterBehaviour() const = 0; + virtual Verbosity verbosity() const = 0; virtual bool skipBenchmarks() const = 0; @@ -1285,7 +1317,6 @@ namespace Catch { virtual void registerTranslator( Detail::unique_ptr&& 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(); @@ -1303,6 +1334,28 @@ namespace Catch { +// Adapted from donated nonius code. + +#ifndef CATCH_CLOCK_HPP_INCLUDED +#define CATCH_CLOCK_HPP_INCLUDED + +#include + +namespace Catch { + namespace Benchmark { + using IDuration = std::chrono::nanoseconds; + using FDuration = std::chrono::duration; + + template + using TimePoint = typename Clock::time_point; + + using default_clock = std::chrono::steady_clock; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CLOCK_HPP_INCLUDED + + // Adapted from donated nonius code. #ifndef CATCH_ESTIMATE_HPP_INCLUDED @@ -1625,7 +1678,7 @@ namespace Catch { callable& operator=(callable&&) = default; }; template - struct model : public callable { + struct model final : public callable { model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} model(Fun const& fun_) : fun(fun_) {} @@ -2305,7 +2358,7 @@ namespace Catch { #ifndef CATCH_TOSTRING_HPP_INCLUDED #define CATCH_TOSTRING_HPP_INCLUDED - +#include #include #include #include @@ -2413,45 +2466,46 @@ namespace Catch { #endif // CATCH_VOID_TYPE_HPP_INCLUDED -#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED +#ifndef CATCH_ENUM_INFO_HPP_INCLUDED +#define CATCH_ENUM_INFO_HPP_INCLUDED +#include +#include #include namespace Catch { - namespace Detail { + struct EnumInfo { StringRef m_name; - std::vector> m_values; + std::vector> m_values; - ~EnumInfo(); - - StringRef lookup( int value ) const; + StringRef lookup( int64_t value ) const; }; - } // namespace Detail - class IMutableEnumValuesRegistry { - public: - virtual ~IMutableEnumValuesRegistry(); // = default; - - virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; - - template - Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { - static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); - std::vector intValues; + EnumInfo makeEnumInfo( StringRef enumName, + StringRef allValueNames, + std::vector const& values ); + template + Detail::EnumInfo makeEnumInfo( StringRef enumName, + StringRef allEnums, + std::initializer_list values ) { + static_assert( sizeof( int64_t ) >= sizeof( E ), + "Cannot serialize enum to int64_t" ); + std::vector intValues; intValues.reserve( values.size() ); - for( auto enumValue : values ) - intValues.push_back( static_cast( enumValue ) ); - return registerEnum( enumName, allEnums, intValues ); + for ( auto enumValue : values ) + intValues.push_back( static_cast( enumValue ) ); + return makeEnumInfo( enumName, allEnums, intValues ); } - }; -} // Catch + std::vector parseEnums( StringRef enums ); -#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED + } // namespace Detail +} // namespace Catch + +#endif // CATCH_ENUM_INFO_HPP_INCLUDED #ifdef CATCH_CONFIG_CPP17_STRING_VIEW #include @@ -2473,13 +2527,9 @@ namespace Catch { namespace Detail { - inline std::size_t catch_strnlen(const char *str, std::size_t n) { - auto ret = std::char_traits::find(str, n, '\0'); - if (ret != nullptr) { - return static_cast(ret - str); - } - return n; - } + std::size_t catch_strnlen(const char *str, std::size_t n); + + std::string formatTimeT( std::time_t time ); constexpr StringRef unprintableString = "{?}"_sr; @@ -2820,7 +2870,10 @@ namespace Catch { } #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER -#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#if defined( CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER ) && \ + defined( CATCH_CONFIG_CPP17_OPTIONAL ) && \ + /* P3168 turned optional into a range, making this ambigous with the range support */ \ + !defined( __cpp_lib_optional_range_support ) #include namespace Catch { template @@ -2844,44 +2897,38 @@ namespace Catch { // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) -#include +# include +# include namespace Catch { namespace Detail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct TupleElementPrinter { - static void print(const Tuple& tuple, std::ostream& os) { - os << (N ? ", " : " ") - << ::Catch::Detail::stringify(std::get(tuple)); - TupleElementPrinter::print(tuple, os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct TupleElementPrinter { - static void print(const Tuple&, std::ostream&) {} - }; - - } + template + void PrintTuple( const Tuple& tuple, + std::ostream& os, + std::index_sequence ) { + // 1 + Account for when the tuple is empty + char a[1 + sizeof...( Is )] = { + ( ( os << ( Is ? ", " : " " ) + << ::Catch::Detail::stringify( std::get( tuple ) ) ), + '\0' )... }; + (void)a; + } + } // namespace Detail - template + template struct StringMaker> { - static std::string convert(const std::tuple& tuple) { + static std::string convert( const std::tuple& tuple ) { ReusableStringStream rss; rss << '{'; - Detail::TupleElementPrinter>::print(tuple, rss.get()); + Detail::PrintTuple( + tuple, + rss.get(), + std::make_index_sequence{} ); rss << " }"; return rss.str(); } }; -} +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) @@ -3068,42 +3115,26 @@ struct ratio_string { const auto systemish = std::chrono::time_point_cast< std::chrono::system_clock::duration>( time_point ); const auto as_time_t = std::chrono::system_clock::to_time_t( systemish ); - -#ifdef _MSC_VER - std::tm timeInfo = {}; - const auto err = gmtime_s( &timeInfo, &as_time_t ); - if ( err ) { - return "gmtime from provided timepoint has failed. This " - "happens e.g. with pre-1970 dates using Microsoft libc"; - } -#else - std::tm* timeInfo = std::gmtime( &as_time_t ); -#endif - - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif - return std::string(timeStamp, timeStampSize - 1); + return ::Catch::Detail::formatTimeT( as_time_t ); } }; } - -#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ -namespace Catch { \ - template<> struct StringMaker { \ - static std::string convert( enumName value ) { \ - static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ - return static_cast(enumInfo.lookup( static_cast( value ) )); \ - } \ - }; \ -} +#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ + namespace Catch { \ + template <> \ + struct StringMaker { \ + static std::string convert( enumName value ) { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + static const auto enumInfo = ::Catch::Detail::makeEnumInfo( \ + #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + return static_cast( \ + enumInfo.lookup( static_cast( value ) ) ); \ + } \ + }; \ + } #define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) @@ -3640,6 +3671,33 @@ namespace Catch { #endif // CATCH_OPTIONAL_HPP_INCLUDED +#ifndef CATCH_PATH_FILTER_HPP_INCLUDED +#define CATCH_PATH_FILTER_HPP_INCLUDED + + +#include + +namespace Catch { + + struct PathFilter { + enum class For { + Section, + Generator, + }; + PathFilter( For type_, std::string filter_ ): + type( type_ ), filter( CATCH_MOVE( filter_ ) ) {} + + For type; + std::string filter; + + friend bool operator==( PathFilter const& lhs, PathFilter const& rhs ); + }; + +} // end namespace Catch + +#endif // CATCH_PATH_FILTER_HPP_INCLUDED + + #ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED #define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED @@ -3807,7 +3865,8 @@ namespace Catch { std::vector reporterSpecifications; std::vector testsOrTags; - std::vector sectionsToRun; + std::vector pathFilters; + bool useNewPathFilteringBehaviour = false; std::string prematureExitGuardFilePath; }; @@ -3830,7 +3889,8 @@ namespace Catch { getProcessedReporterSpecs() const; std::vector const& getTestsOrTags() const override; - std::vector const& getSectionsToRun() const override; + std::vector const& getPathFilters() const override; + bool useNewFilterBehaviour() const override; TestSpec const& testSpec() const override; bool hasTestFilters() const override; @@ -3845,6 +3905,7 @@ namespace Catch { bool includeSuccessfulResults() const override; bool warnAboutMissingAssertions() const override; bool warnAboutUnmatchedTestSpecs() const override; + bool warnAboutInfiniteGenerators() const override; bool zeroTestsCountAsSuccess() const override; ShowDurations showDurations() const override; double minDuration() const override; @@ -3953,9 +4014,9 @@ namespace Catch { #if !defined( CATCH_CONFIG_NO_DEPRECATION_ANNOTATIONS ) -# define DEPRECATED( msg ) [[deprecated( msg )]] +# define CATCH_DEPRECATED( msg ) [[deprecated( msg )]] #else -# define DEPRECATED( msg ) +# define CATCH_DEPRECATED( msg ) #endif #endif // CATCH_DEPRECATION_MACRO_HPP_INCLUDED @@ -3976,16 +4037,14 @@ namespace Catch { // The "ID" of the message, used to know when to remove it from reporter context. unsigned int sequence; - DEPRECATED( "Explicitly use the 'sequence' member instead" ) + CATCH_DEPRECATED( "Explicitly use the 'sequence' member instead" ) bool operator == (MessageInfo const& other) const { return sequence == other.sequence; } - DEPRECATED( "Explicitly use the 'sequence' member instead" ) + CATCH_DEPRECATED( "Explicitly use the 'sequence' member instead" ) bool operator < (MessageInfo const& other) const { return sequence < other.sequence; } - private: - static thread_local unsigned int globalCount; }; } // end namespace Catch @@ -3996,9 +4055,21 @@ namespace Catch { #include namespace Catch { + struct MessageInfo; + struct MessageBuilder; + + namespace Detail { + // The message state affecting functions have to be defined in + // the TU where the thread-local message holders are defined. + // Currently this is catch_run_context.cpp + + void pushScopedMessage( MessageInfo&& message ); + void popScopedMessage( unsigned int messageId ); + void addUnscopedMessage( MessageInfo&& message ); + void emplaceUnscopedMessage( MessageBuilder&& builder ); + } // namespace Detail struct SourceLineInfo; - class IResultCapture; struct MessageStream { @@ -4040,8 +4111,9 @@ namespace Catch { class Capturer { std::vector m_messages; size_t m_captured = 0; + bool m_isScoped = false; public: - Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names, bool isScoped ); Capturer(Capturer const&) = delete; Capturer& operator=(Capturer const&) = delete; @@ -4073,11 +4145,12 @@ namespace Catch { } while( false ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ - Catch::Capturer varName( macroName##_catch_sr, \ - CATCH_INTERNAL_LINEINFO, \ - Catch::ResultWas::Info, \ - #__VA_ARGS__##_catch_sr ); \ +#define INTERNAL_CATCH_CAPTURE( varName, macroName, scopedCapture, ... ) \ + Catch::Capturer varName( macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info, \ + #__VA_ARGS__##_catch_sr, \ + scopedCapture ); \ varName.captureValues( 0, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// @@ -4086,7 +4159,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ - Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + Catch::Detail::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) #if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) @@ -4094,28 +4167,32 @@ namespace Catch { #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__ ) + #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", true, __VA_ARGS__ ) + #define CATCH_UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_UNSCOPED_CAPTURE", false, __VA_ARGS__ ) #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) + #define CATCH_INFO( msg ) (void)(0) + #define CATCH_UNSCOPED_INFO( msg ) (void)(0) + #define CATCH_WARN( msg ) (void)(0) + #define CATCH_CAPTURE( ... ) (void)(0) + #define CATCH_UNSCOPED_CAPTURE( ... ) (void)(0) #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__ ) + #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", true, __VA_ARGS__ ) + #define UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "UNSCOPED_CAPTURE", false, __VA_ARGS__ ) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) - #define INFO( msg ) (void)(0) - #define UNSCOPED_INFO( msg ) (void)(0) - #define WARN( msg ) (void)(0) - #define CAPTURE( ... ) (void)(0) + #define INFO( msg ) (void)(0) + #define UNSCOPED_INFO( msg ) (void)(0) + #define WARN( msg ) (void)(0) + #define CAPTURE( ... ) (void)(0) + #define UNSCOPED_CAPTURE( ... ) (void)(0) #endif // end of user facing macro declarations @@ -5371,7 +5448,7 @@ namespace Catch { void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); template - class BinaryExpr : public ITransientExpression { + class BinaryExpr final : public ITransientExpression { LhsT m_lhs; StringRef m_op; RhsT m_rhs; @@ -5447,7 +5524,7 @@ namespace Catch { }; template - class UnaryExpr : public ITransientExpression { + class UnaryExpr final : public ITransientExpression { LhsT m_lhs; void streamReconstructedExpression( std::ostream &os ) const override { @@ -5648,6 +5725,8 @@ namespace Catch { namespace Catch { + class RunContext; + struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; @@ -5658,7 +5737,12 @@ namespace Catch { AssertionInfo m_assertionInfo; AssertionReaction m_reaction; bool m_completed = false; - IResultCapture& m_resultCapture; + // Since all uses are hidden in the .cpp file, we can directly use + // the final type and avoid going through virtual dispatch, without + // massive compilation time overhead. + RunContext& m_resultCapture; + + void finishIncomplete(); public: AssertionHandler @@ -5667,9 +5751,9 @@ namespace Catch { StringRef capturedExpression, ResultDisposition::Flags resultDisposition ); ~AssertionHandler() { - if ( !m_completed ) { - m_resultCapture.handleIncomplete( m_assertionInfo ); - } + // We want the common fast path inlinable, and the virtual + // dispatch in a function in single TU. + if ( !m_completed ) { finishIncomplete(); } } @@ -5712,6 +5796,13 @@ namespace Catch { #endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +namespace Catch { + namespace Detail { + // Defined in catch_run_context.cpp, where the thread-local data lives. + bool lastAssertionPassed(); + } +} + // 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 @@ -5754,12 +5845,12 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( Catch::getResultCapture().lastAssertionPassed() ) + if( Catch::Detail::lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( !Catch::getResultCapture().lastAssertionPassed() ) + if( !Catch::Detail::lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ @@ -6042,7 +6133,7 @@ namespace Catch { namespace Catch { template -class TestInvokerAsMethod : public ITestInvoker { +class TestInvokerAsMethod final : public ITestInvoker { void (C::*m_testAsMethod)(); public: constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept: @@ -6062,7 +6153,7 @@ Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { } template -class TestInvokerFixture : public ITestInvoker { +class TestInvokerFixture final : public ITestInvoker { void ( C::*m_testAsMethod )() const; Detail::unique_ptr m_fixture = nullptr; @@ -6137,7 +6228,7 @@ struct AutoReg : Detail::NonCopyable { namespace Catch { namespace Detail { struct DummyUse { - DummyUse( void ( * )( int ), Catch::NameAndTags const& ); + DummyUse( void ( * )( int ), Catch::NameAndTags const& ) noexcept; }; } // namespace Detail } // namespace Catch @@ -6988,42 +7079,42 @@ namespace Catch { INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) #endif - #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ - void test();\ - };\ - namespace {\ - namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\ - INTERNAL_CATCH_TYPE_GEN \ - INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ - template\ - struct TestNameClass{\ - void reg_tests(){\ - std::size_t index = 0;\ - using expander = std::size_t[];\ - constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ - constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ - constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */ \ - }\ - };\ - static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ - using TestInit = typename create(Catch::Detail::priority_tag<1>{})), TypeList>::type;\ - TestInit t;\ - t.reg_tests();\ - return 0;\ - }(); \ - }\ - }\ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace {\ template \ - void TestName::test() + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ + template\ + struct TestNameClass{\ + void reg_tests(){\ + std::size_t index = 0;\ + using expander = std::size_t[];\ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = typename create(Catch::Detail::priority_tag<1>{})), TypeList>::type;\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }\ + }\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + void TestName::test() #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ @@ -7041,37 +7132,37 @@ namespace Catch { INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) ) #endif - #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ + namespace {\ template \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ void test();\ };\ - namespace {\ - namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ - INTERNAL_CATCH_TYPE_GEN\ - template\ - struct TestNameClass{\ - void reg_tests(){\ - size_t index = 0;\ - using expander = size_t[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ - }\ - };\ - static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ - using TestInit = typename convert::type;\ - TestInit t;\ - t.reg_tests();\ - return 0;\ - }(); \ - }}\ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - template \ - void TestName::test() + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ + INTERNAL_CATCH_TYPE_GEN\ + template\ + struct TestNameClass{\ + void reg_tests(){\ + size_t index = 0;\ + using expander = size_t[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = typename convert::type;\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }}\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + void TestName::test() #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, TmplList ) @@ -7374,7 +7465,7 @@ namespace Catch { class ExceptionTranslatorRegistrar { template - class ExceptionTranslator : public IExceptionTranslator { + class ExceptionTranslator final : public IExceptionTranslator { public: constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) ) @@ -7478,8 +7569,8 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 11 -#define CATCH_VERSION_PATCH 0 +#define CATCH_VERSION_MINOR 15 +#define CATCH_VERSION_PATCH 1 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7533,6 +7624,24 @@ namespace Catch { +#ifndef CATCH_GENERATORS_THROW_HPP_INCLUDED +#define CATCH_GENERATORS_THROW_HPP_INCLUDED + +namespace Catch { + namespace Generators { + namespace Detail { + + //! Throws GeneratorException with the provided message + [[noreturn]] + void throw_generator_exception( char const* msg ); + + } // namespace Detail + } // namespace Generators +} // namespace Catch + +#endif // CATCH_GENERATORS_THROW_HPP_INCLUDED + + #ifndef CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED #define CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED @@ -7561,6 +7670,15 @@ namespace Catch { //! Customization point for `currentElementAsString` virtual std::string stringifyImpl() const = 0; + /** + * Customization point for skipping to the n-th element + * + * Defaults to successively calling `countedNext`. If there + * are not enough elements to reach the nth one, will throw + * an error. + */ + virtual void skipToNthElementImpl( std::size_t n ); + public: GeneratorUntypedBase() = default; // Generation of copy ops is deprecated (and Clang will complain) @@ -7584,6 +7702,13 @@ namespace Catch { std::size_t currentElementIndex() const { return m_currentElementIndex; } + /** + * Moves the generator forward **to** the n-th element + * + * Cannot move backwards. Can stay in place. + */ + void skipToNthElement( std::size_t n ); + /** * Returns generator's current element as user-friendly string. * @@ -7598,6 +7723,15 @@ namespace Catch { * comes first. */ StringRef currentElementAsString() const; + + /** + * Returns true if calls to `next` will eventually return false + * + * Note that for backwards compatibility this is currently defaulted + * to return `true`, but in the future all generators will have to + * provide their own implementation. + */ + virtual bool isFinite() const; }; using GeneratorBasePtr = Catch::Detail::unique_ptr; @@ -7606,9 +7740,7 @@ namespace Catch { class IGeneratorTracker { public: virtual ~IGeneratorTracker(); // = default; - virtual auto hasGenerator() const -> bool = 0; virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; - virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; }; } // namespace Catch @@ -7622,14 +7754,6 @@ namespace Catch { namespace Generators { -namespace Detail { - - //! Throws GeneratorException with the provided message - [[noreturn]] - void throw_generator_exception(char const * msg); - -} // end namespace detail - template class IGenerator : public GeneratorUntypedBase { std::string stringifyImpl() const override { @@ -7664,6 +7788,9 @@ namespace Detail { bool next() { return m_generator->countedNext(); } + + bool isFinite() const { return m_generator->isFinite(); } + void skipToNthElement( size_t n ) { m_generator->skipToNthElement(n); } }; @@ -7684,6 +7811,8 @@ namespace Detail { bool next() override { return false; } + + bool isFinite() const override { return true; } }; template @@ -7693,6 +7822,15 @@ namespace Detail { "specialization, use SingleValue Generator instead."); std::vector m_values; size_t m_idx = 0; + + void skipToNthElementImpl( std::size_t n ) override { + if ( n >= m_values.size() ) { + Detail::throw_generator_exception( + "Coud not jump to Nth element: not enough elements" ); + } + m_idx = n; + } + public: FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} @@ -7703,6 +7841,8 @@ namespace Detail { ++m_idx; return m_idx < m_values.size(); } + + bool isFinite() const override { return true; } }; template > @@ -7767,6 +7907,14 @@ namespace Detail { } return m_current < m_generators.size(); } + + bool isFinite() const override { + for (auto const& gen : m_generators) { + if (!gen.isFinite()) { return false; + } + } + return true; + } }; @@ -7858,6 +8006,17 @@ namespace Generators { GeneratorWrapper m_generator; size_t m_returned = 0; size_t m_target; + + void skipToNthElementImpl( std::size_t n ) override { + if ( n >= m_target ) { + Detail::throw_generator_exception( + "Coud not jump to Nth element: not enough elements" ); + } + + m_generator.skipToNthElement( n ); + m_returned = n; + } + public: TakeGenerator(size_t target, GeneratorWrapper&& generator): m_generator(CATCH_MOVE(generator)), @@ -7882,6 +8041,8 @@ namespace Generators { } return success; } + + bool isFinite() const override { return true; } }; template @@ -7923,6 +8084,8 @@ namespace Generators { while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); return success; } + + bool isFinite() const override { return m_generator.isFinite(); } }; @@ -7947,6 +8110,9 @@ namespace Generators { m_target_repeats(repeats) { assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + if (!m_generator.isFinite()) { + Detail::throw_generator_exception( "Cannot repeat infinite generator" ); + } } T const& get() const override { @@ -7980,6 +8146,8 @@ namespace Generators { } return m_current_repeat < m_target_repeats; } + + bool isFinite() const override { return m_generator.isFinite(); } }; template @@ -7993,25 +8161,30 @@ namespace Generators { GeneratorWrapper m_generator; Func m_function; // To avoid returning dangling reference, we have to save the values - T m_cache; + mutable Optional m_cache; + + void skipToNthElementImpl( std::size_t n ) override { + m_generator.skipToNthElement( n ); + m_cache.reset(); + } + public: template MapGenerator(F2&& function, GeneratorWrapper&& generator) : m_generator(CATCH_MOVE(generator)), - m_function(CATCH_FORWARD(function)), - m_cache(m_function(m_generator.get())) + m_function(CATCH_FORWARD(function)) {} T const& get() const override { - return m_cache; + if ( !m_cache ) { m_cache = m_function( m_generator.get() ); } + return *m_cache; } bool next() override { - const auto success = m_generator.next(); - if (success) { - m_cache = m_function(m_generator.get()); - } - return success; + m_cache.reset(); + return m_generator.next(); } + + bool isFinite() const override { return m_generator.isFinite(); } }; template > @@ -8033,7 +8206,6 @@ namespace Generators { std::vector m_chunk; size_t m_chunk_size; GeneratorWrapper m_generator; - bool m_used_up = false; public: ChunkGenerator(size_t size, GeneratorWrapper generator) : m_chunk_size(size), m_generator(CATCH_MOVE(generator)) @@ -8062,6 +8234,8 @@ namespace Generators { } return true; } + + bool isFinite() const override { return m_generator.isFinite(); } }; template @@ -8071,6 +8245,56 @@ namespace Generators { ); } + template + class ConcatGenerator final : public IGenerator { + std::vector> m_generators; + size_t m_current_generator = 0; + + void InsertGenerators( GeneratorWrapper&& gen ) { + m_generators.push_back( CATCH_MOVE( gen ) ); + } + + template + void InsertGenerators( GeneratorWrapper&& gen, Generators&&... gens ) { + m_generators.push_back( CATCH_MOVE( gen ) ); + InsertGenerators( CATCH_MOVE( gens )... ); + } + + public: + template + ConcatGenerator( Generators&&... generators ) { + InsertGenerators( CATCH_MOVE( generators )... ); + } + + T const& get() const override { + return m_generators[m_current_generator].get(); + } + bool next() override { + const bool success = m_generators[m_current_generator].next(); + if ( success ) { return true; } + + // If current generator is used up, we have to move to the next one + ++m_current_generator; + return m_current_generator < m_generators.size(); + } + + bool isFinite() const override { + for ( auto const& gen : m_generators ) { + if ( !gen.isFinite() ) { return false; } + } + return true; + } + }; + + template + GeneratorWrapper cat( GeneratorWrapper&& generator, + Generators&&... generators ) { + return GeneratorWrapper( + Catch::Detail::make_unique>( + CATCH_MOVE( generator ), CATCH_MOVE( generators )... ) ); + } + + } // namespace Generators } // namespace Catch @@ -8713,6 +8937,7 @@ class RandomFloatingGenerator final : public IGenerator { m_current_number = m_dist(m_rng); return true; } + bool isFinite() const override { return false; } }; template <> @@ -8730,6 +8955,7 @@ class RandomFloatingGenerator final : public IGenerator @@ -8751,6 +8977,7 @@ class RandomIntegerGenerator final : public IGenerator { m_current_number = m_dist(m_rng); return true; } + bool isFinite() const override { return false; } }; template @@ -8820,6 +9047,8 @@ class RangeGenerator final : public IGenerator { m_current += m_step; return (m_positive) ? (m_current < m_end) : (m_current > m_end); } + + bool isFinite() const override { return true; } }; template @@ -8859,6 +9088,8 @@ class IteratorGenerator final : public IGenerator { ++m_current; return m_current != m_elems.size(); } + + bool isFinite() const override { return true; } }; template - -namespace Catch { - - namespace Detail { - - Catch::Detail::unique_ptr makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ); - - class EnumValuesRegistry : public IMutableEnumValuesRegistry { - - std::vector> m_enumInfos; - - EnumInfo const& registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values) override; - }; - - std::vector parseEnums( StringRef enums ); - - } // Detail - -} // Catch - -#endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED - - #ifndef CATCH_ERRNO_GUARD_HPP_INCLUDED #define CATCH_ERRNO_GUARD_HPP_INCLUDED @@ -9701,7 +9904,7 @@ namespace Catch { namespace Catch { - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + class ExceptionTranslatorRegistry final : public IExceptionTranslatorRegistry { public: ~ExceptionTranslatorRegistry() override; void registerTranslator( Detail::unique_ptr&& translator ); @@ -10094,8 +10297,8 @@ namespace Catch { class JsonValueWriter { public: - JsonValueWriter( std::ostream& os ); - JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); + JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonObjectWriter writeObject() &&; JsonArrayWriter writeArray() &&; @@ -10129,8 +10332,8 @@ namespace Catch { class JsonObjectWriter { public: - JsonObjectWriter( std::ostream& os ); - JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); + JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonObjectWriter( JsonObjectWriter&& source ) noexcept; JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; @@ -10148,8 +10351,8 @@ namespace Catch { class JsonArrayWriter { public: - JsonArrayWriter( std::ostream& os ); - JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); + JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonArrayWriter( JsonArrayWriter&& source ) noexcept; JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; @@ -10390,6 +10593,9 @@ namespace Catch { #include namespace Catch { + +struct PathFilter; + namespace TestCaseTracking { struct NameAndLocation { @@ -10422,7 +10628,7 @@ namespace TestCaseTracking { StringRef name; SourceLineInfo location; - constexpr NameAndLocationRef( StringRef name_, + constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND, SourceLineInfo location_ ): name( name_ ), location( location_ ) {} @@ -10465,12 +10671,23 @@ namespace TestCaseTracking { Children m_children; CycleState m_runState = NotStarted; - public: - ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ): - m_nameAndLocation( CATCH_MOVE(nameAndLoc) ), - m_parent( parent ) - {} + // Members for path filtering + std::vector const* m_filterRef = nullptr; + + // Note: There are 2 dummy section trackers (root, test-case) before + // the first "real" section tracker can be encountered. We start + // the default tracker at -2, so that the first "real" section + // tracker overflows to index 0. + // Nesting depth of this tracker, used to decide which new-style filter applies. + size_t m_allTrackerDepth = static_cast( -2 ); + // Nesting depth of sections (inc. this tracker), used for old-style filters. + // Must be updated by the section tracker on its own. + size_t m_sectionOnlyDepth = static_cast( -2 ); + // Transitory: Remove once we remove backwards compatibility with old-style (v3.x) filters + bool m_newStyleFilters = false; + public: + ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ); // static queries NameAndLocation const& nameAndLocation() const { @@ -10496,6 +10713,11 @@ namespace TestCaseTracking { //! Returns true iff tracker has started bool hasStarted() const; + void setFilters( std::vector const* filters, bool newStyleFilters ) { + m_filterRef = filters; + m_newStyleFilters = newStyleFilters; + } + // actions virtual void close() = 0; // Successfully complete virtual void fail() = 0; @@ -10581,8 +10803,7 @@ namespace TestCaseTracking { void moveToThis(); }; - class SectionTracker : public TrackerBase { - std::vector m_filters; + class SectionTracker final : public TrackerBase { // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`. // Currently it allocates owns the name, so this is safe. If it is later refactored // to not own the name, the name still has to outlive the `ITracker` parent, so @@ -10599,10 +10820,6 @@ namespace TestCaseTracking { void tryOpen(); - void addInitialFilters( std::vector const& filters ); - void addNextFilters( std::vector const& filters ); - //! Returns filters active in this tracker - std::vector const& getFilters() const { return m_filters; } //! Returns whitespace-trimmed name of the tracked section StringRef trimmedName() const; }; @@ -10622,7 +10839,7 @@ using TestCaseTracking::SectionTracker; #define CATCH_THREAD_SUPPORT_HPP_INCLUDED -#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) # include # include #endif @@ -10630,14 +10847,14 @@ using TestCaseTracking::SectionTracker; namespace Catch { namespace Detail { -#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) using Mutex = std::mutex; using LockGuard = std::lock_guard; struct AtomicCounts { - std::atomic passed = 0; - std::atomic failed = 0; - std::atomic failedButOk = 0; - std::atomic skipped = 0; + std::atomic passed{ 0 }; + std::atomic failed{ 0 }; + std::atomic failedButOk{ 0 }; + std::atomic skipped{ 0 }; }; #else // ^^ Use actual mutex, lock and atomics // vv Dummy implementations for single-thread performance @@ -10739,8 +10956,6 @@ namespace Catch { void handleFatalErrorCondition( StringRef message ) override; - bool lastAssertionPassed() override; - public: // !TBD We need to do this another way! bool aborting() const; @@ -10762,7 +10977,7 @@ namespace Catch { ITransientExpression const *expr, bool negated ); - void populateReaction( AssertionReaction& reaction, bool has_normal_disposition ); + void populateReaction( AssertionReaction& reaction, bool has_normal_disposition ) const; // Creates dummy info for unexpected exceptions/fatal errors, // where we do not have the access to one, but we still need @@ -10854,7 +11069,7 @@ namespace Catch { template - class Singleton : SingletonImplT, public ISingleton { + class Singleton final : SingletonImplT, public ISingleton { static auto getInternal() -> Singleton* { static Singleton* s_instance = nullptr; @@ -10942,10 +11157,10 @@ namespace Catch { //! Returns a new string without whitespace at the start/end std::string trim( std::string const& str ); //! Returns a substring of the original ref without whitespace. Beware lifetimes! - StringRef trim(StringRef ref); + StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND ); // !!! Be aware, returns refs into original string - make sure original string outlives them - std::vector splitStringRef( StringRef str, char delimiter ); + std::vector splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); /** @@ -10963,7 +11178,7 @@ namespace Catch { StringRef m_label; public: - constexpr pluralise(std::uint64_t count, StringRef label): + constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND): m_count(count), m_label(label) {} @@ -10985,7 +11200,7 @@ namespace Catch { namespace Catch { struct SourceLineInfo; - class TagAliasRegistry : public ITagAliasRegistry { + class TagAliasRegistry final : public ITagAliasRegistry { public: ~TagAliasRegistry() override; TagAlias const* find( std::string const& alias ) const override; @@ -11045,7 +11260,7 @@ namespace Catch { std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); - class TestRegistry : public ITestCaseRegistry { + class TestRegistry final : public ITestCaseRegistry { public: void registerTest( Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker ); @@ -11442,6 +11657,19 @@ namespace Catch { #endif // CATCH_TEXTFLOW_HPP_INCLUDED +#ifndef CATCH_THREAD_LOCAL_HPP_INCLUDED +#define CATCH_THREAD_LOCAL_HPP_INCLUDED + + +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) +#define CATCH_INTERNAL_THREAD_LOCAL thread_local +#else +#define CATCH_INTERNAL_THREAD_LOCAL +#endif + +#endif // CATCH_THREAD_LOCAL_HPP_INCLUDED + + #ifndef CATCH_TO_STRING_HPP_INCLUDED #define CATCH_TO_STRING_HPP_INCLUDED @@ -11510,7 +11738,7 @@ namespace Catch { public: enum ForWhat { ForTextNodes, ForAttributes }; - constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ): + constexpr XmlEncode( StringRef str CATCH_ATTR_LIFETIMEBOUND, ForWhat forWhat = ForTextNodes ): m_str( str ), m_forWhat( forWhat ) {} @@ -11528,7 +11756,7 @@ namespace Catch { class ScopedElement { public: - ScopedElement( XmlWriter* writer, XmlFormatting fmt ); + ScopedElement( XmlWriter* writer CATCH_ATTR_LIFETIMEBOUND, XmlFormatting fmt ); ScopedElement( ScopedElement&& other ) noexcept; ScopedElement& operator=( ScopedElement&& other ) noexcept; @@ -11560,7 +11788,7 @@ namespace Catch { XmlFormatting m_fmt; }; - XmlWriter( std::ostream& os ); + XmlWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); ~XmlWriter(); XmlWriter( XmlWriter const& ) = delete; @@ -11671,7 +11899,7 @@ namespace Catch { #endif template - class MatchExpr : public ITransientExpression { + class MatchExpr final : public ITransientExpression { ArgT && m_arg; MatcherT const& m_matcher; public: @@ -11758,10 +11986,10 @@ namespace Matchers { class MatcherUntypedBase { public: - MatcherUntypedBase() = default; + constexpr MatcherUntypedBase() = default; - MatcherUntypedBase(MatcherUntypedBase const&) = default; - MatcherUntypedBase(MatcherUntypedBase&&) = default; + constexpr MatcherUntypedBase(MatcherUntypedBase const&) = default; + constexpr MatcherUntypedBase(MatcherUntypedBase&&) = default; MatcherUntypedBase& operator = (MatcherUntypedBase const&) = delete; MatcherUntypedBase& operator = (MatcherUntypedBase&&) = delete; @@ -11769,9 +11997,9 @@ namespace Matchers { std::string toString() const; protected: - virtual ~MatcherUntypedBase(); // = default; - virtual std::string describe() const = 0; - mutable std::string m_cachedToString; + CATCH_DESTRUCTOR_CONSTEXPR virtual ~MatcherUntypedBase() = default; + //! Should be overridden, but we provide default "undescribed" impl + virtual std::string describe() const; }; @@ -11818,11 +12046,15 @@ namespace Matchers { return description; } - friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase const& rhs) { + friend MatchAllOf operator&&( MatchAllOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAllOf operator&& (MatcherBase const& lhs, MatchAllOf&& rhs) { + friend MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -11870,11 +12102,15 @@ namespace Matchers { return description; } - friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase const& rhs) { + friend MatchAnyOf operator||( MatchAnyOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAnyOf operator|| (MatcherBase const& lhs, MatchAnyOf&& rhs) { + friend MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -11894,7 +12130,8 @@ namespace Matchers { MatcherBase const& m_underlyingMatcher; public: - explicit MatchNotOf( MatcherBase const& underlyingMatcher ): + explicit MatchNotOf( MatcherBase const& underlyingMatcher + CATCH_ATTR_LIFETIMEBOUND ): m_underlyingMatcher( underlyingMatcher ) {} @@ -11910,16 +12147,22 @@ namespace Matchers { } // namespace Detail template - Detail::MatchAllOf operator&& (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAllOf{} && lhs && rhs; } + template - Detail::MatchAnyOf operator|| (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAnyOf{} || lhs || rhs; } template - Detail::MatchNotOf operator! (MatcherBase const& matcher) { + Detail::MatchNotOf + operator!( MatcherBase const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOf{ matcher }; } @@ -11938,6 +12181,19 @@ namespace Matchers { #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + CATCH_SUCCEED( #matcher ".match( " #arg " )" ) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + CATCH_SUCCEED( #matcher ".match( " #arg " )" ) + #else + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) CATCH_REQUIRE_THAT( arg, matcher ) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) CATCH_CHECK_THAT( arg, matcher ) + #endif + + #elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) @@ -11949,6 +12205,9 @@ namespace Matchers { #define CATCH_CHECK_THAT( arg, matcher ) (void)(0) #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) (void)(0) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) (void)(0) + #elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) @@ -11960,6 +12219,19 @@ namespace Matchers { #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define STATIC_REQUIRE_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + SUCCEED( #matcher ".match( " #arg " )" ) + #define STATIC_CHECK_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + SUCCEED( #matcher ".match( " #arg " )" ) + #else + #define STATIC_REQUIRE_THAT( arg, matcher ) REQUIRE_THAT( arg, matcher ) + #define STATIC_CHECK_THAT( arg, matcher ) CHECK_THAT( arg, matcher ) + #endif + + #elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) @@ -11971,6 +12243,9 @@ namespace Matchers { #define CHECK_THAT( arg, matcher ) (void)(0) #define REQUIRE_THAT( arg, matcher ) (void)(0) + #define STATIC_REQUIRE_THAT( arg, matcher ) (void)(0) + #define STATIC_CHECK_THAT( arg, matcher ) (void)(0) + #endif // end of user facing macro declarations #endif // CATCH_MATCHERS_HPP_INCLUDED @@ -11993,12 +12268,12 @@ namespace Matchers { namespace Catch { namespace Matchers { class MatcherGenericBase : public MatcherUntypedBase { + std::string describe() const override; public: - MatcherGenericBase() = default; - ~MatcherGenericBase() override; // = default; + constexpr MatcherGenericBase() = default; - MatcherGenericBase(MatcherGenericBase const&) = default; - MatcherGenericBase(MatcherGenericBase&&) = default; + constexpr MatcherGenericBase(MatcherGenericBase const&) = default; + constexpr MatcherGenericBase(MatcherGenericBase&&) = default; MatcherGenericBase& operator=(MatcherGenericBase const&) = delete; MatcherGenericBase& operator=(MatcherGenericBase&&) = delete; @@ -12007,7 +12282,9 @@ namespace Matchers { namespace Detail { template - std::array array_cat(std::array && lhs, std::array && rhs) { + constexpr std::array + array_cat( std::array&& lhs, + std::array&& rhs ) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); std::copy_n(rhs.begin(), M, arr.begin() + N); @@ -12015,7 +12292,8 @@ namespace Matchers { } template - std::array array_cat(std::array && lhs, void const* rhs) { + constexpr std::array + array_cat( std::array&& lhs, void const* rhs ) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); arr[N] = rhs; @@ -12023,7 +12301,8 @@ namespace Matchers { } template - std::array array_cat(void const* lhs, std::array && rhs) { + constexpr std::array + array_cat( void const* lhs, std::array&& rhs ) { std::array arr{ {lhs} }; std::copy_n(rhs.begin(), N, arr.begin() + 1); return arr; @@ -12046,23 +12325,31 @@ namespace Matchers { template - bool match_all_of(Arg&&, std::array const&, std::index_sequence<>) { + constexpr bool match_all_of( Arg&&, + std::array const&, + std::index_sequence<> ) { return true; } template - bool match_all_of(Arg&& arg, std::array const& matchers, std::index_sequence) { + constexpr bool match_all_of( Arg&& arg, + std::array const& matchers, + std::index_sequence ) { return static_cast(matchers[Idx])->match(arg) && match_all_of(arg, matchers, std::index_sequence{}); } template - bool match_any_of(Arg&&, std::array const&, std::index_sequence<>) { + constexpr bool match_any_of( Arg&&, + std::array const&, + std::index_sequence<> ) { return false; } template - bool match_any_of(Arg&& arg, std::array const& matchers, std::index_sequence) { + constexpr bool match_any_of( Arg&& arg, + std::array const& matchers, + std::index_sequence ) { return static_cast(matchers[Idx])->match(arg) || match_any_of(arg, matchers, std::index_sequence{}); } @@ -12083,14 +12370,18 @@ namespace Matchers { public: MatchAllOfGeneric(MatchAllOfGeneric const&) = delete; MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete; - MatchAllOfGeneric(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; + constexpr MatchAllOfGeneric( MatchAllOfGeneric&& ) = default; + constexpr MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} - explicit MatchAllOfGeneric(std::array matchers) : m_matchers{matchers} {} + constexpr MatchAllOfGeneric( + MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) + : m_matchers{ {std::addressof(matchers)...} } {} + constexpr explicit MatchAllOfGeneric( + std::array matchers ): + m_matchers{ matchers } {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return match_all_of(arg, m_matchers, std::index_sequence_for{}); } @@ -12106,28 +12397,30 @@ namespace Matchers { //! Avoids type nesting for `GenericAllOf && GenericAllOf` case template - friend + constexpr friend MatchAllOfGeneric operator && ( - MatchAllOfGeneric&& lhs, - MatchAllOfGeneric&& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAllOf && some matcher` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAllOfGeneric> operator && ( - MatchAllOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(&rhs))}; } //! Avoids type nesting for `some matcher && GenericAllOf` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAllOfGeneric> operator && ( - MatcherLHS const& lhs, - MatchAllOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -12138,14 +12431,18 @@ namespace Matchers { public: MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete; - MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; + constexpr MatchAnyOfGeneric( MatchAnyOfGeneric&& ) = default; + constexpr MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} - explicit MatchAnyOfGeneric(std::array matchers) : m_matchers{matchers} {} + constexpr MatchAnyOfGeneric( + MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) + : m_matchers{ {std::addressof(matchers)...} } {} + constexpr explicit MatchAnyOfGeneric( + std::array matchers ): + m_matchers{ matchers } {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return match_any_of(arg, m_matchers, std::index_sequence_for{}); } @@ -12161,27 +12458,30 @@ namespace Matchers { //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case template - friend MatchAnyOfGeneric operator || ( - MatchAnyOfGeneric&& lhs, - MatchAnyOfGeneric&& rhs) { + constexpr friend MatchAnyOfGeneric + operator||( + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAnyOf || some matcher` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAnyOfGeneric> operator || ( - MatchAnyOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(std::addressof(rhs)))}; } //! Avoids type nesting for `some matcher || GenericAnyOf` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAnyOfGeneric> operator || ( - MatcherLHS const& lhs, - MatchAnyOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND) { return MatchAnyOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -12194,13 +12494,15 @@ namespace Matchers { public: MatchNotOfGeneric(MatchNotOfGeneric const&) = delete; MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete; - MatchNotOfGeneric(MatchNotOfGeneric&&) = default; - MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; + constexpr MatchNotOfGeneric( MatchNotOfGeneric&& ) = default; + constexpr MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; - explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {} + constexpr explicit MatchNotOfGeneric( + MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) + : m_matcher{matcher} {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return !m_matcher.match(arg); } @@ -12209,7 +12511,9 @@ namespace Matchers { } //! Negating negation can just unwrap and return underlying matcher - friend MatcherT const& operator ! (MatchNotOfGeneric const& matcher) { + constexpr friend MatcherT const& + operator!( MatchNotOfGeneric const& matcher + CATCH_ATTR_LIFETIMEBOUND ) { return matcher.m_matcher; } }; @@ -12218,21 +12522,28 @@ namespace Matchers { // compose only generic matchers template - std::enable_if_t, Detail::MatchAllOfGeneric> - operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { + constexpr std::enable_if_t< + Detail::are_generic_matchers_v, + Detail::MatchAllOfGeneric> + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template - std::enable_if_t, Detail::MatchAnyOfGeneric> - operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { + constexpr std::enable_if_t< + Detail::are_generic_matchers_v, + Detail::MatchAnyOfGeneric> + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } //! Wrap provided generic matcher in generic negator template - std::enable_if_t, Detail::MatchNotOfGeneric> - operator ! (MatcherT const& matcher) { + constexpr std::enable_if_t, + Detail::MatchNotOfGeneric> + operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOfGeneric{matcher}; } @@ -12240,25 +12551,29 @@ namespace Matchers { // compose mixed generic and non-generic matchers template std::enable_if_t, Detail::MatchAllOfGeneric>> - operator && (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAllOfGeneric, MatcherRHS>> - operator && (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric>> - operator || (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric, MatcherRHS>> - operator || (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } @@ -12273,7 +12588,7 @@ namespace Catch { class IsEmptyMatcher final : public MatcherGenericBase { public: template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { #if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS) using Catch::Detail::empty; #else @@ -12288,12 +12603,12 @@ namespace Catch { class HasSizeMatcher final : public MatcherGenericBase { std::size_t m_target_size; public: - explicit HasSizeMatcher(std::size_t target_size): + constexpr explicit HasSizeMatcher(std::size_t target_size): m_target_size(target_size) {} template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { #if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS) using Catch::Detail::size; #else @@ -12309,12 +12624,12 @@ namespace Catch { class SizeMatchesMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - explicit SizeMatchesMatcher(Matcher m): + constexpr explicit SizeMatchesMatcher(Matcher m): m_matcher(CATCH_MOVE(m)) {} template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { #if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS) using Catch::Detail::size; #else @@ -12330,11 +12645,13 @@ namespace Catch { //! Creates a matcher that accepts empty ranges/containers - IsEmptyMatcher IsEmpty(); + inline CATCH_DESTRUCTOR_CONSTEXPR IsEmptyMatcher IsEmpty() { return {}; } //! Creates a matcher that accepts ranges/containers with specific size - HasSizeMatcher SizeIs(std::size_t sz); + inline CATCH_DESTRUCTOR_CONSTEXPR HasSizeMatcher SizeIs( std::size_t sz ) { + return HasSizeMatcher{ sz }; + } template - std::enable_if_t, + constexpr std::enable_if_t, SizeMatchesMatcher> SizeIs(Matcher&& m) { return SizeMatchesMatcher{CATCH_FORWARD(m)}; } @@ -12361,7 +12678,7 @@ namespace Catch { Equality m_eq; public: template - ContainsElementMatcher(T2&& target, Equality2&& predicate): + constexpr ContainsElementMatcher(T2&& target, Equality2&& predicate): m_desired(CATCH_FORWARD(target)), m_eq(CATCH_FORWARD(predicate)) {} @@ -12371,7 +12688,7 @@ namespace Catch { } template - bool match( RangeLike&& rng ) const { + constexpr bool match( RangeLike&& rng ) const { for ( auto&& elem : rng ) { if ( m_eq( elem, m_desired ) ) { return true; } } @@ -12387,12 +12704,12 @@ namespace Catch { // Note that we do a copy+move to avoid having to SFINAE this // constructor (and also avoid some perfect forwarding failure // cases) - ContainsMatcherMatcher(Matcher matcher): + constexpr ContainsMatcherMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (m_matcher.match(elem)) { return true; @@ -12412,14 +12729,14 @@ namespace Catch { * Uses `std::equal_to` to do the comparison */ template - std::enable_if_t, + constexpr std::enable_if_t, ContainsElementMatcher>> Contains(T&& elem) { return { CATCH_FORWARD(elem), std::equal_to<>{} }; } //! Creates a matcher that checks whether a range contains element matching a matcher template - std::enable_if_t, + constexpr std::enable_if_t, ContainsMatcherMatcher> Contains(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } @@ -12430,7 +12747,7 @@ namespace Catch { * Uses `eq` to do the comparisons, the element is provided on the rhs */ template - ContainsElementMatcher Contains(T&& elem, Equality&& eq) { + constexpr ContainsElementMatcher Contains(T&& elem, Equality&& eq) { return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) }; } @@ -12645,7 +12962,7 @@ namespace Catch { class AllMatchMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - AllMatchMatcher(Matcher matcher): + constexpr AllMatchMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} @@ -12654,7 +12971,7 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (!m_matcher.match(elem)) { return false; @@ -12669,7 +12986,7 @@ namespace Catch { class NoneMatchMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - NoneMatchMatcher(Matcher matcher): + constexpr NoneMatchMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} @@ -12678,7 +12995,7 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (m_matcher.match(elem)) { return false; @@ -12693,7 +13010,7 @@ namespace Catch { class AnyMatchMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - AnyMatchMatcher(Matcher matcher): + constexpr AnyMatchMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} @@ -12702,7 +13019,7 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (m_matcher.match(elem)) { return true; @@ -12718,7 +13035,7 @@ namespace Catch { std::string describe() const override; template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (!elem) { return false; @@ -12734,7 +13051,7 @@ namespace Catch { std::string describe() const override; template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (elem) { return false; @@ -12750,7 +13067,7 @@ namespace Catch { std::string describe() const override; template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (elem) { return true; @@ -12760,32 +13077,35 @@ namespace Catch { } }; - // Creates a matcher that checks whether all elements in a range match a matcher + //! Creates a matcher that checks whether all elements in a range match a matcher template - AllMatchMatcher AllMatch(Matcher&& matcher) { + constexpr AllMatchMatcher AllMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } - // Creates a matcher that checks whether no element in a range matches a matcher. + //! Creates a matcher that checks whether no element in a range matches a matcher. template - NoneMatchMatcher NoneMatch(Matcher&& matcher) { + constexpr NoneMatchMatcher NoneMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } - // Creates a matcher that checks whether any element in a range matches a matcher. + //! Creates a matcher that checks whether any element in a range matches a matcher. template - AnyMatchMatcher AnyMatch(Matcher&& matcher) { + constexpr AnyMatchMatcher AnyMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } - // Creates a matcher that checks whether all elements in a range are true - AllTrueMatcher AllTrue(); + //! Creates a matcher that checks whether all elements in a range are true + inline CATCH_DESTRUCTOR_CONSTEXPR + AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } - // Creates a matcher that checks whether no element in a range is true - NoneTrueMatcher NoneTrue(); + //! Creates a matcher that checks whether no element in a range is true + inline CATCH_DESTRUCTOR_CONSTEXPR + NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } - // Creates a matcher that checks whether any element in a range is true - AnyTrueMatcher AnyTrue(); + //! Creates a matcher that checks whether any element in a range is true + inline CATCH_DESTRUCTOR_CONSTEXPR + AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } } } @@ -13738,7 +14058,8 @@ namespace Catch { * format */ void defaultListListeners( std::ostream& out, - std::vector const& descriptions ); + std::vector const& descriptions, + Verbosity verbosity ); /** * Lists tag information to the provided stream in user-friendly format @@ -13747,7 +14068,10 @@ namespace Catch { * bases. The output should be backwards compatible with the output of * Catch2 v2 binaries. */ - void defaultListTags( std::ostream& out, std::vector const& tags, bool isFiltered ); + void defaultListTags( std::ostream& out, + std::vector const& tags, + bool isFiltered, + Verbosity verbosity ); /** * Lists test case information to the provided stream in user-friendly @@ -13897,7 +14221,7 @@ namespace Catch { bool testOkToFail ); void writeAssertions(SectionNode const& sectionNode); - void writeAssertion(AssertionStats const& stats); + bool writeAssertion(AssertionStats const& stats); XmlWriter xml; Timer suiteTimer; diff --git a/third_party/Catch2/fuzzing/build_fuzzers.sh b/third_party/Catch2/fuzzing/build_fuzzers.sh old mode 100755 new mode 100644 diff --git a/third_party/Catch2/fuzzing/fuzz_TestSpecParser.cpp b/third_party/Catch2/fuzzing/fuzz_TestSpecParser.cpp index 3aba8c840..6c20dbbf6 100644 --- a/third_party/Catch2/fuzzing/fuzz_TestSpecParser.cpp +++ b/third_party/Catch2/fuzzing/fuzz_TestSpecParser.cpp @@ -9,6 +9,7 @@ #include #include +#include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { diff --git a/third_party/Catch2/fuzzing/fuzz_textflow.cpp b/third_party/Catch2/fuzzing/fuzz_textflow.cpp index 7000f420f..f31193799 100644 --- a/third_party/Catch2/fuzzing/fuzz_textflow.cpp +++ b/third_party/Catch2/fuzzing/fuzz_textflow.cpp @@ -13,6 +13,7 @@ #include #include +#include template diff --git a/third_party/Catch2/meson.build b/third_party/Catch2/meson.build index 16b387969..574086661 100644 --- a/third_party/Catch2/meson.build +++ b/third_party/Catch2/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.11.0', # CML version placeholder, don't delete + version: '3.15.1', # CML version placeholder, don't delete license: 'BSL-1.0', meson_version: '>=0.54.1', ) diff --git a/third_party/Catch2/src/CMakeLists.txt b/third_party/Catch2/src/CMakeLists.txt index 1e3af147a..0ed64ebf8 100644 --- a/third_party/Catch2/src/CMakeLists.txt +++ b/third_party/Catch2/src/CMakeLists.txt @@ -87,7 +87,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_decomposer.hpp ${SOURCES_DIR}/internal/catch_deprecation_macro.hpp ${SOURCES_DIR}/internal/catch_enforce.hpp - ${SOURCES_DIR}/internal/catch_enum_values_registry.hpp + ${SOURCES_DIR}/internal/catch_enum_info.hpp ${SOURCES_DIR}/internal/catch_errno_guard.hpp ${SOURCES_DIR}/internal/catch_exception_translator_registry.hpp ${SOURCES_DIR}/internal/catch_fatal_condition_handler.hpp @@ -98,6 +98,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_jsonwriter.hpp ${SOURCES_DIR}/internal/catch_lazy_expr.hpp ${SOURCES_DIR}/internal/catch_leak_detector.hpp + ${SOURCES_DIR}/internal/catch_lifetimebound.hpp ${SOURCES_DIR}/internal/catch_list.hpp ${SOURCES_DIR}/internal/catch_logical_traits.hpp ${SOURCES_DIR}/internal/catch_message_info.hpp @@ -107,6 +108,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_optional.hpp ${SOURCES_DIR}/internal/catch_output_redirect.hpp ${SOURCES_DIR}/internal/catch_parse_numbers.hpp + ${SOURCES_DIR}/internal/catch_path_filter.hpp ${SOURCES_DIR}/internal/catch_platform.hpp ${SOURCES_DIR}/internal/catch_polyfills.hpp ${SOURCES_DIR}/internal/catch_preprocessor.hpp @@ -139,6 +141,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_test_registry.hpp ${SOURCES_DIR}/internal/catch_test_spec_parser.hpp ${SOURCES_DIR}/internal/catch_textflow.hpp + ${SOURCES_DIR}/internal/catch_thread_local.hpp ${SOURCES_DIR}/internal/catch_thread_support.hpp ${SOURCES_DIR}/internal/catch_to_string.hpp ${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp @@ -178,7 +181,7 @@ set(IMPL_SOURCES ${SOURCES_DIR}/internal/catch_debugger.cpp ${SOURCES_DIR}/internal/catch_decomposer.cpp ${SOURCES_DIR}/internal/catch_enforce.cpp - ${SOURCES_DIR}/internal/catch_enum_values_registry.cpp + ${SOURCES_DIR}/internal/catch_enum_info.cpp ${SOURCES_DIR}/internal/catch_errno_guard.cpp ${SOURCES_DIR}/internal/catch_exception_translator_registry.cpp ${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp @@ -224,7 +227,6 @@ set(INTERFACE_HEADERS ${SOURCES_DIR}/interfaces/catch_interfaces_all.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_capture.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_config.hpp - ${SOURCES_DIR}/interfaces/catch_interfaces_enum_values_registry.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_exception.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp @@ -253,11 +255,13 @@ set(GENERATOR_HEADERS ${SOURCES_DIR}/generators/catch_generators_all.hpp ${SOURCES_DIR}/generators/catch_generators_random.hpp ${SOURCES_DIR}/generators/catch_generators_range.hpp + ${SOURCES_DIR}/generators/catch_generators_throw.hpp ) set(GENERATOR_SOURCES ${SOURCES_DIR}/generators/catch_generator_exception.cpp ${SOURCES_DIR}/generators/catch_generators.cpp ${SOURCES_DIR}/generators/catch_generators_random.cpp + ${SOURCES_DIR}/generators/catch_generators_throw.cpp ) set(GENERATOR_FILES ${GENERATOR_HEADERS} ${GENERATOR_SOURCES}) diff --git a/third_party/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp b/third_party/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp index 14d7f4506..8934f84fe 100644 --- a/third_party/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp +++ b/third_party/Catch2/src/catch2/benchmark/detail/catch_analyse.cpp @@ -36,11 +36,11 @@ namespace Catch { samples.data(), samples.data() + samples.size() ); auto wrap_estimate = [](Estimate e) { - return Estimate { - FDuration(e.point), - FDuration(e.lower_bound), - FDuration(e.upper_bound), - e.confidence_interval, + return Estimate{ + FDuration( e.point ), + FDuration( e.lower_bound ), + FDuration( e.upper_bound ), + e.confidence_interval, }; }; std::vector samples2; diff --git a/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp b/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp index 90b07b76e..1f9ae9fb1 100644 --- a/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp +++ b/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp @@ -41,7 +41,7 @@ namespace Catch { callable& operator=(callable&&) = default; }; template - struct model : public callable { + struct model final : public callable { model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} model(Fun const& fun_) : fun(fun_) {} diff --git a/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp b/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp index 3633bc9f9..e4893aaa2 100644 --- a/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp +++ b/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp @@ -8,6 +8,7 @@ #ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_HPP_INCLUDED +#include #include #include // The fwd decl & default specialization needs to be seen by VS2017 before diff --git a/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp b/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp index 2ccc25d58..fb9cf684e 100644 --- a/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp +++ b/third_party/Catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp @@ -8,14 +8,16 @@ #ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED -#include - namespace Catch { + namespace Detail { + struct DummyTemplateArgPlaceholder; + } + // 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 + // we can prevent multiple declarations in dependencies + template struct BenchmarkStats; } // end namespace Catch diff --git a/third_party/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp b/third_party/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp index 3ebdcc05a..829991496 100644 --- a/third_party/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp +++ b/third_party/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp @@ -14,7 +14,7 @@ namespace Catch { namespace Benchmark { namespace Detail { - struct optimized_away_error : std::exception { + struct optimized_away_error final : std::exception { const char* what() const noexcept override; }; diff --git a/third_party/Catch2/src/catch2/catch_all.hpp b/third_party/Catch2/src/catch2/catch_all.hpp index 2417d8567..82b114059 100644 --- a/third_party/Catch2/src/catch2/catch_all.hpp +++ b/third_party/Catch2/src/catch2/catch_all.hpp @@ -68,7 +68,7 @@ #include #include #include -#include +#include #include #include #include @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +123,7 @@ #include #include #include +#include #include #include #include diff --git a/third_party/Catch2/src/catch2/catch_config.cpp b/third_party/Catch2/src/catch2/catch_config.cpp index fef03d7d4..b4584f3cc 100644 --- a/third_party/Catch2/src/catch2/catch_config.cpp +++ b/third_party/Catch2/src/catch2/catch_config.cpp @@ -92,6 +92,10 @@ namespace Catch { lhs.customOptions == rhs.customOptions; } + bool operator==( PathFilter const& lhs, PathFilter const& rhs ) { + return lhs.type == rhs.type && lhs.filter == rhs.filter; + } + Config::Config( ConfigData const& data ): m_data( data ) { // We need to trim filter specs to avoid trouble with superfluous @@ -101,9 +105,6 @@ namespace Catch { for (auto& elem : m_data.testsOrTags) { elem = trim(elem); } - for (auto& elem : m_data.sectionsToRun) { - elem = trim(elem); - } // Insert the default reporter if user hasn't asked for a specific one if ( m_data.reporterSpecifications.empty() ) { @@ -169,7 +170,8 @@ namespace Catch { bool Config::listListeners() const { return m_data.listListeners; } std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } - std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + std::vector const& Config::getPathFilters() const { return m_data.pathFilters; } + bool Config::useNewFilterBehaviour() const { return m_data.useNewPathFilteringBehaviour; } std::vector const& Config::getReporterSpecs() const { return m_data.reporterSpecifications; @@ -197,6 +199,9 @@ namespace Catch { bool Config::warnAboutUnmatchedTestSpecs() const { return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec ); } + bool Config::warnAboutInfiniteGenerators() const { + return !!( m_data.warnings & WarnAbout::InfiniteGenerator ); + } bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; } ShowDurations Config::showDurations() const { return m_data.showDurations; } double Config::minDuration() const { return m_data.minDuration; } diff --git a/third_party/Catch2/src/catch2/catch_config.hpp b/third_party/Catch2/src/catch2/catch_config.hpp index cdf286ad2..c3fea5616 100644 --- a/third_party/Catch2/src/catch2/catch_config.hpp +++ b/third_party/Catch2/src/catch2/catch_config.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -86,7 +87,8 @@ namespace Catch { std::vector reporterSpecifications; std::vector testsOrTags; - std::vector sectionsToRun; + std::vector pathFilters; + bool useNewPathFilteringBehaviour = false; std::string prematureExitGuardFilePath; }; @@ -109,7 +111,8 @@ namespace Catch { getProcessedReporterSpecs() const; std::vector const& getTestsOrTags() const override; - std::vector const& getSectionsToRun() const override; + std::vector const& getPathFilters() const override; + bool useNewFilterBehaviour() const override; TestSpec const& testSpec() const override; bool hasTestFilters() const override; @@ -124,6 +127,7 @@ namespace Catch { bool includeSuccessfulResults() const override; bool warnAboutMissingAssertions() const override; bool warnAboutUnmatchedTestSpecs() const override; + bool warnAboutInfiniteGenerators() const override; bool zeroTestsCountAsSuccess() const override; ShowDurations showDurations() const override; double minDuration() const override; diff --git a/third_party/Catch2/src/catch2/catch_message.cpp b/third_party/Catch2/src/catch2/catch_message.cpp index dc7d56789..3a83e34e8 100644 --- a/third_party/Catch2/src/catch2/catch_message.cpp +++ b/third_party/Catch2/src/catch2/catch_message.cpp @@ -5,8 +5,8 @@ // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 + #include -#include #include #include @@ -22,7 +22,7 @@ namespace Catch { m_messageId( builder.m_info.sequence ) { MessageInfo info( CATCH_MOVE( builder.m_info ) ); info.message = builder.m_stream.str(); - IResultCapture::pushScopedMessage( CATCH_MOVE( info ) ); + Detail::pushScopedMessage( CATCH_MOVE( info ) ); } ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept: @@ -31,14 +31,16 @@ namespace Catch { } ScopedMessage::~ScopedMessage() { - if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); } + if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); } } Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, - StringRef names ) { + StringRef names, + bool isScoped): + m_isScoped(isScoped) { auto trimmed = [&] (size_t start, size_t end) { while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; @@ -99,15 +101,21 @@ namespace Catch { } Capturer::~Capturer() { assert( m_captured == m_messages.size() ); - for (auto const& message : m_messages) { - IResultCapture::popScopedMessage( message.sequence ); + if ( m_isScoped ) { + for ( auto const& message : m_messages ) { + Detail::popScopedMessage( message.sequence ); + } } } void Capturer::captureValue( size_t index, std::string const& value ) { assert( index < m_messages.size() ); m_messages[index].message += value; - IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) ); + if ( m_isScoped ) { + Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) ); + } else { + Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) ); + } m_captured++; } diff --git a/third_party/Catch2/src/catch2/catch_message.hpp b/third_party/Catch2/src/catch2/catch_message.hpp index 44b07476f..4582654fa 100644 --- a/third_party/Catch2/src/catch2/catch_message.hpp +++ b/third_party/Catch2/src/catch2/catch_message.hpp @@ -13,16 +13,28 @@ #include #include #include +#include #include -#include #include #include namespace Catch { + struct MessageInfo; + struct MessageBuilder; + + namespace Detail { + // The message state affecting functions have to be defined in + // the TU where the thread-local message holders are defined. + // Currently this is catch_run_context.cpp + + void pushScopedMessage( MessageInfo&& message ); + void popScopedMessage( unsigned int messageId ); + void addUnscopedMessage( MessageInfo&& message ); + void emplaceUnscopedMessage( MessageBuilder&& builder ); + } // namespace Detail struct SourceLineInfo; - class IResultCapture; struct MessageStream { @@ -64,8 +76,9 @@ namespace Catch { class Capturer { std::vector m_messages; size_t m_captured = 0; + bool m_isScoped = false; public: - Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names, bool isScoped ); Capturer(Capturer const&) = delete; Capturer& operator=(Capturer const&) = delete; @@ -97,11 +110,12 @@ namespace Catch { } while( false ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ - Catch::Capturer varName( macroName##_catch_sr, \ - CATCH_INTERNAL_LINEINFO, \ - Catch::ResultWas::Info, \ - #__VA_ARGS__##_catch_sr ); \ +#define INTERNAL_CATCH_CAPTURE( varName, macroName, scopedCapture, ... ) \ + Catch::Capturer varName( macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info, \ + #__VA_ARGS__##_catch_sr, \ + scopedCapture ); \ varName.captureValues( 0, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// @@ -110,7 +124,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ - Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + Catch::Detail::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) #if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) @@ -118,28 +132,32 @@ namespace Catch { #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__ ) + #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", true, __VA_ARGS__ ) + #define CATCH_UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_UNSCOPED_CAPTURE", false, __VA_ARGS__ ) #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) + #define CATCH_INFO( msg ) (void)(0) + #define CATCH_UNSCOPED_INFO( msg ) (void)(0) + #define CATCH_WARN( msg ) (void)(0) + #define CATCH_CAPTURE( ... ) (void)(0) + #define CATCH_UNSCOPED_CAPTURE( ... ) (void)(0) #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__ ) + #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", true, __VA_ARGS__ ) + #define UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "UNSCOPED_CAPTURE", false, __VA_ARGS__ ) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) - #define INFO( msg ) (void)(0) - #define UNSCOPED_INFO( msg ) (void)(0) - #define WARN( msg ) (void)(0) - #define CAPTURE( ... ) (void)(0) + #define INFO( msg ) (void)(0) + #define UNSCOPED_INFO( msg ) (void)(0) + #define WARN( msg ) (void)(0) + #define CAPTURE( ... ) (void)(0) + #define UNSCOPED_CAPTURE( ... ) (void)(0) #endif // end of user facing macro declarations diff --git a/third_party/Catch2/src/catch2/catch_registry_hub.cpp b/third_party/Catch2/src/catch2/catch_registry_hub.cpp index 925468faa..25aecdde7 100644 --- a/third_party/Catch2/src/catch2/catch_registry_hub.cpp +++ b/third_party/Catch2/src/catch2/catch_registry_hub.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -72,9 +71,6 @@ namespace Catch { CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); #endif } - IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { - return m_enumValuesRegistry; - } private: TestRegistry m_testCaseRegistry; @@ -82,7 +78,6 @@ namespace Catch { ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; TagAliasRegistry m_tagAliasRegistry; StartupExceptionRegistry m_exceptionRegistry; - Detail::EnumValuesRegistry m_enumValuesRegistry; }; } diff --git a/third_party/Catch2/src/catch2/catch_session.cpp b/third_party/Catch2/src/catch2/catch_session.cpp index 042b7f4d3..0487fe752 100644 --- a/third_party/Catch2/src/catch2/catch_session.cpp +++ b/third_party/Catch2/src/catch2/catch_session.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/third_party/Catch2/src/catch2/catch_tostring.cpp b/third_party/Catch2/src/catch2/catch_tostring.cpp index cc6865674..dad624107 100644 --- a/third_party/Catch2/src/catch2/catch_tostring.cpp +++ b/third_party/Catch2/src/catch2/catch_tostring.cpp @@ -57,6 +57,36 @@ namespace Detail { } } // end unnamed namespace + std::size_t catch_strnlen( const char* str, std::size_t n ) { + auto ret = std::char_traits::find( str, n, '\0' ); + if ( ret != nullptr ) { return static_cast( ret - str ); } + return n; + } + + std::string formatTimeT(std::time_t time) { +#ifdef _MSC_VER + std::tm timeInfo = {}; + const auto err = gmtime_s( &timeInfo, &time ); + if ( err ) { + return "gmtime from provided timepoint has failed. This " + "happens e.g. with pre-1970 dates using Microsoft libc"; + } +#else + std::tm* timeInfo = std::gmtime( &time ); +#endif + + auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" ); + char timeStamp[timeStampSize]; + const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime( timeStamp, timeStampSize, fmt, &timeInfo ); +#else + std::strftime( timeStamp, timeStampSize, fmt, timeInfo ); +#endif + return std::string( timeStamp, timeStampSize - 1 ); + } + std::string convertIntoString(StringRef string, bool escapeInvisibles) { std::string ret; // This is enough for the "don't escape invisibles" case, and a good diff --git a/third_party/Catch2/src/catch2/catch_tostring.hpp b/third_party/Catch2/src/catch2/catch_tostring.hpp index 41f89413c..e421b8b55 100644 --- a/third_party/Catch2/src/catch2/catch_tostring.hpp +++ b/third_party/Catch2/src/catch2/catch_tostring.hpp @@ -8,7 +8,7 @@ #ifndef CATCH_TOSTRING_HPP_INCLUDED #define CATCH_TOSTRING_HPP_INCLUDED - +#include #include #include #include @@ -18,7 +18,8 @@ #include #include #include -#include +#include +#include #ifdef CATCH_CONFIG_CPP17_STRING_VIEW #include @@ -40,13 +41,9 @@ namespace Catch { namespace Detail { - inline std::size_t catch_strnlen(const char *str, std::size_t n) { - auto ret = std::char_traits::find(str, n, '\0'); - if (ret != nullptr) { - return static_cast(ret - str); - } - return n; - } + std::size_t catch_strnlen(const char *str, std::size_t n); + + std::string formatTimeT( std::time_t time ); constexpr StringRef unprintableString = "{?}"_sr; @@ -387,7 +384,10 @@ namespace Catch { } #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER -#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#if defined( CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER ) && \ + defined( CATCH_CONFIG_CPP17_OPTIONAL ) && \ + /* P3168 turned optional into a range, making this ambigous with the range support */ \ + !defined( __cpp_lib_optional_range_support ) #include namespace Catch { template @@ -411,44 +411,38 @@ namespace Catch { // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) -#include +# include +# include namespace Catch { namespace Detail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct TupleElementPrinter { - static void print(const Tuple& tuple, std::ostream& os) { - os << (N ? ", " : " ") - << ::Catch::Detail::stringify(std::get(tuple)); - TupleElementPrinter::print(tuple, os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct TupleElementPrinter { - static void print(const Tuple&, std::ostream&) {} - }; - - } + template + void PrintTuple( const Tuple& tuple, + std::ostream& os, + std::index_sequence ) { + // 1 + Account for when the tuple is empty + char a[1 + sizeof...( Is )] = { + ( ( os << ( Is ? ", " : " " ) + << ::Catch::Detail::stringify( std::get( tuple ) ) ), + '\0' )... }; + (void)a; + } + } // namespace Detail - template + template struct StringMaker> { - static std::string convert(const std::tuple& tuple) { + static std::string convert( const std::tuple& tuple ) { ReusableStringStream rss; rss << '{'; - Detail::TupleElementPrinter>::print(tuple, rss.get()); + Detail::PrintTuple( + tuple, + rss.get(), + std::make_index_sequence{} ); rss << " }"; return rss.str(); } }; -} +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) @@ -635,43 +629,26 @@ struct ratio_string { const auto systemish = std::chrono::time_point_cast< std::chrono::system_clock::duration>( time_point ); const auto as_time_t = std::chrono::system_clock::to_time_t( systemish ); - -#ifdef _MSC_VER - std::tm timeInfo = {}; - const auto err = gmtime_s( &timeInfo, &as_time_t ); - if ( err ) { - return "gmtime from provided timepoint has failed. This " - "happens e.g. with pre-1970 dates using Microsoft libc"; - } -#else - std::tm* timeInfo = std::gmtime( &as_time_t ); -#endif - - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif - return std::string(timeStamp, timeStampSize - 1); + return ::Catch::Detail::formatTimeT( as_time_t ); } }; } -#include - -#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ -namespace Catch { \ - template<> struct StringMaker { \ - static std::string convert( enumName value ) { \ - static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ - return static_cast(enumInfo.lookup( static_cast( value ) )); \ - } \ - }; \ -} +#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ + namespace Catch { \ + template <> \ + struct StringMaker { \ + static std::string convert( enumName value ) { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + static const auto enumInfo = ::Catch::Detail::makeEnumInfo( \ + #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + return static_cast( \ + enumInfo.lookup( static_cast( value ) ) ); \ + } \ + }; \ + } #define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) diff --git a/third_party/Catch2/src/catch2/catch_translate_exception.hpp b/third_party/Catch2/src/catch2/catch_translate_exception.hpp index 4d3bac653..a1f3faa5b 100644 --- a/third_party/Catch2/src/catch2/catch_translate_exception.hpp +++ b/third_party/Catch2/src/catch2/catch_translate_exception.hpp @@ -22,7 +22,7 @@ namespace Catch { class ExceptionTranslatorRegistrar { template - class ExceptionTranslator : public IExceptionTranslator { + class ExceptionTranslator final : public IExceptionTranslator { public: constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) ) diff --git a/third_party/Catch2/src/catch2/catch_user_config.hpp.in b/third_party/Catch2/src/catch2/catch_user_config.hpp.in index 18e2ef1a1..9379e1a1d 100644 --- a/third_party/Catch2/src/catch2/catch_user_config.hpp.in +++ b/third_party/Catch2/src/catch2/catch_user_config.hpp.in @@ -196,12 +196,12 @@ #endif -#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS -#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS +#cmakedefine CATCH_CONFIG_THREAD_SAFE_ASSERTIONS +#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS -#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) && \ - defined( CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) -# error Cannot force EXPERIMENTAL_THREAD_SAFE_ASSERTIONS to both ON and OFF +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) && \ + defined( CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS ) +# error Cannot force THREAD_SAFE_ASSERTIONS to both ON and OFF #endif diff --git a/third_party/Catch2/src/catch2/catch_version.cpp b/third_party/Catch2/src/catch2/catch_version.cpp index 39b0c0beb..c815dba5b 100644 --- a/third_party/Catch2/src/catch2/catch_version.cpp +++ b/third_party/Catch2/src/catch2/catch_version.cpp @@ -36,7 +36,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 11, 0, "", 0 ); + static Version version( 3, 15, 1, "", 0 ); return version; } diff --git a/third_party/Catch2/src/catch2/catch_version_macros.hpp b/third_party/Catch2/src/catch2/catch_version_macros.hpp index 630f5b0ed..f60b3199f 100644 --- a/third_party/Catch2/src/catch2/catch_version_macros.hpp +++ b/third_party/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 11 -#define CATCH_VERSION_PATCH 0 +#define CATCH_VERSION_MINOR 15 +#define CATCH_VERSION_PATCH 1 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/generators/catch_generators.cpp b/third_party/Catch2/src/catch2/generators/catch_generators.cpp index 3514e9f63..f7927eee8 100644 --- a/third_party/Catch2/src/catch2/generators/catch_generators.cpp +++ b/third_party/Catch2/src/catch2/generators/catch_generators.cpp @@ -7,8 +7,6 @@ // SPDX-License-Identifier: BSL-1.0 #include -#include -#include #include namespace Catch { @@ -17,14 +15,6 @@ namespace Catch { namespace Generators { -namespace Detail { - - [[noreturn]] - void throw_generator_exception(char const* msg) { - Catch::throw_exception(GeneratorException{ msg }); - } -} // end namespace Detail - GeneratorUntypedBase::~GeneratorUntypedBase() = default; IGeneratorTracker* acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) { diff --git a/third_party/Catch2/src/catch2/generators/catch_generators.hpp b/third_party/Catch2/src/catch2/generators/catch_generators.hpp index 0f35a9968..afb39f865 100644 --- a/third_party/Catch2/src/catch2/generators/catch_generators.hpp +++ b/third_party/Catch2/src/catch2/generators/catch_generators.hpp @@ -9,6 +9,7 @@ #define CATCH_GENERATORS_HPP_INCLUDED #include +#include #include #include #include @@ -22,14 +23,6 @@ namespace Catch { namespace Generators { -namespace Detail { - - //! Throws GeneratorException with the provided message - [[noreturn]] - void throw_generator_exception(char const * msg); - -} // end namespace detail - template class IGenerator : public GeneratorUntypedBase { std::string stringifyImpl() const override { @@ -64,6 +57,9 @@ namespace Detail { bool next() { return m_generator->countedNext(); } + + bool isFinite() const { return m_generator->isFinite(); } + void skipToNthElement( size_t n ) { m_generator->skipToNthElement(n); } }; @@ -84,6 +80,8 @@ namespace Detail { bool next() override { return false; } + + bool isFinite() const override { return true; } }; template @@ -93,6 +91,15 @@ namespace Detail { "specialization, use SingleValue Generator instead."); std::vector m_values; size_t m_idx = 0; + + void skipToNthElementImpl( std::size_t n ) override { + if ( n >= m_values.size() ) { + Detail::throw_generator_exception( + "Coud not jump to Nth element: not enough elements" ); + } + m_idx = n; + } + public: FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} @@ -103,6 +110,8 @@ namespace Detail { ++m_idx; return m_idx < m_values.size(); } + + bool isFinite() const override { return true; } }; template > @@ -167,6 +176,14 @@ namespace Detail { } return m_current < m_generators.size(); } + + bool isFinite() const override { + for (auto const& gen : m_generators) { + if (!gen.isFinite()) { return false; + } + } + return true; + } }; diff --git a/third_party/Catch2/src/catch2/generators/catch_generators_adapters.hpp b/third_party/Catch2/src/catch2/generators/catch_generators_adapters.hpp index f623bd296..7e21671af 100644 --- a/third_party/Catch2/src/catch2/generators/catch_generators_adapters.hpp +++ b/third_party/Catch2/src/catch2/generators/catch_generators_adapters.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,6 +23,17 @@ namespace Generators { GeneratorWrapper m_generator; size_t m_returned = 0; size_t m_target; + + void skipToNthElementImpl( std::size_t n ) override { + if ( n >= m_target ) { + Detail::throw_generator_exception( + "Coud not jump to Nth element: not enough elements" ); + } + + m_generator.skipToNthElement( n ); + m_returned = n; + } + public: TakeGenerator(size_t target, GeneratorWrapper&& generator): m_generator(CATCH_MOVE(generator)), @@ -46,6 +58,8 @@ namespace Generators { } return success; } + + bool isFinite() const override { return true; } }; template @@ -87,6 +101,8 @@ namespace Generators { while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); return success; } + + bool isFinite() const override { return m_generator.isFinite(); } }; @@ -111,6 +127,9 @@ namespace Generators { m_target_repeats(repeats) { assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + if (!m_generator.isFinite()) { + Detail::throw_generator_exception( "Cannot repeat infinite generator" ); + } } T const& get() const override { @@ -144,6 +163,8 @@ namespace Generators { } return m_current_repeat < m_target_repeats; } + + bool isFinite() const override { return m_generator.isFinite(); } }; template @@ -157,25 +178,30 @@ namespace Generators { GeneratorWrapper m_generator; Func m_function; // To avoid returning dangling reference, we have to save the values - T m_cache; + mutable Optional m_cache; + + void skipToNthElementImpl( std::size_t n ) override { + m_generator.skipToNthElement( n ); + m_cache.reset(); + } + public: template MapGenerator(F2&& function, GeneratorWrapper&& generator) : m_generator(CATCH_MOVE(generator)), - m_function(CATCH_FORWARD(function)), - m_cache(m_function(m_generator.get())) + m_function(CATCH_FORWARD(function)) {} T const& get() const override { - return m_cache; + if ( !m_cache ) { m_cache = m_function( m_generator.get() ); } + return *m_cache; } bool next() override { - const auto success = m_generator.next(); - if (success) { - m_cache = m_function(m_generator.get()); - } - return success; + m_cache.reset(); + return m_generator.next(); } + + bool isFinite() const override { return m_generator.isFinite(); } }; template > @@ -197,7 +223,6 @@ namespace Generators { std::vector m_chunk; size_t m_chunk_size; GeneratorWrapper m_generator; - bool m_used_up = false; public: ChunkGenerator(size_t size, GeneratorWrapper generator) : m_chunk_size(size), m_generator(CATCH_MOVE(generator)) @@ -226,6 +251,8 @@ namespace Generators { } return true; } + + bool isFinite() const override { return m_generator.isFinite(); } }; template @@ -235,6 +262,56 @@ namespace Generators { ); } + template + class ConcatGenerator final : public IGenerator { + std::vector> m_generators; + size_t m_current_generator = 0; + + void InsertGenerators( GeneratorWrapper&& gen ) { + m_generators.push_back( CATCH_MOVE( gen ) ); + } + + template + void InsertGenerators( GeneratorWrapper&& gen, Generators&&... gens ) { + m_generators.push_back( CATCH_MOVE( gen ) ); + InsertGenerators( CATCH_MOVE( gens )... ); + } + + public: + template + ConcatGenerator( Generators&&... generators ) { + InsertGenerators( CATCH_MOVE( generators )... ); + } + + T const& get() const override { + return m_generators[m_current_generator].get(); + } + bool next() override { + const bool success = m_generators[m_current_generator].next(); + if ( success ) { return true; } + + // If current generator is used up, we have to move to the next one + ++m_current_generator; + return m_current_generator < m_generators.size(); + } + + bool isFinite() const override { + for ( auto const& gen : m_generators ) { + if ( !gen.isFinite() ) { return false; } + } + return true; + } + }; + + template + GeneratorWrapper cat( GeneratorWrapper&& generator, + Generators&&... generators ) { + return GeneratorWrapper( + Catch::Detail::make_unique>( + CATCH_MOVE( generator ), CATCH_MOVE( generators )... ) ); + } + + } // namespace Generators } // namespace Catch diff --git a/third_party/Catch2/src/catch2/generators/catch_generators_all.hpp b/third_party/Catch2/src/catch2/generators/catch_generators_all.hpp index c12d314e8..e61218582 100644 --- a/third_party/Catch2/src/catch2/generators/catch_generators_all.hpp +++ b/third_party/Catch2/src/catch2/generators/catch_generators_all.hpp @@ -26,5 +26,6 @@ #include #include #include +#include #endif // CATCH_GENERATORS_ALL_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/generators/catch_generators_random.cpp b/third_party/Catch2/src/catch2/generators/catch_generators_random.cpp index 00a8e634f..d5163c16c 100644 --- a/third_party/Catch2/src/catch2/generators/catch_generators_random.cpp +++ b/third_party/Catch2/src/catch2/generators/catch_generators_random.cpp @@ -37,5 +37,10 @@ namespace Catch { m_current_number = m_pimpl->dist( m_pimpl->rng ); return true; } + + bool RandomFloatingGenerator::isFinite() const { + return false; + } + } // namespace Generators } // namespace Catch diff --git a/third_party/Catch2/src/catch2/generators/catch_generators_random.hpp b/third_party/Catch2/src/catch2/generators/catch_generators_random.hpp index 712835619..e02873600 100644 --- a/third_party/Catch2/src/catch2/generators/catch_generators_random.hpp +++ b/third_party/Catch2/src/catch2/generators/catch_generators_random.hpp @@ -42,6 +42,7 @@ class RandomFloatingGenerator final : public IGenerator { m_current_number = m_dist(m_rng); return true; } + bool isFinite() const override { return false; } }; template <> @@ -59,6 +60,7 @@ class RandomFloatingGenerator final : public IGenerator @@ -80,6 +82,7 @@ class RandomIntegerGenerator final : public IGenerator { m_current_number = m_dist(m_rng); return true; } + bool isFinite() const override { return false; } }; template diff --git a/third_party/Catch2/src/catch2/generators/catch_generators_range.hpp b/third_party/Catch2/src/catch2/generators/catch_generators_range.hpp index 55d673c23..62739b235 100644 --- a/third_party/Catch2/src/catch2/generators/catch_generators_range.hpp +++ b/third_party/Catch2/src/catch2/generators/catch_generators_range.hpp @@ -48,6 +48,8 @@ class RangeGenerator final : public IGenerator { m_current += m_step; return (m_positive) ? (m_current < m_end) : (m_current > m_end); } + + bool isFinite() const override { return true; } }; template @@ -87,6 +89,8 @@ class IteratorGenerator final : public IGenerator { ++m_current; return m_current != m_elems.size(); } + + bool isFinite() const override { return true; } }; template +#include +#include + +namespace Catch { + namespace Generators { + namespace Detail { + + [[noreturn]] + void throw_generator_exception( char const* msg ) { + Catch::throw_exception( GeneratorException{ msg } ); + } + + } // namespace Detail + } // namespace Generators +} // namespace Catch diff --git a/third_party/Catch2/src/catch2/generators/catch_generators_throw.hpp b/third_party/Catch2/src/catch2/generators/catch_generators_throw.hpp new file mode 100644 index 000000000..2725eb6ea --- /dev/null +++ b/third_party/Catch2/src/catch2/generators/catch_generators_throw.hpp @@ -0,0 +1,23 @@ + +// 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_GENERATORS_THROW_HPP_INCLUDED +#define CATCH_GENERATORS_THROW_HPP_INCLUDED + +namespace Catch { + namespace Generators { + namespace Detail { + + //! Throws GeneratorException with the provided message + [[noreturn]] + void throw_generator_exception( char const* msg ); + + } // namespace Detail + } // namespace Generators +} // namespace Catch + +#endif // CATCH_GENERATORS_THROW_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp index a99fdcdc9..87eb259bf 100644 --- a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp +++ b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp @@ -24,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp index 4e27a9858..4484e864d 100644 --- a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp +++ b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp @@ -22,8 +22,6 @@ namespace Catch { struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; - struct MessageInfo; - struct MessageBuilder; struct Counts; struct AssertionReaction; struct SourceLineInfo; @@ -63,10 +61,6 @@ namespace Catch { virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; virtual void benchmarkFailed( StringRef error ) = 0; - static void pushScopedMessage( MessageInfo&& message ); - static void popScopedMessage( unsigned int messageId ); - static void emplaceUnscopedMessage( MessageBuilder&& builder ); - virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual void handleExpr @@ -92,9 +86,6 @@ namespace Catch { ResultWas::OfType resultType, AssertionReaction &reaction ) = 0; - - virtual bool lastAssertionPassed() = 0; - // Deprecated, do not use: virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; diff --git a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp index 82a298dbc..f52315db2 100644 --- a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp +++ b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp @@ -29,6 +29,8 @@ namespace Catch { NoAssertions = 0x01, //! A command line test spec matched no test cases UnmatchedTestSpec = 0x02, + //! The resulting generator in GENERATE is infinite + InfiniteGenerator = 0x04, }; }; enum class ShowDurations { @@ -60,8 +62,9 @@ namespace Catch { class TestSpec; class IStream; + struct PathFilter; - class IConfig : public Detail::NonCopyable { + class IConfig : Detail::NonCopyable { public: virtual ~IConfig(); @@ -71,6 +74,7 @@ namespace Catch { virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual bool warnAboutUnmatchedTestSpecs() const = 0; + virtual bool warnAboutInfiniteGenerators() const = 0; virtual bool zeroTestsCountAsSuccess() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; @@ -84,7 +88,9 @@ namespace Catch { virtual unsigned int shardCount() const = 0; virtual unsigned int shardIndex() const = 0; virtual ColourMode defaultColourMode() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; + virtual std::vector const& getPathFilters() const = 0; + virtual bool useNewFilterBehaviour() const = 0; + virtual Verbosity verbosity() const = 0; virtual bool skipBenchmarks() const = 0; diff --git a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp deleted file mode 100644 index 38b052d90..000000000 --- a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp +++ /dev/null @@ -1,47 +0,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 -#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED - -#include - -#include - -namespace Catch { - - namespace Detail { - struct EnumInfo { - StringRef m_name; - std::vector> m_values; - - ~EnumInfo(); - - StringRef lookup( int value ) const; - }; - } // namespace Detail - - class IMutableEnumValuesRegistry { - public: - virtual ~IMutableEnumValuesRegistry(); // = default; - - virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; - - template - Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { - static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); - std::vector intValues; - intValues.reserve( values.size() ); - for( auto enumValue : values ) - intValues.push_back( static_cast( enumValue ) ); - return registerEnum( enumName, allEnums, intValues ); - } - }; - -} // Catch - -#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.cpp b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.cpp index e9fa5ddac..8442be4ec 100644 --- a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.cpp +++ b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.cpp @@ -7,6 +7,8 @@ // SPDX-License-Identifier: BSL-1.0 #include +#include + #include namespace Catch { @@ -21,6 +23,31 @@ namespace Catch { return ret; } + void GeneratorUntypedBase::skipToNthElementImpl( std::size_t n ) { + for ( size_t i = m_currentElementIndex; i < n; ++i ) { + bool isValid = next(); + if ( !isValid ) { + Detail::throw_generator_exception( + "Coud not jump to Nth element: not enough elements" ); + } + } + } + + void GeneratorUntypedBase::skipToNthElement( std::size_t n ) { + if ( n < m_currentElementIndex ) { + Detail::throw_generator_exception( + "Tried to jump generator backwards" ); + } + if ( n == m_currentElementIndex ) { return; } + + skipToNthElementImpl(n); + // Fixup tracking after moving the generator forward + // * Ensure that the correct element index is set after skipping + // * Invalidate cache + m_currentElementIndex = n; + m_stringReprCache.clear(); + } + StringRef GeneratorUntypedBase::currentElementAsString() const { if ( m_stringReprCache.empty() ) { m_stringReprCache = stringifyImpl(); @@ -28,5 +55,7 @@ namespace Catch { return m_stringReprCache; } + bool GeneratorUntypedBase::isFinite() const { return true; } + } // namespace Generators } // namespace Catch diff --git a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.hpp b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.hpp index d70cb593b..2b2d7c0e8 100644 --- a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.hpp +++ b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_generatortracker.hpp @@ -35,6 +35,15 @@ namespace Catch { //! Customization point for `currentElementAsString` virtual std::string stringifyImpl() const = 0; + /** + * Customization point for skipping to the n-th element + * + * Defaults to successively calling `countedNext`. If there + * are not enough elements to reach the nth one, will throw + * an error. + */ + virtual void skipToNthElementImpl( std::size_t n ); + public: GeneratorUntypedBase() = default; // Generation of copy ops is deprecated (and Clang will complain) @@ -58,6 +67,13 @@ namespace Catch { std::size_t currentElementIndex() const { return m_currentElementIndex; } + /** + * Moves the generator forward **to** the n-th element + * + * Cannot move backwards. Can stay in place. + */ + void skipToNthElement( std::size_t n ); + /** * Returns generator's current element as user-friendly string. * @@ -72,6 +88,15 @@ namespace Catch { * comes first. */ StringRef currentElementAsString() const; + + /** + * Returns true if calls to `next` will eventually return false + * + * Note that for backwards compatibility this is currently defaulted + * to return `true`, but in the future all generators will have to + * provide their own implementation. + */ + virtual bool isFinite() const; }; using GeneratorBasePtr = Catch::Detail::unique_ptr; @@ -80,9 +105,7 @@ namespace Catch { class IGeneratorTracker { public: virtual ~IGeneratorTracker(); // = default; - virtual auto hasGenerator() const -> bool = 0; virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; - virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; }; } // namespace Catch diff --git a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp index 113f223e8..3695a1926 100644 --- a/third_party/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp +++ b/third_party/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp @@ -53,7 +53,6 @@ namespace Catch { virtual void registerTranslator( Detail::unique_ptr&& 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(); diff --git a/third_party/Catch2/src/catch2/internal/catch_assertion_handler.cpp b/third_party/Catch2/src/catch2/internal/catch_assertion_handler.cpp index 9a28e79c2..79d5dcd85 100644 --- a/third_party/Catch2/src/catch2/internal/catch_assertion_handler.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_assertion_handler.cpp @@ -6,21 +6,28 @@ // SPDX-License-Identifier: BSL-1.0 #include +#include #include +#include #include #include +#include #include #include namespace Catch { + void AssertionHandler::finishIncomplete() { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + AssertionHandler::AssertionHandler ( StringRef macroName, SourceLineInfo const& lineInfo, StringRef capturedExpression, ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, - m_resultCapture( getResultCapture() ) + m_resultCapture( static_cast(getResultCapture()) ) { m_resultCapture.notifyAssertionStarted( m_assertionInfo ); } diff --git a/third_party/Catch2/src/catch2/internal/catch_assertion_handler.hpp b/third_party/Catch2/src/catch2/internal/catch_assertion_handler.hpp index c71c6898f..93a70b1a8 100644 --- a/third_party/Catch2/src/catch2/internal/catch_assertion_handler.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_assertion_handler.hpp @@ -10,12 +10,13 @@ #include #include -#include #include namespace Catch { + class RunContext; + struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; @@ -26,7 +27,12 @@ namespace Catch { AssertionInfo m_assertionInfo; AssertionReaction m_reaction; bool m_completed = false; - IResultCapture& m_resultCapture; + // Since all uses are hidden in the .cpp file, we can directly use + // the final type and avoid going through virtual dispatch, without + // massive compilation time overhead. + RunContext& m_resultCapture; + + void finishIncomplete(); public: AssertionHandler @@ -35,9 +41,9 @@ namespace Catch { StringRef capturedExpression, ResultDisposition::Flags resultDisposition ); ~AssertionHandler() { - if ( !m_completed ) { - m_resultCapture.handleIncomplete( m_assertionInfo ); - } + // We want the common fast path inlinable, and the virtual + // dispatch in a function in single TU. + if ( !m_completed ) { finishIncomplete(); } } diff --git a/third_party/Catch2/src/catch2/internal/catch_commandline.cpp b/third_party/Catch2/src/catch2/internal/catch_commandline.cpp index db9035072..4ce423972 100644 --- a/third_party/Catch2/src/catch2/internal/catch_commandline.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_commandline.cpp @@ -32,6 +32,9 @@ namespace Catch { } else if ( warning == "UnmatchedTestSpec" ) { config.warnings = static_cast(config.warnings | WarnAbout::UnmatchedTestSpec); return ParserResult::ok( ParseResultType::Matched ); + } else if ( warning == "InfiniteGenerators" ) { + config.warnings = static_cast(config.warnings | WarnAbout::InfiniteGenerator); + return ParserResult::ok( ParseResultType::Matched ); } return ParserResult ::runtimeError( @@ -189,6 +192,19 @@ namespace Catch { config.shardCount = *parsedCount; return ParserResult::ok( ParseResultType::Matched ); }; + auto const setBenchmarkSamples = [&]( std::string const& samples ) { + auto parsedSamples = parseUInt( samples ); + if ( !parsedSamples ) { + return ParserResult::runtimeError( + "Could not parse '" + samples + "' as benchmark samples" ); + } + if ( *parsedSamples == 0 ) { + return ParserResult::runtimeError( + "Benchmark samples must be greater than 0" ); + } + config.benchmarkSamples = *parsedSamples; + return ParserResult::ok( ParseResultType::Matched ); + }; auto const setShardIndex = [&](std::string const& shardIndex) { auto parsedIndex = parseUInt( shardIndex ); @@ -200,6 +216,43 @@ namespace Catch { return ParserResult::ok( ParseResultType::Matched ); }; + auto const setSectionFilter = [&]( std::string const& sectionFilter ) { + config.pathFilters.emplace_back( PathFilter::For::Section, trim(sectionFilter) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setGeneratorFilter = [&]( std::string const& generatorFilter ) { + if (generatorFilter != "*") { + // TODO: avoid re-parsing the index? + auto parsedIndex = parseUInt( generatorFilter ); + if ( !parsedIndex ) { + return ParserResult::runtimeError( "Could not parse '" + + generatorFilter + + "' as generator index" ); + } + } + config.useNewPathFilteringBehaviour = true; + config.pathFilters.emplace_back( PathFilter::For::Generator, trim(generatorFilter) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + // Copy-capturing other `setFoo` functions enables calling them later, + // as the config ref remains valid, but the local lambda vars won't. + auto const setPathFilter = [=, &config]( std::string const& pathFilter ) { + config.useNewPathFilteringBehaviour = true; + if ( pathFilter.size() < 3 ) { + return ParserResult::runtimeError( + "Path filter '" + pathFilter + "' is too short" ); + } + if ( startsWith( pathFilter, "g:" ) ) { + return setGeneratorFilter( pathFilter.substr( 2 ) ); + } + if ( startsWith( pathFilter, "c:" ) ) { + return setSectionFilter( pathFilter.substr( 2 ) ); + } + return ParserResult::runtimeError( "Path filter '" + pathFilter + + "' has unknown type prefix" ); + }; + + auto cli = ExeName( config.processName ) | Help( config.showHelp ) @@ -245,9 +298,15 @@ namespace Catch { | Opt( config.filenamesAsTags ) ["-#"]["--filenames-as-tags"] ( "adds a tag for the filename" ) - | Opt( config.sectionsToRun, "section name" ) + | Opt( accept_many, setSectionFilter, "section name" ) ["-c"]["--section"] ( "specify section to run" ) + | Opt( accept_many, setGeneratorFilter, "index spec" ) + ["-g"]["--generator-index"] + ( "specify generator elements to try" ) + | Opt( accept_many, setPathFilter, "path filter spec" ) + ["-p"]["--path-filter"] + ( "qualified path filter" ) | Opt( setVerbosity, "quiet|normal|high" ) ["-v"]["--verbosity"] ( "set output verbosity" ) @@ -265,7 +324,7 @@ namespace Catch { ( "list all listeners" ) | Opt( setTestOrder, "decl|lex|rand" ) ["--order"] - ( "test case order (defaults to decl)" ) + ( "test case order (defaults to rand)" ) | Opt( setRngSeed, "'time'|'random-device'|number" ) ["--rng-seed"] ( "set a specific seed for random numbers" ) @@ -281,7 +340,7 @@ namespace Catch { | Opt( config.skipBenchmarks) ["--skip-benchmarks"] ( "disable running benchmarks") - | Opt( config.benchmarkSamples, "samples" ) + | Opt( setBenchmarkSamples, "samples" ) ["--benchmark-samples"] ( "number of samples to collect (default: 100)" ) | Opt( config.benchmarkResamples, "resamples" ) diff --git a/third_party/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp b/third_party/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp index 983fb63b6..2d6d0c751 100644 --- a/third_party/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp @@ -27,16 +27,24 @@ #include #include -#ifdef __cplusplus - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif +#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +#endif -# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) -# define CATCH_CPP20_OR_GREATER -# endif +#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +# define CATCH_CPP20_OR_GREATER +#endif +// Matchers are only constexpr-able in C++20 +#if defined( CATCH_CPP20_OR_GREATER ) && \ + defined( __cpp_constexpr_dynamic_alloc ) && \ + __cpp_constexpr_dynamic_alloc >= 201907L && \ + /* GCC < 13 define the feature macro, but compiler bugs stop us from using it */ \ + ( !defined( __GNUC__ ) || __GNUC__ >= 13 || defined(__clang__) ) +# define CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED +# define CATCH_DESTRUCTOR_CONSTEXPR constexpr +#else +# define CATCH_DESTRUCTOR_CONSTEXPR #endif // Only GCC compiler should be used in this block, so other compilers trying to @@ -110,10 +118,15 @@ # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wc++20-extensions\"" ) # else -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) # endif +# if ( __clang_major__ >= 22 ) +# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wc2y-extensions\"" ) +# endif + # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) @@ -208,7 +221,7 @@ //////////////////////////////////////////////////////////////////////////////// // Visual C++ -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) // We want to defer to nvcc-specific warning suppression if we are compiled // with nvcc masquerading for MSVC. @@ -219,6 +232,11 @@ __pragma( warning( pop ) ) # endif +// Suppress MSVC C++ Core Guidelines checker warning 26426: +// "Global initializer calls a non-constexpr function (i.22)" +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + __pragma( warning( disable : 26426 ) ) + // Universal Windows platform does not support SEH # if !defined(CATCH_PLATFORM_WINDOWS_UWP) # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH @@ -424,6 +442,9 @@ #if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) # define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS #endif +#if !defined( CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS +#endif #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS diff --git a/third_party/Catch2/src/catch2/internal/catch_console_colour.cpp b/third_party/Catch2/src/catch2/internal/catch_console_colour.cpp index 948c4cb61..0c9ef5461 100644 --- a/third_party/Catch2/src/catch2/internal/catch_console_colour.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_console_colour.cpp @@ -164,7 +164,9 @@ namespace { #if defined( CATCH_PLATFORM_LINUX ) \ || defined( CATCH_PLATFORM_MAC ) \ || defined( __GLIBC__ ) \ - || defined( __FreeBSD__ ) \ + || (defined( __FreeBSD__ ) \ + /* PlayStation platform does not have `isatty()` */ \ + && !defined(CATCH_PLATFORM_PLAYSTATION)) \ || defined( CATCH_PLATFORM_QNX ) # define CATCH_INTERNAL_HAS_ISATTY # include diff --git a/third_party/Catch2/src/catch2/internal/catch_decomposer.hpp b/third_party/Catch2/src/catch2/internal/catch_decomposer.hpp index fcc017dda..ff628482e 100644 --- a/third_party/Catch2/src/catch2/internal/catch_decomposer.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_decomposer.hpp @@ -193,7 +193,7 @@ namespace Catch { void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); template - class BinaryExpr : public ITransientExpression { + class BinaryExpr final : public ITransientExpression { LhsT m_lhs; StringRef m_op; RhsT m_rhs; @@ -269,7 +269,7 @@ namespace Catch { }; template - class UnaryExpr : public ITransientExpression { + class UnaryExpr final : public ITransientExpression { LhsT m_lhs; void streamReconstructedExpression( std::ostream &os ) const override { diff --git a/third_party/Catch2/src/catch2/internal/catch_deprecation_macro.hpp b/third_party/Catch2/src/catch2/internal/catch_deprecation_macro.hpp index 7834882b7..7712e20de 100644 --- a/third_party/Catch2/src/catch2/internal/catch_deprecation_macro.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_deprecation_macro.hpp @@ -11,9 +11,9 @@ #include #if !defined( CATCH_CONFIG_NO_DEPRECATION_ANNOTATIONS ) -# define DEPRECATED( msg ) [[deprecated( msg )]] +# define CATCH_DEPRECATED( msg ) [[deprecated( msg )]] #else -# define DEPRECATED( msg ) +# define CATCH_DEPRECATED( msg ) #endif #endif // CATCH_DEPRECATION_MACRO_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/internal/catch_enum_values_registry.cpp b/third_party/Catch2/src/catch2/internal/catch_enum_info.cpp similarity index 65% rename from third_party/Catch2/src/catch2/internal/catch_enum_values_registry.cpp rename to third_party/Catch2/src/catch2/internal/catch_enum_info.cpp index a94b60881..0793abd9e 100644 --- a/third_party/Catch2/src/catch2/internal/catch_enum_values_registry.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_enum_info.cpp @@ -5,15 +5,14 @@ // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 -#include + +#include #include #include namespace Catch { - IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default; - namespace Detail { namespace { @@ -39,9 +38,7 @@ namespace Catch { return parsed; } - EnumInfo::~EnumInfo() = default; - - StringRef EnumInfo::lookup( int value ) const { + StringRef EnumInfo::lookup( int64_t value ) const { for( auto const& valueToName : m_values ) { if( valueToName.first == value ) return valueToName.second; @@ -49,25 +46,20 @@ namespace Catch { return "{** unexpected enum value **}"_sr; } - Catch::Detail::unique_ptr makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ) { - auto enumInfo = Catch::Detail::make_unique(); - enumInfo->m_name = enumName; - enumInfo->m_values.reserve( values.size() ); + EnumInfo makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ) { + EnumInfo enumInfo; + enumInfo.m_name = enumName; + enumInfo.m_values.reserve( values.size() ); const auto valueNames = Catch::Detail::parseEnums( allValueNames ); assert( valueNames.size() == values.size() ); - std::size_t i = 0; - for( auto value : values ) - enumInfo->m_values.emplace_back(value, valueNames[i++]); + for (size_t i = 0; i < values.size(); ++i) { + enumInfo.m_values.emplace_back( values[i], valueNames[i] ); + } return enumInfo; } - EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values ) { - m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); - return *m_enumInfos.back(); - } - } // Detail } // Catch diff --git a/third_party/Catch2/src/catch2/internal/catch_enum_info.hpp b/third_party/Catch2/src/catch2/internal/catch_enum_info.hpp new file mode 100644 index 000000000..b3036a821 --- /dev/null +++ b/third_party/Catch2/src/catch2/internal/catch_enum_info.hpp @@ -0,0 +1,48 @@ + +// 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_ENUM_INFO_HPP_INCLUDED +#define CATCH_ENUM_INFO_HPP_INCLUDED + +#include + +#include +#include +#include + +namespace Catch { + namespace Detail { + + struct EnumInfo { + StringRef m_name; + std::vector> m_values; + + StringRef lookup( int64_t value ) const; + }; + + EnumInfo makeEnumInfo( StringRef enumName, + StringRef allValueNames, + std::vector const& values ); + template + Detail::EnumInfo makeEnumInfo( StringRef enumName, + StringRef allEnums, + std::initializer_list values ) { + static_assert( sizeof( int64_t ) >= sizeof( E ), + "Cannot serialize enum to int64_t" ); + std::vector intValues; + intValues.reserve( values.size() ); + for ( auto enumValue : values ) + intValues.push_back( static_cast( enumValue ) ); + return makeEnumInfo( enumName, allEnums, intValues ); + } + + std::vector parseEnums( StringRef enums ); + + } // namespace Detail +} // namespace Catch + +#endif // CATCH_ENUM_INFO_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/internal/catch_enum_values_registry.hpp b/third_party/Catch2/src/catch2/internal/catch_enum_values_registry.hpp deleted file mode 100644 index de994c359..000000000 --- a/third_party/Catch2/src/catch2/internal/catch_enum_values_registry.hpp +++ /dev/null @@ -1,36 +0,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 -#ifndef CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED -#define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED - -#include -#include -#include - -#include - -namespace Catch { - - namespace Detail { - - Catch::Detail::unique_ptr makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ); - - class EnumValuesRegistry : public IMutableEnumValuesRegistry { - - std::vector> m_enumInfos; - - EnumInfo const& registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values) override; - }; - - std::vector parseEnums( StringRef enums ); - - } // Detail - -} // Catch - -#endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp b/third_party/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp index 32917d41a..d00fdd25c 100644 --- a/third_party/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp @@ -15,7 +15,7 @@ namespace Catch { - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + class ExceptionTranslatorRegistry final : public IExceptionTranslatorRegistry { public: ~ExceptionTranslatorRegistry() override; void registerTranslator( Detail::unique_ptr&& translator ); diff --git a/third_party/Catch2/src/catch2/internal/catch_jsonwriter.hpp b/third_party/Catch2/src/catch2/internal/catch_jsonwriter.hpp index 23b56d13a..8b3d2bc19 100644 --- a/third_party/Catch2/src/catch2/internal/catch_jsonwriter.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_jsonwriter.hpp @@ -8,6 +8,7 @@ #ifndef CATCH_JSONWRITER_HPP_INCLUDED #define CATCH_JSONWRITER_HPP_INCLUDED +#include #include #include @@ -27,8 +28,8 @@ namespace Catch { class JsonValueWriter { public: - JsonValueWriter( std::ostream& os ); - JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); + JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonObjectWriter writeObject() &&; JsonArrayWriter writeArray() &&; @@ -62,8 +63,8 @@ namespace Catch { class JsonObjectWriter { public: - JsonObjectWriter( std::ostream& os ); - JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); + JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonObjectWriter( JsonObjectWriter&& source ) noexcept; JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; @@ -81,8 +82,8 @@ namespace Catch { class JsonArrayWriter { public: - JsonArrayWriter( std::ostream& os ); - JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); + JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonArrayWriter( JsonArrayWriter&& source ) noexcept; JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; diff --git a/third_party/Catch2/src/catch2/internal/catch_lifetimebound.hpp b/third_party/Catch2/src/catch2/internal/catch_lifetimebound.hpp new file mode 100644 index 000000000..8114dc70e --- /dev/null +++ b/third_party/Catch2/src/catch2/internal/catch_lifetimebound.hpp @@ -0,0 +1,24 @@ + +// 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_LIFETIMEBOUND_HPP_INCLUDED +#define CATCH_LIFETIMEBOUND_HPP_INCLUDED + +#if !defined( __has_cpp_attribute ) +# define CATCH_ATTR_LIFETIMEBOUND +#elif __has_cpp_attribute( msvc::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]] +#elif __has_cpp_attribute( clang::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] +#elif __has_cpp_attribute( lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]] +#else +# define CATCH_ATTR_LIFETIMEBOUND +#endif + +#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/internal/catch_message_info.cpp b/third_party/Catch2/src/catch2/internal/catch_message_info.cpp index bc887abfd..9f8690a42 100644 --- a/third_party/Catch2/src/catch2/internal/catch_message_info.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_message_info.cpp @@ -7,20 +7,24 @@ // SPDX-License-Identifier: BSL-1.0 #include +#include namespace Catch { + namespace { + // Messages are owned by their individual threads, so the counter should + // be thread-local as well. Alternative consideration: atomic counter, + // so threads don't share IDs and things are easier to debug. + static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0; + } + MessageInfo::MessageInfo( StringRef _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), lineInfo( _lineInfo ), type( _type ), - sequence( ++globalCount ) + sequence( ++messageIDCounter ) {} - // Messages are owned by their individual threads, so the counter should be thread-local as well. - // Alternative consideration: atomic, so threads don't share IDs and things are easier to debug. - thread_local unsigned int MessageInfo::globalCount = 0; - } // end namespace Catch diff --git a/third_party/Catch2/src/catch2/internal/catch_message_info.hpp b/third_party/Catch2/src/catch2/internal/catch_message_info.hpp index 8c18186ba..72f4516b2 100644 --- a/third_party/Catch2/src/catch2/internal/catch_message_info.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_message_info.hpp @@ -29,16 +29,14 @@ namespace Catch { // The "ID" of the message, used to know when to remove it from reporter context. unsigned int sequence; - DEPRECATED( "Explicitly use the 'sequence' member instead" ) + CATCH_DEPRECATED( "Explicitly use the 'sequence' member instead" ) bool operator == (MessageInfo const& other) const { return sequence == other.sequence; } - DEPRECATED( "Explicitly use the 'sequence' member instead" ) + CATCH_DEPRECATED( "Explicitly use the 'sequence' member instead" ) bool operator < (MessageInfo const& other) const { return sequence < other.sequence; } - private: - static thread_local unsigned int globalCount; }; } // end namespace Catch diff --git a/third_party/Catch2/src/catch2/internal/catch_output_redirect.cpp b/third_party/Catch2/src/catch2/internal/catch_output_redirect.cpp index 245e1376c..0ea126a5b 100644 --- a/third_party/Catch2/src/catch2/internal/catch_output_redirect.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_output_redirect.cpp @@ -33,7 +33,7 @@ namespace Catch { namespace { //! A no-op implementation, used if no reporter wants output //! redirection. - class NoopRedirect : public OutputRedirect { + class NoopRedirect final : public OutputRedirect { void activateImpl() override {} void deactivateImpl() override {} std::string getStdout() override { return {}; } @@ -70,7 +70,7 @@ namespace Catch { * Redirects the `std::cout`, `std::cerr`, `std::clog` streams, * but does not touch the actual `stdout`/`stderr` file descriptors. */ - class StreamRedirect : public OutputRedirect { + class StreamRedirect final : public OutputRedirect { ReusableStringStream m_redirectedOut, m_redirectedErr; RedirectedStreamNew m_cout, m_cerr, m_clog; @@ -181,7 +181,7 @@ namespace Catch { * Works by replacing the file descriptors numbered 1 and 2 * with an open temporary file. */ - class FileRedirect : public OutputRedirect { + class FileRedirect final : public OutputRedirect { TempFile m_outFile, m_errFile; int m_originalOut = -1; int m_originalErr = -1; diff --git a/third_party/Catch2/src/catch2/internal/catch_path_filter.hpp b/third_party/Catch2/src/catch2/internal/catch_path_filter.hpp new file mode 100644 index 000000000..9040efeca --- /dev/null +++ b/third_party/Catch2/src/catch2/internal/catch_path_filter.hpp @@ -0,0 +1,33 @@ + +// 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_PATH_FILTER_HPP_INCLUDED +#define CATCH_PATH_FILTER_HPP_INCLUDED + +#include + +#include + +namespace Catch { + + struct PathFilter { + enum class For { + Section, + Generator, + }; + PathFilter( For type_, std::string filter_ ): + type( type_ ), filter( CATCH_MOVE( filter_ ) ) {} + + For type; + std::string filter; + + friend bool operator==( PathFilter const& lhs, PathFilter const& rhs ); + }; + +} // end namespace Catch + +#endif // CATCH_PATH_FILTER_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/internal/catch_run_context.cpp b/third_party/Catch2/src/catch2/internal/catch_run_context.cpp index 0029bf561..a435170f4 100644 --- a/third_party/Catch2/src/catch2/internal/catch_run_context.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_run_context.cpp @@ -8,8 +8,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -19,8 +21,12 @@ #include #include #include +#include #include +#include +#include #include +#include #include #include @@ -32,12 +38,51 @@ namespace Catch { struct GeneratorTracker final : TestCaseTracking::TrackerBase, IGeneratorTracker { GeneratorBasePtr m_generator; + // Filtered generator has moved to specific index due to + // a filter, it needs special handling of `countedNext()` + bool m_isFiltered = false; GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, - ITracker* parent ): - TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} + ITracker* parent, + GeneratorBasePtr&& generator ): + TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ), + m_generator( CATCH_MOVE( generator ) ) { + assert( m_generator && + "Cannot create tracker without generator" ); + + // Handle potential filter and move forward here... + // Old style filters do not affect generators at all + if (m_newStyleFilters && m_allTrackerDepth < m_filterRef->size()) { + auto const& filter = + ( *m_filterRef )[m_allTrackerDepth]; + // Generator cannot be un-entered the way a section + // can be, so the tracker has to throw for a wrong + // filter to stop the execution flow. + if (filter.type == PathFilter::For::Section) { + // We want the semantics of `SKIP()`, but we inline it + // to avoid issues with conditionally prefixed macros + INTERNAL_CATCH_MSG( + "SKIP", + Catch::ResultWas::ExplicitSkip, + Catch::ResultDisposition::Normal, + "" ); + Catch::Detail::Unreachable(); + } + // '*' is the wildcard for "all elements in generator" + // used for filtering sections below the generator, but + // not the generator itself. + if ( filter.filter != "*" ) { + m_isFiltered = true; + // TBD: We assume that the filter was validated as + // number during parsing. We should pass it + // as number from the CLI parser. + size_t targetIndex = std::stoul( filter.filter ); + m_generator->skipToNthElement( targetIndex ); + } + } + } static GeneratorTracker* acquire( TrackerContext& ctx, @@ -81,9 +126,6 @@ namespace Catch { // 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) @@ -112,29 +154,24 @@ namespace Catch { // _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( *parent ); - auto const& filters = parentSection.getFilters(); - // No filters -> no restrictions on running sections - if ( filters.empty() ) { return true; } + // No filters left -> no restrictions on running sections + size_t childDepth = 1 + (m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth); + if ( childDepth >= m_filterRef->size() ) { + return true; + } + // If we are using the new style filters, we need to check + // whether the successive filter is for section or a generator. + if ( m_newStyleFilters + && (*m_filterRef)[childDepth].type != PathFilter::For::Section ) { + return false; + } + // Look for any child section that could match the remaining filters for ( auto const& child : m_children ) { if ( child->isSectionTracker() && - std::find( filters.begin(), - filters.end(), - static_cast( - *child ) - .trimmedName() ) != - filters.end() ) { + static_cast( *child ) + .trimmedName() == StringRef((*m_filterRef)[childDepth].filter) ) { return true; } } @@ -146,9 +183,10 @@ namespace Catch { // 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() ) ) { + if ( should_wait_for_child + || ( m_runState == CompletedSuccessfully + && !m_isFiltered // filtered generators cannot meaningfully move forward, as they would get past the filter + && m_generator->countedNext() ) ) { m_children.clear(); m_runState = Executing; } @@ -158,9 +196,6 @@ namespace Catch { auto getGenerator() const -> GeneratorBasePtr const& override { return m_generator; } - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = CATCH_MOVE( generator ); - } }; } // namespace } @@ -177,29 +212,122 @@ namespace Catch { // should also be thread local. For now we just use naked globals // below, in the future we will want to allocate piece of memory // from heap, to avoid consuming too much thread-local storage. + // + // Note that we also don't want non-trivial the thread-local variables + // below be initialized for every thread, only for those that touch + // Catch2. To make this work with both GCC/Clang and MSVC, we have to + // make them thread-local magic statics. (Class-level statics have the + // desired semantics on GCC, but not on MSVC). // This is used for the "if" part of CHECKED_IF/CHECKED_ELSE - static thread_local bool g_lastAssertionPassed = false; + static CATCH_INTERNAL_THREAD_LOCAL bool g_lastAssertionPassed = false; // This is the source location for last encountered macro. It is // used to provide the users with more precise location of error // when an unexpected exception/fatal error happens. - static thread_local SourceLineInfo g_lastKnownLineInfo("DummyLocation", static_cast(-1)); + static CATCH_INTERNAL_THREAD_LOCAL SourceLineInfo + g_lastKnownLineInfo( "DummyLocation", static_cast( -1 ) ); // Should we clear message scopes before sending off the messages to // reporter? Set in `assertionPassedFastPath` to avoid doing the full // clear there for performance reasons. - static thread_local bool g_clearMessageScopes = false; + static CATCH_INTERNAL_THREAD_LOCAL bool g_clearMessageScopes = false; + + + // Holds the data for both scoped and unscoped messages together, + // to avoid issues where their lifetimes start in wrong order, + // and then are destroyed in wrong order. + class MessageHolder { + // The actual message vector passed to the reporters + std::vector messages; + // IDs of messages from UNSCOPED_X macros, which we have to + // remove manually. + std::vector unscoped_ids; + + public: + // We do not need to special-case the unscoped messages when + // we only keep around the raw msg ids. + ~MessageHolder() = default; + + void addUnscopedMessage( MessageInfo&& info ) { + repairUnscopedMessageInvariant(); + unscoped_ids.push_back( info.sequence ); + messages.push_back( CATCH_MOVE( info ) ); + } + + void addUnscopedMessage(MessageBuilder&& builder) { + MessageInfo info( CATCH_MOVE( builder.m_info ) ); + info.message = builder.m_stream.str(); + addUnscopedMessage( CATCH_MOVE( info ) ); + } + + void addScopedMessage(MessageInfo&& info) { + messages.push_back( CATCH_MOVE( info ) ); + } + + std::vector const& getMessages() const { + return messages; + } + + void removeMessage( unsigned int messageId ) { + // Note: On average, it would probably be better to look for + // the message backwards. However, we do not expect to have + // to deal with more messages than low single digits, so + // the improvement is tiny, and we would have to hand-write + // the loop to avoid terrible codegen of reverse iterators + // in debug mode. + auto iter = + std::find_if( messages.begin(), + messages.end(), + [messageId]( MessageInfo const& msg ) { + return msg.sequence == messageId; + } ); + assert( iter != messages.end() && + "Trying to remove non-existent message." ); + messages.erase( iter ); + } + + void removeUnscopedMessages() { + for ( const auto messageId : unscoped_ids ) { + removeMessage( messageId ); + } + unscoped_ids.clear(); + g_clearMessageScopes = false; + } + + void repairUnscopedMessageInvariant() { + if ( g_clearMessageScopes ) { removeUnscopedMessages(); } + g_clearMessageScopes = false; + } + }; CATCH_INTERNAL_START_WARNINGS_SUPPRESSION CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - // Actual messages to be provided to the reporter - static thread_local std::vector g_messages; - - // Owners for the UNSCOPED_X information macro - static thread_local std::vector g_messageScopes; + static MessageHolder& g_messageHolder() { + static CATCH_INTERNAL_THREAD_LOCAL MessageHolder value; + return value; + } CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + void pushScopedMessage( MessageInfo&& message ) { + Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) ); + } + + void popScopedMessage( unsigned int messageId ) { + Detail::g_messageHolder().removeMessage( messageId ); + } + + void emplaceUnscopedMessage( MessageBuilder&& builder ) { + Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) ); + } + + void addUnscopedMessage( MessageInfo&& message ) { + Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) ); + } + + bool lastAssertionPassed() { return Detail::g_lastAssertionPassed; } + } // namespace Detail RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) @@ -214,6 +342,13 @@ namespace Catch { { getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); + + // TODO: HACK! + // We need to make sure the underlying cache is initialized + // while we are guaranteed to be running in a single thread, + // because the initialization is not thread-safe. + ReusableStringStream rss; + (void)rss; } RunContext::~RunContext() { @@ -233,7 +368,8 @@ namespace Catch { ITracker& rootTracker = m_trackerContext.startRun(); assert(rootTracker.isSectionTracker()); - static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + rootTracker.setFilters( &m_config->getPathFilters(), + m_config->useNewFilterBehaviour() ); // We intentionally only seed the internal RNG once per test case, // before it is first invoked. The reason for that is a complex @@ -325,32 +461,29 @@ namespace Catch { Detail::g_lastAssertionPassed = true; } else if (!result.succeeded()) { Detail::g_lastAssertionPassed = false; - if (result.isOk()) { - } - else if( m_activeTestCase->getTestCaseInfo().okToFail() ) // Read from a shared state established before the threads could start, this is fine + if (result.isOk()) {} + else if( m_activeTestCase->getTestCaseInfo().okToFail() ) { // Read from a shared state established before the threads could start, this is fine m_atomicAssertionCount.failedButOk++; - else + } else { m_atomicAssertionCount.failed++; - } - else { + } + } else { Detail::g_lastAssertionPassed = true; } - if ( Detail::g_clearMessageScopes ) { - Detail::g_messageScopes.clear(); - Detail::g_clearMessageScopes = false; - } + auto& msgHolder = Detail::g_messageHolder(); + msgHolder.repairUnscopedMessageInvariant(); // From here, we are touching shared state and need mutex. Detail::LockGuard lock( m_assertionMutex ); { auto _ = scopedDeactivate( *m_outputRedirect ); updateTotalsFromAtomics(); - m_reporter->assertionEnded( AssertionStats( result, Detail::g_messages, m_totals ) ); + m_reporter->assertionEnded( AssertionStats( result, msgHolder.getMessages(), m_totals ) ); } if ( result.getResultType() != ResultWas::Warning ) { - Detail::g_messageScopes.clear(); + msgHolder.removeUnscopedMessages(); } // Reset working state. assertion info will be reset after @@ -407,18 +540,32 @@ namespace Catch { SourceLineInfo lineInfo, Generators::GeneratorBasePtr&& generator ) { + // TBD: Do we want to avoid the warning if the generator is filtered? + if ( m_config->warnAboutInfiniteGenerators() && + !generator->isFinite() ) { + // We want the semantics of `FAIL()`, but we inline it + // to avoid issues with conditionally prefixed macros + INTERNAL_CATCH_MSG( "FAIL", + Catch::ResultWas::ExplicitFailure, + Catch::ResultDisposition::Normal, + "GENERATE() would run infinitely" ); + } + auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast( generatorName ), lineInfo ); auto& currentTracker = m_trackerContext.currentTracker(); assert( currentTracker.nameAndLocation() != nameAndLoc && "Trying to create tracker for a generator that already has one" ); - auto newTracker = Catch::Detail::make_unique( - CATCH_MOVE(nameAndLoc), m_trackerContext, ¤tTracker ); + auto newTracker = + Catch::Detail::make_unique( + CATCH_MOVE( nameAndLoc ), + m_trackerContext, + ¤tTracker, + CATCH_MOVE( generator ) ); auto ret = newTracker.get(); currentTracker.addChild( CATCH_MOVE( newTracker ) ); - ret->setGenerator( CATCH_MOVE( generator ) ); ret->open(); return ret; } @@ -502,7 +649,7 @@ namespace Catch { // and since IResultCapture::getLastResult is deprecated, // we will leave it as is, until it is finally removed. Detail::LockGuard _( m_assertionMutex ); - return &(*m_lastResult); + return &*m_lastResult; } void RunContext::exceptionEarlyReported() { @@ -576,10 +723,6 @@ namespace Catch { m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); } - bool RunContext::lastAssertionPassed() { - return Detail::g_lastAssertionPassed; - } - void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) { // We want to save the line info for better experience with unexpected assertions Detail::g_lastKnownLineInfo = lineInfo; @@ -639,10 +782,10 @@ namespace Catch { m_testCaseTracker->close(); handleUnfinishedSections(); - Detail::g_messageScopes.clear(); - // TBD: At this point, m_messages should be empty. Do we want to - // assert that this is true, or keep the defensive clear call? - Detail::g_messages.clear(); + auto& msgHolder = Detail::g_messageHolder(); + msgHolder.removeUnscopedMessages(); + assert( msgHolder.getMessages().empty() && + "There should be no leftover messages after the test ends" ); SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions); m_reporter->sectionEnded(testCaseSectionStats); @@ -758,7 +901,7 @@ namespace Catch { } void RunContext::populateReaction( AssertionReaction& reaction, - bool has_normal_disposition ) { + bool has_normal_disposition ) const { reaction.shouldDebugBreak = m_shouldDebugBreak; reaction.shouldThrow = aborting() || has_normal_disposition; } @@ -809,28 +952,6 @@ namespace Catch { } } - void IResultCapture::pushScopedMessage( MessageInfo&& message ) { - Detail::g_messages.push_back( CATCH_MOVE( message ) ); - } - - void IResultCapture::popScopedMessage( unsigned int messageId ) { - // Note: On average, it would probably be better to look for the message - // backwards. However, we do not expect to have to deal with more - // messages than low single digits, so the optimization is tiny, - // and we would have to hand-write the loop to avoid terrible - // codegen of reverse iterators in debug mode. - Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(), - Detail::g_messages.end(), - [=]( MessageInfo const& msg ) { - return msg.sequence == - messageId; - } ) ); - } - - void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) { - Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) ); - } - void seedRng(IConfig const& config) { sharedRng().seed(config.rngSeed()); } diff --git a/third_party/Catch2/src/catch2/internal/catch_run_context.hpp b/third_party/Catch2/src/catch2/internal/catch_run_context.hpp index dee851e04..f333735db 100644 --- a/third_party/Catch2/src/catch2/internal/catch_run_context.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_run_context.hpp @@ -102,8 +102,6 @@ namespace Catch { void handleFatalErrorCondition( StringRef message ) override; - bool lastAssertionPassed() override; - public: // !TBD We need to do this another way! bool aborting() const; @@ -125,7 +123,7 @@ namespace Catch { ITransientExpression const *expr, bool negated ); - void populateReaction( AssertionReaction& reaction, bool has_normal_disposition ); + void populateReaction( AssertionReaction& reaction, bool has_normal_disposition ) const; // Creates dummy info for unexpected exceptions/fatal errors, // where we do not have the access to one, but we still need diff --git a/third_party/Catch2/src/catch2/internal/catch_singletons.hpp b/third_party/Catch2/src/catch2/internal/catch_singletons.hpp index a28a13daa..74dc6bb0f 100644 --- a/third_party/Catch2/src/catch2/internal/catch_singletons.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_singletons.hpp @@ -20,7 +20,7 @@ namespace Catch { template - class Singleton : SingletonImplT, public ISingleton { + class Singleton final : SingletonImplT, public ISingleton { static auto getInternal() -> Singleton* { static Singleton* s_instance = nullptr; diff --git a/third_party/Catch2/src/catch2/internal/catch_string_manip.hpp b/third_party/Catch2/src/catch2/internal/catch_string_manip.hpp index dc0c552c4..4251f83d8 100644 --- a/third_party/Catch2/src/catch2/internal/catch_string_manip.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_string_manip.hpp @@ -8,6 +8,7 @@ #ifndef CATCH_STRING_MANIP_HPP_INCLUDED #define CATCH_STRING_MANIP_HPP_INCLUDED +#include #include #include @@ -28,10 +29,10 @@ namespace Catch { //! Returns a new string without whitespace at the start/end std::string trim( std::string const& str ); //! Returns a substring of the original ref without whitespace. Beware lifetimes! - StringRef trim(StringRef ref); + StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND ); // !!! Be aware, returns refs into original string - make sure original string outlives them - std::vector splitStringRef( StringRef str, char delimiter ); + std::vector splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); /** @@ -49,7 +50,7 @@ namespace Catch { StringRef m_label; public: - constexpr pluralise(std::uint64_t count, StringRef label): + constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND): m_count(count), m_label(label) {} diff --git a/third_party/Catch2/src/catch2/internal/catch_stringref.hpp b/third_party/Catch2/src/catch2/internal/catch_stringref.hpp index 421ce7129..e70925bcd 100644 --- a/third_party/Catch2/src/catch2/internal/catch_stringref.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_stringref.hpp @@ -8,11 +8,12 @@ #ifndef CATCH_STRINGREF_HPP_INCLUDED #define CATCH_STRINGREF_HPP_INCLUDED +#include + #include #include #include #include - #include namespace Catch { @@ -36,14 +37,16 @@ namespace Catch { public: // construction constexpr StringRef() noexcept = default; - StringRef( char const* rawChars ) noexcept; + StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept; - constexpr StringRef( char const* rawChars, size_type size ) noexcept + constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND, + size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} - StringRef( std::string const& stdString ) noexcept + StringRef( + std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} @@ -89,7 +92,7 @@ namespace Catch { } // Returns the current start pointer. May not be null-terminated. - constexpr char const* data() const noexcept { + constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND { return m_start; } diff --git a/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp b/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp index 510df167f..e87e40506 100644 --- a/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp @@ -16,10 +16,11 @@ namespace Catch { TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); - if( it != m_registry.end() ) - return &(it->second); - else + if ( it != m_registry.end() ) { + return &it->second; + } else { return nullptr; + } } std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { diff --git a/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.hpp b/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.hpp index 64c0f8f3c..a359b4950 100644 --- a/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_tag_alias_registry.hpp @@ -17,7 +17,7 @@ namespace Catch { struct SourceLineInfo; - class TagAliasRegistry : public ITagAliasRegistry { + class TagAliasRegistry final : public ITagAliasRegistry { public: ~TagAliasRegistry() override; TagAlias const* find( std::string const& alias ) const override; diff --git a/third_party/Catch2/src/catch2/internal/catch_template_test_registry.hpp b/third_party/Catch2/src/catch2/internal/catch_template_test_registry.hpp index 7aec888fe..400c308c7 100644 --- a/third_party/Catch2/src/catch2/internal/catch_template_test_registry.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_template_test_registry.hpp @@ -245,42 +245,42 @@ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) #endif - #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace {\ template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ - void test();\ - };\ - namespace {\ - namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\ - INTERNAL_CATCH_TYPE_GEN \ - INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ - template\ - struct TestNameClass{\ - void reg_tests(){\ - std::size_t index = 0;\ - using expander = std::size_t[];\ - constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ - constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ - constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */ \ - }\ - };\ - static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ - using TestInit = typename create(Catch::Detail::priority_tag<1>{})), TypeList>::type;\ - TestInit t;\ - t.reg_tests();\ - return 0;\ - }(); \ - }\ - }\ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - template \ - void TestName::test() + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ + template\ + struct TestNameClass{\ + void reg_tests(){\ + std::size_t index = 0;\ + using expander = std::size_t[];\ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = typename create(Catch::Detail::priority_tag<1>{})), TypeList>::type;\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }\ + }\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + void TestName::test() #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ @@ -298,37 +298,37 @@ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) ) #endif - #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ + namespace {\ template \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ void test();\ };\ - namespace {\ - namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ - INTERNAL_CATCH_TYPE_GEN\ - template\ - struct TestNameClass{\ - void reg_tests(){\ - size_t index = 0;\ - using expander = size_t[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ - }\ - };\ - static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ - using TestInit = typename convert::type;\ - TestInit t;\ - t.reg_tests();\ - return 0;\ - }(); \ - }}\ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - template \ - void TestName::test() + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ + INTERNAL_CATCH_TYPE_GEN\ + template\ + struct TestNameClass{\ + void reg_tests(){\ + size_t index = 0;\ + using expander = size_t[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = typename convert::type;\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }}\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + void TestName::test() #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, TmplList ) diff --git a/third_party/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp b/third_party/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp index fbca89f90..52c167325 100644 --- a/third_party/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp @@ -28,7 +28,7 @@ namespace Catch { std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); - class TestRegistry : public ITestCaseRegistry { + class TestRegistry final : public ITestCaseRegistry { public: void registerTest( Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker ); diff --git a/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.cpp b/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.cpp index 1470b91c6..05bfff8e1 100644 --- a/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.cpp @@ -8,8 +8,9 @@ #include #include -#include #include +#include +#include #include #include @@ -27,6 +28,17 @@ namespace TestCaseTracking { location( _location ) {} + ITracker::ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ): + m_nameAndLocation( CATCH_MOVE( nameAndLoc ) ), m_parent( parent ) { + if ( m_parent ) { + m_allTrackerDepth = m_parent->m_allTrackerDepth + 1; + // We leave section trackers to bump themselves up, as + // we cannot use `isSectionTracker` in constructor + m_sectionOnlyDepth = m_parent->m_sectionOnlyDepth; + m_filterRef = m_parent->m_filterRef; + m_newStyleFilters = m_parent->m_newStyleFilters; + } + } ITracker::~ITracker() = default; @@ -159,25 +171,32 @@ namespace TestCaseTracking { : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) { - if( parent ) { - while ( !parent->isSectionTracker() ) { - parent = parent->parent(); - } - - SectionTracker& parentSection = static_cast( *parent ); - addNextFilters( parentSection.m_filters ); + if( m_parent ) { + ++m_sectionOnlyDepth; } } bool SectionTracker::isComplete() const { - bool complete = true; - - if (m_filters.empty() - || m_filters[0].empty() - || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { - complete = TrackerBase::isComplete(); + // If there are active filters AND we do not pass them, + // the section is always "completed" + const size_t filterIndex = + m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth; + if ( filterIndex < m_filterRef->size() ) { + // There is active filter, check it + // 1) New style filter must explicitly target section + if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type != + PathFilter::For::Section ) { + return true; + } + // 2) Both style filters must match the trimmed name exactly + if ( m_trimmed_name != + StringRef( ( *m_filterRef )[filterIndex].filter ) ) { + return true; + } } - return complete; + + // Otherwise we delegate to the generic processing + return TrackerBase::isComplete(); } bool SectionTracker::isSectionTracker() const { return true; } @@ -213,19 +232,6 @@ namespace TestCaseTracking { open(); } - void SectionTracker::addInitialFilters( std::vector const& filters ) { - if( !filters.empty() ) { - m_filters.reserve( m_filters.size() + filters.size() + 2 ); - m_filters.emplace_back(StringRef{}); // Root - should never be consulted - m_filters.emplace_back(StringRef{}); // Test Case - not a section filter - m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); - } - } - void SectionTracker::addNextFilters( std::vector const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); - } - StringRef SectionTracker::trimmedName() const { return m_trimmed_name; } diff --git a/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.hpp b/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.hpp index 50278c910..48700e991 100644 --- a/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_test_case_tracker.hpp @@ -8,6 +8,7 @@ #ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED +#include #include #include #include @@ -16,6 +17,9 @@ #include namespace Catch { + +struct PathFilter; + namespace TestCaseTracking { struct NameAndLocation { @@ -48,7 +52,7 @@ namespace TestCaseTracking { StringRef name; SourceLineInfo location; - constexpr NameAndLocationRef( StringRef name_, + constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND, SourceLineInfo location_ ): name( name_ ), location( location_ ) {} @@ -91,12 +95,23 @@ namespace TestCaseTracking { Children m_children; CycleState m_runState = NotStarted; - public: - ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ): - m_nameAndLocation( CATCH_MOVE(nameAndLoc) ), - m_parent( parent ) - {} + // Members for path filtering + std::vector const* m_filterRef = nullptr; + + // Note: There are 2 dummy section trackers (root, test-case) before + // the first "real" section tracker can be encountered. We start + // the default tracker at -2, so that the first "real" section + // tracker overflows to index 0. + // Nesting depth of this tracker, used to decide which new-style filter applies. + size_t m_allTrackerDepth = static_cast( -2 ); + // Nesting depth of sections (inc. this tracker), used for old-style filters. + // Must be updated by the section tracker on its own. + size_t m_sectionOnlyDepth = static_cast( -2 ); + // Transitory: Remove once we remove backwards compatibility with old-style (v3.x) filters + bool m_newStyleFilters = false; + public: + ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ); // static queries NameAndLocation const& nameAndLocation() const { @@ -122,6 +137,11 @@ namespace TestCaseTracking { //! Returns true iff tracker has started bool hasStarted() const; + void setFilters( std::vector const* filters, bool newStyleFilters ) { + m_filterRef = filters; + m_newStyleFilters = newStyleFilters; + } + // actions virtual void close() = 0; // Successfully complete virtual void fail() = 0; @@ -207,8 +227,7 @@ namespace TestCaseTracking { void moveToThis(); }; - class SectionTracker : public TrackerBase { - std::vector m_filters; + class SectionTracker final : public TrackerBase { // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`. // Currently it allocates owns the name, so this is safe. If it is later refactored // to not own the name, the name still has to outlive the `ITracker` parent, so @@ -225,10 +244,6 @@ namespace TestCaseTracking { void tryOpen(); - void addInitialFilters( std::vector const& filters ); - void addNextFilters( std::vector const& filters ); - //! Returns filters active in this tracker - std::vector const& getFilters() const { return m_filters; } //! Returns whitespace-trimmed name of the tracked section StringRef trimmedName() const; }; diff --git a/third_party/Catch2/src/catch2/internal/catch_test_failure_exception.cpp b/third_party/Catch2/src/catch2/internal/catch_test_failure_exception.cpp index 8ea313131..8c34278ff 100644 --- a/third_party/Catch2/src/catch2/internal/catch_test_failure_exception.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_test_failure_exception.cpp @@ -14,7 +14,7 @@ namespace Catch { void throw_test_failure_exception() { #if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw TestFailureException{}; + throw TestFailureException{}; //NOLINT(bugprone-std-exception-baseclass) #else CATCH_ERROR( "Test failure requires aborting test!" ); #endif @@ -22,7 +22,7 @@ namespace Catch { void throw_test_skip_exception() { #if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); + throw Catch::TestSkipException(); //NOLINT(bugprone-std-exception-baseclass) #else CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); #endif diff --git a/third_party/Catch2/src/catch2/internal/catch_test_macro_impl.hpp b/third_party/Catch2/src/catch2/internal/catch_test_macro_impl.hpp index ccd5bb357..f38ec6066 100644 --- a/third_party/Catch2/src/catch2/internal/catch_test_macro_impl.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_test_macro_impl.hpp @@ -11,10 +11,16 @@ #include #include #include -#include #include #include +namespace Catch { + namespace Detail { + // Defined in catch_run_context.cpp, where the thread-local data lives. + bool lastAssertionPassed(); + } +} + // 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 @@ -57,12 +63,12 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( Catch::getResultCapture().lastAssertionPassed() ) + if( Catch::Detail::lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( !Catch::getResultCapture().lastAssertionPassed() ) + if( !Catch::Detail::lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ diff --git a/third_party/Catch2/src/catch2/internal/catch_test_registry.hpp b/third_party/Catch2/src/catch2/internal/catch_test_registry.hpp index 5c3a226d6..213b2bdcb 100644 --- a/third_party/Catch2/src/catch2/internal/catch_test_registry.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_test_registry.hpp @@ -29,7 +29,7 @@ namespace Catch { template -class TestInvokerAsMethod : public ITestInvoker { +class TestInvokerAsMethod final : public ITestInvoker { void (C::*m_testAsMethod)(); public: constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept: @@ -49,7 +49,7 @@ Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { } template -class TestInvokerFixture : public ITestInvoker { +class TestInvokerFixture final : public ITestInvoker { void ( C::*m_testAsMethod )() const; Detail::unique_ptr m_fixture = nullptr; @@ -124,7 +124,7 @@ struct AutoReg : Detail::NonCopyable { namespace Catch { namespace Detail { struct DummyUse { - DummyUse( void ( * )( int ), Catch::NameAndTags const& ); + DummyUse( void ( * )( int ), Catch::NameAndTags const& ) noexcept; }; } // namespace Detail } // namespace Catch diff --git a/third_party/Catch2/src/catch2/internal/catch_textflow.cpp b/third_party/Catch2/src/catch2/internal/catch_textflow.cpp index 1c21d20e5..0646b10f9 100644 --- a/third_party/Catch2/src/catch2/internal/catch_textflow.cpp +++ b/third_party/Catch2/src/catch2/internal/catch_textflow.cpp @@ -26,6 +26,10 @@ namespace { return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; } + bool isUtf8ContinuationByte( char c ) { + return ( static_cast( c ) & 0xC0 ) == 0x80; + } + } // namespace namespace Catch { @@ -52,6 +56,11 @@ namespace Catch { if ( it != m_string.end() ) { ++m_size; ++it; + // Skip UTF-8 continuation bytes + while ( it != m_string.end() && + isUtf8ContinuationByte( *it ) ) { + ++it; + } } } } @@ -114,6 +123,11 @@ namespace Catch { void AnsiSkippingString::const_iterator::advance() { assert( m_it != m_string->end() ); m_it++; + // Skip UTF-8 continuation bytes + while ( m_it != m_string->end() && + isUtf8ContinuationByte( *m_it ) ) { + m_it++; + } tryParseAnsiEscapes(); } @@ -133,6 +147,11 @@ namespace Catch { assert( *m_it == '\033' ); m_it--; } + // Skip back over UTF-8 continuation bytes to the leading byte + while ( m_it != m_string->begin() && + isUtf8ContinuationByte( *m_it ) ) { + m_it--; + } } static bool isBoundary( AnsiSkippingString const& line, diff --git a/third_party/Catch2/src/catch2/internal/catch_thread_local.hpp b/third_party/Catch2/src/catch2/internal/catch_thread_local.hpp new file mode 100644 index 000000000..83213a44c --- /dev/null +++ b/third_party/Catch2/src/catch2/internal/catch_thread_local.hpp @@ -0,0 +1,19 @@ + +// 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_THREAD_LOCAL_HPP_INCLUDED +#define CATCH_THREAD_LOCAL_HPP_INCLUDED + +#include + +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) +#define CATCH_INTERNAL_THREAD_LOCAL thread_local +#else +#define CATCH_INTERNAL_THREAD_LOCAL +#endif + +#endif // CATCH_THREAD_LOCAL_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/internal/catch_thread_support.hpp b/third_party/Catch2/src/catch2/internal/catch_thread_support.hpp index c15980b7e..5d76ea320 100644 --- a/third_party/Catch2/src/catch2/internal/catch_thread_support.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_thread_support.hpp @@ -10,7 +10,7 @@ #include -#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) # include # include #endif @@ -19,14 +19,14 @@ namespace Catch { namespace Detail { -#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) using Mutex = std::mutex; using LockGuard = std::lock_guard; struct AtomicCounts { - std::atomic passed = 0; - std::atomic failed = 0; - std::atomic failedButOk = 0; - std::atomic skipped = 0; + std::atomic passed{ 0 }; + std::atomic failed{ 0 }; + std::atomic failedButOk{ 0 }; + std::atomic skipped{ 0 }; }; #else // ^^ Use actual mutex, lock and atomics // vv Dummy implementations for single-thread performance diff --git a/third_party/Catch2/src/catch2/internal/catch_unique_name.hpp b/third_party/Catch2/src/catch2/internal/catch_unique_name.hpp index c6e1c2caa..b8c08fa23 100644 --- a/third_party/Catch2/src/catch2/internal/catch_unique_name.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_unique_name.hpp @@ -8,9 +8,22 @@ #ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED #define CATCH_UNIQUE_NAME_HPP_INCLUDED +#include #include + +// Fixme: Clang 22 has an annoying bug where the localized suppression +// below does not actually suppress the extension warning from +// using __COUNTER__, so we have to leak the suppression for the +// whole TU. Hopefully Clang 23 fixes this before full release. +// As AppleClang does its own thing version-wise, we ignore it +// completely. +#if defined( __clang__ ) && ( __clang_major__ >= 22 ) && !defined( __APPLE__ ) +CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS +#endif + #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 diff --git a/third_party/Catch2/src/catch2/internal/catch_xmlwriter.hpp b/third_party/Catch2/src/catch2/internal/catch_xmlwriter.hpp index 22b42c5c8..0b549cd77 100644 --- a/third_party/Catch2/src/catch2/internal/catch_xmlwriter.hpp +++ b/third_party/Catch2/src/catch2/internal/catch_xmlwriter.hpp @@ -8,6 +8,7 @@ #ifndef CATCH_XMLWRITER_HPP_INCLUDED #define CATCH_XMLWRITER_HPP_INCLUDED +#include #include #include @@ -43,7 +44,7 @@ namespace Catch { public: enum ForWhat { ForTextNodes, ForAttributes }; - constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ): + constexpr XmlEncode( StringRef str CATCH_ATTR_LIFETIMEBOUND, ForWhat forWhat = ForTextNodes ): m_str( str ), m_forWhat( forWhat ) {} @@ -61,7 +62,7 @@ namespace Catch { class ScopedElement { public: - ScopedElement( XmlWriter* writer, XmlFormatting fmt ); + ScopedElement( XmlWriter* writer CATCH_ATTR_LIFETIMEBOUND, XmlFormatting fmt ); ScopedElement( ScopedElement&& other ) noexcept; ScopedElement& operator=( ScopedElement&& other ) noexcept; @@ -93,7 +94,7 @@ namespace Catch { XmlFormatting m_fmt; }; - XmlWriter( std::ostream& os ); + XmlWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); ~XmlWriter(); XmlWriter( XmlWriter const& ) = delete; diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers.cpp b/third_party/Catch2/src/catch2/matchers/catch_matchers.cpp index 123b30413..d1add581c 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers.cpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers.cpp @@ -13,13 +13,13 @@ namespace Catch { namespace Matchers { std::string MatcherUntypedBase::toString() const { - if (m_cachedToString.empty()) { - m_cachedToString = describe(); - } - return m_cachedToString; + return describe(); } - MatcherUntypedBase::~MatcherUntypedBase() = default; + std::string MatcherUntypedBase::describe() const { + using namespace std::string_literals; + return "Undescribed matcher"s; + } } // namespace Matchers } // namespace Catch diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers.hpp b/third_party/Catch2/src/catch2/matchers/catch_matchers.hpp index 3d996c39f..6d30c7f04 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers.hpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -19,10 +20,10 @@ namespace Matchers { class MatcherUntypedBase { public: - MatcherUntypedBase() = default; + constexpr MatcherUntypedBase() = default; - MatcherUntypedBase(MatcherUntypedBase const&) = default; - MatcherUntypedBase(MatcherUntypedBase&&) = default; + constexpr MatcherUntypedBase(MatcherUntypedBase const&) = default; + constexpr MatcherUntypedBase(MatcherUntypedBase&&) = default; MatcherUntypedBase& operator = (MatcherUntypedBase const&) = delete; MatcherUntypedBase& operator = (MatcherUntypedBase&&) = delete; @@ -30,9 +31,9 @@ namespace Matchers { std::string toString() const; protected: - virtual ~MatcherUntypedBase(); // = default; - virtual std::string describe() const = 0; - mutable std::string m_cachedToString; + CATCH_DESTRUCTOR_CONSTEXPR virtual ~MatcherUntypedBase() = default; + //! Should be overridden, but we provide default "undescribed" impl + virtual std::string describe() const; }; @@ -79,11 +80,15 @@ namespace Matchers { return description; } - friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase const& rhs) { + friend MatchAllOf operator&&( MatchAllOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAllOf operator&& (MatcherBase const& lhs, MatchAllOf&& rhs) { + friend MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -131,11 +136,15 @@ namespace Matchers { return description; } - friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase const& rhs) { + friend MatchAnyOf operator||( MatchAnyOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAnyOf operator|| (MatcherBase const& lhs, MatchAnyOf&& rhs) { + friend MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -155,7 +164,8 @@ namespace Matchers { MatcherBase const& m_underlyingMatcher; public: - explicit MatchNotOf( MatcherBase const& underlyingMatcher ): + explicit MatchNotOf( MatcherBase const& underlyingMatcher + CATCH_ATTR_LIFETIMEBOUND ): m_underlyingMatcher( underlyingMatcher ) {} @@ -171,16 +181,22 @@ namespace Matchers { } // namespace Detail template - Detail::MatchAllOf operator&& (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAllOf{} && lhs && rhs; } + template - Detail::MatchAnyOf operator|| (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAnyOf{} || lhs || rhs; } template - Detail::MatchNotOf operator! (MatcherBase const& matcher) { + Detail::MatchNotOf + operator!( MatcherBase const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOf{ matcher }; } @@ -199,6 +215,19 @@ namespace Matchers { #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + CATCH_SUCCEED( #matcher ".match( " #arg " )" ) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + CATCH_SUCCEED( #matcher ".match( " #arg " )" ) + #else + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) CATCH_REQUIRE_THAT( arg, matcher ) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) CATCH_CHECK_THAT( arg, matcher ) + #endif + + #elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) @@ -210,6 +239,9 @@ namespace Matchers { #define CATCH_CHECK_THAT( arg, matcher ) (void)(0) #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) (void)(0) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) (void)(0) + #elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) @@ -221,6 +253,19 @@ namespace Matchers { #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define STATIC_REQUIRE_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + SUCCEED( #matcher ".match( " #arg " )" ) + #define STATIC_CHECK_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + SUCCEED( #matcher ".match( " #arg " )" ) + #else + #define STATIC_REQUIRE_THAT( arg, matcher ) REQUIRE_THAT( arg, matcher ) + #define STATIC_CHECK_THAT( arg, matcher ) CHECK_THAT( arg, matcher ) + #endif + + #elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) @@ -232,6 +277,9 @@ namespace Matchers { #define CHECK_THAT( arg, matcher ) (void)(0) #define REQUIRE_THAT( arg, matcher ) (void)(0) + #define STATIC_REQUIRE_THAT( arg, matcher ) (void)(0) + #define STATIC_CHECK_THAT( arg, matcher ) (void)(0) + #endif // end of user facing macro declarations #endif // CATCH_MATCHERS_HPP_INCLUDED diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.cpp b/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.cpp index f0c535bcb..6297221e3 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.cpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.cpp @@ -22,13 +22,5 @@ namespace Matchers { return sstr.str(); } - IsEmptyMatcher IsEmpty() { - return {}; - } - - HasSizeMatcher SizeIs(std::size_t sz) { - return HasSizeMatcher{ sz }; - } - } // end namespace Matchers } // end namespace Catch diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp b/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp index 33795fbed..552bbab13 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp @@ -18,7 +18,7 @@ namespace Catch { class IsEmptyMatcher final : public MatcherGenericBase { public: template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { #if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS) using Catch::Detail::empty; #else @@ -33,12 +33,12 @@ namespace Catch { class HasSizeMatcher final : public MatcherGenericBase { std::size_t m_target_size; public: - explicit HasSizeMatcher(std::size_t target_size): + constexpr explicit HasSizeMatcher(std::size_t target_size): m_target_size(target_size) {} template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { #if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS) using Catch::Detail::size; #else @@ -54,12 +54,12 @@ namespace Catch { class SizeMatchesMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - explicit SizeMatchesMatcher(Matcher m): + constexpr explicit SizeMatchesMatcher(Matcher m): m_matcher(CATCH_MOVE(m)) {} template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { #if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS) using Catch::Detail::size; #else @@ -75,11 +75,13 @@ namespace Catch { //! Creates a matcher that accepts empty ranges/containers - IsEmptyMatcher IsEmpty(); + inline CATCH_DESTRUCTOR_CONSTEXPR IsEmptyMatcher IsEmpty() { return {}; } //! Creates a matcher that accepts ranges/containers with specific size - HasSizeMatcher SizeIs(std::size_t sz); + inline CATCH_DESTRUCTOR_CONSTEXPR HasSizeMatcher SizeIs( std::size_t sz ) { + return HasSizeMatcher{ sz }; + } template - std::enable_if_t, + constexpr std::enable_if_t, SizeMatchesMatcher> SizeIs(Matcher&& m) { return SizeMatchesMatcher{CATCH_FORWARD(m)}; } diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers_contains.hpp b/third_party/Catch2/src/catch2/matchers/catch_matchers_contains.hpp index 90cfe641e..851680309 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers_contains.hpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers_contains.hpp @@ -23,7 +23,7 @@ namespace Catch { Equality m_eq; public: template - ContainsElementMatcher(T2&& target, Equality2&& predicate): + constexpr ContainsElementMatcher(T2&& target, Equality2&& predicate): m_desired(CATCH_FORWARD(target)), m_eq(CATCH_FORWARD(predicate)) {} @@ -33,7 +33,7 @@ namespace Catch { } template - bool match( RangeLike&& rng ) const { + constexpr bool match( RangeLike&& rng ) const { for ( auto&& elem : rng ) { if ( m_eq( elem, m_desired ) ) { return true; } } @@ -49,12 +49,12 @@ namespace Catch { // Note that we do a copy+move to avoid having to SFINAE this // constructor (and also avoid some perfect forwarding failure // cases) - ContainsMatcherMatcher(Matcher matcher): + constexpr ContainsMatcherMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (m_matcher.match(elem)) { return true; @@ -74,14 +74,14 @@ namespace Catch { * Uses `std::equal_to` to do the comparison */ template - std::enable_if_t, + constexpr std::enable_if_t, ContainsElementMatcher>> Contains(T&& elem) { return { CATCH_FORWARD(elem), std::equal_to<>{} }; } //! Creates a matcher that checks whether a range contains element matching a matcher template - std::enable_if_t, + constexpr std::enable_if_t, ContainsMatcherMatcher> Contains(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } @@ -92,7 +92,7 @@ namespace Catch { * Uses `eq` to do the comparisons, the element is provided on the rhs */ template - ContainsElementMatcher Contains(T&& elem, Equality&& eq) { + constexpr ContainsElementMatcher Contains(T&& elem, Equality&& eq) { return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) }; } diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.cpp b/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.cpp index 5a7524672..aeb1cc405 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.cpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.cpp @@ -9,16 +9,12 @@ namespace Catch { namespace Matchers { - std::string AllTrueMatcher::describe() const { return "contains only true"; } - AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } + std::string AllTrueMatcher::describe() const { return "contains only true"; } std::string NoneTrueMatcher::describe() const { return "contains no true"; } - NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } - std::string AnyTrueMatcher::describe() const { return "contains at least one true"; } - AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } } // namespace Matchers } // namespace Catch diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp b/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp index 977b0c762..566846488 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp @@ -18,7 +18,7 @@ namespace Catch { class AllMatchMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - AllMatchMatcher(Matcher matcher): + constexpr AllMatchMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} @@ -27,7 +27,7 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (!m_matcher.match(elem)) { return false; @@ -42,7 +42,7 @@ namespace Catch { class NoneMatchMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - NoneMatchMatcher(Matcher matcher): + constexpr NoneMatchMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} @@ -51,7 +51,7 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (m_matcher.match(elem)) { return false; @@ -66,7 +66,7 @@ namespace Catch { class AnyMatchMatcher final : public MatcherGenericBase { Matcher m_matcher; public: - AnyMatchMatcher(Matcher matcher): + constexpr AnyMatchMatcher(Matcher matcher): m_matcher(CATCH_MOVE(matcher)) {} @@ -75,7 +75,7 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (m_matcher.match(elem)) { return true; @@ -91,7 +91,7 @@ namespace Catch { std::string describe() const override; template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (!elem) { return false; @@ -107,7 +107,7 @@ namespace Catch { std::string describe() const override; template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (elem) { return false; @@ -123,7 +123,7 @@ namespace Catch { std::string describe() const override; template - bool match(RangeLike&& rng) const { + constexpr bool match(RangeLike&& rng) const { for (auto&& elem : rng) { if (elem) { return true; @@ -133,32 +133,35 @@ namespace Catch { } }; - // Creates a matcher that checks whether all elements in a range match a matcher + //! Creates a matcher that checks whether all elements in a range match a matcher template - AllMatchMatcher AllMatch(Matcher&& matcher) { + constexpr AllMatchMatcher AllMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } - // Creates a matcher that checks whether no element in a range matches a matcher. + //! Creates a matcher that checks whether no element in a range matches a matcher. template - NoneMatchMatcher NoneMatch(Matcher&& matcher) { + constexpr NoneMatchMatcher NoneMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } - // Creates a matcher that checks whether any element in a range matches a matcher. + //! Creates a matcher that checks whether any element in a range matches a matcher. template - AnyMatchMatcher AnyMatch(Matcher&& matcher) { + constexpr AnyMatchMatcher AnyMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } - // Creates a matcher that checks whether all elements in a range are true - AllTrueMatcher AllTrue(); + //! Creates a matcher that checks whether all elements in a range are true + inline CATCH_DESTRUCTOR_CONSTEXPR + AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } - // Creates a matcher that checks whether no element in a range is true - NoneTrueMatcher NoneTrue(); + //! Creates a matcher that checks whether no element in a range is true + inline CATCH_DESTRUCTOR_CONSTEXPR + NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } - // Creates a matcher that checks whether any element in a range is true - AnyTrueMatcher AnyTrue(); + //! Creates a matcher that checks whether any element in a range is true + inline CATCH_DESTRUCTOR_CONSTEXPR + AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } } } diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.cpp b/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.cpp index 2fc529d2b..e755b479d 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.cpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.cpp @@ -9,7 +9,11 @@ namespace Catch { namespace Matchers { - MatcherGenericBase::~MatcherGenericBase() = default; + + std::string MatcherGenericBase::describe() const { + using namespace std::string_literals; + return "Undescribed generic matcher"s; + } namespace Detail { diff --git a/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.hpp b/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.hpp index fc9fcb2bd..2c6247f86 100644 --- a/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.hpp +++ b/third_party/Catch2/src/catch2/matchers/catch_matchers_templated.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -21,12 +22,12 @@ namespace Catch { namespace Matchers { class MatcherGenericBase : public MatcherUntypedBase { + std::string describe() const override; public: - MatcherGenericBase() = default; - ~MatcherGenericBase() override; // = default; + constexpr MatcherGenericBase() = default; - MatcherGenericBase(MatcherGenericBase const&) = default; - MatcherGenericBase(MatcherGenericBase&&) = default; + constexpr MatcherGenericBase(MatcherGenericBase const&) = default; + constexpr MatcherGenericBase(MatcherGenericBase&&) = default; MatcherGenericBase& operator=(MatcherGenericBase const&) = delete; MatcherGenericBase& operator=(MatcherGenericBase&&) = delete; @@ -35,7 +36,9 @@ namespace Matchers { namespace Detail { template - std::array array_cat(std::array && lhs, std::array && rhs) { + constexpr std::array + array_cat( std::array&& lhs, + std::array&& rhs ) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); std::copy_n(rhs.begin(), M, arr.begin() + N); @@ -43,7 +46,8 @@ namespace Matchers { } template - std::array array_cat(std::array && lhs, void const* rhs) { + constexpr std::array + array_cat( std::array&& lhs, void const* rhs ) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); arr[N] = rhs; @@ -51,7 +55,8 @@ namespace Matchers { } template - std::array array_cat(void const* lhs, std::array && rhs) { + constexpr std::array + array_cat( void const* lhs, std::array&& rhs ) { std::array arr{ {lhs} }; std::copy_n(rhs.begin(), N, arr.begin() + 1); return arr; @@ -74,23 +79,31 @@ namespace Matchers { template - bool match_all_of(Arg&&, std::array const&, std::index_sequence<>) { + constexpr bool match_all_of( Arg&&, + std::array const&, + std::index_sequence<> ) { return true; } template - bool match_all_of(Arg&& arg, std::array const& matchers, std::index_sequence) { + constexpr bool match_all_of( Arg&& arg, + std::array const& matchers, + std::index_sequence ) { return static_cast(matchers[Idx])->match(arg) && match_all_of(arg, matchers, std::index_sequence{}); } template - bool match_any_of(Arg&&, std::array const&, std::index_sequence<>) { + constexpr bool match_any_of( Arg&&, + std::array const&, + std::index_sequence<> ) { return false; } template - bool match_any_of(Arg&& arg, std::array const& matchers, std::index_sequence) { + constexpr bool match_any_of( Arg&& arg, + std::array const& matchers, + std::index_sequence ) { return static_cast(matchers[Idx])->match(arg) || match_any_of(arg, matchers, std::index_sequence{}); } @@ -111,14 +124,18 @@ namespace Matchers { public: MatchAllOfGeneric(MatchAllOfGeneric const&) = delete; MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete; - MatchAllOfGeneric(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; + constexpr MatchAllOfGeneric( MatchAllOfGeneric&& ) = default; + constexpr MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} - explicit MatchAllOfGeneric(std::array matchers) : m_matchers{matchers} {} + constexpr MatchAllOfGeneric( + MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) + : m_matchers{ {std::addressof(matchers)...} } {} + constexpr explicit MatchAllOfGeneric( + std::array matchers ): + m_matchers{ matchers } {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return match_all_of(arg, m_matchers, std::index_sequence_for{}); } @@ -134,28 +151,30 @@ namespace Matchers { //! Avoids type nesting for `GenericAllOf && GenericAllOf` case template - friend + constexpr friend MatchAllOfGeneric operator && ( - MatchAllOfGeneric&& lhs, - MatchAllOfGeneric&& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAllOf && some matcher` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAllOfGeneric> operator && ( - MatchAllOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(&rhs))}; } //! Avoids type nesting for `some matcher && GenericAllOf` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAllOfGeneric> operator && ( - MatcherLHS const& lhs, - MatchAllOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -166,14 +185,18 @@ namespace Matchers { public: MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete; - MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; + constexpr MatchAnyOfGeneric( MatchAnyOfGeneric&& ) = default; + constexpr MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} - explicit MatchAnyOfGeneric(std::array matchers) : m_matchers{matchers} {} + constexpr MatchAnyOfGeneric( + MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) + : m_matchers{ {std::addressof(matchers)...} } {} + constexpr explicit MatchAnyOfGeneric( + std::array matchers ): + m_matchers{ matchers } {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return match_any_of(arg, m_matchers, std::index_sequence_for{}); } @@ -189,27 +212,30 @@ namespace Matchers { //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case template - friend MatchAnyOfGeneric operator || ( - MatchAnyOfGeneric&& lhs, - MatchAnyOfGeneric&& rhs) { + constexpr friend MatchAnyOfGeneric + operator||( + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } //! Avoids type nesting for `GenericAnyOf || some matcher` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAnyOfGeneric> operator || ( - MatchAnyOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(std::addressof(rhs)))}; } //! Avoids type nesting for `some matcher || GenericAnyOf` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAnyOfGeneric> operator || ( - MatcherLHS const& lhs, - MatchAnyOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND) { return MatchAnyOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -222,13 +248,15 @@ namespace Matchers { public: MatchNotOfGeneric(MatchNotOfGeneric const&) = delete; MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete; - MatchNotOfGeneric(MatchNotOfGeneric&&) = default; - MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; + constexpr MatchNotOfGeneric( MatchNotOfGeneric&& ) = default; + constexpr MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; - explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {} + constexpr explicit MatchNotOfGeneric( + MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) + : m_matcher{matcher} {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return !m_matcher.match(arg); } @@ -237,7 +265,9 @@ namespace Matchers { } //! Negating negation can just unwrap and return underlying matcher - friend MatcherT const& operator ! (MatchNotOfGeneric const& matcher) { + constexpr friend MatcherT const& + operator!( MatchNotOfGeneric const& matcher + CATCH_ATTR_LIFETIMEBOUND ) { return matcher.m_matcher; } }; @@ -246,21 +276,28 @@ namespace Matchers { // compose only generic matchers template - std::enable_if_t, Detail::MatchAllOfGeneric> - operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { + constexpr std::enable_if_t< + Detail::are_generic_matchers_v, + Detail::MatchAllOfGeneric> + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template - std::enable_if_t, Detail::MatchAnyOfGeneric> - operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { + constexpr std::enable_if_t< + Detail::are_generic_matchers_v, + Detail::MatchAnyOfGeneric> + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } //! Wrap provided generic matcher in generic negator template - std::enable_if_t, Detail::MatchNotOfGeneric> - operator ! (MatcherT const& matcher) { + constexpr std::enable_if_t, + Detail::MatchNotOfGeneric> + operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOfGeneric{matcher}; } @@ -268,25 +305,29 @@ namespace Matchers { // compose mixed generic and non-generic matchers template std::enable_if_t, Detail::MatchAllOfGeneric>> - operator && (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAllOfGeneric, MatcherRHS>> - operator && (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric>> - operator || (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric, MatcherRHS>> - operator || (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } diff --git a/third_party/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp b/third_party/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp index 24a3f8b69..1aa503853 100644 --- a/third_party/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp +++ b/third_party/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp @@ -29,7 +29,7 @@ namespace Catch { #endif template - class MatchExpr : public ITransientExpression { + class MatchExpr final : public ITransientExpression { ArgT && m_arg; MatcherT const& m_matcher; public: diff --git a/third_party/Catch2/src/catch2/meson.build b/third_party/Catch2/src/catch2/meson.build index 60f7777a9..94e0496af 100644 --- a/third_party/Catch2/src/catch2/meson.build +++ b/third_party/Catch2/src/catch2/meson.build @@ -61,10 +61,10 @@ internal_headers = [ 'generators/catch_generators_all.hpp', 'generators/catch_generators_random.hpp', 'generators/catch_generators_range.hpp', + 'generators/catch_generators_throw.hpp', 'interfaces/catch_interfaces_all.hpp', 'interfaces/catch_interfaces_capture.hpp', 'interfaces/catch_interfaces_config.hpp', - 'interfaces/catch_interfaces_enum_values_registry.hpp', 'interfaces/catch_interfaces_exception.hpp', 'interfaces/catch_interfaces_generatortracker.hpp', 'interfaces/catch_interfaces_registry_hub.hpp', @@ -94,7 +94,7 @@ internal_headers = [ 'internal/catch_decomposer.hpp', 'internal/catch_deprecation_macro.hpp', 'internal/catch_enforce.hpp', - 'internal/catch_enum_values_registry.hpp', + 'internal/catch_enum_info.hpp', 'internal/catch_errno_guard.hpp', 'internal/catch_exception_translator_registry.hpp', 'internal/catch_fatal_condition_handler.hpp', @@ -105,6 +105,7 @@ internal_headers = [ 'internal/catch_jsonwriter.hpp', 'internal/catch_lazy_expr.hpp', 'internal/catch_leak_detector.hpp', + 'internal/catch_lifetimebound.hpp', 'internal/catch_list.hpp', 'internal/catch_logical_traits.hpp', 'internal/catch_message_info.hpp', @@ -114,6 +115,7 @@ internal_headers = [ 'internal/catch_optional.hpp', 'internal/catch_output_redirect.hpp', 'internal/catch_parse_numbers.hpp', + 'internal/catch_path_filter.hpp', 'internal/catch_platform.hpp', 'internal/catch_polyfills.hpp', 'internal/catch_preprocessor.hpp', @@ -147,6 +149,7 @@ internal_headers = [ 'internal/catch_test_registry.hpp', 'internal/catch_test_spec_parser.hpp', 'internal/catch_textflow.hpp', + 'internal/catch_thread_local.hpp', 'internal/catch_thread_support.hpp', 'internal/catch_to_string.hpp', 'internal/catch_uncaught_exceptions.hpp', @@ -201,6 +204,7 @@ internal_sources = files( 'generators/catch_generator_exception.cpp', 'generators/catch_generators.cpp', 'generators/catch_generators_random.cpp', + 'generators/catch_generators_throw.cpp', 'interfaces/catch_interfaces_capture.cpp', 'interfaces/catch_interfaces_config.cpp', 'interfaces/catch_interfaces_exception.cpp', @@ -219,7 +223,7 @@ internal_sources = files( 'internal/catch_debugger.cpp', 'internal/catch_decomposer.cpp', 'internal/catch_enforce.cpp', - 'internal/catch_enum_values_registry.cpp', + 'internal/catch_enum_info.cpp', 'internal/catch_errno_guard.cpp', 'internal/catch_exception_translator_registry.cpp', 'internal/catch_fatal_condition_handler.cpp', diff --git a/third_party/Catch2/src/catch2/reporters/catch_reporter_common_base.cpp b/third_party/Catch2/src/catch2/reporters/catch_reporter_common_base.cpp index a1ca76a0f..0a3671e12 100644 --- a/third_party/Catch2/src/catch2/reporters/catch_reporter_common_base.cpp +++ b/third_party/Catch2/src/catch2/reporters/catch_reporter_common_base.cpp @@ -26,12 +26,12 @@ namespace Catch { void ReporterBase::listReporters( std::vector const& descriptions ) { - defaultListReporters(m_stream, descriptions, m_config->verbosity()); + defaultListReporters( m_stream, descriptions, m_config->verbosity() ); } void ReporterBase::listListeners( std::vector const& descriptions ) { - defaultListListeners( m_stream, descriptions ); + defaultListListeners( m_stream, descriptions, m_config->verbosity() ); } void ReporterBase::listTests(std::vector const& tests) { @@ -43,7 +43,7 @@ namespace Catch { } void ReporterBase::listTags(std::vector const& tags) { - defaultListTags( m_stream, tags, m_config->hasTestFilters() ); + defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_config->verbosity() ); } } // namespace Catch diff --git a/third_party/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp b/third_party/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp index 09169632b..dc5ae4b05 100644 --- a/third_party/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp +++ b/third_party/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp @@ -20,9 +20,8 @@ namespace Catch { bool operator()( Detail::unique_ptr const& node ) const { - return ( - ( node->stats.sectionInfo.name == m_other.name ) && - ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) ); + return node->stats.sectionInfo.name == m_other.name + && node->stats.sectionInfo.lineInfo == m_other.lineInfo; } void operator=( BySectionInfo const& ) = delete; diff --git a/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.cpp b/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.cpp index c710ac43c..0044efb5d 100644 --- a/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.cpp +++ b/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.cpp @@ -143,7 +143,15 @@ namespace Catch { } void defaultListListeners( std::ostream& out, - std::vector const& descriptions ) { + std::vector const& descriptions, + Verbosity verbosity ) { + if ( verbosity == Verbosity::Quiet ) { + for ( auto const& desc : descriptions ) { + out << desc.name << '\n'; + } + return; + } + out << "Registered listeners:\n"; if(descriptions.empty()) { @@ -176,7 +184,14 @@ namespace Catch { void defaultListTags( std::ostream& out, std::vector const& tags, - bool isFiltered ) { + bool isFiltered, + Verbosity verbosity ) { + if (verbosity == Verbosity::Quiet) { + for (auto const& tagCount : tags) { + out << tagCount.all() << '\n'; + } + return; + } if ( isFiltered ) { out << "Tags for matching test cases:\n"; } else { @@ -184,7 +199,7 @@ namespace Catch { } // minimum whitespace to pad tag counts, possibly overwritten below - size_t maxTagCountLen = 2; + int maxTagCountLen = 2; // determine necessary padding for tag count column if ( ! tags.empty() ) { @@ -195,11 +210,11 @@ namespace Catch { return lhs.count < rhs.count; } ) ->count; - + // more padding necessary for 3+ digits if (maxTagCount >= 100) { auto numDigits = 1 + std::floor( std::log10( maxTagCount ) ); - maxTagCountLen = static_cast( numDigits ); + maxTagCountLen = static_cast( numDigits ); } } diff --git a/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.hpp b/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.hpp index 316cb4048..469888884 100644 --- a/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.hpp +++ b/third_party/Catch2/src/catch2/reporters/catch_reporter_helpers.hpp @@ -55,7 +55,8 @@ namespace Catch { * format */ void defaultListListeners( std::ostream& out, - std::vector const& descriptions ); + std::vector const& descriptions, + Verbosity verbosity ); /** * Lists tag information to the provided stream in user-friendly format @@ -64,7 +65,10 @@ namespace Catch { * bases. The output should be backwards compatible with the output of * Catch2 v2 binaries. */ - void defaultListTags( std::ostream& out, std::vector const& tags, bool isFiltered ); + void defaultListTags( std::ostream& out, + std::vector const& tags, + bool isFiltered, + Verbosity verbosity ); /** * Lists test case information to the provided stream in user-friendly diff --git a/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.cpp b/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.cpp index df34f17bb..00b458761 100644 --- a/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.cpp +++ b/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.cpp @@ -241,70 +241,77 @@ namespace Catch { void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { if (assertionOrBenchmark.isAssertion()) { - writeAssertion(assertionOrBenchmark.asAssertion()); + // JUnit XML format supports only 1 error/failure/skip + // assertion elements per test case + if (writeAssertion(assertionOrBenchmark.asAssertion())) { + break; + } } } } - void JunitReporter::writeAssertion( AssertionStats const& stats ) { + bool JunitReporter::writeAssertion( AssertionStats const& stats ) { AssertionResult const& result = stats.assertionResult; - if ( !result.isOk() || - result.getResultType() == ResultWas::ExplicitSkip ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - case ResultWas::ExpressionFailed: - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - case ResultWas::ExplicitSkip: - elementName = "skipped"; - break; - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } + if ( result.isOk() && + result.getResultType() != ResultWas::ExplicitSkip ) { + return false; + } + std::string elementName; + switch ( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + case ResultWas::ExplicitSkip: + elementName = "skipped"; + break; + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - xml.writeAttribute( "message"_sr, result.getExpression() ); - xml.writeAttribute( "type"_sr, result.getTestMacroName() ); + xml.writeAttribute( "message"_sr, result.getExpression() ); + xml.writeAttribute( "type"_sr, result.getTestMacroName() ); - ReusableStringStream rss; - if ( result.getResultType() == ResultWas::ExplicitSkip ) { - rss << "SKIPPED\n"; - } else { - rss << "FAILED" << ":\n"; - if (result.hasExpression()) { - rss << " "; - rss << result.getExpressionInMacro(); - rss << '\n'; - } - if (result.hasExpandedExpression()) { - rss << "with expansion:\n"; - rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n'; - } + ReusableStringStream rss; + if ( result.getResultType() == ResultWas::ExplicitSkip ) { + rss << "SKIPPED\n"; + } else { + rss << "FAILED:\n"; + if ( result.hasExpression() ) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; } + if ( result.hasExpandedExpression() ) { + rss << "with expansion:\n"; + rss << TextFlow::Column( result.getExpandedExpression() ) + .indent( 2 ) + << '\n'; + } + } - if( result.hasMessage() ) - rss << result.getMessage() << '\n'; - for( auto const& msg : stats.infoMessages ) - if( msg.type == ResultWas::Info ) - rss << msg.message << '\n'; - - rss << "at " << result.getSourceInfo(); - xml.writeText( rss.str(), XmlFormatting::Newline ); + if ( result.hasMessage() ) { rss << result.getMessage() << '\n'; } + for ( auto const& msg : stats.infoMessages ) { + if ( msg.type == ResultWas::Info ) { rss << msg.message << '\n'; } } + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), XmlFormatting::Newline ); + return true; } } // end namespace Catch diff --git a/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.hpp b/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.hpp index 7cb53c25b..a265f78a8 100644 --- a/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.hpp +++ b/third_party/Catch2/src/catch2/reporters/catch_reporter_junit.hpp @@ -41,7 +41,7 @@ namespace Catch { bool testOkToFail ); void writeAssertions(SectionNode const& sectionNode); - void writeAssertion(AssertionStats const& stats); + bool writeAssertion(AssertionStats const& stats); XmlWriter xml; Timer suiteTimer; diff --git a/third_party/Catch2/tools/scripts/approvalTests.py b/third_party/Catch2/tools/scripts/approvalTests.py old mode 100755 new mode 100644 index 144edc065..72ff26caf --- a/third_party/Catch2/tools/scripts/approvalTests.py +++ b/third_party/Catch2/tools/scripts/approvalTests.py @@ -56,7 +56,7 @@ def get_unapprovedResultsPath(baseName): ''', re.VERBOSE) lineNumberParser = re.compile(r' line="[0-9]*"') hexParser = re.compile(r'\b(0[xX][0-9a-fA-F]+)\b') -# Note: junit must serialize time with 3 (or or less) decimal places +# Note: junit must serialize time with 3 (or less) decimal places # before generalizing this parser, make sure that this is checked # in other places too. junitDurationsParser = re.compile(r' time="[0-9]+\.[0-9]{3}"') diff --git a/third_party/Catch2/tools/scripts/approve.py b/third_party/Catch2/tools/scripts/approve.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/buildAndTest.sh b/third_party/Catch2/tools/scripts/buildAndTest.sh old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/checkConvenienceHeaders.py b/third_party/Catch2/tools/scripts/checkConvenienceHeaders.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/checkDuplicateFilenames.py b/third_party/Catch2/tools/scripts/checkDuplicateFilenames.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/checkLicense.py b/third_party/Catch2/tools/scripts/checkLicense.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/developBuild.py b/third_party/Catch2/tools/scripts/developBuild.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/fixWhitespace.py b/third_party/Catch2/tools/scripts/fixWhitespace.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/generateAmalgamatedFiles.py b/third_party/Catch2/tools/scripts/generateAmalgamatedFiles.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/majorRelease.py b/third_party/Catch2/tools/scripts/majorRelease.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/minorRelease.py b/third_party/Catch2/tools/scripts/minorRelease.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/patchRelease.py b/third_party/Catch2/tools/scripts/patchRelease.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/updateDocumentSnippets.py b/third_party/Catch2/tools/scripts/updateDocumentSnippets.py old mode 100755 new mode 100644 diff --git a/third_party/Catch2/tools/scripts/updateDocumentToC.py b/third_party/Catch2/tools/scripts/updateDocumentToC.py old mode 100755 new mode 100644 From efc7888ac1f69b9d801ff5e69016c0f4d221a13c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 05:10:45 +0000 Subject: [PATCH 3/3] Restore executable bit on vendored Catch2 scripts Lost during the raw-content download used to bump Catch2 to 3.15.1. --- third_party/Catch2/CMake/llvm-cov-wrapper | 0 third_party/Catch2/conanfile.py | 0 third_party/Catch2/fuzzing/build_fuzzers.sh | 0 third_party/Catch2/tools/scripts/approvalTests.py | 0 third_party/Catch2/tools/scripts/approve.py | 0 third_party/Catch2/tools/scripts/buildAndTest.sh | 0 third_party/Catch2/tools/scripts/checkConvenienceHeaders.py | 0 third_party/Catch2/tools/scripts/checkDuplicateFilenames.py | 0 third_party/Catch2/tools/scripts/checkLicense.py | 0 third_party/Catch2/tools/scripts/developBuild.py | 0 third_party/Catch2/tools/scripts/fixWhitespace.py | 0 third_party/Catch2/tools/scripts/generateAmalgamatedFiles.py | 0 third_party/Catch2/tools/scripts/majorRelease.py | 0 third_party/Catch2/tools/scripts/minorRelease.py | 0 third_party/Catch2/tools/scripts/patchRelease.py | 0 third_party/Catch2/tools/scripts/updateDocumentSnippets.py | 0 third_party/Catch2/tools/scripts/updateDocumentToC.py | 0 17 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 third_party/Catch2/CMake/llvm-cov-wrapper mode change 100644 => 100755 third_party/Catch2/conanfile.py mode change 100644 => 100755 third_party/Catch2/fuzzing/build_fuzzers.sh mode change 100644 => 100755 third_party/Catch2/tools/scripts/approvalTests.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/approve.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/buildAndTest.sh mode change 100644 => 100755 third_party/Catch2/tools/scripts/checkConvenienceHeaders.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/checkDuplicateFilenames.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/checkLicense.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/developBuild.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/fixWhitespace.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/generateAmalgamatedFiles.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/majorRelease.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/minorRelease.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/patchRelease.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/updateDocumentSnippets.py mode change 100644 => 100755 third_party/Catch2/tools/scripts/updateDocumentToC.py diff --git a/third_party/Catch2/CMake/llvm-cov-wrapper b/third_party/Catch2/CMake/llvm-cov-wrapper old mode 100644 new mode 100755 diff --git a/third_party/Catch2/conanfile.py b/third_party/Catch2/conanfile.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/fuzzing/build_fuzzers.sh b/third_party/Catch2/fuzzing/build_fuzzers.sh old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/approvalTests.py b/third_party/Catch2/tools/scripts/approvalTests.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/approve.py b/third_party/Catch2/tools/scripts/approve.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/buildAndTest.sh b/third_party/Catch2/tools/scripts/buildAndTest.sh old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/checkConvenienceHeaders.py b/third_party/Catch2/tools/scripts/checkConvenienceHeaders.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/checkDuplicateFilenames.py b/third_party/Catch2/tools/scripts/checkDuplicateFilenames.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/checkLicense.py b/third_party/Catch2/tools/scripts/checkLicense.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/developBuild.py b/third_party/Catch2/tools/scripts/developBuild.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/fixWhitespace.py b/third_party/Catch2/tools/scripts/fixWhitespace.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/generateAmalgamatedFiles.py b/third_party/Catch2/tools/scripts/generateAmalgamatedFiles.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/majorRelease.py b/third_party/Catch2/tools/scripts/majorRelease.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/minorRelease.py b/third_party/Catch2/tools/scripts/minorRelease.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/patchRelease.py b/third_party/Catch2/tools/scripts/patchRelease.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/updateDocumentSnippets.py b/third_party/Catch2/tools/scripts/updateDocumentSnippets.py old mode 100644 new mode 100755 diff --git a/third_party/Catch2/tools/scripts/updateDocumentToC.py b/third_party/Catch2/tools/scripts/updateDocumentToC.py old mode 100644 new mode 100755