Compare commits

..
4 Commits
Author SHA1 Message Date
Chris Thrasher 8efee9b763 Use std::string_view 2025-09-22 11:50:44 -06:00
Chris Thrasher a5735af2f7 Use std::optional 2025-09-22 11:35:23 -06:00
Chris Thrasher db46dc4cb2 Upgrade to C++17 2025-09-22 11:35:22 -06:00
Chris Thrasher c6cefc6596 Disable reproducible build flags for dev builds 2025-09-21 21:43:51 -06:00
315 changed files with 5244 additions and 18729 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
---
Language: Cpp
Standard: c++14
Standard: c++17
# Note that we cannot use IncludeIsMainRegex functionality, because it
# does not support includes in angle brackets (<>)
+2 -6
View File
@@ -28,14 +28,12 @@ Checks: >-
-modernize-concat-nested-namespaces,
-modernize-use-nodiscard,
-modernize-use-default-member-init,
-modernize-type-traits,# we need to support C++14,
-modernize-deprecated-headers,
,# There's a lot of these and most of them are probably not useful,
-modernize-pass-by-value,
-modernize-use-string-view, # We support C++14,
performance-*,
performance-enum-size,
-performance-enum-size,
portability-*,
@@ -59,8 +57,6 @@ Checks: >-
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-readability-avoid-return-with-void-value,
,# We prefer if defined(FOO) form because it is easier to extend later,
-readability-use-concise-preprocessor-directives,
,# time hogs,
-bugprone-throw-keyword-missing,
@@ -81,5 +77,5 @@ WarningsAsErrors: >-
readability-duplicate-include,
HeaderFilterRegex: '.*\.(c|cxx|cpp)$'
FormatStyle: none
CheckOptions: []
CheckOptions: {}
...
+2 -6
View File
@@ -2,10 +2,6 @@ name: Linux Builds (Bazel)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build_and_test_ubuntu:
name: Linux Ubuntu 22.04 Bazel build <GCC 11.2.0>
@@ -16,10 +12,10 @@ jobs:
compilation_mode: [fastbuild, dbg, opt]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Mount Bazel cache
uses: actions/cache@v5
uses: actions/cache@v3
with:
path: "/home/runner/.cache/bazel"
key: bazel-ubuntu22-gcc11
+3 -8
View File
@@ -2,13 +2,9 @@ name: Linux Builds (Meson)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: meson ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}}
name: meson ${{matrix.cxx}}, ${{matrix.build_type}}
runs-on: ubuntu-22.04
strategy:
fail-fast: false
@@ -17,13 +13,12 @@ jobs:
- g++-11
- clang++-11
build_type: [debug, release]
std: [14, 17]
include:
- cxx: clang++-11
other_pkgs: clang-11
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Prepare environment
run: |
@@ -33,7 +28,7 @@ jobs:
- name: Configure
env:
CXX: ${{matrix.cxx}}
CXXFLAGS: -std=c++${{matrix.std}} ${{matrix.cxxflags}}
CXXFLAGS: -std=c++17 ${{matrix.cxxflags}}
# Note: $GITHUB_WORKSPACE is distinct from ${{runner.workspace}}.
# This is important
run: |
+4 -24
View File
@@ -5,13 +5,9 @@ name: Linux Builds (Complex)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: ${{matrix.build_description}}, ${{matrix.cxx}}, C++${{matrix.std}} ${{matrix.build_type}}
name: ${{matrix.build_description}}, ${{matrix.cxx}}, ${{matrix.build_type}}
runs-on: ubuntu-22.04
strategy:
fail-fast: false
@@ -24,7 +20,6 @@ jobs:
- cxx: clang++-14
build_description: Surrogates build
build_type: Debug
std: 14
other_pkgs: clang-14
cmake_configurations: -DCATCH_BUILD_SURROGATES=ON
@@ -32,13 +27,11 @@ jobs:
- cxx: g++-11
build_description: Extras + Examples
build_type: Debug
std: 14
other_pkgs: g++-11
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
- cxx: g++-11
build_description: Extras + Examples
build_type: Release
std: 14
other_pkgs: g++-11
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
@@ -46,36 +39,25 @@ jobs:
- cxx: clang++-14
build_description: Extras + Examples
build_type: Debug
std: 17
other_pkgs: clang-14
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
- cxx: clang++-14
build_description: Extras + Examples
build_type: Release
std: 17
other_pkgs: clang-14
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
# Configure tests with Clang-14
- cxx: clang++-14
build_description: CMake configuration tests
build_type: Debug
std: 14
other_pkgs: clang-14
cmake_configurations: -DCATCH_ENABLE_CONFIGURE_TESTS=ON
# Valgrind test Clang-14
# - cxx: clang++-14
# build_description: Valgrind tests
# build_type: Debug
# std: 14
# other_pkgs: clang-14 valgrind
# cmake_configurations: -DMEMORYCHECK_COMMAND=`which valgrind` -DMEMORYCHECK_COMMAND_OPTIONS="-q --track-origins=yes --leak-check=full --num-callers=50 --show-leak-kinds=definite --error-exitcode=1"
# other_ctest_args: -T memcheck -LE uses-python
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Prepare environment
run: |
@@ -87,20 +69,19 @@ jobs:
cmake --preset basic-tests -GNinja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_CXX_COMPILER=${{matrix.cxx}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
${{matrix.cmake_configurations}}
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build -j
run: ctest --test-dir build -j --output-on-failure
clang-tidy:
name: clang-tidy
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Prepare environment
run: |
@@ -120,7 +101,6 @@ jobs:
-DCMAKE_CXX_COMPILER_LAUNCHER=/usr/bin/true \
-DCMAKE_CXX_COMPILER=clang++-15 \
-DCMAKE_CXX_LINK_EXECUTABLE=/usr/bin/true \
-DCMAKE_CXX_STANDARD=17 \
-DCMAKE_RANLIB=/usr/bin/true \
-DCATCH_BUILD_EXAMPLES=ON \
-DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
+3 -19
View File
@@ -2,10 +2,6 @@ name: Linux Builds (Basic)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}}
@@ -18,7 +14,6 @@ jobs:
- g++-10
- g++-11
- g++-12
- clang++-6.0
- clang++-7
- clang++-8
- clang++-9
@@ -28,7 +23,7 @@ jobs:
- clang++-13
- clang++-14
build_type: [Debug, Release]
std: [14]
std: [17]
include:
- cxx: g++-9
other_pkgs: g++-9
@@ -38,8 +33,6 @@ jobs:
other_pkgs: g++-11
- cxx: g++-12
other_pkgs: g++-12
- cxx: clang++-6.0
other_pkgs: clang-6.0
- cxx: clang++-7
other_pkgs: clang-7
- cxx: clang++-8
@@ -56,15 +49,6 @@ jobs:
other_pkgs: clang-13
- cxx: clang++-14
other_pkgs: clang-14
# Clang 14 + C++17
- cxx: clang++-14
build_type: Debug
std: 17
other_pkgs: clang-14
- cxx: clang++-14
build_type: Release
std: 17
other_pkgs: clang-14
- cxx: clang++-14
build_type: Debug
std: 20
@@ -83,7 +67,7 @@ jobs:
other_pkgs: g++-11
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Add repositories for older compilers
run: |
@@ -106,4 +90,4 @@ jobs:
run: cmake --build build
- name: Test
run: ctest --test-dir build -j
run: ctest --test-dir build -j --output-on-failure
+5 -9
View File
@@ -2,28 +2,24 @@ name: Mac Builds
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
# From macos-14 forward, the baseline "macos-X" image is Arm based,
# and not Intel based.
runs-on: ${{matrix.image}}
strategy:
fail-fast: false
matrix:
image: [macos-14, macos-15, macos-15-intel]
image: [macos-13, macos-14, macos-15]
build_type: [Debug, Release]
std: [14, 17]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Configure
run: |
cmake --preset basic-tests -GNinja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
-DCATCH_BUILD_EXAMPLES=ON \
-DCATCH_BUILD_EXTRA_TESTS=ON
@@ -31,4 +27,4 @@ jobs:
run: cmake --build build
- name: Test
run: ctest --test-dir build -j
run: ctest --test-dir build -j --output-on-failure
@@ -1,43 +0,0 @@
name: Mac Sanitizer Builds
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
runs-on: ${{matrix.image}}
strategy:
fail-fast: false
matrix:
image: [macos-15, macos-15-intel]
build_type: [Debug]
std: [17]
sanitizer: [thread, address]
include:
- sanitizer: thread
preset: basic-tests
filter: -R ThreadSafetyTests
- sanitizer: address
preset: most-tests
filter:
steps:
- uses: actions/checkout@v6
- name: Configure
run: |
CFXXFLAGS=-fsanitize=${{matrix.sanitizer}},undefined
cmake --preset ${{matrix.preset}} -GNinja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
-DCATCH_BUILD_EXTRA_TESTS=ON
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build ${{matrix.filter}}
@@ -18,7 +18,7 @@ jobs:
profile_generate: 'false'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install conan
run: pip install conan==${{matrix.conan_version}}
+2 -2
View File
@@ -9,10 +9,10 @@ jobs:
steps:
- name: Checkout source code
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup Dependencies
uses: actions/setup-python@v6
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Install checkguard
+3 -9
View File
@@ -2,13 +2,9 @@ name: Windows Builds (Basic)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: ${{matrix.os}}, ${{matrix.std}}, ${{matrix.build_type}}, ${{matrix.platform}}
name: ${{matrix.os}}, ${{matrix.build_type}}, ${{matrix.platform}}
runs-on: ${{matrix.os}}
strategy:
fail-fast: false
@@ -16,20 +12,18 @@ jobs:
os: [windows-2022, windows-2025]
platform: [Win32, x64]
build_type: [Debug, Release]
std: [14, 17]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Configure build
run: |
cmake --preset all-tests `
-A ${{matrix.platform}} `
-DCMAKE_CXX_STANDARD=${{matrix.std}} `
- name: Build tests
run: cmake --build build --config ${{matrix.build_type}} --parallel %NUMBER_OF_PROCESSORS%
shell: cmd
- name: Run tests
run: ctest --test-dir build -C ${{matrix.build_type}} -j %NUMBER_OF_PROCESSORS%
run: ctest --test-dir build -C ${{matrix.build_type}} -j %NUMBER_OF_PROCESSORS% --output-on-failure
shell: cmd
+2 -12
View File
@@ -27,11 +27,6 @@ expand_template(
"#cmakedefine CATCH_CONFIG_COLOUR_WIN32": "",
"#cmakedefine CATCH_CONFIG_COUNTER": "",
"#cmakedefine CATCH_CONFIG_CPP11_TO_STRING": "",
"#cmakedefine CATCH_CONFIG_CPP17_BYTE": "",
"#cmakedefine CATCH_CONFIG_CPP17_OPTIONAL": "",
"#cmakedefine CATCH_CONFIG_CPP17_STRING_VIEW": "",
"#cmakedefine CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS": "",
"#cmakedefine CATCH_CONFIG_CPP17_VARIANT": "",
"#cmakedefine CATCH_CONFIG_DEPRECATION_ANNOTATIONS": "",
"#cmakedefine CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER": "",
"#cmakedefine CATCH_CONFIG_DISABLE_EXCEPTIONS": "",
@@ -51,11 +46,6 @@ expand_template(
"#cmakedefine CATCH_CONFIG_NO_COLOUR_WIN32": "",
"#cmakedefine CATCH_CONFIG_NO_COUNTER": "",
"#cmakedefine CATCH_CONFIG_NO_CPP11_TO_STRING": "",
"#cmakedefine CATCH_CONFIG_NO_CPP17_BYTE": "",
"#cmakedefine CATCH_CONFIG_NO_CPP17_OPTIONAL": "",
"#cmakedefine CATCH_CONFIG_NO_CPP17_STRING_VIEW": "",
"#cmakedefine CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS": "",
"#cmakedefine CATCH_CONFIG_NO_CPP17_VARIANT": "",
"#cmakedefine CATCH_CONFIG_NO_DEPRECATION_ANNOTATIONS": "",
"#cmakedefine CATCH_CONFIG_NO_GETENV": "",
"#cmakedefine CATCH_CONFIG_NO_GLOBAL_NEXTAFTER": "",
@@ -76,8 +66,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_THREAD_SAFE_ASSERTIONS": "",
"#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS": "",
"#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "",
"#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "",
},
template = "src/catch2/catch_user_config.hpp.in",
)
+1 -6
View File
@@ -31,11 +31,6 @@ set(_OverridableOptions
"COLOUR_WIN32"
"COUNTER"
"CPP11_TO_STRING"
"CPP17_BYTE"
"CPP17_OPTIONAL"
"CPP17_STRING_VIEW"
"CPP17_UNCAUGHT_EXCEPTIONS"
"CPP17_VARIANT"
"GLOBAL_NEXTAFTER"
"POSIX_SIGNALS"
"USE_ASYNC"
@@ -45,7 +40,7 @@ set(_OverridableOptions
"EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT"
"USE_BUILTIN_CONSTANT_P"
"DEPRECATION_ANNOTATIONS"
"THREAD_SAFE_ASSERTIONS"
"EXPERIMENTAL_THREAD_SAFE_ASSERTIONS"
)
foreach(OptionName ${_OverridableOptions})
+1 -1
View File
@@ -3,7 +3,7 @@ libdir=${prefix}/@lib_dir@
pkg_version=@Catch2_VERSION@
Name: Catch2 with main function
Description: A modern, C++-native test framework for C++14 and above (links in default main)
Description: A modern, C++-native test framework for C++17 and above (links in default main)
URL: https://github.com/catchorg/Catch2
Version: ${pkg_version}
Requires: catch2 = ${pkg_version}
+1 -1
View File
@@ -4,7 +4,7 @@ includedir=${prefix}/@include_dir@
libdir=${prefix}/@lib_dir@
Name: Catch2
Description: A modern, C++-native, test framework for C++14 and above
Description: A modern, C++-native, test framework for C++17 and above
URL: https://github.com/catchorg/Catch2
Version: @Catch2_VERSION@
Cflags: -I${includedir}
+1 -7
View File
@@ -20,7 +20,6 @@ cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATC
cmake_dependent_option(CATCH_BUILD_EXAMPLES "Build code examples" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_FUZZERS "Build fuzzers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_BENCHMARKS "Build the benchmarks" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
@@ -35,7 +34,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
VERSION 3.16.0 # CML version placeholder, don't delete
VERSION 3.10.0 # CML version placeholder, don't delete
LANGUAGES CXX
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
DESCRIPTION "A modern, C++-native, unit test framework."
@@ -78,11 +77,6 @@ set(SELF_TEST_DIR ${CATCH_DIR}/tests/SelfTest)
# We need to bring-in the variables defined there to this scope
add_subdirectory(src)
if (CATCH_BUILD_BENCHMARKS)
set(CMAKE_FOLDER "benchmarks")
add_subdirectory(benchmarks)
endif()
# Build tests only if requested
if(BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
+6 -14
View File
@@ -11,27 +11,19 @@
"CMAKE_CXX_EXTENSIONS": "OFF",
"CMAKE_CXX_STANDARD_REQUIRED": "ON",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CATCH_DEVELOPMENT_BUILD": "ON"
}
},
{
"name": "most-tests",
"inherits": "basic-tests",
"displayName": "Full development build",
"description": "Enables development build with extended set of tests (still relatively cheap to build)",
"cacheVariables": {
"CATCH_BUILD_EXAMPLES": "ON",
"CATCH_BUILD_EXTRA_TESTS": "ON",
"CATCH_BUILD_SURROGATES": "ON",
"CATCH_BUILD_BENCHMARKS": "ON"
"CATCH_DEVELOPMENT_BUILD": "ON",
"CATCH_ENABLE_REPRODUCIBLE_BUILD": "OFF"
}
},
{
"name": "all-tests",
"inherits": "most-tests",
"inherits": "basic-tests",
"displayName": "Full development build",
"description": "Enables development build with examples and ALL tests",
"cacheVariables": {
"CATCH_BUILD_EXAMPLES": "ON",
"CATCH_BUILD_EXTRA_TESTS": "ON",
"CATCH_BUILD_SURROGATES": "ON",
"CATCH_ENABLE_CONFIGURE_TESTS": "ON",
"CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON"
}
+266 -432
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,5 +1,5 @@
module(name = "catch2")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "rules_cc", version = "0.2.16")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "rules_license", version = "1.0.0")
-21
View File
@@ -1,21 +0,0 @@
include(CatchMiscFunctions)
add_executable(AssertionsFastPath
runtime_assertion_benches.cpp
)
add_executable(AssertionsSlowPath
runtime_assertion_benches.cpp
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 EmptyExecutable)
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
-30
View File
@@ -1,30 +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
#include <catch2/reporters/catch_reporter_event_listener.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>
/**
* Event listener that listens to all assertions, forcing assertion slow path
*/
namespace {
class AssertionSlowPathListener : public Catch::EventListenerBase {
public:
static std::string getDescription() {
return "Validates ordering of Catch2's listener events";
}
AssertionSlowPathListener(Catch::IConfig const* config) :
EventListenerBase(config) {
m_preferences.shouldReportAllAssertions = true;
m_preferences.shouldReportAllAssertionStarts = true;
}
};
CATCH_REGISTER_LISTENER( AssertionSlowPathListener )
}
@@ -1,206 +0,0 @@
#!/usr/bin/env python3
# Copyright Catch2 Authors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
import argparse
import copy
import json
import os
import re
import statistics
import subprocess
import sys
import tempfile
import time
HERE = os.path.dirname(os.path.abspath(__file__))
TEMPLATE = os.path.join(HERE, "listing_template.json")
SHIM = os.path.join(HERE, "copy_shim.cmake")
COUNTS = [1, 10, 500, 1000, 2000, 4000, 8000, 16000]
#COUNTS = [1, 10, 100]
REPEATS = 5
def load_template(path: str) -> dict:
with open(path, encoding="utf-8") as f:
catch_out = json.load(f)
if 'listings' not in catch_out or 'tests' not in catch_out['listings']:
sys.exit(f"Template '{path}' does not contain the expected 'listings.tests' data")
return catch_out
def synthesize_listing(template_doc: dict, count: int, file, with_tags: bool) -> str:
"""Writes JSON listing with exactly `count` test cases into `file`.
The template entries are cycled through, and each name is made unique by
appending an index, so that every registered CTest test has a distinct name.
"""
# To avoid messing up the template tests by deletion, we have
# to take a deepcopy before the changes.
doc_copy = copy.deepcopy(template_doc)
template_tests = doc_copy['listings']['tests']
if not with_tags:
for test in template_tests:
del test['tags']
num_original = len(template_tests)
out_tests = []
for i in range(count):
entry = template_tests[i % num_original].copy()
entry["name"] = f"{entry['name']} #{i // num_original}"
out_tests.append(entry)
doc_copy['listings']['tests'] = out_tests
# indent=2 matches Catch2's pretty printing
json.dump(doc_copy, file, indent=2)
file.write('\n')
def build_command(args, listing_path: str, ctest_file: str, add_tags: bool) -> list[str]:
# The executor needs to be a CMake list, so it has to be separated by ';'
executor = ";".join(['cmake', f"-DBENCH_LISTING={listing_path}", "-P", SHIM, "--"])
return [
'cmake',
"-DTEST_TARGET=benchmark",
f"-DTEST_EXECUTABLE={listing_path}",
f"-DTEST_EXECUTOR={executor}",
f"-DTEST_WORKING_DIR={os.path.dirname(ctest_file)}",
f"-DCTEST_FILE={ctest_file}",
f"-DADD_TAGS_AS_LABELS={'TRUE' if add_tags else 'FALSE'}",
"-P",
args.script,
]
def run_once(cmd: list[str]) -> float:
start = time.monotonic()
result = subprocess.run(cmd, capture_output=True, text=True)
elapsed = time.monotonic() - start
if result.returncode != 0:
sys.stderr.write("Benchmark invocation failed:\n")
sys.stderr.write(f" cmd: {' '.join(cmd)}\n")
sys.stderr.write(f" stdout: {result.stdout}\n")
sys.stderr.write(f" stderr: {result.stderr}\n")
sys.exit(1)
return elapsed
def verify_output(ctest_file: str, count: int, add_tags: bool):
"""Sanity-check the generated CTest script and return its add_test count."""
with open(ctest_file, encoding="utf-8") as f:
content = f.read()
add_test_lines = sum(1 for line in content.splitlines() if line.startswith("add_test("))
if add_test_lines != count:
sys.exit(
f"Sanity check failed: expected {count} add_test() lines, "
f"found {add_test_lines} in {ctest_file}"
)
if add_tags and count > 0 and "LABELS" not in content:
sys.exit(
f"Sanity check failed: ADD_TAGS_AS_LABELS was on but no LABELS found "
f"in {ctest_file}"
)
def cmake_version() -> str:
stdout = subprocess.run(
['cmake', "--version"], capture_output=True, text=True, check=True
).stdout
match = re.match("cmake version (.+)", stdout)
return match.group(1)
def print_table(counts, tag_modes, results):
timing_by_ct = {(count, tags): median_time for (count, tags, median_time) in results}
headers = ["N"]
if False in tag_modes:
headers.append("tags:off")
if True in tag_modes:
headers.append("tags:on")
def fmt_cell(count, tags_on):
median_time = timing_by_ct[(count, tags_on)]
return f"{median_time:8.1f} ms"
# Header
row = f"{headers[0]:>6}"
if False in tag_modes:
row += f" {headers[1]:>14}"
if True in tag_modes:
row += f" {headers[2]:>14}"
print()
print("-" * len(row))
print(row)
print("-" * len(row))
for count in counts:
row = f"{count:>6}"
if False in tag_modes:
row += f" {fmt_cell(count, False):>14}"
if True in tag_modes:
row += f" {fmt_cell(count, True):>14}"
print(row)
def main():
parser = argparse.ArgumentParser(
description="Benchmark the CMake-side cost of catch_discover_tests."
)
parser.add_argument(
"--script",
required=True,
help="Path to the CatchAddTests.cmake to benchmark.",
)
args = parser.parse_args()
if not os.path.exists(args.script):
sys.exit(f"Script to benchmark '{args.script}' does not exist")
if not os.path.exists(SHIM):
sys.exit(f"Copy-shim '{SHIM}' does not exist")
tag_modes = [False, True]
template_doc = load_template(TEMPLATE)
print(f"Benchmarking: {os.path.abspath(args.script)}")
print(f"CMake version: {cmake_version()}")
print(f"Ns: {COUNTS}")
print(f"Repeats: {REPEATS}")
print()
results = [] # (count, tags_on, median_ms)
with tempfile.TemporaryDirectory() as workdir:
os.makedirs(workdir, exist_ok=True)
for count in COUNTS:
listing_path = os.path.join(workdir, f"listing-{count}.json")
for add_tags in tag_modes:
with open(listing_path, "w", encoding="utf-8") as f:
synthesize_listing(template_doc, count, f, add_tags)
mode = "on" if add_tags else "off"
ctest_file = os.path.join(workdir, f"ctest-{count}-{mode}.cmake")
cmd = build_command(args, listing_path, ctest_file, add_tags)
# Untimed warmup run (populates any filesystem/OS caches).
run_once(cmd)
verify_output(ctest_file, count, add_tags)
timings = [run_once(cmd) for _ in range(REPEATS)]
median_ms = statistics.median(timings) * 1000.0
results.append((count, add_tags, median_ms))
print(f'N = {count} done in ~{len(tag_modes) * sum(timings):.2f} s')
print_table(COUNTS, tag_modes, results)
if __name__ == "__main__":
main()
-40
View File
@@ -1,40 +0,0 @@
# SPDX-License-Identifier: BSL-1.0
# Fake Catch2 binary used by benchmark_discovery.py.
#
# catch_discover_tests_impl discovers tests by running
#
# ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ... --list-tests --reporter json --out <file>
#
# and then reading the JSON listing back from <file>. This script fakes
# being Catch2 binary by taking the fake JSON listing prepared by
# `benchmark_discovery.py` and copying it over to the file path specified
# after the `--out` argument.
#
# The fake JSON listing is passed as BENCH_LISTING CMake argument.
if(NOT DEFINED BENCH_LISTING)
message(FATAL_ERROR "copy_shim: BENCH_LISTING is not set")
endif()
if(NOT EXISTS "${BENCH_LISTING}")
message(FATAL_ERROR "copy_shim: BENCH_LISTING '${BENCH_LISTING}' does not exist")
endif()
# FIXME: better handling of bad --out
# Find the "--out <file>" argument pair among the forwarded arguments.
set(_out_file "")
math(EXPR _last "${CMAKE_ARGC} - 1")
foreach(_i RANGE 0 ${_last})
if("${CMAKE_ARGV${_i}}" STREQUAL "--out")
math(EXPR _next "${_i} + 1")
set(_out_file "${CMAKE_ARGV${_next}}")
break()
endif()
endforeach()
if(_out_file STREQUAL "")
message(FATAL_ERROR "copy_shim: could not find '--out <file>' in arguments")
endif()
file(COPY_FILE "${BENCH_LISTING}" "${_out_file}")
@@ -1,85 +0,0 @@
{
"version": 2,
"metadata": {
"name": "benchmark-template",
"catch2-version": "3.15.2"
},
"listings": {
"tests": [
{
"name": "Comparing function pointers",
"tags": [
"function pointer",
"Tricky"
]
},
{
"name": "Testing checked-if 4",
"tags": [
"!shouldfail",
"checked-if"
]
},
{
"name": "count_equidistant_floats - double",
"tags": [
"approvals",
"distance",
"floating-point"
]
},
{
"name": "Usage of AllTrue range matcher",
"tags": [
"matchers",
"quantifiers",
"templated"
]
},
{
"name": "Exception matchers that succeed",
"tags": [
"!throws",
"exceptions",
"matchers"
]
},
{
"name": "A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - float",
"class-name": "Template_Fixture",
"tags": [
"class",
"template"
]
},
{
"name": "Approximate PI",
"tags": [
"Approx",
"PI"
]
},
{
"name": "TextFlow::Column respects width setting",
"tags": [
"approvals",
"column",
"TextFlow"
]
},
{
"name": "Generators internals",
"tags": [
"generators",
"internals"
]
},
{
"name": "Mayfail test case with nested sections",
"tags": [
"!mayfail"
]
}
]
}
}
-16
View File
@@ -1,16 +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
/**\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 <catch2/catch_test_macros.hpp>
-117
View File
@@ -1,117 +0,0 @@
<a id="top"></a>
# 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)
* 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
## Misc. benchmarks
### `catch_discover_tests`
The first JSON-based implementation of `catch_discover_tests` turned out
to have quadratic complexity in number of tests, which meant that registering
say 1k test cases took ~4s , and it became unusably slow with larger
test suites.
To prevent backsliding, and enable future optimizations, there is now
a benchmarking script for `catch_discover_tests` in the `discover_tests`
directory.
`discover_tests/benchmark_discovery.py` runs the real `catch_discover_tests`
implementation (through CMake script-mode) on a synthesized JSON listing
(generated from `discover_tests/listing_template.json`) through an executor
shim (`discover_tests/copy_shim.cmake`). This means it can run even without
real test binary.
If the JSON output from Catch2 changes, the synthesized JSON listing
can be updated by taking a real listing from `SelfTest` binary, and pruning
it down to keep only ~10 entries.
#### Examples
**Benchmark the current `catch_discover_tests`, with and without tags-as-labels**
```text
./benchmarks/discover_tests/benchmark_discovery.py --script ./extras/CatchAddTests.cmake
```
Note that 8k test cases is unrealistically high, but still useful to see scaling.
Benchmark for single test case is useful to provide estimate of the flat
overhead from using `catch_discover_tests` at all.
-54
View File
@@ -1,54 +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
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_templated.hpp>
namespace {
class MatchAllMatcher final : public Catch::Matchers::MatcherGenericBase {
public:
template <typename Any>
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( "REQUIRE_THAT", "[assertions][matchers]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE_THAT( 1, MatchAll() );
}
}
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
+1 -2
View File
@@ -24,8 +24,7 @@ Once you're up and running consider the following reference material.
* [String Conversions](tostring.md#top)
**Running:**
* [Command line reference](command-line.md#top)
* [Running specific section/generator](filtering-execution-path.md#top)
* [Command line](command-line.md#top)
**Odds and ends:**
* [Frequently Asked Questions (FAQ)](faq.md#top)
+1 -1
View File
@@ -26,7 +26,7 @@ The ```CHECK``` family are equivalent but execution continues in the same test c
Evaluates the expression and records the result. If an exception is thrown, it is caught, reported, and counted as a failure. These are the macros you will use most of the time.
Examples:
```cpp
```
CHECK( str == "string value" );
CHECK( thisReturnsTrue() );
REQUIRE( i == 42 );
-7
View File
@@ -66,24 +66,17 @@ test execution. Specifically it understands
* JUnit output path via `XML_OUTPUT_FILE`
* Test filtering via `TESTBRIDGE_TEST_ONLY`
* Test sharding via `TEST_SHARD_INDEX`, `TEST_TOTAL_SHARDS`, and `TEST_SHARD_STATUS_FILE`
* Creating a file to signal premature test exit via `TEST_PREMATURE_EXIT_FILE`
* Setting the RNG seed via `TEST_RANDOM_SEED`
> Support for `XML_OUTPUT_FILE` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 3.0.1
> Support for `TESTBRIDGE_TEST_ONLY` and sharding was introduced in Catch2 3.2.0
> Support for `TEST_PREMATURE_EXIT_FILE` and `TEST_RANDOM_SEED` was introduced in Catch2 3.11.0
This integration is enabled via either a [compile time configuration
option](configuration.md#bazel-support), or via `BAZEL_TEST` environment
variable set to "1".
> Support for `BAZEL_TEST` was [introduced](https://github.com/catchorg/Catch2/pull/2459) in Catch2 3.1.0
Note that if both the Bazel environment var and command line option for
something are used, the environment variable wins.
## Low-level tools
-11
View File
@@ -212,17 +212,6 @@ execution (useful e.g. in cross-compilation environments).
calling ``catch_discover_tests``. This provides a mechanism for globally
selecting a preferred test discovery behavior.
_Note that on Apple Silicon with the Xcode generator you must use `PRE_TEST`,
e.g. `catch_discover_tests(tests DISCOVERY_MODE PRE_TEST)`. With the default
`POST_BUILD` mode the build fails with `Result: Subprocess killed`, because
macOS on Apple Silicon refuses to run unsigned binaries and Xcode code-signs
the test executable only **after** the post-build script that `POST_BUILD`
mode uses to run it for test discovery. `PRE_TEST` avoids this by delaying
discovery until test time, when the executable is already signed. The same
limitation affects CMake's `gtest_discover_tests`; see
[Catch2 #2411](https://github.com/catchorg/Catch2/issues/2411) and
[CMake #21845](https://gitlab.kitware.com/cmake/cmake/-/issues/21845)._
* `SKIP_IS_FAILURE`
Skipped tests will be marked as failed instead.
+40 -41
View File
@@ -26,13 +26,12 @@
[Disable statistical analysis of collected benchmark samples](#disable-statistical-analysis-of-collected-benchmark-samples)<br>
[Specify the amount of time in milliseconds spent on warming up each test](#specify-the-amount-of-time-in-milliseconds-spent-on-warming-up-each-test)<br>
[Usage](#usage)<br>
[Specify the section/generator element to run](#specify-the-sectiongenerator-element-to-run)<br>
[Specify the section to run](#specify-the-section-to-run)<br>
[Filenames as tags](#filenames-as-tags)<br>
[Override output colouring](#override-output-colouring)<br>
[Test Sharding](#test-sharding)<br>
[Allow running the binary without tests](#allow-running-the-binary-without-tests)<br>
[Output verbosity](#output-verbosity)<br>
[Create file to guard against silent early termination](#create-file-to-guard-against-silent-early-termination)<br>
Catch works quite nicely without any command line options at all - but for those times when you want greater control the following options are available.
Click one of the following links to take you straight to that option - or scroll on to browse the available options.
@@ -52,8 +51,6 @@ Click one of the following links to take you straight to that option - or scroll
<a href="#reporting-timings"> ` -d, --durations`</a><br />
<a href="#input-file"> ` -f, --input-file`</a><br />
<a href="#run-section"> ` -c, --section`</a><br />
<a href="#path-filtering"> ` -g, --generator-index`</a><br />
<a href="#path-filtering"> ` -p, --path-filter`</a><br />
<a href="#filenames-as-tags"> ` -#, --filenames-as-tags`</a><br />
@@ -202,13 +199,8 @@ as many times as you want, e.g. `--reporter xml::out=someFile.xml` or
The keys must either be prefixed by "X", in which case they are not parsed
by Catch2 and are only passed down to the reporter, or one of options
hardcoded into Catch2. Currently there are 3 supported options:
* ["out"](#sending-output-to-a-file)
* ["colour-mode"](#colour-mode)
* ["verbosity"](#output-verbosity)
> Support for per-reporter verbosity option was added in Catch2 3.16.0
hardcoded into Catch2. Currently there are only 2,
["out"](#sending-output-to-a-file), and ["colour-mode"](#colour-mode).
_Note that the reporter might still check the X-prefixed options for
validity, and throw an error if they are wrong._
@@ -294,10 +286,9 @@ as follows:
| Option | `normal` (default) | `quiet` | `high` |
|--------------------|---------------------------------|---------------------|-----------------------------------------|
| `--list-tests` | Test names and tags | Test names only | Same as `normal`, plus source code line |
| `--list-tags` | Tags and counts | Tags only | Same as `normal` |
| `--list-tags` | Tags and counts | Same as `normal` | Same as `normal` |
| `--list-reporters` | Reporter names and descriptions | Reporter names only | Same as `normal` |
| `--list-listeners` | Listener names and descriptions | Listener names only | Same as `normal` |
| `--list-listeners` | Listener names and descriptions | Same as `normal` | Same as `normal` |
<a id="sending-output-to-a-file"></a>
## Sending output to a file
@@ -366,13 +357,10 @@ There are currently two warnings implemented:
// (e.g. `REQUIRE`) is encountered.
UnmatchedTestSpec // Fail test run if any of the CLI test specs did
// not match any tests.
InfiniteGenerators // Fail if GENERATE would run infinitely
```
> `UnmatchedTestSpec` was introduced in Catch2 3.0.1.
> `InfiniteGenerators` was introduced in Catch2 3.13.0
<a id="reporting-timings"></a>
## Reporting timings
@@ -540,19 +528,45 @@ Prints the command line arguments to stdout
<a id="run-section"></a>
<a id="path-filtering"></a>
## Specify the section/generator element to run
## Specify the section to run
<pre>-c, --section &lt;section name&gt;</pre>
<pre>-g, --generator-index &lt;index in generator&gt;</pre>
<pre>-p, --path-filter &lt;path filter spec&gt;</pre>
> The generator and generic path filtering was added in Catch2 3.13.0
To limit execution to a specific section within a test case, use this option one or more times.
To narrow to sub-sections use multiple instances, where each subsequent instance specifies a deeper nesting level.
These arguments allow you to run specific section(s) in a test case, or
only get specific element from a generator. All the variants form a shared
stack of filters.
E.g. if you have:
[See the full documentation of path filtering for more details](filtering-execution-path.md#top)
<pre>
TEST_CASE( "Test" ) {
SECTION( "sa" ) {
SECTION( "sb" ) {
/*...*/
}
SECTION( "sc" ) {
/*...*/
}
}
SECTION( "sd" ) {
/*...*/
}
}
</pre>
Then you can run `sb` with:
<pre>./MyExe Test -c sa -c sb</pre>
Or run just `sd` with:
<pre>./MyExe Test -c sd</pre>
To run all of `sa`, including `sb` and `sc` use:
<pre>./MyExe Test -c sa</pre>
There are some limitations of this feature to be aware of:
- Code outside of sections being skipped will still be executed - e.g. any set-up code in the TEST_CASE before the
start of the first section.</br>
- At time of writing, wildcards are not supported in section names.
- If you specify a section without narrowing to a test case first then all test cases will be executed
(but only matching sections within them).
<a id="filenames-as-tags"></a>
@@ -635,21 +649,6 @@ ignored.
Verbosity defaults to _normal_.
## Create file to guard against silent early termination
<pre>--premature-exit-guard-file &lt;path&gt;</pre>
> Introduced in Catch2 3.11.0
Tells Catch2 to create an empty file at specified path before the tests
start, and delete it after the tests finish. If the file is present after
the process stops, it can be assumed that the testing binary exited
prematurely, e.g. due to the OOM killer.
All directories in the path must already exist. If this option is used
and Catch2 cannot create the file (e.g. the location is not writable),
the test run will fail.
---
[Home](Readme.md#top)
+7 -29
View File
@@ -10,14 +10,13 @@
[Default reporter](#default-reporter)<br>
[Bazel support](#bazel-support)<br>
[C++11 toggles](#c11-toggles)<br>
[C++17 toggles](#c17-toggles)<br>
[Other toggles](#other-toggles)<br>
[Enabling stringification](#enabling-stringification)<br>
[Disabling exceptions](#disabling-exceptions)<br>
[Disabling deprecation warnings](#disabling-deprecation-warnings)<br>
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
[Static analysis support](#static-analysis-support)<br>
[Thread safety in assertions (and messages)](#thread-safety-in-assertions-and-messages)<br>
[Experimental thread safety](#experimental-thread-safety)<br>
Catch2 is designed to "just work" as much as possible, and most of the
configuration options below are changed automatically during compilation,
@@ -29,7 +28,7 @@ with the same name.
## Prefixing Catch macros
CATCH_CONFIG_PREFIX_ALL // Prefix all macros with CATCH_
CATCH_CONFIG_PREFIX_MESSAGES // Prefix only message macros ((UNSCOPED_)INFO, WARN, (UNSCOPED_)CAPTURE)
CATCH_CONFIG_PREFIX_MESSAGES // Prefix only INFO, UNSCOPED_INFO, WARN and CAPTURE
To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```).
@@ -128,23 +127,6 @@ Catch's selection, by defining either `CATCH_CONFIG_CPP11_TO_STRING` or
`CATCH_CONFIG_NO_CPP11_TO_STRING`.
## C++17 toggles
CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS // Override std::uncaught_exceptions (instead of std::uncaught_exception) support detection
CATCH_CONFIG_CPP17_STRING_VIEW // Override std::string_view support detection (Catch provides a StringMaker specialization by default)
CATCH_CONFIG_CPP17_VARIANT // Override std::variant support detection (checked by CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER)
CATCH_CONFIG_CPP17_OPTIONAL // Override std::optional support detection (checked by CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER)
CATCH_CONFIG_CPP17_BYTE // Override std::byte support detection (Catch provides a StringMaker specialization by default)
> `CATCH_CONFIG_CPP17_STRING_VIEW` was [introduced](https://github.com/catchorg/Catch2/issues/1376) in Catch2 2.4.1.
Catch contains basic compiler/standard detection and attempts to use
some C++17 features whenever appropriate. This automatic detection
can be manually overridden in both directions, that is, a feature
can be enabled by defining the macro in the table above, and disabled
by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`.
## Other toggles
CATCH_CONFIG_COUNTER // Use __COUNTER__ to generate unique names for test cases
@@ -221,8 +203,8 @@ By default, Catch does not stringify some types from the standard library. This
CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER // Provide StringMaker specialization for std::pair
CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER // Provide StringMaker specialization for std::tuple
CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER // Provide StringMaker specialization for std::variant, std::monostate (on C++17)
CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Provide StringMaker specialization for std::optional (on C++17)
CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER // Provide StringMaker specialization for std::variant, std::monostate
CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Provide StringMaker specialization for std::optional
CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS // Defines all of the above
> `CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1380) in Catch2 2.4.1.
@@ -316,21 +298,17 @@ no backwards compatibility guarantees._
are not meant to be runnable, only "scannable".
<a id="experimental-thread-safety"></a>
## Thread safety in assertions (and messages)
## Experimental thread safety
> Introduced in Catch2 3.9.0
> Made non-experimental in Catch2 3.12.0
Catch2 can optionally support thread-safe assertions, that means, multiple
user-spawned threads can use the assertion macros at the same time. Due
to the performance cost this imposes even on single-threaded usage, Catch2
defaults to non-thread-safe assertions.
CATCH_CONFIG_THREAD_SAFE_ASSERTIONS // enables thread safe assertions
CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS // force-disables thread safe assertions
CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // enables thread safe assertions
CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // force-disables thread safe assertions
See [the documentation on thread safety in Catch2](thread-safety.md#top)
for details on which macros are safe and other notes.
+1 -22
View File
@@ -7,8 +7,6 @@
[Writing documentation](#writing-documentation)<br>
[Writing code](#writing-code)<br>
[CoC](#coc)<br>
[Using LLMs when contributing](#using-llms-when-contributing)<br>
So you want to contribute something to Catch2? That's great! Whether it's
a bug fix, a new feature, support for additional compilers - or just
@@ -213,16 +211,10 @@ and so on.
### C++ standard version
Catch2 currently targets C++14 as the minimum supported C++ version.
Catch2 currently targets C++17 as the minimum supported C++ version.
Features from higher language versions should be used only sparingly,
when the benefits from using them outweigh the maintenance overhead.
Example of good use of polyfilling features is our use of `conjunction`,
where if available we use `std::conjunction` and otherwise provide our
own implementation. The reason it is good is that the surface area for
maintenance is quite small, and `std::conjunction` can directly use
compiler built-ins, thus providing significant compilation benefits.
Example of bad use of polyfilling features would be to keep around two
sets of metaprogramming in the stringification implementation, once
using C++14 compliant TMP and once using C++17's `if constexpr`. While
@@ -334,19 +326,6 @@ When adding new `CATCH_CONFIG` option, there are multiple places to edit:
This project has a [CoC](../CODE_OF_CONDUCT.md). Please adhere to it
while contributing to Catch2.
## Using LLMs when contributing
I do not care whether you used LLM for your contribution. What I care
about is the quality of the contribution, not whether you made it through
prompting LLM, letting GAs run wild, dictated it into the computer, wrote
it using your nose or used butterflies to flip the correct bits.
The flipside of this is that I am also not going to iterate your LLM for
you. If a PR looks LLM generated and does not pass the muster to be merged
as-is, I am going to close it.
-----------
_This documentation will always be in-progress as new information comes
-26
View File
@@ -48,32 +48,6 @@ If you are mutating the fixture instance from within the test case, and
want to keep doing so in the future, mark the mutated members as `mutable`.
### Section-only filtering with `-c/--section`
> Deprecated in Catch2 3.13.0
Currently, if you use only `-c/--section` parameters to decide which
sections to enter, the filtering ignores generators completely. In the
future, using only `-c/--section` will behave the same way as if you
specified the filters through the new `-p/--path-filter` parameter, which
means that generators are taken into account.
### Generator interfaces
#### Defaulted `UntypedGeneratorBase::isFinite()`
> Deprecated in Catch2 3.13.0
The `UntypedGeneratorBase` currently provides a default implementation
for `isFinite` that always returns `true`. This was done to keep backwards
compatibility with pre-existing generators, as infinite generators can
be diagnosed as errors in some cases.
In the future, all generators will be expected to override `isFinite`.
---
[Home](Readme.md#top)
+1 -1
View File
@@ -10,7 +10,7 @@ in-memory logs if they are not needed (the test case passed).
Unlike reporters, each registered event listener is always active. Event
listeners are always notified before reporter(s).
To write your own event listener, you should derive from `Catch::EventListenerBase`,
To write your own event listener, you should derive from `Catch::TestEventListenerBase`,
as it provides empty stubs for all reporter events, allowing you to
only override events you care for. Afterwards you have to register it
with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows
-222
View File
@@ -1,222 +0,0 @@
<a id="top"></a>
# How to run specific section/generator
> The generator and generic path filtering was added in Catch2 3.13.0
Catch2 supports picking specific path through a test case by filtering
sections and generator indices to run through. This is done by using one
of the three commandline parameters, one or more times.
```
-c, --section <section name>
-g, --generator-index <index in generator>
-p, --path-filter <path filter spec>
```
All the variants form a shared stack of filters, but if you use only
`-c`/`--section` form to specify section filters, you will get the old
behaviour, which does not affect generators at all. If you also use either
`-g`/`--generator-index`, or `-p`/`--path-filter`, you will get the new
behaviour, which can also filter generator elements.
Both the new and old filter behaviours include some potentially surprising
things:
* Code outside of sections being skipped will still be executed. E.g.
any setup code in the TEST_CASE that lives outside of sections.
* Path filters filter the prefix of the path. So if you specify single
filter, it affects only the top level sections/generator, with their
child sections/generators being unfiltered.
* Path filters are independent of test case selection, Catch2 will try
to follow the path filters in all selected test cases. This means
that if you specify path filters without a test case filter, Catch2
will try to apply the path filters inside every registered test case.
## Old behaviour
> The old behaviour was deprecated in Catch2 3.13.0
```
-c, --section <section name>
```
The argument to `-c`/`--section` can be any arbitrary string. When Catch2
is deciding whether to enter a section, it will check its trimmed name
against the appropriate trimmed section filter. If they are the same,
the section can be opened. If not, Catch2 will skip over that section.
### Examples
#### Simple section nesting
Given
```cpp
TEST_CASE( "foo" ) {
REQUIRE( true );
SECTION( "A" ) {
SECTION( "A1" ) { REQUIRE( true ); }
SECTION( "A2" ) { REQUIRE( true ); }
}
SECTION( "B" ) {
SECTION( "B1" ) { REQUIRE( true ); }
SECTION( "B2" ) { REQUIRE( true ); }
}
}
```
* `./tests foo -c A` runs section "A" and both of its subsections,
resulting in 4 assertions.
* `./tests foo -c A -c B` runs section "A", but none of its subsections,
resulting in 1 assertion (the one before "A").
* `./tests foo -c A -c A1` runs section "A" and only the "A1" subsection,
resulting in 2 assertions.
#### Sections with nested generators
Note that old behaviour completely _ignores_ generators. This means both
that they can't be filtered, but also that they aren't taken into account
for the filter depth. In other words, given
```cpp
TEST_CASE( "bar" ) {
REQUIRE( true );
SECTION( "A" ) { REQUIRE( true ); }
SECTION( "B" ) {
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
}
```
* `./tests bar -c A` results in 2 assertions.
* `./tests bar -c B -c i=2` results in 4 assertions, because the whole
generator in section "B" has to be used up, but the dynamic section is
only entered when the generator returns 2 as the value for `i`.
* `./tests bar -c B -c i=4` results in 3 assertions, because the assertion
outside of section is executed every time the test case is entered, and
the generator forces the test case to rerun 3 times before it is used up,
even though the dynamic section will never be entered.
#### Section with sibling generators
For cases where sections have sibling generators, the filtering can get
even more surprising.
```cpp
TEST_CASE( "qux" ) {
REQUIRE( true );
SECTION( "A" ) { REQUIRE( true ); }
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
```
* `./tests qux -c A` results in **4** assertions, because section "A" is
entered once, but the sibling generator has to be exhausted, and
the first assertion is executed once per generator element.
* `./tests qux -c i=2` also results in 4 assertions. Once again,
the generator has to be exhausted and the dynamic section is entered
once.
## New behaviour
> The new behaviour was introduced in Catch2 3.13.0
```
-g, --generator-index <index in generator>
-p, --path-filter <path filter spec>
```
The argument to `-g`/`--generator-index` must be either a non-negative
number, which is interpreted as the index of the desired element from
the generator, or "\*", which allows all elements from the generator.
Providing index outside of the generator is an error.
The argument to `-p`/`--path-filter` must start with either "c:" for
a section filter, or with "g:" for a generator filter. Everything past
the colon is then parsed as either a section filter, or a generator filter.
Note that using `p`/`--path-filter` enables new filtering behaviour, even
if it is only used to add section filters.
There is another important difference between filtering out sections and
generators. A section can be left un-entered, but a generator always has
to be active. For this reason, if generator fails a filter
(e.g. there is a section filter at given depth instead), it has to stop
the execution of the test case. Currently, this is done via `SKIP()`
equivalent, causing the section to be considered skipped.
### Examples
#### Nested generators
```cpp
TEST_CASE( "waldo" ) {
auto i = GENERATE( 1, 10, 100 );
auto j = GENERATE( 2, 20, 200 );
CAPTURE( i, j );
REQUIRE( true );
}
```
* `./tests waldo -g 1` results in 3 assertions, with `i := 10`, because
the second nested generator is unfiltered.
* `./tests waldo -g 1 -g 2` results in 1 assertion, with `i := 10, j := 200`.
* `./tests waldo -g * -g 2` results in 3 assertions, all with `j := 200`.
* `./tests waldo -g 1 -g *` results in 3 assertions, all with `i := 10`.
* `./tests waldo -g 3` results in 1 **failed** assertion, because the first
generator does not have 3rd element.
* `./tests waldo -g * -g 3` results in 3 **failed** assertions, as the
second generator does not have 3rd element, but we have to exhaust the
first generator.
#### Generator with a nested dynamic section
```cpp
TEST_CASE( "grault" ) {
REQUIRE( true );
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
```
* `./tests grault -p g:1` results in 2 assertions, as there is no filter
on the dynamic section.
* `./tests grault -p g:1 -p c:i=2` results in 2 assertions, as the filter
on the dynamic section matches the element given from the generator.
* `./tests grault -p g:1 -p c:i=3` results in 1 assertion, as the generator
is limited to only try `i := 2` and the dynamic section is filtered out.
#### Section with a sibling generator
Because generators have to stop test execution when they don't pass filter,
it is impossible to run only a section with sibling generator without
triggering a test case skip. Consider this test case from an earlier example:
```cpp
TEST_CASE( "qux" ) {
REQUIRE( true );
SECTION( "A" ) { REQUIRE( true ); }
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
```
* `./tests qux -p g:1` results in 2 assertions, as the dynamic section is
entered only once.
* `./tests qux -p g:1 -p c:i=1` results in 1 assertion, as the dynamic
section filter is incompatible with the generator filter.
* `./tests qux -p c:A` results in 2 assertions **and a skipped test case**.
This is because the generator is sibling to section "A", and thus reads
the same section filter. However, it is not a section and as thus cannot
proceed.
* `./tests qux -p c:i=2` results in 1 assertion **and a skipped test case**.
Once again, the first filter in the filter stack is a section filter,
and thus the generator cannot proceed.
Compare this with the old filter behaviour, where `./tests qux -c i=2`
would instead result in 4 assertions, because the generator would go
through all elements.
---
[Home](Readme.md#top)
+2 -65
View File
@@ -1,12 +1,6 @@
<a id="top"></a>
# Data Generators
**Contents**<br>
[Combining `GENERATE` and `SECTION`.](#combining-generate-and-section)<br>
[Provided generators](#provided-generators)<br>
[Generator interface](#generator-interface)<br>
[Other usage examples](#other-usage-examples)<br>
> Introduced in Catch2 2.6.0.
Data generators (also known as _data driven/parametrized test cases_)
@@ -112,7 +106,7 @@ a test case,
* 2 fundamental generators
* `SingleValueGenerator<T>` -- contains only single element
* `FixedValuesGenerator<T>` -- contains multiple elements
* 6 generic generators that modify other generators (defined in `catch2/generators/catch_generators_adapters.hpp`)
* 5 generic generators that modify other generators (defined in `catch2/generators/catch_generators_adapters.hpp`)
* `FilterGenerator<T, Predicate>` -- filters out elements from a generator
for which the predicate returns "false"
* `TakeGenerator<T>` -- takes first `n` elements from a generator
@@ -120,7 +114,6 @@ a test case,
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
on elements from a different generator
* `ChunkGenerator<T>` -- returns chunks (inside `std::vector`) of n elements from a generator
* `ConcatGenerator<T>` -- returns elements from multiple generators as if they were one
* 2 random generators (defined in `catch2/generators/catch_generators_random.hpp`)
* `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
* `RandomFloatGenerator<Float>` -- generates random Floats from range
@@ -132,8 +125,6 @@ a test case,
> `IteratorGenerator<T>` was introduced in Catch2 2.10.0.
> `ConcatGenerator<T>` was introduced in Catch2 3.13.0
The generators also have associated helper functions that infer their
type, making their usage much nicer. These are
@@ -151,7 +142,6 @@ type, making their usage much nicer. These are
* `range(Arithmetic start, Arithmetic end, Arithmetic step)` for `RangeGenerator<Arithmetic>` with a custom step size
* `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator<T>`
* `from_range(Container const&)` for `IteratorGenerator<T>`
* `cat(GeneratorWrapper<T>&&...)` for `ConcatGenerator<T>`
> `chunk()`, `random()` and both `range()` functions were introduced in Catch2 2.7.0.
@@ -159,8 +149,6 @@ type, making their usage much nicer. These are
> `range()` for floating point numbers has been introduced in Catch2 2.11.0
> `cat` has been introduced in Catch2 3.13.0
And can be used as shown in the example below to create a generator
that returns 100 odd random number:
@@ -264,34 +252,9 @@ struct IGenerator : GeneratorUntypedBase {
// Returns user-friendly string showing the current generator element
// Does not have to be overridden, IGenerator provides default implementation
virtual std::string stringifyImpl() const;
/**
* Customization point for `skipToNthElement`
*
* Does not have to be overridden, there is a default implementation.
* Can be overridden for better performance.
*
* If there are not enough elements, shall throw an error.
*
* Going backwards is not supported.
*/
virtual void skipToNthElementImpl( std::size_t n );
/**
* 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 = 0;
};
```
> `skipToNthElementImpl` was added in Catch2 3.13.0
> `isFinite` was added in Catch2 3.13.0
However, to be able to use your custom generator inside `GENERATE`, it
will need to be wrapped inside a `GeneratorWrapper<T>`.
`GeneratorWrapper<T>` is a value wrapper around a
@@ -312,35 +275,9 @@ There are two ways to handle this, depending on whether you want this
to be an error or not.
* If empty generator **is** an error, throw an exception in constructor.
* If empty generator **is not** an error, use the [`SKIP` macro](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
* If empty generator **is not** an error, use the [`SKIP`](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
## Other usage examples
### Adding a reproducer to random tests
If you use generators to generate random inputs for testing, you might
want to combine them with specific inputs, e.g. reproducers for previously
found issues.
Because `GENERATE` accepts multiple values/generators, the basic case is simple:
```cpp
const int input = GENERATE(1, 2, take(10, random(10, 10'000'000)));
```
This will set `input` first to "1", then to "2", and then to 10 random
integers.
But if you process the random inputs further (e.g. via `map`), you can't
rely on `GENERATE`'s support for multiple generators. In that case, you
have to use the `cat` generator combinator.
```cpp
const auto input = GENERATE(
map( foo,
cat( value( 4 ), take( 10, random( 10, 10'000'000 ) ) ) ) );
```
This will set `input` first to `foo(4)`, before transforming the 10 random
integers through `foo`.
---
+11 -22
View File
@@ -26,22 +26,19 @@ started" and "Section B", while the third one will only report "Test case
started" as the extra info.
## Logging outside of current scope
## Logging without local scope
> `UNSCOPED_INFO` was [introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
> `UNSCOPED_CAPTURE` was introduced in Catch2 3.13.0.
`UNSCOPED_INFO` is similar to `INFO` with two key differences:
The `UNSCOPED_X` macros are similar to their plain `X` macro counterparts,
with two key differences:
- The lifetime of an unscoped message is not tied to its own scope.
- Lifetime of an unscoped message is not tied to its own scope.
- An unscoped message can be reported by the first following assertion only, regardless of the result of that assertion.
In other words, the `UNSCOPED_X` macros are useful to add extra information
to the next assertion, e.g. from helper functions or inner scopes.
In other words, lifetime of `UNSCOPED_INFO` is limited by the following assertion (or by the end of test case/section, whichever comes first) whereas lifetime of `INFO` is limited by its own scope.
These differences make this macro useful for reporting information from helper functions or inner scopes. An example:
An example:
```cpp
void print_some_info() {
UNSCOPED_INFO("Info from helper");
@@ -86,16 +83,9 @@ Second info
Second unscoped info
```
Note that unscoped messages are not passed between test cases, even if
there were no assertions between them.
## Streaming macros
Apart from `CAPTURE` (and its close sibling, `UNSCOPED_CAPTURE`), message
macros support gradual streaming of messages and values in the same way
that the standard streams do.
All these macros allow heterogeneous sequences of values to be streaming using the insertion operator (```<<```) in the same way that std::ostream, std::cout, etc support it.
E.g.:
```c++
@@ -109,6 +99,9 @@ These macros come in three forms:
The message is logged to a buffer, but only reported with next assertions that are logged. This allows you to log contextual information in case of failures which is not shown during a successful test run (for the console reporter, without -s). Messages are removed from the buffer at the end of their scope, so may be used, for example, in loops.
_Note that in Catch2 2.x.x `INFO` can be used without a trailing semicolon as there is a trailing semicolon inside macro.
This semicolon will be removed with next major version. It is highly advised to use a trailing semicolon after `INFO` macro._
**UNSCOPED_INFO(** _message expression_ **)**
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
@@ -135,10 +128,6 @@ AS `FAIL`, but does not abort the test
**CAPTURE(** _expression1_, _expression2_, ... **)**
**UNSCOPED_CAPTURE(** _expression1_, _expression2_, ... **)**
> `UNSCOPED_CAPTURE` was introduced in Catch2 3.13.0.
Sometimes you just want to log a value of variable, or expression. For
convenience, we provide the `CAPTURE` macro, that can take a variable,
or an expression, and prints out that variable/expression and its value
-37
View File
@@ -6,7 +6,6 @@
[Built-in matchers](#built-in-matchers)<br>
[Writing custom matchers (old style)](#writing-custom-matchers-old-style)<br>
[Writing custom matchers (new style)](#writing-custom-matchers-new-style)<br>
[Constexpr matchers](#constexpr-matchers)<br>
Matchers, as popularized by the [Hamcrest](https://en.wikipedia.org/wiki/Hamcrest)
framework are an alternative way to write assertions, useful for tests
@@ -472,42 +471,6 @@ and new style matchers arbitrarily.
> `MatcherGenericBase` lives in `catch2/matchers/catch_matchers_templated.hpp`
## Constexpr matchers
> Support for constexpr matchers was introduced in Catch2 3.15.0
When compiled for C++20, the new-style matchers (can) support `constexpr`
matching, albeit not `constexpr` stringification. The matcher combinators
require C++26 (or at least P2738) to be `constexpr` compatible.
This can be used together with the `STATIC_REQUIRE_THAT` macro to write
matcher-based static assertions like this:
```cpp
TEST_CASE("Constexpr support for matchers", "[constexpr][matchers]") {
STATIC_REQUIRE_THAT( 1, MatchAll() );
STATIC_REQUIRE_THAT( 1, MatchAll() || MatchAll() );
STATIC_REQUIRE_THAT( 1, !!MatchAll() );
}
```
### First party constexpr matchers
Some (but not all) of Catch2's generic matchers support `constexpr`
matching. Currently, this includes:
* `IsEmpty()`
* `SizeIs(size_t target_size)`, `SizeIs(Matcher size_matcher)`
* `AllMatch(Matcher element_matcher)`
* `AnyMatch(Matcher element_matcher)`
* `NoneMatch(Matcher element_matcher)`
* `AllTrue()`, `AnyTrue()`, `NoneTrue()`
* `Contains(T&& target_element, Comparator = std::equal_to<>{})`
* `Contains(Matcher element_matcher)`
* `RangeEquals(TargetRangeLike&&, Comparator = std::equal_to<>{})`
* `UnorderedRangeEquals(TargetRangeLike&&, Comparator = std::equal_to<>{})`
---
[Home](Readme.md#top)
+4 -28
View File
@@ -72,8 +72,8 @@ including the Catch2 header.
Example:
```cpp
TEST_CASE("STATIC_REQUIRE showcase", "[traits]") {
STATIC_REQUIRE( std::is_void<void>::value );
STATIC_REQUIRE_FALSE( std::is_void<int>::value );
STATIC_REQUIRE( std::is_void_v<void> );
STATIC_REQUIRE_FALSE( std::is_void_v<int> );
}
```
@@ -86,35 +86,11 @@ becomes equivalent to `CHECK` instead of `REQUIRE`.
Example:
```cpp
TEST_CASE("STATIC_CHECK showcase", "[traits]") {
STATIC_CHECK( std::is_void<void>::value );
STATIC_CHECK_FALSE( std::is_void<int>::value );
STATIC_CHECK( std::is_void_v<void> );
STATIC_CHECK_FALSE( std::is_void_v<int> );
}
```
* `STATIC_REQUIRE_THAT` and `STATIC_CHECK_THAT`
> `STATIC_REQUIRE_THAT` and `STATIC_CHECK_THAT` was introduced in Catch2 3.15.0
`STATIC_{REQUIRE,CHECK}_THAT` are analogous to `STATIC_{REQUIRE,CHECK}`,
but for matchers. They are always defined, even if the current compiler
does not support `constexpr` matchers, but in that case the compilation
will always fail.
Just like `STATIC_{REQUIRE,CHECK}`, `STATIC_{REQUIRE,CHECK}_THAT` can be
delayed into runtime through the `CATCH_CONFIG_RUNTIME_STATIC_REQUIRE`
configuration option.
Example:
```cpp
TEST_CASE("Constexpr support for matchers", "[constexpr][matchers]") {
STATIC_REQUIRE_THAT( 1, MatchAll() );
STATIC_REQUIRE_THAT( 1, MatchAll() && MatchAll() );
STATIC_REQUIRE_THAT( 1, MatchAll() || MatchAll() );
STATIC_REQUIRE_THAT( 1, !!MatchAll() );
}
```
## Test case related macros
* `REGISTER_TEST_CASE`
+1 -1
View File
@@ -65,7 +65,7 @@ int main( int argc, char* argv[] ) {
returnCode = session.run();
// returnCode encodes the type of error that occurred. See the
// returnCode encodes the type of error that occured. See the
// integer constants in catch_session.hpp for more information
// on what each return code means.
-181
View File
@@ -2,15 +2,6 @@
# Release notes
**Contents**<br>
[3.16.0](#3160)<br>
[3.15.3](#3153)<br>
[3.15.2](#3152)<br>
[3.15.1](#3151)<br>
[3.15.0](#3150)<br>
[3.14.0](#3140)<br>
[3.13.0](#3130)<br>
[3.12.0](#3120)<br>
[3.11.0](#3110)<br>
[3.10.0](#3100)<br>
[3.9.1](#391)<br>
[3.9.0](#390)<br>
@@ -79,178 +70,6 @@
[Even Older versions](#even-older-versions)<br>
## 3.16.0
### Fixes
* Multiple fixes in `catch_discover_tests`:
* Fixed `<target>_TESTS` variable accumulating JSON fragments alongside test names.
* This was introduced during the refactoring in last release.
* Fixed `<target>_TESTS` variable from `catch_discover_tests` not escaping test names to be properly parsed by CMake.
* This means that e.g. test names with semicolons will not be split into multiple partial test names.
* This bug has existed since the first version of the script.
* Fixed `TEST_PREFIX`/`TEST_SUFFIX` args having leading/trailing whitespace stripped.
* Added workaround for Clang 20-21 compile error with `TEMPLATE_PRODUCT_TEST_CASE` (#3115, #3173)
### Improvements
* Verbosity option is now handled per reporter.
* The standalone `--verbosity` flag is propagated to all reporters as default, just like `--colour-mode`.
* The JSON reporter considers verbosity when listing tests.
* Another set of performance improvements for `catch_discover_tests` performance
* The newest version can register about 3k tests in 1 second, up from 1k previously.
* The initial `TEST_CASE` registration is slightly faster.
* Reduced overhead from first instantiating `BENCHMARK` machinery in a TU.
* Improved compilation speed when multiple templated test case macros use the same types.
* Rewrote implementation of the string matchers
* Case-sensitive matching (the default) is significantly faster.
* Case-insensitive matching is faster in most cases.
* Added optimizer barrier to calls into benchmarks without return values
* This limits the optimizations compiler can perform **between calls** into the benchmarked function, improving the accuracy.
* Added warning that checks for using sharding without deterministic test order (#3186)
## 3.15.3
### Fixes
* The JSON reporter's number handling is locale-independent. (#3176)
* Removed leftover debug message from `catch_discover_tests` (#3177)
* Fixed typo in the "Could not jump to the Nth element" exception message (#3181)
### Improvements
* `catch_discover_tests` registers tests in deterministic (alphabetical) order.
* `catch_discover_tests` has been rewritten to be massively faster.
* Preparing the actual CTest script is significantly faster.
* Parsing the JSON test array is significantly faster.
* Running without `ADD_TAGS_AS_LABELS` set is 10-15% faster (as opposed to being the same speed) (#3169)
* The new implementation is about 4x-5x faster, so registering 500 tests now takes ~350ms (down from 1.1s).
* JSON writing is faster
* Small improvement in writing non-string values
* ~6-40% improvement in writing string values that do not need escaping
* ~1-6% improvement in writing string values that do need escaping
* Better support for infs and NaNs in JSON output
## 3.15.2
### Fixes
* Fixed `--warn InfiniteGenerators` triggering even if the generator was limited to specific element via path filtering.
* Fixed `-Wunused-parameter` triggering in `-fnoexceptions` builds.
### Improvements
* `catch_discover_tests` can handle cases where the binary prints out non-Catch2 output due to global constructors (#3162)
## 3.15.1
### Fixes
* Fixed potential OOB access when looking for start of broken UTF-8 sequence during linebreaking (#3096)
* Fixed `TEMPLATE_LIST_TEST_CASE_METHOD` and `CATCH_TEMPLATE_PRODUCT_TEST_CASE` potentially causing ODR violations (#3161)
## 3.15.0
### Fixes
* Fixed ambiguous overload issue with `std::optional<T>` in C++26. (#3095)
* Since `std::optional` was turned into a range, the partial specialization of `StringMaker` for range-like types and for `std::optional<T>` were in conflict.
### Improvements
* Simplified `CATCH_REGISTER_ENUM` internals for faster compilation and retrieval.
* Successful assertion are slightly (1-2%) faster.
* Generic (new-style) matchers support constexpr matching in C++20.
* Combining matchers requires C++26.
* Catch2-provided generic matchers are all constexpr enabled.
* Added `STATIC_REQUIRE_THAT` for compile-time matcher assertions.
* This requires the compiler to support enough `constexpr` for matchers, see above.
## 3.14.0
### Fixes
* Added missing `<cstdint>` includes. (#3078)
* Fixed suppression of empty variadic macro arguments warning on Clang <19. (#3085)
* Fixed `catch_discover_tests` failing during `PRE_TEST` discovery if a target does not have discoverable tests. (#3075)
* Fixed build of the main library failing with `CATCH_CONFIG_PREFIX_ALL` defined. (#3087)
* JUnit reporter outputs single failed (errored/skipped) assertion per test case. (#1919)
### Improvements
* The default implementation of `--list-tags` and `--list-listeners` has a quiet variant.
* Suppressed the new Clang warning about `__COUNTER__` usage. (#3076)
* Line-wrapping counts utf-8 codepoints instead of bytes. (#1022, #3086)
* Combining character sequences are still miscounted, but Catch2 does not aim to fully support Unicode.
## 3.13.0
### Fixes
* `--benchmark-samples 0` no longer hard crashes (#3056)
* The CLI validation fails instead.
* Fixed warning suppression macros being doubly defined when using Clang on Windows (#3060)
### Improvements
* Suppressed static analysis 26426 diagnostic for MSVC (#3057)
* Renamed the internal deprecation macro from `DEPRECATED` to `CATCH_DEPRECATED` to avoid conflicts (#3058)
* Added `UNSCOPED_CAPTURE` macro (#2954)
* Added `ConcatGenerator` to combine multiple separate generator into one
* The short form is `cat`
* Generators can now jump forward to nth element efficiently
* Custom generators that can jump forward efficiently should override `skipToNthElementImpl`
* Generators can declare themselves infinite
* The generator base defaults to declaring itself finite for backwards compatibility
* Custom generators should override `isFinite()` to return the proper value
* Added `--warn InfiniteGenerators` to error out on `GENERATE` being given an infinite generator
* Extended options for section filtering from CLI to include generators
* The user can specify which element from the generator to use in the test case
* See documentation for how the new filters work and how they can be specified
* `MapGenerator` only calls the mapping function if the output will be used
## 3.12.0
### Fixes
* Fixed unscoped messages after a passing fast-pathed assertion being lost.
* Fixed the help string for `--order` to mention random order as the default. (#3045)
* Fixed small documentation typos. (#3039)
* Fixed compilation with `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS` for older C++ standards.
* Fixed a thread-safety issue with message macros being used too early after the process starts.
* Fixed automatic configuration to properly handle PlayStation platform. (#3054)
* **Fixed the _weird_ behaviour of section filtering when specifying multiple filters.** (#3038)
* See #3038 for more details.
### Improvements
* Added `lifetimebound` attribute to various places.
* As an example, compiler that supports lifetime analysis will now diagnose invalid use of Matcher combinators.
* Minor compile-time improvements to stringification. (#3028)
* `std::tuple` printer does not recurse.
* Some implementation details were outlined into the cpp file.
* Global variables will only be marked with `thread_local` in thread-safe builds. (#3044)
### Miscellaneous
* The thread safety support is no longer experimental.
* The new CMake option and C++ define is now `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS`.
## 3.11.0
### Fixes
* Fixed building on non-desktop GDK platforms (#3029)
* Fixed message macros being susceptible to race in specific scenario (#3031)
* Catch2's SEH filter will call the previously installed filter after reporting the error (#3033)
### Improvements
* Handling of scoped messages (e.g. `CAPTURE`) is a bit faster.
* Better out-of-the-box support for QNX (#2953)
* Improved performance of assertions by up-to 10%
* Release mode assertion fast-path sees the biggest improvement.
* Faster processing of non-escaped strings in `--invisibles` mode.
* Added support for Bazel's `TEST_RANDOM_SEED` env var (#3021)
* Added support for Bazel's `TEST_PREMATURE_EXIT_FILE` env var (#3020)
* This creates a file that is deleted if the tests exit normally, but stays around if the process dies unexpectedly.
* This functionality is also exposed through CLI as `--premature-exit-guard-file`
### Miscellaneous
* **[Tuple.app](https://tuple.app/catch2) has sponsored Catch2**
## 3.10.0
### Fixes
+5 -5
View File
@@ -109,10 +109,10 @@ and then `assertionEnded` event is emitted.
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
```cpp
void benchmarkPreparing( StringRef name ) override;
void benchmarkPreparing( std::string_view name ) override;
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
void benchmarkFailed( StringRef error ) override;
void benchmarkFailed( std::string_view error ) override;
```
Due to the benchmark lifecycle being bit more complicated, the benchmarking
@@ -153,9 +153,9 @@ void listTags( std::vector<TagInfo> const& tagInfos );
## Miscellaneous events
```cpp
void reportInvalidTestSpec( StringRef unmatchedSpec );
void fatalErrorEncountered( StringRef error );
void noMatchingTestCases( StringRef unmatchedSpec );
void reportInvalidTestSpec( std::string_view unmatchedSpec );
void fatalErrorEncountered( std::string_view error );
void noMatchingTestCases( std::string_view unmatchedSpec );
```
These are one-off events that do not neatly fit into other categories.
+4 -4
View File
@@ -43,13 +43,13 @@ them write into different destinations. The two main uses of this are
Specifying multiple reporter looks like this:
```
--reporter JUnit::out=result-junit.xml --reporter console::out=-::colour-mode=ansi::verbosity=quiet
--reporter JUnit::out=result-junit.xml --reporter console::out=-::colour-mode=ansi
```
This tells Catch2 to use two reporters, `JUnit` reporter that writes
its machine-readable XML output to file `result-junit.xml`, and the
`console` reporter that writes its user-friendly output to stdout, uses
ANSI colour codes for colouring the output and is set to "quiet" verbosity.
`console` reporter that writes its user-friendly output to stdout and
uses ANSI colour codes for colouring the output.
Using multiple reporters (or one reporter and one-or-more [event
listeners](event-listeners.md#top)) can have surprisingly complex semantics
@@ -110,7 +110,7 @@ passing and failing assertions.
_Generally we recommend that if you override a member function from either
of the bases, you call into the base's implementation first. This is not
necessary in all cases, but it is safer and easier._
necessarily in all cases, but it is safer and easier._
Writing your own reporter then looks like this:
+1 -1
View File
@@ -87,7 +87,7 @@ TEST_CASE("complex test case") {
```
This test case will report 5 passing assertions; one for each of the three
values in section `a1`, and then two in section `a2`, from values 2 and 6.
values in section `a1`, and then two in section `a2`, from values 2 and 4.
Note that as soon as one section is skipped, the entire test case will
be reported as _skipped_ (unless there is a failing assertion, in which
+5 -7
View File
@@ -36,12 +36,10 @@ Tags allow an arbitrary number of additional strings to be associated with a tes
As an example - given the following test cases:
```cpp
TEST_CASE( "A", "[widget]" ) { /* ... */ }
TEST_CASE( "B", "[widget]" ) { /* ... */ }
TEST_CASE( "C", "[gadget]" ) { /* ... */ }
TEST_CASE( "D", "[widget][gadget]" ) { /* ... */ }
```
TEST_CASE( "A", "[widget]" ) { /* ... */ }
TEST_CASE( "B", "[widget]" ) { /* ... */ }
TEST_CASE( "C", "[gadget]" ) { /* ... */ }
TEST_CASE( "D", "[widget][gadget]" ) { /* ... */ }
The tag expression, ```"[widget]"``` selects A, B & D. ```"[gadget]"``` selects C & D. ```"[widget][gadget]"``` selects just D and ```"[widget],[gadget]"``` selects all four test cases.
@@ -266,7 +264,7 @@ You can also have different arities in the _template-arg_ packs:
```cpp
TEMPLATE_PRODUCT_TEST_CASE("Product with differing arities", "[template][product]", std::tuple, (int, (int, double), (int, double, float))) {
TestType x;
REQUIRE(std::tuple_size<TestType>::value >= 1);
REQUIRE(std::tuple_size_v<TestType> >= 1);
}
```
+1 -1
View File
@@ -20,7 +20,7 @@ test case macros is not thread-safe. The way sections define paths through
the test is incompatible with user spawning threads arbitrarily, so this
limitation is here to stay.
**Important: thread safety in Catch2 is [opt-in](configuration.md#thread-safety)**
**Important: thread safety in Catch2 is [opt-in](configuration.md#experimental-thread-safety)**
## Using assertion macros from spawned threads
+1 -1
View File
@@ -15,7 +15,7 @@ So what does Catch2 bring to the party that differentiates it from these? Apart
## Key Features
* Quick and easy to get started. Just download two files, add them into your project and you're away.
* No external dependencies. As long as you can compile C++14 and have the C++ standard library available.
* No external dependencies. As long as you can compile C++17 and have the C++ standard library available.
* Write test cases as, self-registering, functions (or methods, if you prefer).
* Divide test cases into sections, each of which is run in isolation (eliminates the need for fixtures).
* Use BDD-style Given-When-Then sections as well as traditional unit test cases.
+1 -1
View File
@@ -27,7 +27,7 @@ TEST_CASE( "Factorials of 1 and higher are computed (pass)", "[single-file]" ) {
}
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 010-TestCase 010-TestCase.cpp && 010-TestCase --success
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 010-TestCase 010-TestCase.cpp && 010-TestCase --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 010-TestCase.cpp && 010-TestCase --success
// Expected compact output (all assertions):
+2 -2
View File
@@ -18,8 +18,8 @@ TEST_CASE( "1: All test cases reside in other .cpp files (empty)", "[multi-file:
// Here just to show there are two source files via option --list-tests.
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 020-TestCase-1.cpp
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 020-TestCase TestCase-1.o 020-TestCase-2.cpp && 020-TestCase --success
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 020-TestCase-1.cpp
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 020-TestCase TestCase-1.o 020-TestCase-2.cpp && 020-TestCase --success
//
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -c 020-TestCase-1.cpp
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -Fe020-TestCase.exe 020-TestCase-1.obj 020-TestCase-2.cpp && 020-TestCase --success
+1 -1
View File
@@ -61,7 +61,7 @@ TEST_CASE( "Assert that something is false (continue after failure)", "[check-fa
}
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 030-Asn-Require-Check 030-Asn-Require-Check.cpp && 030-Asn-Require-Check --success
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 030-Asn-Require-Check 030-Asn-Require-Check.cpp && 030-Asn-Require-Check --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 030-Asn-Require-Check.cpp && 030-Asn-Require-Check --success
// Expected compact output (all assertions):
+1 -1
View File
@@ -53,7 +53,7 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
}
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 100-Fix-Section 100-Fix-Section.cpp && 100-Fix-Section --success
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 100-Fix-Section 100-Fix-Section.cpp && 100-Fix-Section --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 100-Fix-Section.cpp && 100-Fix-Section --success
// Expected compact output (all assertions):
+2 -2
View File
@@ -60,11 +60,11 @@ TEST_CASE_METHOD( UniqueTestsFixture, "Create Employee/Normal", "[create]" ) {
}
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp && 110-Fix-ClassFixture --success
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp && 110-Fix-ClassFixture --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 110-Fix-ClassFixture.cpp && 110-Fix-ClassFixture --success
//
// Compile with pkg-config:
// - g++ -std=c++14 -Wall $(pkg-config catch2-with-main --cflags) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp $(pkg-config catch2-with-main --libs)
// - g++ -std=c++17 -Wall $(pkg-config catch2-with-main --cflags) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp $(pkg-config catch2-with-main --libs)
// Expected compact output (all assertions):
//
+1 -1
View File
@@ -56,7 +56,7 @@ SCENARIO( "vectors can be sized and resized", "[vector]" ) {
}
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 120-Bdd-ScenarioGivenWhenThen 120-Bdd-ScenarioGivenWhenThen.cpp && 120-Bdd-ScenarioGivenWhenThen --success
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 120-Bdd-ScenarioGivenWhenThen 120-Bdd-ScenarioGivenWhenThen.cpp && 120-Bdd-ScenarioGivenWhenThen --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 120-Bdd-ScenarioGivenWhenThen.cpp && 120-Bdd-ScenarioGivenWhenThen --success
// Expected compact output (all assertions):
+4 -4
View File
@@ -131,7 +131,7 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
}
// struct Tag {
// StringRef original, lowerCased;
// std::string_view original, lowerCased;
// };
//
//
@@ -221,9 +221,9 @@ void print( std::ostream& os, int const level, std::string const& title, Catch::
// struct AssertionInfo
// {
// StringRef macroName;
// std::string_view macroName;
// SourceLineInfo lineInfo;
// StringRef capturedExpression;
// std::string_view capturedExpression;
// ResultDisposition::Flags resultDisposition;
// };
@@ -427,7 +427,7 @@ TEST_CASE_METHOD( Fixture, "3: Testcase with class-based fixture", "[tag-C][tag-
}
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 210-Evt-EventListeners 210-Evt-EventListeners.cpp && 210-Evt-EventListeners --success
// - g++ -std=c++17 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 210-Evt-EventListeners 210-Evt-EventListeners.cpp && 210-Evt-EventListeners --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 210-Evt-EventListeners.cpp && 210-Evt-EventListeners --success
// Expected compact output (all assertions):
-18
View File
@@ -39,24 +39,6 @@ public:
current_number = m_dist(m_rand);
return true;
}
bool isFinite() const override { return false; }
// Note: this improves the performance only a bit, but it is here
// to show how you can override the skip functionality.
void skipToNthElementImpl( std::size_t n ) override {
auto current_index = currentElementIndex();
assert(current_index <= n);
// We cannot jump forward the underlying generator directly,
// because we do not know how many bits each distributed number
// would consume to be generated.
for (; current_index < n; ++current_index) {
(void)m_dist(m_rand);
}
// We do not have to touch the current element index; it is handled
// by the base class.
}
};
// Avoids -Wweak-vtables
-2
View File
@@ -40,8 +40,6 @@ public:
bool next() override {
return !!std::getline(m_stream, m_line);
}
bool isFinite() const override { return true; }
};
std::string const& LineGenerator::get() const {
+5 -18
View File
@@ -22,24 +22,21 @@ struct TestSubject {
TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][generator]") {
using std::make_tuple;
// do setup here as normal
TestSubject subj;
SECTION("This section is run for each row in the table") {
std::string test_input;
size_t expected_output;
std::tie( test_input, expected_output ) =
const auto [test_input, expected_output] =
GENERATE( table<std::string, size_t>(
{ /* In this case one of the parameters to our test case is the
* expected output, but this is not required. There could be
* multiple expected values in the table, which can have any
* (fixed) number of columns.
*/
make_tuple( "one", 3 ),
make_tuple( "two", 3 ),
make_tuple( "three", 5 ),
make_tuple( "four", 4 ) } ) );
{ "one", 3 },
{ "two", 3 },
{ "three", 5 },
{ "four", 4 } } ) );
// run the test
auto result = subj.GetLength(test_input);
@@ -50,14 +47,4 @@ TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][genera
} // end section
}
/* Possible simplifications where less legacy toolchain support is needed:
*
* - With libstdc++6 or newer, the make_tuple() calls can be omitted
* (technically C++17 but does not require -std in GCC/Clang). See
* https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
*
* - In C++17 mode std::tie() and the preceding variable declarations can be
* replaced by structured bindings: auto [test_input, expected] = GENERATE(
* table<std::string, size_t>({ ...
*/
// Compiling and running this file will result in 4 successful assertions
+6 -17
View File
@@ -44,7 +44,7 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
``catch_discover_tests`` sets up a post-build command on the test executable
that generates the list of tests by parsing the output from running the test
with the ``--list-tests --reporter json`` argument. This ensures that the full
with the ``--list-test-names-only`` argument. This ensures that the full
list of tests is obtained. Since test discovery occurs at build time, it is
not necessary to re-run CMake when the list of tests changes.
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
@@ -58,11 +58,6 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
directory property. The set of discovered tests is made accessible to such a
script via the ``<target>_TESTS`` variable.
Note that ``<target>_TESTS`` variable contains test names with brackets
("[", "]") escaped into ASCII char 2, 3 respectively, to work around CMake's
list parsing rules. You have to unescape them back for each element to get
the original names.
The options are:
``target``
@@ -72,7 +67,7 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
``TEST_SPEC arg1...``
Specifies test cases, wildcarded test cases, tags and tag expressions to
pass to the Catch executable when listing the tests.
pass to the Catch executable with the ``--list-test-names-only`` argument.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
@@ -151,12 +146,6 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
calling ``catch_discover_tests``. This provides a mechanism for globally selecting
a preferred test discovery behavior without having to modify each call site.
On Apple Silicon with the Xcode generator you must use ``PRE_TEST``. With the
default ``POST_BUILD`` mode the build fails with ``Result: Subprocess killed``,
because macOS on Apple Silicon refuses to run unsigned binaries and Xcode
code-signs the test executable only after the post-build script that
``POST_BUILD`` mode uses to run it for test discovery. See Catch2 issue #2411.
``SKIP_IS_FAILURE``
Disables skipped test detection.
@@ -231,8 +220,8 @@ function(catch_discover_tests TARGET)
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX='${_TEST_PREFIX}'"
-D "TEST_SUFFIX='${_TEST_SUFFIX}'"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "TEST_REPORTER=${_REPORTER}"
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
@@ -277,8 +266,8 @@ function(catch_discover_tests TARGET)
" TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n"
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
" TEST_PREFIX" " [==['" "${_TEST_PREFIX}" "']==]" "\n"
" TEST_SUFFIX" " [==['" "${_TEST_SUFFIX}" "']==]" "\n"
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
" TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n"
" TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n"
+52 -348
View File
@@ -1,220 +1,23 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
# Because natively using CMake's JSON processing for arrays leads to quadratic
# running times, we do terrible hack and split JSON array by object boundary
# + commas and try to reconstruct valid JSON objects. During this, we need
# to replace CMake characters that could be in the test name/tags with
# placeholder, so it doesn't affect CMake's processing of the strings/lists
# we create during the parsing.
#
# We use 0x01, 0x02, 0x03, and 0x04 as placeholder bytes, as those cannot
# exist in JSON unescaped.
#
# 0x01 <=> ';' (CMake list separator)
# 0x02 <-> '[' (opens a CMake bracket-argument context)
# 0x03 <-> ']' (closes a CMake bracket-argument context)
# 0x04 == element boundary marker used while splitting the tests array
#
string(ASCII 1 _SemicolonEscape)
string(ASCII 2 _OpenBracketEscape)
string(ASCII 3 _CloseBracketEscape)
string(ASCII 4 _BoundaryEscape)
# As far as I can tell, the only way of having unclosed '[' or ']' in the
# list of test names we define for CTest, is to escape them into completely
# different characters. Otherwise, they will break parsing of unrelated
# semicolons (inserted by CMake after parsing the list from the user-facing
# space separated format). This means that the consumers of the list have
# to unescape them back, but that's CMake :v
#
# 0x02 <-> '['
# 0x03 <-> ']'
#
string(ASCII 2 _OpenBracketListingEscape)
string(ASCII 3 _CloseBracketListingEscape)
# Placeholder bytes in the listing would break our parsing hack, so
# we check they don't exist. They shouldn't exist in valid JSON, but
# the reporter might not be escaping them properly.
function(validate_input_noescapes listing_var)
foreach(byte "${_SemicolonEscape}" "${_BoundaryEscape}" "${_OpenBracketEscape}" "${_CloseBracketEscape}")
string(FIND "${${listing_var}}" "${byte}" found)
if(NOT found EQUAL -1)
message(FATAL_ERROR
"The test listing contains raw control byte (0x01-0x04) which should not "
"be there. This means either bad escaping in JSON reporter, or corrupted file. "
)
endif()
endforeach()
endfunction()
# Replaces relevant characters with their placeholders, see the top of this file.
# Modifies argument `var` in place.
function(magic_escape_chars var)
set(value "${${var}}")
string(REPLACE ";" "${_SemicolonEscape}" value "${value}")
string(REPLACE "[" "${_OpenBracketEscape}" value "${value}")
string(REPLACE "]" "${_CloseBracketEscape}" value "${value}")
set(${var} "${value}" PARENT_SCOPE)
endfunction()
# Turns placeholders back into original characters, see the top of this file.
# Modifies argument `var` in place.
function(magic_unescape_chars var)
set(value "${${var}}")
string(REPLACE "${_SemicolonEscape}" ";" value "${value}")
string(REPLACE "${_OpenBracketEscape}" "[" value "${value}")
string(REPLACE "${_CloseBracketEscape}" "]" value "${value}")
set(${var} "${value}" PARENT_SCOPE)
endfunction()
# Abuses knowledge of Catch2's JSON reporter output for listing tests to
# split JSON array of the test listings into a CMake list of strings,
# with each element being the JSON string of one array entry.
#
# This avoids the terrible quadratic running time of using CMake's JSON
# support to parse the JSON reporter output "properly", where the whole
# JSON array of tests is parsed again for every element. Instead, we can
# use the CMake's API to only parse the individual test's objects, which
# are usually small and only have to be reparsed fixed number of times
# (once for test names, once for labels).
#
# We process the string representing the JSON array by splitting it on
# `}<ws>*,<ws>*{` and then checking for each chunk whether it is a valid
# JSON object representing Catch2's test. If not (e.g. because we split
# on the presence of `}<ws>*,<ws>*{` inside a test name), then we append
# the next chunk to the current one and check again. And again, until
# we get back to a valid JSON.
#
# Note that to support passing the object strings back from the function,
# they will still contain the placeholders and need to be unescaped before
# further processing (e.g. sending them into CMake's JSON parsing API).
function(split_json_array json_array_var out_var)
# We have to pass the input by var name to avoid CMake processing
# the input as an arg.
set(json_in "${${json_array_var}}")
# Strip the array brackets at the start and end of the JSON array.
# Must happen before we escape the other [] instances below from the
# actual array data.
string(REGEX REPLACE "^[ \t\r\n]*\\[" "" json_in "${json_in}")
string(REGEX REPLACE "\\][ \t\r\n]*$" "" json_in "${json_in}")
magic_escape_chars(json_in)
# We need to keep the whitespace around comma around, so that if we
# split inside the test object, we can reconstruct it losslessly.
string(REGEX REPLACE "(}[ \t\r\n]*)[,]([ \t\r\n]*{)" "\\1${_BoundaryEscape}\\2" json_in "${json_in}")
# We escaped all list separators above, so now we can turn the JSON
# string into a CMake list of fragments in single pass.
string(REPLACE "${_BoundaryEscape}" ";" fragments "${json_in}")
# And now we have to reconstruct the actual JSON structure from fragments.
set(array_elements "")
set(accumulator "")
foreach(next_fragment IN LISTS fragments)
if(accumulator)
set(accumulator "${accumulator},${next_fragment}")
else()
set(accumulator "${next_fragment}")
endif()
# Because the fragments (might) contain invalid JSON characters due
# to escaping, we have to unescape it before checking if we can parse it.
set(maybe_json "${accumulator}")
magic_unescape_chars(maybe_json)
string(JSON unused ERROR_VARIABLE err GET "${maybe_json}" "name")
if(err STREQUAL "NOTFOUND")
list(APPEND array_elements "${accumulator}")
set(accumulator "")
endif()
endforeach()
set(${out_var} "${array_elements}" PARENT_SCOPE)
endfunction()
# Prepare (a part of) command with bracketed arguments and return it via `out_var`.
#
# This allows the registration script to escape parts of the test script
# only once, instead of escaping the unchanged arguments over and over again.
function(prepare_command_fragment out_var)
function(add_command NAME)
set(_args "")
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
math(EXPR _last_arg ${ARGC}-1)
if(_last_arg GREATER_EQUAL 1)
foreach(_n RANGE 1 ${_last_arg})
set(_arg "${ARGV${_n}}")
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
endif()
set(${out_var} "${_args}" PARENT_SCOPE)
endfunction()
# Generates random filename in the temp folder.
# Temp folder is retrieved by checking env vars from various platforms.
function(make_temp_file_path OUT_VARIABLE FALLBACK_PATH)
set(TEMP_DIR "")
set(ENV_VARS
# From XDG base dir specification
XDG_RUNTIME_DIR
# From POSIX standard
TMPDIR
# From Windows
TMP
TEMP
)
foreach(var ${ENV_VARS})
if(DEFINED ENV{${var}} AND NOT "$ENV{${var}}" STREQUAL "")
set(TEMP_DIR "$ENV{${var}}")
break()
foreach(_n RANGE 1 ${_last_arg})
set(_arg "${ARGV${_n}}")
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
# If all checks fail, we use the fallback path
if(TEMP_DIR STREQUAL "")
set(TEMP_DIR "${FALLBACK_PATH}")
endif()
file(TO_CMAKE_PATH "${TEMP_DIR}" TEMP_DIR)
# Generate the random file name
string(RANDOM LENGTH 8 RAND_ID)
set(FINAL_TEMP_PATH "${TEMP_DIR}/Catch2-test-listing.${RAND_ID}.json")
set(${OUT_VARIABLE} "${FINAL_TEMP_PATH}" PARENT_SCOPE)
endfunction()
# Computes the path of the test-list file based on the path for the main
# CTest script file (passed in `CTEST_FILE`).
#
# It works by replacing the `_tests` part of the original name with
# `_test-list`, or failing that, it appends `-list.cmake` instead.
#
# <base>_tests.cmake -> <base>_test-list.cmake
# <base>_tests-Debug.cmake -> <base>_test-list-Debug.cmake
function(make_test_list_file_path CTEST_FILE OUT_VARIABLE)
if("${CTEST_FILE}" MATCHES "_tests(.*)\\.cmake$")
string(REGEX REPLACE "_tests(.*)\\.cmake$" "_test-list\\1.cmake" list_file "${CTEST_FILE}")
else()
set(list_file "${CTEST_FILE}-list.cmake")
endif()
set(${OUT_VARIABLE} "${list_file}" PARENT_SCOPE)
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
function(catch_discover_tests_impl)
cmake_parse_arguments(
""
""
@@ -223,22 +26,9 @@ function(catch_discover_tests_impl)
${ARGN}
)
# We write the list of all discovered tests into a separate file
make_test_list_file_path("${_CTEST_FILE}" _CTEST_LIST_FILE)
# We periodically append to the output files below, so we have to ensure
# that it is empty at the start, or we get duplicated test scripts.
file(REMOVE "${_CTEST_FILE}")
file(REMOVE "${_CTEST_LIST_FILE}")
# Size (in Bytes) at which the intermediate `script` var is dumped to file.
set(_WriteToFileThreshold 50000)
set(add_tags "${_ADD_TAGS_AS_LABELS}")
# TEST_{PREFIX,SUFFIX} is enclosed in single quotes to keep ensure
# leading/trailing whitespace isn't trimmed by CMake's argument passing.
string(REGEX REPLACE "^'(.*)'$" "\\1" prefix "${_TEST_PREFIX}")
string(REGEX REPLACE "^'(.*)'$" "\\1" suffix "${_TEST_SUFFIX}")
set(prefix "${_TEST_PREFIX}")
set(suffix "${_TEST_SUFFIX}")
set(spec ${_TEST_SPEC})
set(extra_args ${_TEST_EXTRA_ARGS})
set(properties ${_TEST_PROPERTIES})
@@ -252,10 +42,6 @@ function(catch_discover_tests_impl)
set(script)
set(suite)
set(tests)
# Holds the list of all registered test names **as a string**, not list.
# This avoids issue of CMake removing semicolon escapes even inside
# bracket-quoted strings.
set(_test_names)
if(WIN32)
set(dl_paths_variable_name PATH)
@@ -286,19 +72,8 @@ function(catch_discover_tests_impl)
set(ENV{DYLD_FRAMEWORK_PATH} "${paths}")
endif()
make_temp_file_path(listing_output_path "${_TEST_WORKING_DIR}")
set(_Verbosity "quiet")
if (add_tags)
set(_Verbosity "normal")
endif()
execute_process(
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec}
--list-tests
--reporter "json::verbosity=${_Verbosity}"
--out "${listing_output_path}"
--order lex # Make sure the output order, and thus test registration order, is consistent across runs.
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --reporter json
OUTPUT_VARIABLE listing_output
RESULT_VARIABLE result
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
@@ -311,11 +86,6 @@ function(catch_discover_tests_impl)
)
endif()
# Read the JSON output back from the output file and validate it.
file(READ ${listing_output_path} listing_output)
file(REMOVE ${listing_output_path})
validate_input_noescapes(listing_output)
# Prepare reporter
if(reporter)
set(reporter_arg "--reporter ${reporter}")
@@ -366,87 +136,25 @@ function(catch_discover_tests_impl)
# Parse JSON output for list of tests/class names/tags
string(JSON version GET "${listing_output}" "version")
if(NOT version STREQUAL "2")
if(NOT version STREQUAL "1")
message(FATAL_ERROR "Unsupported catch output version: '${version}'")
endif()
# Extract just the JSON array with tests and then split them into
# individual objects.
# Speed-up reparsing by cutting away unneeded parts of JSON.
string(JSON test_listing GET "${listing_output}" "listings" "tests")
split_json_array(test_listing tests)
string(JSON num_tests LENGTH "${test_listing}")
# Exit early if no tests are detected
if(NOT tests)
# Still emit an (empty) test list file and have the main script include
# it, so that consumers relying on the `${_TEST_LIST}` variable and on the
# include structure get consistent behavior regardless of test count.
file(WRITE "${_CTEST_LIST_FILE}" "set(${_TEST_LIST})\n")
file(WRITE "${_CTEST_FILE}" "include(\"${_CTEST_LIST_FILE}\")\n")
if(num_tests STREQUAL "0")
return()
endif()
# CMake's foreach-RANGE is inclusive, so we have to subtract 1
math(EXPR num_tests "${num_tests} - 1")
# The 'set(VAR` header for the test list has to be written separately,
# so that each test name can be appended file without further processing.
set(test_names "set(${_TEST_LIST}")
# Most of the commands/arguments in the CTest script are identical
# for every test registered with one `catch_discover_tests` call.
# To avoid repeating the work in escaping them, we escape them before
# the per-test loop and reuse the escaped fragments.
#
# `add_test` calls are
# add_test(<name><exec><exe><escaped_name><extra_args><reporter><out_dir>)
# Of these, <exec>,<exe>,<extra_args>, and <reporter> are the same between
# all tests.
prepare_command_fragment(_exec_exe_fragment
${_TEST_EXECUTOR}
"${_TEST_EXECUTABLE}"
)
prepare_command_fragment(_args_reporter_fragment
${extra_args}
"${reporter_arg}"
)
# `set_tests_properties` calls are
# set_tests_properties(<name> PROPERTIES WORKING_DIRECTORY <dir> <properties>)
# set_tests_properties(<name> PROPERTIES ENVIRONMENT_MODIFICATION <env_mod>)
# Of these, only the <name> changes between tests.
prepare_command_fragment(_properties_fragment
PROPERTIES
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
${properties}
)
# Env modification is optional, so we prepare it in a separate command
if(environment_modifications)
prepare_command_fragment(_env_modification_fragment
PROPERTIES
ENVIRONMENT_MODIFICATION "${environment_modifications}"
)
endif()
# Each element in the tests is JSON-string representing one test object.
# We have to parse it and then turn it into CTest script commands.
foreach(single_test IN LISTS tests)
# Because appending to the same string in CMake has quadratic runtime,
# we flush the script into the file periodically to avoid the worst case.
string(LENGTH "${script}" script_len)
if (script_len GREATER _WriteToFileThreshold)
file(APPEND "${_CTEST_FILE}" "${script}")
set(script "")
endif()
string(LENGTH "${test_names}" names_len)
if (names_len GREATER _WriteToFileThreshold)
file(APPEND "${_CTEST_LIST_FILE}" "${test_names}")
set(test_names "")
endif()
# The elements are still escaped and contain JSON-invalid characters,
# they have to be unescaped before parsing them as JSON.
magic_unescape_chars(single_test)
if(add_tags)
string(JSON test_tags GET "${single_test}" "tags")
endif()
foreach(idx RANGE ${num_tests})
string(JSON single_test GET ${test_listing} ${idx})
string(JSON test_tags GET "${single_test}" "tags")
string(JSON plain_name GET "${single_test}" "name")
# Escape characters in test case names that would be parsed by Catch2
@@ -461,15 +169,22 @@ function(catch_discover_tests_impl)
set(output_dir_arg "--out ${output_dir}/${output_prefix}${escaped_name_clean}${output_suffix}")
endif()
set(full_name "${prefix}${plain_name}${suffix}")
prepare_command_fragment(_full_name_fragment "${full_name}")
prepare_command_fragment(_escaped_name_fragment "${escaped_name}")
prepare_command_fragment(_outdir_fragment "${output_dir_arg}")
string(APPEND script
"add_test(${_full_name_fragment}${_exec_exe_fragment}${_escaped_name_fragment}${_args_reporter_fragment}${_outdir_fragment})\n")
string(APPEND script
"set_tests_properties(${_full_name_fragment}${_properties_fragment})\n")
# ...and add to script
add_command(add_test
"${prefix}${plain_name}${suffix}"
${_TEST_EXECUTOR}
"${_TEST_EXECUTABLE}"
"${escaped_name}"
${extra_args}
"${reporter_arg}"
"${output_dir_arg}"
)
add_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
${properties}
)
if(add_tags)
string(JSON num_tags LENGTH "${test_tags}")
@@ -489,44 +204,33 @@ function(catch_discover_tests_impl)
list(APPEND tag_list "${a_tag}")
endforeach()
prepare_command_fragment(_labels_fragment
add_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
LABELS "${tag_list}"
)
string(APPEND script "set_tests_properties(${_full_name_fragment}${_labels_fragment})\n")
endif()
endif(add_tags)
if(environment_modifications)
string(APPEND script "set_tests_properties(${_full_name_fragment}${_env_modification_fragment})\n")
add_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
ENVIRONMENT_MODIFICATION "${environment_modifications}")
endif()
# The test name has to be escaped using the same rules as prepare_command
# uses for the arguments, so that it keeps being single element in list
# even with weird characters and semicolons.
set(full_list_name "${prefix}${plain_name}${suffix}")
string(REPLACE ";" "\\;" full_list_name "${full_list_name}")
string(REPLACE "[" "${_OpenBracketListingEscape}" full_list_name "${full_list_name}")
string(REPLACE "]" "${_CloseBracketListingEscape}" full_list_name "${full_list_name}")
if(full_list_name MATCHES "[^-./:a-zA-Z0-9_]")
# The space before the start of quote is important, so that we get
# space-separated list in the final file.
string(APPEND test_names " [==[${full_list_name}]==]")
else()
string(APPEND test_names " ${full_list_name}")
endif()
list(APPEND tests "${prefix}${plain_name}${suffix}")
endforeach()
# Write any test names leftovers we have
file(APPEND "${_CTEST_LIST_FILE}" "${test_names})\n")
# Write any main script leftovers we have, and append the include of test names
file(APPEND "${_CTEST_FILE}" "${script}")
file(APPEND "${_CTEST_FILE}" "include(\"${_CTEST_LIST_FILE}\")\n")
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${_TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${_CTEST_FILE}" "${script}")
endfunction()
# To enable `include`ing this file in the unit test scripts, we only run
# the impl if an actual `TEST_EXECUTABLE` is provided.
if(CMAKE_SCRIPT_MODE_FILE AND DEFINED TEST_EXECUTABLE)
if(CMAKE_SCRIPT_MODE_FILE)
catch_discover_tests_impl(
TEST_EXECUTABLE ${TEST_EXECUTABLE}
TEST_EXECUTOR ${TEST_EXECUTOR}
File diff suppressed because it is too large Load Diff
+552 -1048
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -9,7 +9,6 @@
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/internal/catch_tag_alias_registry.hpp>
#include <cstdint>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
-1
View File
@@ -13,7 +13,6 @@
#include <string>
#include <string_view>
#include <cstdint>
template<class Callback>
+1 -1
View File
@@ -8,7 +8,7 @@
project(
'catch2',
'cpp',
version: '3.16.0', # CML version placeholder, don't delete
version: '3.10.0', # CML version placeholder, don't delete
license: 'BSL-1.0',
meson_version: '>=0.54.1',
)
+6 -17
View File
@@ -32,7 +32,6 @@ set(BENCHMARK_HEADERS
${SOURCES_DIR}/benchmark/detail/catch_timing.hpp
)
set(BENCHMARK_SOURCES
${SOURCES_DIR}/benchmark/catch_benchmark.cpp
${SOURCES_DIR}/benchmark/catch_chronometer.cpp
${SOURCES_DIR}/benchmark/detail/catch_analyse.cpp
${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.cpp
@@ -77,18 +76,16 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_config_counter.hpp
${SOURCES_DIR}/internal/catch_config_prefix_messages.hpp
${SOURCES_DIR}/internal/catch_config_static_analysis_support.hpp
${SOURCES_DIR}/internal/catch_config_uncaught_exceptions.hpp
${SOURCES_DIR}/internal/catch_config_wchar.hpp
${SOURCES_DIR}/internal/catch_console_colour.hpp
${SOURCES_DIR}/internal/catch_console_width.hpp
${SOURCES_DIR}/internal/catch_container_nonmembers.hpp
${SOURCES_DIR}/internal/catch_context.hpp
${SOURCES_DIR}/internal/catch_debug_console.hpp
${SOURCES_DIR}/internal/catch_debugger.hpp
${SOURCES_DIR}/internal/catch_decomposer.hpp
${SOURCES_DIR}/internal/catch_deprecation_macro.hpp
${SOURCES_DIR}/internal/catch_enforce.hpp
${SOURCES_DIR}/internal/catch_enum_info.hpp
${SOURCES_DIR}/internal/catch_enum_values_registry.hpp
${SOURCES_DIR}/internal/catch_errno_guard.hpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.hpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.hpp
@@ -99,17 +96,13 @@ 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
${SOURCES_DIR}/internal/catch_meta.hpp
${SOURCES_DIR}/internal/catch_move_and_forward.hpp
${SOURCES_DIR}/internal/catch_noncopyable.hpp
${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
@@ -131,7 +124,6 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_stdstreams.hpp
${SOURCES_DIR}/internal/catch_stream_end_stop.hpp
${SOURCES_DIR}/internal/catch_string_manip.hpp
${SOURCES_DIR}/internal/catch_stringref.hpp
${SOURCES_DIR}/internal/catch_tag_alias_registry.hpp
${SOURCES_DIR}/internal/catch_template_test_registry.hpp
${SOURCES_DIR}/internal/catch_test_case_info_hasher.hpp
@@ -142,7 +134,6 @@ 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
@@ -182,7 +173,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_info.cpp
${SOURCES_DIR}/internal/catch_enum_values_registry.cpp
${SOURCES_DIR}/internal/catch_errno_guard.cpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.cpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp
@@ -209,7 +200,6 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_startup_exception_registry.cpp
${SOURCES_DIR}/internal/catch_stdstreams.cpp
${SOURCES_DIR}/internal/catch_string_manip.cpp
${SOURCES_DIR}/internal/catch_stringref.cpp
${SOURCES_DIR}/internal/catch_tag_alias_registry.cpp
${SOURCES_DIR}/internal/catch_test_case_info_hasher.cpp
${SOURCES_DIR}/internal/catch_test_case_registry_impl.cpp
@@ -228,6 +218,7 @@ 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
@@ -256,13 +247,11 @@ 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})
@@ -370,8 +359,8 @@ set_target_properties(Catch2 PROPERTIES
SOVERSION ${PROJECT_VERSION}
)
# require C++14
target_compile_features(Catch2 PUBLIC cxx_std_14)
# require C++17
target_compile_features(Catch2 PUBLIC cxx_std_17)
configure_file(
"${SOURCES_DIR}/catch_user_config.hpp.in"
@@ -455,7 +444,7 @@ if(CATCH_BUILD_EXAMPLES OR CATCH_BUILD_EXTRA_TESTS)
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_definitions(Catch2_buildall_interface INTERFACE CATCH_CONFIG_STATIC)
target_compile_features(Catch2_buildall_interface INTERFACE cxx_std_14)
target_compile_features(Catch2_buildall_interface INTERFACE cxx_std_17)
endif()
list(APPEND CATCH_IMPL_TARGETS Catch2 Catch2WithMain)
-54
View File
@@ -1,54 +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
#include <catch2/benchmark/catch_benchmark.hpp>
#include <algorithm>
#include <chrono>
#include <cmath>
namespace Catch {
namespace Benchmark {
namespace Detail {
Environment measure_environment_default() {
return Detail::measure_environment<default_clock>();
}
ExecutionPlan prepare_default( const IConfig& cfg,
Environment env,
BenchmarkFunction&& fun ) {
// This mirrors Benchmark::prepare<default_clock>(), but with the
// clock fixed so it is instantiated once here in the library.
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
auto run_time = std::max(
min_time,
std::chrono::duration_cast<decltype( min_time )>(
cfg.benchmarkWarmupTime() ) );
auto&& test = Detail::run_for_at_least<default_clock>(
std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
int new_iters = static_cast<int>(
std::ceil( min_time * test.iterations / test.elapsed ) );
return { new_iters,
test.elapsed / test.iterations * new_iters *
cfg.benchmarkSamples(),
CATCH_MOVE( fun ),
std::chrono::duration_cast<FDuration>(
cfg.benchmarkWarmupTime() ),
Detail::warmup_iterations };
}
std::vector<FDuration> run_plan_default( ExecutionPlan const& plan,
const IConfig& cfg,
Environment env ) {
return plan.run<default_clock>( cfg, env );
}
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
+12 -88
View File
@@ -33,94 +33,9 @@
#include <exception>
#include <string>
#include <cmath>
#include <type_traits>
namespace Catch {
namespace Benchmark {
namespace Detail {
template <typename Clock>
ExecutionPlan prepare( const IConfig& cfg,
Environment env,
BenchmarkFunction&& fun ) {
auto min_time =
env.clock_resolution.mean * Detail::minimum_ticks;
auto run_time =
std::max( min_time,
std::chrono::duration_cast<decltype( min_time )>(
cfg.benchmarkWarmupTime() ) );
auto&& test = Detail::run_for_at_least<Clock>(
std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
int new_iters = static_cast<int>(
std::ceil( min_time * test.iterations / test.elapsed ) );
return { new_iters,
test.elapsed / test.iterations * new_iters *
cfg.benchmarkSamples(),
CATCH_MOVE( fun ),
std::chrono::duration_cast<FDuration>(
cfg.benchmarkWarmupTime() ),
Detail::warmup_iterations };
}
// These are wrappers for their respective function templated
// over `default_clock`. This allows outlining the usual use
// of the template into single TU and save on compilation costs.
Environment measure_environment_default();
ExecutionPlan prepare_default( const IConfig& cfg,
Environment env,
BenchmarkFunction&& fun );
std::vector<FDuration> run_plan_default( ExecutionPlan const& plan,
const IConfig& cfg,
Environment env );
template <typename Clock>
std::enable_if_t<std::is_same<Clock, default_clock>::value,
Environment>
measureEnvironmentDispatch() {
return measure_environment_default();
}
template <typename Clock>
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
Environment>
measureEnvironmentDispatch() {
return measure_environment<Clock>();
}
template <typename Clock>
std::enable_if_t<std::is_same<Clock, default_clock>::value,
std::vector<FDuration>>
runPlanDispatch( ExecutionPlan const& plan,
const IConfig& cfg,
Environment env ) {
return run_plan_default( plan, cfg, env );
}
template <typename Clock>
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
std::vector<FDuration>>
runPlanDispatch( ExecutionPlan const& plan,
const IConfig& cfg,
Environment env ) {
return plan.template run<Clock>( cfg, env );
}
template <typename Clock>
std::enable_if_t<std::is_same<Clock, default_clock>::value,
ExecutionPlan>
prepareDispatch( const IConfig& cfg,
Environment env,
BenchmarkFunction&& fun ) {
return prepare_default( cfg, env, CATCH_MOVE( fun ) );
}
template <typename Clock>
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
ExecutionPlan>
prepareDispatch( const IConfig& cfg,
Environment env,
BenchmarkFunction&& fun ) {
return prepare<Clock>( cfg, env, CATCH_MOVE( fun ) );
}
} // namespace Detail
struct Benchmark {
Benchmark(std::string&& benchmarkName)
: name(CATCH_MOVE(benchmarkName)) {}
@@ -129,18 +44,27 @@ namespace Catch {
Benchmark(std::string&& benchmarkName , FUN &&func)
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
template <typename Clock>
ExecutionPlan prepare(const IConfig &cfg, Environment env) {
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
}
template <typename Clock = default_clock>
void run() {
static_assert( Clock::is_steady,
"Benchmarking clock should be steady" );
auto const* cfg = getCurrentContext().getConfig();
auto env = Detail::measureEnvironmentDispatch<Clock>();
auto env = Detail::measure_environment<Clock>();
getResultCapture().benchmarkPreparing(name);
CATCH_TRY{
auto plan = user_code([&] {
return Detail::prepareDispatch<Clock>( *cfg, env, CATCH_MOVE(fun) );
return prepare<Clock>(*cfg, env);
});
BenchmarkInfo info {
@@ -156,7 +80,7 @@ namespace Catch {
getResultCapture().benchmarkStarting(info);
auto samples = user_code([&] {
return Detail::runPlanDispatch<Clock>( plan, *cfg, env );
return plan.template run<Clock>(*cfg, env);
});
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
+3 -10
View File
@@ -34,7 +34,7 @@ namespace Catch {
} // namespace Detail
#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
#if defined(_MSC_VER)
#if defined(_MSVC_VER)
#pragma optimize("", off)
#elif defined(__IAR_SYSTEMS_ICC__)
// For IAR the pragma only affects the following function
@@ -64,20 +64,13 @@ namespace Catch {
}
template <typename Fn, typename... Args>
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<!std::is_same<void, decltype(fn(args...))>::value> {
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<!std::is_same_v<void, decltype(fn(args...))>> {
deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...));
}
template <typename Fn, typename... Args>
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same_v<void, decltype(fn(args...))>> {
CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...);
// In the non-void case, we pass the result through `deoptimize_value`
// to force the compiler to keep it. We have no return value here,
// but add an optimizer barrier (ideally a memory clobber) to force
// the _side effects_ of the loop be visible (e.g. writes to globals).
// Note that writes to benchmark-locals can be optimized away, as
// we would expect in normal code.
Detail::optimizer_barrier();
}
} // namespace Benchmark
} // namespace Catch
@@ -36,17 +36,17 @@ namespace Catch {
samples.data(), samples.data() + samples.size() );
auto wrap_estimate = [](Estimate<double> e) {
return Estimate<FDuration>{
FDuration( e.point ),
FDuration( e.lower_bound ),
FDuration( e.upper_bound ),
e.confidence_interval,
return Estimate<FDuration> {
FDuration(e.point),
FDuration(e.lower_bound),
FDuration(e.upper_bound),
e.confidence_interval,
};
};
std::vector<FDuration> samples2;
samples2.reserve(samples.size());
for (auto s : samples) {
samples2.push_back( FDuration( s ) );
samples2.emplace_back( s );
}
return {
@@ -21,7 +21,7 @@ namespace Catch {
namespace Benchmark {
namespace Detail {
template <typename T, typename U>
static constexpr bool is_related_v = std::is_same<std::decay_t<T>, std::decay_t<U>>::value;
static constexpr bool is_related_v = std::is_same_v<std::decay_t<T>, std::decay_t<U>>;
/// We need to reinvent std::function because every piece of code that might add overhead
/// in a measurement context needs to have consistent performance characteristics so that we
@@ -41,7 +41,7 @@ namespace Catch {
callable& operator=(callable&&) = default;
};
template <typename Fun>
struct model final : public callable {
struct model : public callable {
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(fun_) {}
@@ -8,7 +8,6 @@
#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED
#define CATCH_BENCHMARK_STATS_HPP_INCLUDED
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp>
// The fwd decl & default specialization needs to be seen by VS2017 before
@@ -8,16 +8,14 @@
#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
namespace Catch {
#include <catch2/benchmark/catch_clock.hpp>
namespace Detail {
struct DummyTemplateArgPlaceholder;
}
namespace Catch {
// We cannot forward declare the type with default template argument
// multiple times, so it is split out into a separate header so that
// we can prevent multiple declarations in dependencies
template <typename Duration = Detail::DummyTemplateArgPlaceholder>
// we can prevent multiple declarations in dependees
template <typename Duration = Benchmark::FDuration>
struct BenchmarkStats;
} // end namespace Catch
@@ -14,7 +14,7 @@
namespace Catch {
namespace Benchmark {
namespace Detail {
struct optimized_away_error final : std::exception {
struct optimized_away_error : std::exception {
const char* what() const noexcept override;
};
+1 -9
View File
@@ -57,18 +57,16 @@
#include <catch2/internal/catch_config_counter.hpp>
#include <catch2/internal/catch_config_prefix_messages.hpp>
#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_config_uncaught_exceptions.hpp>
#include <catch2/internal/catch_config_wchar.hpp>
#include <catch2/internal/catch_console_colour.hpp>
#include <catch2/internal/catch_console_width.hpp>
#include <catch2/internal/catch_container_nonmembers.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_debug_console.hpp>
#include <catch2/internal/catch_debugger.hpp>
#include <catch2/internal/catch_decomposer.hpp>
#include <catch2/internal/catch_deprecation_macro.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/internal/catch_errno_guard.hpp>
#include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
@@ -79,17 +77,13 @@
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_list.hpp>
#include <catch2/internal/catch_logical_traits.hpp>
#include <catch2/internal/catch_message_info.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/internal/catch_optional.hpp>
#include <catch2/internal/catch_output_redirect.hpp>
#include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_path_filter.hpp>
#include <catch2/internal/catch_platform.hpp>
#include <catch2/internal/catch_polyfills.hpp>
#include <catch2/internal/catch_preprocessor.hpp>
@@ -112,7 +106,6 @@
#include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_stream_end_stop.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_template_test_registry.hpp>
#include <catch2/internal/catch_test_case_info_hasher.hpp>
@@ -123,7 +116,6 @@
#include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_thread_local.hpp>
#include <catch2/internal/catch_thread_support.hpp>
#include <catch2/internal/catch_to_string.hpp>
#include <catch2/internal/catch_uncaught_exceptions.hpp>
+13 -13
View File
@@ -29,7 +29,7 @@ namespace Catch {
Approx operator-() const;
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
Approx operator()( T const& value ) const {
Approx approx( static_cast<double>(value) );
approx.m_epsilon = m_epsilon;
@@ -38,67 +38,67 @@ namespace Catch {
return approx;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
explicit Approx( T const& value ): Approx(static_cast<double>(value))
{}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator == ( const T& lhs, Approx const& rhs ) {
auto lhs_v = static_cast<double>(lhs);
return rhs.equalityComparisonImpl(lhs_v);
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator == ( Approx const& lhs, const T& rhs ) {
return operator==( rhs, lhs );
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator != ( T const& lhs, Approx const& rhs ) {
return !operator==( lhs, rhs );
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator != ( Approx const& lhs, T const& rhs ) {
return !operator==( rhs, lhs );
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator <= ( T const& lhs, Approx const& rhs ) {
return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator <= ( Approx const& lhs, T const& rhs ) {
return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator >= ( T const& lhs, Approx const& rhs ) {
return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
friend bool operator >= ( Approx const& lhs, T const& rhs ) {
return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
Approx& epsilon( T const& newEpsilon ) {
const auto epsilonAsDouble = static_cast<double>(newEpsilon);
setEpsilon(epsilonAsDouble);
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
Approx& margin( T const& newMargin ) {
const auto marginAsDouble = static_cast<double>(newMargin);
setMargin(marginAsDouble);
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
template <typename T, typename = std::enable_if_t<std::is_constructible_v<double, T>>>
Approx& scale( T const& newScale ) {
m_scale = static_cast<double>(newScale);
return *this;
+4 -3
View File
@@ -10,16 +10,17 @@
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <string_view>
namespace Catch {
struct AssertionInfo {
// AssertionInfo() = delete;
StringRef macroName;
std::string_view macroName;
SourceLineInfo lineInfo;
StringRef capturedExpression;
std::string_view capturedExpression;
ResultDisposition::Flags resultDisposition;
};
+2 -2
View File
@@ -91,14 +91,14 @@ namespace Catch {
: expr;
}
StringRef AssertionResult::getMessage() const {
std::string_view AssertionResult::getMessage() const {
return m_resultData.message;
}
SourceLineInfo AssertionResult::getSourceInfo() const {
return m_info.lineInfo;
}
StringRef AssertionResult::getTestMacroName() const {
std::string_view AssertionResult::getTestMacroName() const {
return m_info.macroName;
}
+2 -3
View File
@@ -11,7 +11,6 @@
#include <catch2/catch_assertion_info.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <string>
@@ -46,9 +45,9 @@ namespace Catch {
std::string getExpressionInMacro() const;
bool hasExpandedExpression() const;
std::string getExpandedExpression() const;
StringRef getMessage() const;
std::string_view getMessage() const;
SourceLineInfo getSourceInfo() const;
StringRef getTestMacroName() const;
std::string_view getTestMacroName() const;
//protected:
AssertionInfo m_info;
+10 -43
View File
@@ -10,13 +10,13 @@
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
#include <catch2/internal/catch_getenv.hpp>
#include <fstream>
#include <optional>
namespace Catch {
@@ -34,7 +34,7 @@ namespace Catch {
std::string shardFilePath;
};
static Optional<bazelShardingOptions> readBazelShardingOptions() {
static std::optional<bazelShardingOptions> readBazelShardingOptions() {
const auto bazelShardIndex = Detail::getEnv( "TEST_SHARD_INDEX" );
const auto bazelShardTotal = Detail::getEnv( "TEST_TOTAL_SHARDS" );
const auto bazelShardInfoFile = Detail::getEnv( "TEST_SHARD_STATUS_FILE" );
@@ -89,14 +89,9 @@ namespace Catch {
return lhs.name == rhs.name &&
lhs.outputFilename == rhs.outputFilename &&
lhs.colourMode == rhs.colourMode &&
lhs.verbosity == rhs.verbosity &&
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
@@ -106,6 +101,9 @@ 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() ) {
@@ -121,8 +119,6 @@ namespace Catch {
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
}
// Reading bazel env vars can change some parts of the config data,
// so we have to process the bazel env before acting on the config.
if ( enableBazelEnvSupport() ) {
readBazelEnvVars();
}
@@ -146,7 +142,7 @@ namespace Catch {
// We do the default-output check separately, while always
// using the default output below to make the code simpler
// and avoid superfluous copies.
if ( reporterSpec.outputFile().none() ) {
if ( !reporterSpec.outputFile() ) {
CATCH_ENFORCE( !defaultOutputUsed,
"Internal error: cannot use default output for "
"multiple reporters" );
@@ -157,8 +153,7 @@ namespace Catch {
reporterSpec.name(),
reporterSpec.outputFile() ? *reporterSpec.outputFile()
: data.defaultOutputFilename,
reporterSpec.colourMode().valueOr( data.defaultColourMode ),
reporterSpec.verbosity().valueOr( data.verbosity ),
reporterSpec.colourMode().value_or( data.defaultColourMode ),
reporterSpec.customOptions() } );
}
}
@@ -172,8 +167,7 @@ namespace Catch {
bool Config::listListeners() const { return m_data.listListeners; }
std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
std::vector<PathFilter> const& Config::getPathFilters() const { return m_data.pathFilters; }
bool Config::useNewFilterBehaviour() const { return m_data.useNewPathFilteringBehaviour; }
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
std::vector<ReporterSpec> const& Config::getReporterSpecs() const {
return m_data.reporterSpecifications;
@@ -189,11 +183,9 @@ namespace Catch {
bool Config::showHelp() const { return m_data.showHelp; }
std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
// IConfig interface
bool Config::allowThrows() const { return !m_data.noThrow; }
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
std::string_view Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
bool Config::warnAboutMissingAssertions() const {
return !!( m_data.warnings & WarnAbout::NoAssertions );
@@ -201,15 +193,11 @@ 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; }
TestRunOrder Config::runOrder() const { return m_data.runOrder; }
uint32_t Config::rngSeed() const { return m_data.rngSeed; }
bool Config::rngSeedWasFixed() const { return m_data.rngSeedWasFixed; }
unsigned int Config::shardCount() const { return m_data.shardCount; }
unsigned int Config::shardIndex() const { return m_data.shardIndex; }
ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; }
@@ -235,7 +223,7 @@ namespace Catch {
if ( bazelOutputFile ) {
m_data.reporterSpecifications.push_back(
{ "junit", std::string( bazelOutputFile ), {}, {}, {} } );
{ "junit", std::string( bazelOutputFile ), {}, {} } );
}
const auto bazelTestSpec = Detail::getEnv( "TESTBRIDGE_TEST_ONLY" );
@@ -256,27 +244,6 @@ namespace Catch {
m_data.shardCount = bazelShardOptions->shardCount;
}
}
const auto bazelExitGuardFile = Detail::getEnv( "TEST_PREMATURE_EXIT_FILE" );
if (bazelExitGuardFile) {
m_data.prematureExitGuardFilePath = bazelExitGuardFile;
}
const auto bazelRandomSeed = Detail::getEnv( "TEST_RANDOM_SEED" );
if ( bazelRandomSeed ) {
auto parsedSeed = parseUInt( bazelRandomSeed, 0 );
if ( !parsedSeed ) {
// Currently we handle issues with parsing other Bazel Env
// options by warning and ignoring the issue. So we do the
// same for random seed option.
Catch::cerr()
<< "Warning: could not parse 'TEST_RANDOM_SEED' ('"
<< bazelRandomSeed << "') as proper seed.\n";
} else {
m_data.rngSeed = *parsedSeed;
m_data.rngSeedWasFixed = true;
}
}
}
} // end namespace Catch
+3 -16
View File
@@ -11,9 +11,6 @@
#include <catch2/catch_test_spec.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_optional.hpp>
#include <catch2/internal/catch_path_filter.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_random_seed_generation.hpp>
#include <catch2/internal/catch_reporter_spec_parser.hpp>
@@ -35,7 +32,6 @@ namespace Catch {
std::string name;
std::string outputFilename;
ColourMode colourMode;
Verbosity verbosity;
std::map<std::string, std::string> customOptions;
friend bool operator==( ProcessedReporterSpec const& lhs,
ProcessedReporterSpec const& rhs );
@@ -63,7 +59,6 @@ namespace Catch {
int abortAfter = -1;
uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default);
bool rngSeedWasFixed = false;
unsigned int shardCount = 1;
unsigned int shardIndex = 0;
@@ -89,10 +84,7 @@ namespace Catch {
std::vector<ReporterSpec> reporterSpecifications;
std::vector<std::string> testsOrTags;
std::vector<PathFilter> pathFilters;
bool useNewPathFilteringBehaviour = false;
std::string prematureExitGuardFilePath;
std::vector<std::string> sectionsToRun;
};
@@ -113,29 +105,24 @@ namespace Catch {
getProcessedReporterSpecs() const;
std::vector<std::string> const& getTestsOrTags() const override;
std::vector<PathFilter> const& getPathFilters() const override;
bool useNewFilterBehaviour() const override;
std::vector<std::string> const& getSectionsToRun() const override;
TestSpec const& testSpec() const override;
bool hasTestFilters() const override;
bool showHelp() const;
std::string const& getExitGuardFilePath() const;
// IConfig interface
bool allowThrows() const override;
StringRef name() const override;
std::string_view name() const override;
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;
TestRunOrder runOrder() const override;
uint32_t rngSeed() const override;
bool rngSeedWasFixed() const;
unsigned int shardCount() const override;
unsigned int shardIndex() const override;
ColourMode defaultColourMode() const override;
+11 -18
View File
@@ -5,8 +5,8 @@
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_message.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
@@ -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();
Detail::pushScopedMessage( CATCH_MOVE( info ) );
getResultCapture().pushScopedMessage( CATCH_MOVE(info) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
@@ -31,16 +31,15 @@ namespace Catch {
}
ScopedMessage::~ScopedMessage() {
if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); }
if ( !m_moved ) { getResultCapture().popScopedMessage( m_messageId ); }
}
Capturer::Capturer( StringRef macroName,
Capturer::Capturer( std::string_view macroName,
SourceLineInfo const& lineInfo,
ResultWas::OfType resultType,
StringRef names,
bool isScoped):
m_isScoped(isScoped) {
std::string_view names ):
m_resultCapture( getResultCapture() ) {
auto trimmed = [&] (size_t start, size_t end) {
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;
@@ -87,7 +86,7 @@ namespace Catch {
if (start != pos && openings.empty()) {
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message += trimmed(start, pos);
m_messages.back().message += " := "_sr;
m_messages.back().message += " := ";
start = pos;
}
break;
@@ -97,25 +96,19 @@ namespace Catch {
assert(openings.empty() && "Mismatched openings");
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message += trimmed(start, names.size() - 1);
m_messages.back().message += " := "_sr;
m_messages.back().message += " := ";
}
Capturer::~Capturer() {
assert( m_captured == m_messages.size() );
if ( m_isScoped ) {
for ( auto const& message : m_messages ) {
Detail::popScopedMessage( message.sequence );
}
for (auto const& message : m_messages) {
m_resultCapture.popScopedMessage( message.sequence );
}
}
void Capturer::captureValue( size_t index, std::string const& value ) {
assert( index < m_messages.size() );
m_messages[index].message += value;
if ( m_isScoped ) {
Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
} else {
Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
}
m_resultCapture.pushScopedMessage( CATCH_MOVE(m_messages[index]) );
m_captured++;
}
+23 -40
View File
@@ -13,28 +13,16 @@
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stream_end_stop.hpp>
#include <catch2/internal/catch_message_info.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/catch_tostring.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <string>
#include <vector>
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 {
@@ -48,7 +36,7 @@ namespace Catch {
};
struct MessageBuilder : MessageStream {
MessageBuilder( StringRef macroName,
MessageBuilder( std::string_view macroName,
SourceLineInfo const& lineInfo,
ResultWas::OfType type ):
m_info(macroName, lineInfo, type) {}
@@ -75,10 +63,10 @@ namespace Catch {
class Capturer {
std::vector<MessageInfo> m_messages;
IResultCapture& m_resultCapture;
size_t m_captured = 0;
bool m_isScoped = false;
public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names, bool isScoped );
Capturer( std::string_view macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, std::string_view names );
Capturer(Capturer const&) = delete;
Capturer& operator=(Capturer const&) = delete;
@@ -104,27 +92,26 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
do { \
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, std::string_view(), resultDisposition ); \
catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
catchAssertionHandler.complete(); \
} while( false )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_CAPTURE( varName, macroName, scopedCapture, ... ) \
Catch::Capturer varName( macroName##_catch_sr, \
CATCH_INTERNAL_LINEINFO, \
Catch::ResultWas::Info, \
#__VA_ARGS__##_catch_sr, \
scopedCapture ); \
#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
Catch::Capturer varName( macroName, \
CATCH_INTERNAL_LINEINFO, \
Catch::ResultWas::Info, \
#__VA_ARGS__ ); \
varName.captureValues( 0, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_INFO( macroName, log ) \
const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
Catch::Detail::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
@@ -132,32 +119,28 @@ 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", true, __VA_ARGS__ )
#define CATCH_UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_UNSCOPED_CAPTURE", false, __VA_ARGS__ )
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __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_UNSCOPED_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)
#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", true, __VA_ARGS__ )
#define UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "UNSCOPED_CAPTURE", false, __VA_ARGS__ )
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __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 UNSCOPED_CAPTURE( ... ) (void)(0)
#define INFO( msg ) (void)(0)
#define UNSCOPED_INFO( msg ) (void)(0)
#define WARN( msg ) (void)(0)
#define CAPTURE( ... ) (void)(0)
#endif // end of user facing macro declarations
+5 -3
View File
@@ -15,6 +15,7 @@
#include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_startup_exception_registry.hpp>
#include <catch2/internal/catch_singletons.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
@@ -71,9 +72,8 @@ namespace Catch {
CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
#endif
}
ITestCaseRegistry& getMutableTestCaseRegistry() override {
return m_testCaseRegistry;
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
return m_enumValuesRegistry;
}
private:
@@ -82,6 +82,7 @@ namespace Catch {
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry;
StartupExceptionRegistry m_exceptionRegistry;
Detail::EnumValuesRegistry m_enumValuesRegistry;
};
}
@@ -95,6 +96,7 @@ namespace Catch {
}
void cleanUp() {
cleanupSingletons();
cleanUpContext();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
-1
View File
@@ -10,7 +10,6 @@
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/catch_totals.hpp>
#include <string>
+3 -71
View File
@@ -21,14 +21,11 @@
#include <catch2/reporters/catch_reporter_multi.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <iomanip>
#include <set>
@@ -52,7 +49,6 @@ namespace Catch {
ReporterConfig( config,
makeStream( spec.outputFilename ),
spec.colourMode,
spec.verbosity,
spec.customOptions ) );
}
@@ -69,7 +65,6 @@ namespace Catch {
ReporterConfig( config,
makeStream( reporterSpec.outputFilename ),
reporterSpec.colourMode,
reporterSpec.verbosity,
reporterSpec.customOptions ) ) );
}
@@ -140,50 +135,8 @@ namespace Catch {
};
void applyFilenamesAsTags() {
getMutableRegistryHub().getMutableTestCaseRegistry().enableFilenameTags();
}
// Creates empty file at path. The path must be writable, we do not
// try to create directories in path because that's hard in C++14.
void setUpGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
#if defined( _MSC_VER )
std::FILE* file = nullptr;
if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) {
char msgBuffer[100];
const auto err = errno;
std::string errMsg;
if ( !strerror_s( msgBuffer, err ) ) {
errMsg = msgBuffer;
} else {
errMsg = "Could not translate errno to a string";
}
#else
std::FILE* file = std::fopen( guardFilePath.c_str(), "w" );
if ( !file ) {
const auto err = errno;
const char* errMsg = std::strerror( err );
#endif
CATCH_RUNTIME_ERROR( "Could not open the exit guard file '"
<< guardFilePath << "' because '"
<< errMsg << "' (" << err << ')' );
}
const int ret = std::fclose( file );
CATCH_ENFORCE(
ret == 0,
"Error when closing the exit guard file: " << ret );
}
}
// Removes file at path. Assuming we created it in setUpGuardFile.
void tearDownGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
const int ret = std::remove( guardFilePath.c_str() );
CATCH_ENFORCE(
ret == 0,
"Error when removing the exit guard file: " << ret );
for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) {
testInfo->addFilenameTag();
}
}
@@ -305,7 +258,6 @@ namespace Catch {
static_cast<void>(std::getchar());
}
int exitCode = runInternal();
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
static_cast<void>(std::getchar());
@@ -346,23 +298,6 @@ namespace Catch {
CATCH_TRY {
config(); // Force config to be constructed
if ( m_config->shardCount() > 1 &&
m_config->runOrder() == TestRunOrder::Randomized &&
!m_config->rngSeedWasFixed() ) {
Catch::cerr()
<< "Warning: using sharding (--shard-count) with random "
"order (--order rand, the default) and without a fixed "
"numeric --rng-seed does not guarantee disjoint coverage "
"between shard invocations. Pass the same numeric "
"--rng-seed to every shard, or use --order decl or "
"--order lex instead.\n"
<< std::flush;
}
// We need to retrieve potential Bazel config with the full Config
// constructor, so we have to create the guard file after it is created.
setUpGuardFile( m_config->getExitGuardFilePath() );
seedRng( *m_config );
if (m_configData.filenamesAsTags) {
@@ -392,12 +327,9 @@ namespace Catch {
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
auto const totals = tests.execute();
// If we got here, running the tests finished normally-enough.
// They might've failed, but that would've been reported elsewhere.
tearDownGuardFile( m_config->getExitGuardFilePath() );
if ( tests.hadUnmatchedTestSpecs()
&& m_config->warnAboutUnmatchedTestSpecs() ) {
// UnmatchedTestSpecExitCode
return UnmatchedTestSpecExitCode;
}
-1
View File
@@ -16,7 +16,6 @@
namespace Catch {
// TODO: Use C++17 `inline` variables
constexpr int UnspecifiedErrorExitCode = 1;
constexpr int NoTestsRunExitCode = 2;
constexpr int UnmatchedTestSpecExitCode = 3;
+19 -18
View File
@@ -14,6 +14,7 @@
#include <cassert>
#include <cctype>
#include <algorithm>
#include <string_view>
namespace Catch {
@@ -47,28 +48,28 @@ namespace Catch {
return tcp != TestCaseProperties::None;
}
TestCaseProperties parseSpecialTag( StringRef tag ) {
TestCaseProperties parseSpecialTag( std::string_view tag ) {
if( !tag.empty() && tag[0] == '.' )
return TestCaseProperties::IsHidden;
else if( tag == "!throws"_sr )
else if( tag == "!throws" )
return TestCaseProperties::Throws;
else if( tag == "!shouldfail"_sr )
else if( tag == "!shouldfail" )
return TestCaseProperties::ShouldFail;
else if( tag == "!mayfail"_sr )
else if( tag == "!mayfail" )
return TestCaseProperties::MayFail;
else if( tag == "!nonportable"_sr )
else if( tag == "!nonportable" )
return TestCaseProperties::NonPortable;
else if( tag == "!benchmark"_sr )
else if( tag == "!benchmark" )
return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden;
else
return TestCaseProperties::None;
}
bool isReservedTag( StringRef tag ) {
bool isReservedTag( std::string_view tag ) {
return parseSpecialTag( tag ) == TestCaseProperties::None
&& tag.size() > 0
&& !std::isalnum( static_cast<unsigned char>(tag[0]) );
}
void enforceNotReservedTag( StringRef tag, SourceLineInfo const& _lineInfo ) {
void enforceNotReservedTag( std::string_view tag, SourceLineInfo const& _lineInfo ) {
CATCH_ENFORCE( !isReservedTag(tag),
"Tag name: [" << tag << "] is not allowed.\n"
<< "Tag names starting with non alphanumeric characters are reserved\n"
@@ -80,13 +81,13 @@ namespace Catch {
return "Anonymous test case " + std::to_string(++counter);
}
constexpr StringRef extractFilenamePart(StringRef filename) {
constexpr std::string_view extractFilenamePart(std::string_view filename) {
size_t lastDot = filename.size();
while (lastDot > 0 && filename[lastDot - 1] != '.') {
--lastDot;
}
// In theory we could have filename without any extension in it
if ( lastDot == 0 ) { return StringRef(); }
if ( lastDot == 0 ) { return std::string_view(); }
--lastDot;
size_t nameStart = lastDot;
@@ -98,7 +99,7 @@ namespace Catch {
}
// Returns the upper bound on size of extra tags ([#file]+[.])
constexpr size_t sizeOfExtraTags(StringRef filepath) {
constexpr size_t sizeOfExtraTags(std::string_view filepath) {
// [.] is 3, [#] is another 3
const size_t extras = 3 + 3;
return extractFilenamePart(filepath).size() + extras;
@@ -115,20 +116,20 @@ namespace Catch {
}
Detail::unique_ptr<TestCaseInfo>
makeTestCaseInfo(StringRef _className,
makeTestCaseInfo(std::string_view _className,
NameAndTags const& nameAndTags,
SourceLineInfo const& _lineInfo ) {
return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo);
}
TestCaseInfo::TestCaseInfo(StringRef _className,
TestCaseInfo::TestCaseInfo(std::string_view _className,
NameAndTags const& _nameAndTags,
SourceLineInfo const& _lineInfo):
name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ),
className( _className ),
lineInfo( _lineInfo )
{
StringRef originalTags = _nameAndTags.tags;
std::string_view originalTags = _nameAndTags.tags;
// We need to reserve enough space to store all of the tags
// (including optional hidden tag and filename tag)
auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file);
@@ -163,7 +164,7 @@ namespace Catch {
// We need to check the tag for special meanings, copy
// it over to backing storage and actually reference the
// backing storage in the saved tags
StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
std::string_view tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
CATCH_ENFORCE( !tagStr.empty(),
"Found an empty tag while registering test case '"
<< _nameAndTags.name << "' at "
@@ -190,7 +191,7 @@ namespace Catch {
// Add [.] if relevant
if (isHidden()) {
internalAppendTag("."_sr);
internalAppendTag(".");
}
// Sort and prepare tags
@@ -235,13 +236,13 @@ namespace Catch {
return ret;
}
void TestCaseInfo::internalAppendTag(StringRef tagStr) {
void TestCaseInfo::internalAppendTag(std::string_view tagStr) {
backingTags += '[';
const auto backingStart = backingTags.size();
backingTags += tagStr;
const auto backingEnd = backingTags.size();
backingTags += ']';
tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart));
tags.emplace_back(std::string_view(backingTags.c_str() + backingStart, backingEnd - backingStart));
}
bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) {
+7 -7
View File
@@ -11,12 +11,12 @@
#include <catch2/interfaces/catch_interfaces_test_invoker.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
#ifdef __clang__
@@ -34,10 +34,10 @@ namespace Catch {
* as "cool-tag" internally.
*/
struct Tag {
constexpr Tag(StringRef original_):
constexpr Tag(std::string_view original_):
original(original_)
{}
StringRef original;
std::string_view original;
friend bool operator< ( Tag const& lhs, Tag const& rhs );
friend bool operator==( Tag const& lhs, Tag const& rhs );
@@ -67,7 +67,7 @@ namespace Catch {
*/
struct TestCaseInfo : Detail::NonCopyable {
TestCaseInfo(StringRef _className,
TestCaseInfo(std::string_view _className,
NameAndTags const& _nameAndTags,
SourceLineInfo const& _lineInfo);
@@ -87,12 +87,12 @@ namespace Catch {
std::string tagsAsString() const;
std::string name;
StringRef className;
std::string_view className;
private:
std::string backingTags;
// Internally we copy tags to the backing storage and then add
// refs to this storage to the tags vector.
void internalAppendTag(StringRef tagString);
void internalAppendTag(std::string_view tagString);
public:
std::vector<Tag> tags;
SourceLineInfo lineInfo;
@@ -130,7 +130,7 @@ namespace Catch {
};
Detail::unique_ptr<TestCaseInfo>
makeTestCaseInfo( StringRef className,
makeTestCaseInfo( std::string_view className,
NameAndTags const& nameAndTags,
SourceLineInfo const& lineInfo );
}
+3 -3
View File
@@ -8,13 +8,13 @@
#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED
#define CATCH_TEST_RUN_INFO_HPP_INCLUDED
#include <catch2/internal/catch_stringref.hpp>
#include <string_view>
namespace Catch {
struct TestRunInfo {
constexpr TestRunInfo(StringRef _name) : name(_name) {}
StringRef name;
constexpr TestRunInfo(std::string_view _name) : name(_name) {}
std::string_view name;
};
} // end namespace Catch
+23 -59
View File
@@ -13,6 +13,7 @@
#include <catch2/internal/catch_polyfills.hpp>
#include <iomanip>
#include <cstddef>
namespace Catch {
@@ -57,75 +58,45 @@ namespace Detail {
}
} // end unnamed namespace
std::size_t catch_strnlen( const char* str, std::size_t n ) {
auto ret = std::char_traits<char>::find( str, n, '\0' );
if ( ret != nullptr ) { return static_cast<std::size_t>( ret - str ); }
return n;
}
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 convertIntoString(std::string_view string, bool escapeInvisibles) {
std::string ret;
// This is enough for the "don't escape invisibles" case, and a good
// lower bound on the "escape invisibles" case.
ret.reserve( string.size() + 2 );
ret.reserve(string.size() + 2);
if ( !escapeInvisibles ) {
if (!escapeInvisibles) {
ret += '"';
ret += string;
ret += '"';
return ret;
}
size_t last_start = 0;
auto write_to = [&]( size_t idx ) {
if ( last_start < idx ) {
ret += string.substr( last_start, idx - last_start );
}
last_start = idx + 1;
};
ret += '"';
for ( size_t i = 0; i < string.size(); ++i ) {
const char c = string[i];
if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
write_to( i );
if ( c == '\r' ) { ret.append( "\\r" ); }
if ( c == '\n' ) { ret.append( "\\n" ); }
if ( c == '\t' ) { ret.append( "\\t" ); }
if ( c == '\f' ) { ret.append( "\\f" ); }
for (char c : string) {
switch (c) {
case '\r':
ret.append("\\r");
break;
case '\n':
ret.append("\\n");
break;
case '\t':
ret.append("\\t");
break;
case '\f':
ret.append("\\f");
break;
default:
ret.push_back(c);
break;
}
}
write_to( string.size() );
ret += '"';
return ret;
}
std::string convertIntoString(StringRef string) {
std::string convertIntoString(std::string_view string) {
return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles());
}
@@ -165,11 +136,9 @@ std::string StringMaker<std::string>::convert(const std::string& str) {
return Detail::convertIntoString( str );
}
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
std::string StringMaker<std::string_view>::convert(std::string_view str) {
return Detail::convertIntoString( StringRef( str.data(), str.size() ) );
return Detail::convertIntoString( std::string_view( str.data(), str.size() ) );
}
#endif
std::string StringMaker<char const*>::convert(char const* str) {
if (str) {
@@ -196,11 +165,9 @@ std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
return ::Catch::Detail::stringify(s);
}
# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) {
return StringMaker<std::wstring>::convert(std::wstring(str));
}
# endif
std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {
if (str) {
@@ -218,12 +185,9 @@ std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
}
#endif
#if defined(CATCH_CONFIG_CPP17_BYTE)
#include <cstddef>
std::string StringMaker<std::byte>::convert(std::byte value) {
return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
}
#endif // defined(CATCH_CONFIG_CPP17_BYTE)
std::string StringMaker<int>::convert(int value) {
return ::Catch::Detail::stringify(static_cast<long long>(value));
+80 -65
View File
@@ -8,7 +8,7 @@
#ifndef CATCH_TOSTRING_HPP_INCLUDED
#define CATCH_TOSTRING_HPP_INCLUDED
#include <ctime>
#include <vector>
#include <cstddef>
#include <type_traits>
@@ -18,12 +18,9 @@
#include <catch2/internal/catch_config_wchar.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_void_type.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
#include <string_view>
#endif
#ifdef _MSC_VER
#pragma warning(push)
@@ -41,18 +38,22 @@ namespace Catch {
namespace Detail {
std::size_t catch_strnlen(const char *str, std::size_t n);
inline std::size_t catch_strnlen(const char *str, std::size_t n) {
auto ret = std::char_traits<char>::find(str, n, '\0');
if (ret != nullptr) {
return static_cast<std::size_t>(ret - str);
}
return n;
}
std::string formatTimeT( std::time_t time );
constexpr StringRef unprintableString = "{?}"_sr;
constexpr std::string_view unprintableString = "{?}";
//! Encases `string in quotes, and optionally escapes invisibles
std::string convertIntoString( StringRef string, bool escapeInvisibles );
std::string convertIntoString( std::string_view string, bool escapeInvisibles );
//! Encases `string` in quotes, and escapes invisibles if user requested
//! it via CLI
std::string convertIntoString( StringRef string );
std::string convertIntoString( std::string_view string );
std::string rawMemoryToString( const void *object, std::size_t size );
@@ -75,13 +76,13 @@ namespace Catch {
template<typename T>
std::enable_if_t<
!std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
!std::is_enum_v<T> && !std::is_base_of_v<std::exception, T>,
std::string> convertUnstreamable( T const& ) {
return std::string(Detail::unprintableString);
}
template<typename T>
std::enable_if_t<
!std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
!std::is_enum_v<T> && std::is_base_of_v<std::exception, T>,
std::string> convertUnstreamable(T const& ex) {
return ex.what();
}
@@ -89,7 +90,7 @@ namespace Catch {
template<typename T>
std::enable_if_t<
std::is_enum<T>::value,
std::is_enum_v<T>,
std::string> convertUnstreamable( T const& value ) {
return convertUnknownEnumToString( value );
}
@@ -170,12 +171,10 @@ namespace Catch {
static std::string convert(const std::string& str);
};
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
template<>
struct StringMaker<std::string_view> {
static std::string convert(std::string_view str);
};
#endif
template<>
struct StringMaker<char const *> {
@@ -192,12 +191,10 @@ namespace Catch {
static std::string convert(const std::wstring& wstr);
};
# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
template<>
struct StringMaker<std::wstring_view> {
static std::string convert(std::wstring_view str);
};
# endif
template<>
struct StringMaker<wchar_t const *> {
@@ -213,7 +210,7 @@ namespace Catch {
struct StringMaker<char[SZ]> {
static std::string convert(char const* str) {
return Detail::convertIntoString(
StringRef( str, Detail::catch_strnlen( str, SZ ) ) );
std::string_view( str, Detail::catch_strnlen( str, SZ ) ) );
}
};
template<size_t SZ>
@@ -221,7 +218,7 @@ namespace Catch {
static std::string convert(signed char const* str) {
auto reinterpreted = reinterpret_cast<char const*>(str);
return Detail::convertIntoString(
StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
std::string_view(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
}
};
template<size_t SZ>
@@ -229,16 +226,14 @@ namespace Catch {
static std::string convert(unsigned char const* str) {
auto reinterpreted = reinterpret_cast<char const*>(str);
return Detail::convertIntoString(
StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
std::string_view(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
}
};
#if defined(CATCH_CONFIG_CPP17_BYTE)
template<>
struct StringMaker<std::byte> {
static std::string convert(std::byte value);
};
#endif // defined(CATCH_CONFIG_CPP17_BYTE)
template<>
struct StringMaker<int> {
static std::string convert(int value);
@@ -384,10 +379,7 @@ namespace Catch {
}
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
#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 )
#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER)
#include <optional>
namespace Catch {
template<typename T>
@@ -411,41 +403,47 @@ namespace Catch {
// Separate std::tuple specialization
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
# include <tuple>
# include <utility>
#include <tuple>
namespace Catch {
namespace Detail {
template <typename Tuple, std::size_t... Is>
void PrintTuple( const Tuple& tuple,
std::ostream& os,
std::index_sequence<Is...> ) {
// 1 + Account for when the tuple is empty
char a[1 + sizeof...( Is )] = {
( ( os << ( Is ? ", " : " " )
<< ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
'\0' )... };
(void)a;
}
template<
typename Tuple,
std::size_t N = 0,
bool = (N < std::tuple_size_v<Tuple>)
>
struct TupleElementPrinter {
static void print(const Tuple& tuple, std::ostream& os) {
os << (N ? ", " : " ")
<< ::Catch::Detail::stringify(std::get<N>(tuple));
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
}
};
} // namespace Detail
template<
typename Tuple,
std::size_t N
>
struct TupleElementPrinter<Tuple, N, false> {
static void print(const Tuple&, std::ostream&) {}
};
template <typename... Types>
}
template<typename ...Types>
struct StringMaker<std::tuple<Types...>> {
static std::string convert( const std::tuple<Types...>& tuple ) {
static std::string convert(const std::tuple<Types...>& tuple) {
ReusableStringStream rss;
rss << '{';
Detail::PrintTuple(
tuple,
rss.get(),
std::make_index_sequence<sizeof...( Types )>{} );
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
rss << " }";
return rss.str();
}
};
} // namespace Catch
}
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER)
#include <variant>
namespace Catch {
template<>
@@ -629,26 +627,43 @@ struct ratio_string<std::milli> {
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 );
return ::Catch::Detail::formatTimeT( as_time_t );
#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);
}
};
}
#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
namespace Catch { \
template <> \
struct StringMaker<enumName> { \
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<std::string>( \
enumInfo.lookup( static_cast<int64_t>( value ) ) ); \
} \
}; \
}
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
namespace Catch { \
template<> struct StringMaker<enumName> { \
static std::string convert( enumName value ) { \
static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
} \
}; \
}
#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
+1 -3
View File
@@ -22,7 +22,7 @@ namespace Catch {
class ExceptionTranslatorRegistrar {
template<typename T>
class ExceptionTranslator final : public IExceptionTranslator {
class ExceptionTranslator : public IExceptionTranslator {
public:
constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) )
@@ -41,8 +41,6 @@ namespace Catch {
return m_translateFunction( ex );
}
#else
(void)it;
(void)itEnd;
return "You should never get here!";
#endif
}
+5 -55
View File
@@ -60,56 +60,6 @@
#cmakedefine CATCH_CONFIG_CPP17_BYTE
#cmakedefine CATCH_CONFIG_NO_CPP17_BYTE
#if defined( CATCH_CONFIG_CPP17_BYTE ) && \
defined( CATCH_CONFIG_NO_CPP17_BYTE )
# error Cannot force CPP17_BYTE to both ON and OFF
#endif
#cmakedefine CATCH_CONFIG_CPP17_OPTIONAL
#cmakedefine CATCH_CONFIG_NO_CPP17_OPTIONAL
#if defined( CATCH_CONFIG_CPP17_OPTIONAL ) && \
defined( CATCH_CONFIG_NO_CPP17_OPTIONAL )
# error Cannot force CPP17_OPTIONAL to both ON and OFF
#endif
#cmakedefine CATCH_CONFIG_CPP17_STRING_VIEW
#cmakedefine CATCH_CONFIG_NO_CPP17_STRING_VIEW
#if defined( CATCH_CONFIG_CPP17_STRING_VIEW ) && \
defined( CATCH_CONFIG_NO_CPP17_STRING_VIEW )
# error Cannot force CPP17_STRING_VIEW to both ON and OFF
#endif
#cmakedefine CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
#cmakedefine CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS
#if defined( CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS ) && \
defined( CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS )
# error Cannot force CPP17_UNCAUGHT_EXCEPTIONS to both ON and OFF
#endif
#cmakedefine CATCH_CONFIG_CPP17_VARIANT
#cmakedefine CATCH_CONFIG_NO_CPP17_VARIANT
#if defined( CATCH_CONFIG_CPP17_VARIANT ) && \
defined( CATCH_CONFIG_NO_CPP17_VARIANT )
# error Cannot force CPP17_VARIANT to both ON and OFF
#endif
#cmakedefine CATCH_CONFIG_GLOBAL_NEXTAFTER
#cmakedefine CATCH_CONFIG_NO_GLOBAL_NEXTAFTER
@@ -196,12 +146,12 @@
#endif
#cmakedefine CATCH_CONFIG_THREAD_SAFE_ASSERTIONS
#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS
#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS
#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS
#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
#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
#endif
+1 -1
View File
@@ -36,7 +36,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 16, 0, "", 0 );
static Version version( 3, 10, 0, "", 0 );
return version;
}
+1 -1
View File
@@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 16
#define CATCH_VERSION_MINOR 10
#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
+12 -2
View File
@@ -7,6 +7,8 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/generators/catch_generator_exception.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
namespace Catch {
@@ -15,13 +17,21 @@ 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 ) {
IGeneratorTracker* acquireGeneratorTracker(std::string_view generatorName, SourceLineInfo const& lineInfo ) {
return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
}
IGeneratorTracker* createGeneratorTracker( StringRef generatorName,
IGeneratorTracker* createGeneratorTracker( std::string_view generatorName,
SourceLineInfo lineInfo,
GeneratorBasePtr&& generator ) {
return getResultCapture().createGeneratorTracker(
+14 -31
View File
@@ -9,10 +9,8 @@
#define CATCH_GENERATORS_HPP_INCLUDED
#include <catch2/catch_tostring.hpp>
#include <catch2/generators/catch_generators_throw.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_unique_name.hpp>
@@ -23,6 +21,14 @@ namespace Catch {
namespace Generators {
namespace Detail {
//! Throws GeneratorException with the provided message
[[noreturn]]
void throw_generator_exception(char const * msg);
} // end namespace detail
template<typename T>
class IGenerator : public GeneratorUntypedBase {
std::string stringifyImpl() const override {
@@ -57,9 +63,6 @@ namespace Generators {
bool next() {
return m_generator->countedNext();
}
bool isFinite() const { return m_generator->isFinite(); }
void skipToNthElement( size_t n ) { m_generator->skipToNthElement(n); }
};
@@ -80,26 +83,15 @@ namespace Generators {
bool next() override {
return false;
}
bool isFinite() const override { return true; }
};
template<typename T>
class FixedValuesGenerator final : public IGenerator<T> {
static_assert(!std::is_same<T, bool>::value,
static_assert(!std::is_same_v<T, bool>,
"FixedValuesGenerator does not support bools because of std::vector<bool>"
"specialization, use SingleValue Generator instead.");
std::vector<T> m_values;
size_t m_idx = 0;
void skipToNthElementImpl( std::size_t n ) override {
if ( n >= m_values.size() ) {
Detail::throw_generator_exception(
"Could not jump to Nth element: not enough elements" );
}
m_idx = n;
}
public:
FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
@@ -110,8 +102,6 @@ namespace Generators {
++m_idx;
return m_idx < m_values.size();
}
bool isFinite() const override { return true; }
};
template <typename T, typename DecayedT = std::decay_t<T>>
@@ -140,7 +130,7 @@ namespace Generators {
m_generators.emplace_back( value( CATCH_MOVE( val ) ) );
}
template <typename U>
std::enable_if_t<!std::is_same<std::decay_t<U>, T>::value>
std::enable_if_t<!std::is_same_v<std::decay_t<U>, T>>
add_generator( U&& val ) {
add_generator( T( CATCH_FORWARD( val ) ) );
}
@@ -176,13 +166,6 @@ namespace Generators {
}
return m_current < m_generators.size();
}
bool isFinite() const override {
for (auto const& gen : m_generators) {
if (!gen.isFinite()) { return false; }
}
return true;
}
};
@@ -213,14 +196,14 @@ namespace Generators {
return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... );
}
IGeneratorTracker* acquireGeneratorTracker( StringRef generatorName,
IGeneratorTracker* acquireGeneratorTracker( std::string_view generatorName,
SourceLineInfo const& lineInfo );
IGeneratorTracker* createGeneratorTracker( StringRef generatorName,
IGeneratorTracker* createGeneratorTracker( std::string_view generatorName,
SourceLineInfo lineInfo,
GeneratorBasePtr&& generator );
template<typename L>
auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> typename decltype(generatorExpression())::type {
auto generate( std::string_view generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> typename decltype(generatorExpression())::type {
using UnderlyingType = typename decltype(generatorExpression())::type;
IGeneratorTracker* tracker = acquireGeneratorTracker( generatorName, lineInfo );
@@ -241,7 +224,7 @@ namespace Generators {
} // namespace Generators
} // namespace Catch
#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr
#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__
#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__)
#define GENERATE( ... ) \
@@ -11,7 +11,6 @@
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_optional.hpp>
#include <cassert>
@@ -23,17 +22,6 @@ namespace Generators {
GeneratorWrapper<T> 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(
"Could not jump to Nth element: not enough elements" );
}
m_generator.skipToNthElement( n );
m_returned = n;
}
public:
TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
m_generator(CATCH_MOVE(generator)),
@@ -58,8 +46,6 @@ namespace Generators {
}
return success;
}
bool isFinite() const override { return true; }
};
template <typename T>
@@ -72,7 +58,7 @@ namespace Generators {
class FilterGenerator final : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
Predicate m_predicate;
static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
static_assert(!std::is_reference_v<Predicate>, "This would most likely result in a dangling reference");
public:
template <typename P>
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
@@ -101,8 +87,6 @@ namespace Generators {
while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
return success;
}
bool isFinite() const override { return m_generator.isFinite(); }
};
@@ -113,7 +97,7 @@ namespace Generators {
template <typename T>
class RepeatGenerator final : public IGenerator<T> {
static_assert(!std::is_same<T, bool>::value,
static_assert(!std::is_same_v<T, bool>,
"RepeatGenerator currently does not support bools"
"because of std::vector<bool> specialization");
GeneratorWrapper<T> m_generator;
@@ -127,9 +111,6 @@ 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 {
@@ -163,8 +144,6 @@ namespace Generators {
}
return m_current_repeat < m_target_repeats;
}
bool isFinite() const override { return m_generator.isFinite(); }
};
template <typename T>
@@ -178,30 +157,25 @@ namespace Generators {
GeneratorWrapper<U> m_generator;
Func m_function;
// To avoid returning dangling reference, we have to save the values
mutable Optional<T> m_cache;
void skipToNthElementImpl( std::size_t n ) override {
m_generator.skipToNthElement( n );
m_cache.reset();
}
T m_cache;
public:
template <typename F2 = Func>
MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
m_generator(CATCH_MOVE(generator)),
m_function(CATCH_FORWARD(function))
m_function(CATCH_FORWARD(function)),
m_cache(m_function(m_generator.get()))
{}
T const& get() const override {
if ( !m_cache ) { m_cache = m_function( m_generator.get() ); }
return *m_cache;
return m_cache;
}
bool next() override {
m_cache.reset();
return m_generator.next();
const auto success = m_generator.next();
if (success) {
m_cache = m_function(m_generator.get());
}
return success;
}
bool isFinite() const override { return m_generator.isFinite(); }
};
template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
@@ -223,6 +197,7 @@ namespace Generators {
std::vector<T> m_chunk;
size_t m_chunk_size;
GeneratorWrapper<T> m_generator;
bool m_used_up = false;
public:
ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
m_chunk_size(size), m_generator(CATCH_MOVE(generator))
@@ -251,8 +226,6 @@ namespace Generators {
}
return true;
}
bool isFinite() const override { return m_generator.isFinite(); }
};
template <typename T>
@@ -262,56 +235,6 @@ namespace Generators {
);
}
template <typename T>
class ConcatGenerator final : public IGenerator<T> {
std::vector<GeneratorWrapper<T>> m_generators;
size_t m_current_generator = 0;
void InsertGenerators( GeneratorWrapper<T>&& gen ) {
m_generators.push_back( CATCH_MOVE( gen ) );
}
template <typename... Generators>
void InsertGenerators( GeneratorWrapper<T>&& gen, Generators&&... gens ) {
m_generators.push_back( CATCH_MOVE( gen ) );
InsertGenerators( CATCH_MOVE( gens )... );
}
public:
template <typename... Generators>
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 <typename T, typename... Generators>
GeneratorWrapper<T> cat( GeneratorWrapper<T>&& generator,
Generators&&... generators ) {
return GeneratorWrapper<T>(
Catch::Detail::make_unique<ConcatGenerator<T>>(
CATCH_MOVE( generator ), CATCH_MOVE( generators )... ) );
}
} // namespace Generators
} // namespace Catch

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