Compare commits

...

52 Commits

Author SHA1 Message Date
Martin Hořeňovský 8b08d4d795 v3.15.3 2026-07-26 22:19:31 +02:00
Martin Hořeňovský 1079da4c5f Avoid quadratic JSON array parse behaviour in catch_discover_tests
Using CMake's `string(JSON` to parse JSON array leads to quadratic
running time in number of tests, see https://gitlab.kitware.com/cmake/cmake/-/work_items/27985

This leads to _terrible_ runtime for `catch_discover_tests` when called
on binaries with lot of tests (1k+). To get reasonable runtimes, we have
to avoid using `string(JSON` to parse out the individual test objects
from the array with all tests.

This commit replaces the sane approach of using real JSON parser with
a set of terrible hacks, where we use CMake's string APIs to split the
JSON array on what looks like object boundary (`}<ws>*,<ws>*{`), and then
checking whether the resulting thing can be parsed as JSON object. If not,
we append the next piece and check again. And again, and again, until we
get a proper JSON object.

This is all around a hilariously terrible idea, however:

1) It works in practice for all tested inputs.
2) It improves the time it takes to run `catch_discover_tests` on binary
   with 1k tests from 4.2s to 1.1s and 2k tests from 16s to 3.9s.
2026-07-26 19:51:58 +02:00
Martin Hořeňovský 60c8b87829 Avoid n^2 behaviour when appending script commands in catch_discover_tests
Repeatedly calling `string(APPEND` on the same destination string
leads to quadratic running time. We avoid this by flushing the CTest
script into a file periodically, currently every 50kB of text.

This can cause _slight_ slowdown just around the flush boundary (each
file write is quite expensive, so if we flush just before the last test
is written, it hurts), but it avoids terrible performance for large
test suites.

The performance is roughly equal at 500 tests to discover, and clearly
wins at more; for 1/2/4/8 k tests, the improvements are 0.3/1.6/6.4/27 s.
2026-07-26 15:50:33 +02:00
Martin Hořeňovský 64a551e2e7 Optimize adding test commands in catch_discover_tests
The previous approach was for `add_command` to behave as append function
via concatenating the command string internally and then saving it into
`PARENT_SCOPE`. Because this in practice ended up meaning concatenating
a copy of the string inside the function and then overwriting the string
outside the function, the performance was lacking.

The new approach is for `prepare_command` to only escape & return the
command from single call, and the caller is responsible for concatenating
the result. Since the actual concatenation no longer crosses function
scope boundaries, the performance is much better, even though the scaling
is still quadratic.

The new approach only takes 1/4-1/5 of the time, saving ~1s at 1k tests,
4.5s at 2k tests and 19s at 4k tests.
2026-07-26 15:50:31 +02:00
Martin Hořeňovský 2b971368dd Determinize the test registration order in catch_discover_tests 2026-07-26 15:23:39 +02:00
Martin Hořeňovský f5db82a5c4 Add Python script for benchmarking catch_discover_tests 2026-07-24 23:53:50 +02:00
Martin Hořeňovský dcbb2d5d84 Fix bad ToC link in docs/command-line.md 2026-07-24 23:52:44 +02:00
dvir arad 1a9625a6e7 Fix typo in generator exception message ("Coud" -> "Could")
The "Could not jump to Nth element: not enough elements" message thrown
by throw_generator_exception() had "Coud" misspelled in all three places
it appears (catch_generators.hpp, catch_generators_adapters.hpp, and
catch_interfaces_generatortracker.cpp).
2026-07-24 17:12:50 +02:00
Martin Hořeňovský bb8873ccd7 JSONWriter uses LUT for faster checking if a char needs escaping
This provides nice speedup when writing strings that don't need
escaping, at about 6% in Debug build and ~40% in Release build.
If the strings do need escaping the speedup is much smaller, at
~1% in Debug and ~6% in Release build, as the cost of actually
escaping the strings dwarves the cost of checking.
2026-07-23 19:10:14 +02:00
Martin Hořeňovský 46bdf1a04c Optimize writing of non-string values to JSON
In practice, the majority of values the JSON writer handles are
strings, but this makes the code more obvious and can still provide
a small speed-up.
2026-07-23 14:34:43 +02:00
Martin Hořeňovský c267251e70 Better support for infs and NaNs in JSON writer
As the types and values sent into the writer are determined by Catch2,
I do not expect to actually need this support, but it is better to have
it and not be surprised in the future.
2026-07-23 14:26:26 +02:00
Martin Hořeňovský 0cc833ea95 Make JSONWriter's number handling locale-independent
Closes #3176
2026-07-23 00:10:28 +02:00
Martin Hořeňovský 8494e2dce4 Add TEST_CASE with newlines & stuff to the catch_discover_tests's tests 2026-07-22 21:11:58 +02:00
Phil Nash eb431764b4 Document that DISCOVERY_MODE PRE_TEST should be used with catch_discover_tests when used with XCode.
This addresses https://github.com/catchorg/Catch2/issues/2411 by making the "workaround" the official approach and is consistent with how gtest and cmake deal with the same situation.
2026-07-22 20:50:17 +02:00
Manuel Knörle ae5d271da2 Remove debug message for environment variable check 2026-07-15 11:19:22 +02:00
Nikolay Baklicharov a15f718c82 Optimize JSON parsing in catch_discover_tests() 2026-07-08 10:29:59 +02:00
Martin Hořeňovský 191fa38c9b v3.15.2 2026-07-07 20:44:33 +02:00
Martin Hořeňovský dd94b9a780 Fix --warn InfiniteGenerators firing even if the generator was filtered 2026-07-07 14:09:11 +02:00
Martin Hořeňovský 9f7c9f6872 Add extra test binary & test for CATCH_CONFIG_FAST_COMPILE
Closes #3100
2026-07-06 11:03:47 +02:00
Martin Hořeňovský 919385f704 Fix some test binaries in ExtraTests not having warnings enabled 2026-07-06 10:22:55 +02:00
Martin Hořeňovský 15d52830ee Fix -Wunused-parameter warning with exceptions disabled
Closes #3114
2026-07-05 12:08:06 +02:00
Martin Hořeňovský 9ec44dd62b catch_discover_tests uses tempfile to retrieve JSON from the binary
This allows it to deal with badly behaved code, where 3rd party
dependencies write into stdout during global construction.

Closes #3162
Closes #3166
2026-07-04 16:36:05 +02:00
Martin Hořeňovský 675f9eaeb1 Add LLM policy to contributing.md 2026-06-14 20:25:57 +02:00
Martin Hořeňovský bcfb10e498 v3.15.1 2026-06-14 10:57:30 +02:00
Dominic Koepke b7e0310fbe fix: move struct TestName of INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2 into the anonymous namespace 2026-06-09 19:17:14 +02:00
Dominic Koepke 6d4ea62200 fix: move struct TestName of INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2 into the anonymous namespace 2026-06-09 19:17:14 +02:00
offa 195231c59a docs: Set missing syntax language 2026-05-31 17:17:46 +02:00
offa 3a865e8c0f docs: Enable syntax coloring for code example 2026-05-31 17:17:46 +02:00
Dirk Müller 69e0473f6e Avoid a potential underflow when iterating over invalid inputs
The forward iteration logic already bounds-check for m_it != m_string->end(),
do the same for the backward iteration. The issue with the assert is
that the assert() might not be compiled in, and it is happening after
the dereference, so it was too late.
2026-05-14 10:58:09 +02:00
Martin Hořeňovský 6ee0826dca v3.15.0 2026-05-12 13:16:46 +02:00
Martin Hořeňovský 47ea57d74f Constexpr matching support in the range equals matchers 2026-05-12 11:05:33 +02:00
Martin Hořeňovský d838f88b9c Constexpr matching support in generic Contains matchers 2026-05-12 11:05:31 +02:00
Martin Hořeňovský c267b6eb4d Constexpr matching support in the quantifier matchers 2026-05-12 11:05:31 +02:00
Martin Hořeňovský 3cdae5faf0 Constexpr matching support in IsEmpty and SizeIs matchers 2026-05-12 11:05:29 +02:00
Martin Hořeňovský 651247c7f4 Support for constexpr matchers in C++20 (P0784)
To make this all work, I had to remove the stringification cache
from matchers. In theory, this can cause performance penalty in
cases where single matcher instance is stringified multiple times,
but in practice this does not happen much, and the difference is
surprisingly small anyway, because the performance of stringification
is already horrible and full of allocating strings just to throw
them away.

The matcher combinators need P2738 from C++26 to be `constexpr`.

Closes #3091
2026-05-12 11:05:02 +02:00
Martin Hořeňovský 15b9393f0f Don't check if __cplusplus is defined 2026-05-10 20:48:58 +02:00
Martin Hořeňovský a18badd10f Workaround P3168 causing ambiguous overload issues with StringMaker
P3168 turned `std::optional` into a range type, so the partial specialization
of `StringMaker` for `std::optional<T>` conflicted with the partial
specialization for range types. Ideally we will fix this in the future
to support user-provided partial specializations for range-like types,
but for now we just disable the partial specialization for `std::optional<T>`
if P3168 is implemented.
2026-05-09 22:24:12 +02:00
Martin Hořeňovský 54af40652a Add more runtime benchmark recipe TODOs 2026-05-07 21:13:11 +02:00
Martin Hořeňovský 5f47160bb2 Add benchmark for catch_test_macros.hpp inclusion 2026-05-07 20:27:09 +02:00
Martin Hořeňovský e83528c6eb Add readme to benchmarks/ with some simple examples 2026-05-07 10:43:24 +02:00
Martin Hořeňovský fd31a62547 Add benchmark for REQUIRE_THAT into runtime_assertion_benches.cpp
I also changed the names of other benchmark `TEST_CASE`s so that
they are easier to use.
2026-05-07 10:18:21 +02:00
Martin Hořeňovský 4df8fee92d IConfig derives from NonCopyable privately 2026-05-07 10:12:28 +02:00
Ole Schmidt 300c5d3eed Fix Wconversion warning in catch_reporter_helpers.cpp 2026-05-06 09:58:01 +02:00
Martin Hořeňovský 11a96e186a Cleanups from static analysis 2026-04-24 11:21:04 +02:00
Martin Hořeňovský 10f62484bf AssertionHandler uses RunContext directly to avoid virtual dispatch 2026-04-13 22:56:55 +02:00
Martin Hořeňovský be2dfb45cc Update Doxyfile 2026-04-08 15:06:36 +02:00
Martin Hořeňovský 47200ddbee Rework internals of CATCH_REGISTER_ENUM
The old internals reached into the global hub to stash the allocation(s)
for enum value -> string value there, then kept around a potentially
invalid (in case the hub was cleaned up) reference into it, going through
whole bunch of virtual dispatch in the process.

The new internals just store the data in a static variable inside the
`StringMaker` specialization. This avoids potential lifetime issues,
avoids all virtual dispatch and (almost) reduces the include bloat in
the main header path.

The reason for (almost) there is that for full include correctness,
`EnumInfo` needs `<utility>` include for `std::pair`. However, this brings
in things like `std::relops`, because the std headers in C++ are dumb.
As this was not included before, and instead we relied on `std::pair`
existing in an internal stdlib header that we transitively included, the
full include size ends up bigger than before.
2026-04-08 12:57:09 +02:00
Martin Hořeňovský e51dcdcc59 Mark TestInvoker{AsMethod,Fixture} as final 2026-04-07 14:36:08 +02:00
Martin Hořeňovský 77eae8bd2a Turn IResultCapture::lastAssertionPassed into a free function
The thread-safety changes in assertions & messages turned whether
the last assertion passed or failed into thread-local state,
instead of being member of the `RunContext`. However, this change
was not reflected in the API `CHECKED_IF`/`CHECKED_ELSE` used,
which in turn required `catch_test_macro_impl.hpp` to include
`catch_interfaces_capture.hpp` for it.

Thanks to the combination of this commit and the previous similar
commit for the message stack handling, the main include path does
not need to include `catch_interfaces_capture.hpp` anymore.
2026-04-07 14:33:39 +02:00
Martin Hořeňovský 3ab0d7cef3 Turn IResultCapture::{push,pop}*Message into free functions
The thread-safety changes in assertions & messages turned the message
stacks into thread-local state, instead of it being member of `RunContext`.
The relevant {push,pop}(Un)ScopedMessage functions were turned from member
functions into static functions, but this left `catch_message.hpp` with
dependency on `IResultCapture`'s definition for the functions, and thus
it still had to include `catch_interfaces_capture.hpp`.

With this change, `catch_message.hpp` no longer needs
`catch_interfaces_capture.hpp`.
2026-04-07 14:28:11 +02:00
Martin Hořeňovský e83218c2df Move all use of IResultCapture in AssertionHandler into the cpp file
This has two advantages

1) Removing the include of `catch_interfaces_capture.hpp` from the
   header and potentially allowing further pruning later.
2) Providing small-but-measurable 1-2% speedup for assertions.
2026-04-07 09:32:07 +02:00
Martin Hořeňovský 548e14a8c8 Mark some extra classes final 2026-04-07 00:13:17 +02:00
120 changed files with 4052 additions and 1339 deletions
+3
View File
@@ -32,6 +32,7 @@ Checks: >-
-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,
@@ -58,6 +59,8 @@ 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,
+1 -1
View File
@@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
VERSION 3.14.0 # CML version placeholder, don't delete
VERSION 3.15.3 # CML version placeholder, don't delete
LANGUAGES CXX
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
DESCRIPTION "A modern, C++-native, unit test framework."
+432 -266
View File
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -9,8 +9,13 @@ add_executable(AssertionsSlowPath
assertion_listener.cpp
)
add_executable(EmptyExecutable
only_include.cpp
)
target_link_libraries(AssertionsFastPath PRIVATE Catch2::Catch2WithMain)
target_link_libraries(AssertionsSlowPath PRIVATE Catch2::Catch2WithMain)
target_link_libraries(EmptyExecutable PRIVATE Catch2::Catch2WithMain)
list(APPEND CATCH_TEST_TARGETS AssertionsFastPath AssertionsSlowPath)
list(APPEND CATCH_TEST_TARGETS AssertionsFastPath AssertionsSlowPath EmptyExecutable)
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
@@ -0,0 +1,200 @@
#!/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 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]
#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) -> 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.
"""
doc_copy = template_doc.copy()
template_tests = template_doc['listings']['tests']
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")
with open(listing_path, "w", encoding="utf-8") as f:
synthesize_listing(template_doc, count, f)
for add_tags in tag_modes:
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
@@ -0,0 +1,40 @@
# 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}")
@@ -0,0 +1,134 @@
{
"version": 1,
"metadata": {
"name": "benchmark-template",
"catch2-version": "3.15.2"
},
"listings": {
"tests": [
{
"name": "Comparing function pointers",
"class-name": "",
"tags": [
"function pointer",
"Tricky"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp",
"line": 266
}
},
{
"name": "Testing checked-if 4",
"class-name": "",
"tags": [
"!shouldfail",
"checked-if"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp",
"line": 216
}
},
{
"name": "count_equidistant_floats - double",
"class-name": "",
"tags": [
"approvals",
"distance",
"floating-point"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp",
"line": 103
}
},
{
"name": "Usage of AllTrue range matcher",
"class-name": "",
"tags": [
"matchers",
"quantifiers",
"templated"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp",
"line": 372
}
},
{
"name": "Exception matchers that succeed",
"class-name": "",
"tags": [
"!throws",
"exceptions",
"matchers"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp",
"line": 428
}
},
{
"name": "A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - float",
"class-name": "Template_Fixture",
"tags": [
"class",
"template"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/UsageTests/Class.tests.cpp",
"line": 82
}
},
{
"name": "Approximate PI",
"class-name": "",
"tags": [
"Approx",
"PI"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/UsageTests/Approx.tests.cpp",
"line": 134
}
},
{
"name": "TextFlow::Column respects width setting",
"class-name": "",
"tags": [
"approvals",
"column",
"TextFlow"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp",
"line": 38
}
},
{
"name": "Generators internals",
"class-name": "",
"tags": [
"generators",
"internals"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp",
"line": 24
}
},
{
"name": "Mayfail test case with nested sections",
"class-name": "",
"tags": [
"!mayfail"
],
"source-location": {
"filename": "/mnt/c/ubuntu/Catch2/tests/SelfTest/UsageTests/Condition.tests.cpp",
"line": 83
}
}
]
}
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
/**\file
* "empty" include of catch_test_macros.hpp
*
* This file does only one thing: include `catch2/catch_test_macros.hpp`,
* so that we can measure the compilation cost of the header.
*/
#include <catch2/catch_test_macros.hpp>
+117
View File
@@ -0,0 +1,117 @@
<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.
+36 -9
View File
@@ -7,21 +7,48 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_templated.hpp>
TEST_CASE("Simple REQUIRE - 10M") {
for (size_t i = 0; i < 10'000'000; ++i) {
REQUIRE(true);
namespace {
class MatchAllMatcher final : public Catch::Matchers::MatcherGenericBase {
public:
template <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("Simple NOTHROW - 10M") {
for (size_t i = 0; i < 10'000'000; ++i) {
REQUIRE_NOTHROW([](){}());
TEST_CASE( "REQUIRE_THAT", "[assertions][matchers]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE_THAT( 1, MatchAll() );
}
}
TEST_CASE("Simple THROWS - 10M") {
for (size_t i = 0; i < 10'000'000; ++i) {
REQUIRE_THROWS([]() { throw 1; }());
TEST_CASE( "REQUIRE_NOTHROW", "[assertions][exceptions]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE_NOTHROW( []() {}() );
}
}
TEST_CASE( "REQUIRE_THROWS", "[assertions][exceptions]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE_THROWS( []() { throw 1; }() );
}
}
} // namespace
+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 );
+11
View File
@@ -212,6 +212,17 @@ 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.
+1 -1
View File
@@ -26,7 +26,7 @@
[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 to run](#specify-the-section-to-run)<br>
[Specify the section/generator element to run](#specify-the-sectiongenerator-element-to-run)<br>
[Filenames as tags](#filenames-as-tags)<br>
[Override output colouring](#override-output-colouring)<br>
[Test Sharding](#test-sharding)<br>
+15
View File
@@ -7,6 +7,8 @@
[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
@@ -332,6 +334,19 @@ 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
+37
View File
@@ -6,6 +6,7 @@
[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
@@ -471,6 +472,42 @@ 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)
+24
View File
@@ -91,6 +91,30 @@ TEST_CASE("STATIC_CHECK showcase", "[traits]") {
}
```
* `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`
+59
View File
@@ -2,6 +2,10 @@
# Release notes
**Contents**<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>
@@ -74,6 +78,61 @@
[Even Older versions](#even-older-versions)<br>
## 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
+6 -4
View File
@@ -36,10 +36,12 @@ Tags allow an arbitrary number of additional strings to be associated with a tes
As an example - given the following test cases:
TEST_CASE( "A", "[widget]" ) { /* ... */ }
TEST_CASE( "B", "[widget]" ) { /* ... */ }
TEST_CASE( "C", "[gadget]" ) { /* ... */ }
TEST_CASE( "D", "[widget][gadget]" ) { /* ... */ }
```cpp
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.
+8 -2
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-test-names-only`` argument. This ensures that the full
with the ``--list-tests --reporter json`` 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
@@ -67,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 with the ``--list-test-names-only`` argument.
pass to the Catch executable when listing the tests.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
@@ -146,6 +146,12 @@ 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.
+229 -20
View File
@@ -1,7 +1,146 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
function(add_command NAME)
# 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 == element boundary marker used while splitting the tests array
# 0x03 <-> '[' (opens a CMake bracket-argument context)
# 0x04 <-> ']' (closes a CMake bracket-argument context)
#
string(ASCII 1 _SemicolonEscape)
string(ASCII 2 _BoundaryEscape)
string(ASCII 3 _OpenBracketEscape)
string(ASCII 4 _CloseBracketEscape)
# 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()
# TBD: Further possible optimization is that most arguments for per-test
# `prepare_command` call are constant across one invocation of
# `catch_discover_tests`, and thus need checking and escaping only
# once, instead of for each test.
# This would provide nice speed-up of the actual command preparation,
# but it will make the script much harder to read, and it is utterly
# dwarfed by the quadratic scaling of parsing JSON arrays in CMake.
# Prepare command with escaped (bracketed) arguments and return it via `_Command` out variable.
#
# To avoid quadratic performance when concatenating all commands together,
# the actual concatenation must be done by the caller, by appending it
# into a string of all other commands.
function(prepare_command NAME)
set(_args "")
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
math(EXPR _last_arg ${ARGC}-1)
@@ -13,11 +152,45 @@ function(add_command NAME)
set(_args "${_args} ${_arg}")
endif()
endforeach()
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
set(_Command "${NAME}(${_args})\n" 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()
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()
function(catch_discover_tests_impl)
cmake_parse_arguments(
""
""
@@ -26,6 +199,12 @@ function(catch_discover_tests_impl)
${ARGN}
)
# We periodically append to the output file below, so we have to ensure
# that it is empty at the start, or we get duplicated test scripts.
file(REMOVE "${_CTEST_FILE}")
# Size (in Bytes) at which the intermediate `script` var is dumped to file.
set(_WriteToFileThreshold 50000)
set(add_tags "${_ADD_TAGS_AS_LABELS}")
set(prefix "${_TEST_PREFIX}")
set(suffix "${_TEST_SUFFIX}")
@@ -72,8 +251,14 @@ function(catch_discover_tests_impl)
set(ENV{DYLD_FRAMEWORK_PATH} "${paths}")
endif()
make_temp_file_path(listing_output_path "${_TEST_WORKING_DIR}")
execute_process(
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --reporter json
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec}
--list-tests
--reporter json
--out "${listing_output_path}"
--order lex # Make sure the output order, and thus test registration order, is consistent across runs.
OUTPUT_VARIABLE listing_output
RESULT_VARIABLE result
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
@@ -86,6 +271,11 @@ 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}")
@@ -140,22 +330,34 @@ function(catch_discover_tests_impl)
message(FATAL_ERROR "Unsupported catch output version: '${version}'")
endif()
# Speed-up reparsing by cutting away unneeded parts of JSON.
# Extract just the JSON array with tests and then split them into
# individual objects.
string(JSON test_listing GET "${listing_output}" "listings" "tests")
string(JSON num_tests LENGTH "${test_listing}")
split_json_array(test_listing tests)
# Exit early if no tests are detected
if(num_tests STREQUAL "0")
if(NOT tests)
file(WRITE "${_CTEST_FILE}" "")
return()
endif()
# CMake's foreach-RANGE is inclusive, so we have to subtract 1
math(EXPR num_tests "${num_tests} - 1")
# 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()
foreach(idx RANGE ${num_tests})
string(JSON single_test GET ${test_listing} ${idx})
string(JSON test_tags GET "${single_test}" "tags")
# 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()
string(JSON plain_name GET "${single_test}" "name")
# Escape characters in test case names that would be parsed by Catch2
@@ -171,7 +373,7 @@ function(catch_discover_tests_impl)
endif()
# ...and add to script
add_command(add_test
prepare_command(add_test
"${prefix}${plain_name}${suffix}"
${_TEST_EXECUTOR}
"${_TEST_EXECUTABLE}"
@@ -180,12 +382,14 @@ function(catch_discover_tests_impl)
"${reporter_arg}"
"${output_dir_arg}"
)
add_command(set_tests_properties
string(APPEND script "${_Command}")
prepare_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
${properties}
)
string(APPEND script "${_Command}")
if(add_tags)
string(JSON num_tags LENGTH "${test_tags}")
@@ -205,19 +409,21 @@ function(catch_discover_tests_impl)
list(APPEND tag_list "${a_tag}")
endforeach()
add_command(set_tests_properties
prepare_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
LABELS "${tag_list}"
)
string(APPEND script "${_Command}")
endif()
endif(add_tags)
if(environment_modifications)
add_command(set_tests_properties
prepare_command(set_tests_properties
"${prefix}${plain_name}${suffix}"
PROPERTIES
ENVIRONMENT_MODIFICATION "${environment_modifications}")
string(APPEND script "${_Command}")
endif()
list(APPEND tests "${prefix}${plain_name}${suffix}")
@@ -225,13 +431,16 @@ function(catch_discover_tests_impl)
# 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})
prepare_command(set ${_TEST_LIST} ${tests})
string(APPEND script "${_Command}")
# Write CTest script
file(WRITE "${_CTEST_FILE}" "${script}")
# Write any script leftovers we have
file(APPEND "${_CTEST_FILE}" "${script}")
endfunction()
if(CMAKE_SCRIPT_MODE_FILE)
# 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)
catch_discover_tests_impl(
TEST_EXECUTABLE ${TEST_EXECUTABLE}
TEST_EXECUTOR ${TEST_EXECUTOR}
+186 -129
View File
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
// Catch v3.14.0
// Generated: 2026-04-05 15:03:01.631668
// Catch v3.15.3
// Generated: 2026-07-26 22:17:52.418168
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -74,11 +74,11 @@ namespace Catch {
samples.data(), samples.data() + samples.size() );
auto wrap_estimate = [](Estimate<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;
@@ -147,7 +147,7 @@ namespace Catch {
namespace Catch {
namespace Benchmark {
namespace Detail {
struct optimized_away_error : std::exception {
struct optimized_away_error final : std::exception {
const char* what() const noexcept override;
};
@@ -995,6 +995,7 @@ namespace Catch {
#include <cassert>
#include <stack>
@@ -1007,7 +1008,7 @@ namespace Catch {
m_messageId( builder.m_info.sequence ) {
MessageInfo info( CATCH_MOVE( builder.m_info ) );
info.message = builder.m_stream.str();
IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
Detail::pushScopedMessage( CATCH_MOVE( info ) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
@@ -1016,7 +1017,7 @@ namespace Catch {
}
ScopedMessage::~ScopedMessage() {
if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); }
}
@@ -1088,7 +1089,7 @@ namespace Catch {
assert( m_captured == m_messages.size() );
if ( m_isScoped ) {
for ( auto const& message : m_messages ) {
IResultCapture::popScopedMessage( message.sequence );
Detail::popScopedMessage( message.sequence );
}
}
}
@@ -1097,9 +1098,9 @@ namespace Catch {
assert( index < m_messages.size() );
m_messages[index].message += value;
if ( m_isScoped ) {
IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
} else {
IResultCapture::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
}
m_captured++;
}
@@ -1160,9 +1161,6 @@ namespace Catch {
CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
#endif
}
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
return m_enumValuesRegistry;
}
private:
TestRegistry m_testCaseRegistry;
@@ -1170,7 +1168,6 @@ namespace Catch {
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry;
StartupExceptionRegistry m_exceptionRegistry;
Detail::EnumValuesRegistry m_enumValuesRegistry;
};
}
@@ -2397,7 +2394,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 14, 0, "", 0 );
static Version version( 3, 15, 3, "", 0 );
return version;
}
@@ -2544,7 +2541,7 @@ namespace Catch {
bool isValid = next();
if ( !isValid ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
"Could not jump to Nth element: not enough elements" );
}
}
}
@@ -2686,13 +2683,17 @@ namespace Catch {
namespace Catch {
void AssertionHandler::finishIncomplete() {
m_resultCapture.handleIncomplete( m_assertionInfo );
}
AssertionHandler::AssertionHandler
( StringRef macroName,
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
m_resultCapture( getResultCapture() )
m_resultCapture( static_cast<RunContext&>(getResultCapture()) )
{
m_resultCapture.notifyAssertionStarted( m_assertionInfo );
}
@@ -4088,12 +4089,11 @@ namespace Catch {
#include <cassert>
namespace Catch {
IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
namespace Detail {
namespace {
@@ -4119,9 +4119,7 @@ namespace Catch {
return parsed;
}
EnumInfo::~EnumInfo() = default;
StringRef EnumInfo::lookup( int value ) const {
StringRef EnumInfo::lookup( int64_t value ) const {
for( auto const& valueToName : m_values ) {
if( valueToName.first == value )
return valueToName.second;
@@ -4129,25 +4127,20 @@ namespace Catch {
return "{** unexpected enum value **}"_sr;
}
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
auto enumInfo = Catch::Detail::make_unique<EnumInfo>();
enumInfo->m_name = enumName;
enumInfo->m_values.reserve( values.size() );
EnumInfo makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int64_t> const& values ) {
EnumInfo enumInfo;
enumInfo.m_name = enumName;
enumInfo.m_values.reserve( values.size() );
const auto valueNames = Catch::Detail::parseEnums( allValueNames );
assert( valueNames.size() == values.size() );
std::size_t i = 0;
for( auto value : values )
enumInfo->m_values.emplace_back(value, valueNames[i++]);
for (size_t i = 0; i < values.size(); ++i) {
enumInfo.m_values.emplace_back( values[i], valueNames[i] );
}
return enumInfo;
}
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
return *m_enumInfos.back();
}
} // Detail
} // Catch
@@ -4685,12 +4678,28 @@ namespace Detail {
#include <cmath>
#include <locale>
namespace Catch {
namespace {
static bool needsEscape( char c ) {
return c == '"' || c == '\\' || c == '\b' || c == '\f' ||
c == '\n' || c == '\r' || c == '\t';
struct EscapeLUT {
bool escape[256] = {};
constexpr EscapeLUT() {
escape[static_cast<unsigned char>( '"' )] = true;
escape[static_cast<unsigned char>( '\\' )] = true;
escape[static_cast<unsigned char>( '\b' )] = true;
escape[static_cast<unsigned char>( '\f' )] = true;
escape[static_cast<unsigned char>( '\n' )] = true;
escape[static_cast<unsigned char>( '\r' )] = true;
escape[static_cast<unsigned char>( '\t' )] = true;
}
};
static constexpr EscapeLUT escapeLUT{};
static constexpr bool needsEscape( char c ) {
return escapeLUT.escape[static_cast<unsigned char>( c )];
}
static Catch::StringRef makeEscapeStringRef( char c ) {
@@ -4802,7 +4811,10 @@ namespace Catch {
JsonValueWriter::JsonValueWriter( std::ostream& os,
std::uint64_t indent_level ):
m_os{ os }, m_indent_level{ indent_level } {}
m_os{ os }, m_indent_level{ indent_level } {
// We use C locale so that writing of numerical values is locale-independent.
m_sstream.imbue( std::locale::classic() );
}
JsonObjectWriter JsonValueWriter::writeObject() && {
return JsonObjectWriter{ m_os, m_indent_level };
@@ -4816,26 +4828,55 @@ namespace Catch {
writeImpl( value, true );
}
void JsonValueWriter::write( float value ) && {
writeFloatingPoint( value );
}
void JsonValueWriter::write( double value ) && {
writeFloatingPoint( value );
}
void JsonValueWriter::write( bool value ) && {
writeImpl( value ? "true"_sr : "false"_sr, false );
}
template <typename T>
void JsonValueWriter::writeFloatingPoint( T value ) {
if ( Catch::isnan( value ) ) {
writeImpl( "NaN"_sr, false );
} else if ( std::isinf( value ) ) {
writeImpl( value < 0 ? "-Infinity"_sr : "Infinity"_sr, false );
} else {
m_sstream << value;
writeImpl( m_sstream.str(), false );
}
}
void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
if ( quote ) { m_os << '"'; }
size_t current_start = 0;
for ( size_t i = 0; i < value.size(); ++i ) {
if ( needsEscape( value[i] ) ) {
if ( current_start < i ) {
m_os << value.substr( current_start, i - current_start );
// Escaping only makes sense for actual strings, which are passed
// with `quote == true`. Non-quoted values are things like bools
// and numbers, which cannot create inputs that need escaping.
if ( !quote ) {
m_os << value;
} else {
m_os << '"';
size_t current_start = 0;
for ( size_t i = 0; i < value.size(); ++i ) {
if ( needsEscape( value[i] ) ) {
if ( current_start < i ) {
m_os
<< value.substr( current_start, i - current_start );
}
m_os << makeEscapeStringRef( value[i] );
current_start = i + 1;
}
m_os << makeEscapeStringRef( value[i] );
current_start = i + 1;
}
if ( current_start < value.size() ) {
m_os << value.substr( current_start,
value.size() - current_start );
}
m_os << '"';
}
if ( current_start < value.size() ) {
m_os << value.substr( current_start, value.size() - current_start );
}
if ( quote ) { m_os << '"'; }
}
} // namespace Catch
@@ -5072,7 +5113,7 @@ namespace Catch {
namespace {
//! A no-op implementation, used if no reporter wants output
//! redirection.
class NoopRedirect : public OutputRedirect {
class NoopRedirect final : public OutputRedirect {
void activateImpl() override {}
void deactivateImpl() override {}
std::string getStdout() override { return {}; }
@@ -5109,7 +5150,7 @@ namespace Catch {
* Redirects the `std::cout`, `std::cerr`, `std::clog` streams,
* but does not touch the actual `stdout`/`stderr` file descriptors.
*/
class StreamRedirect : public OutputRedirect {
class StreamRedirect final : public OutputRedirect {
ReusableStringStream m_redirectedOut, m_redirectedErr;
RedirectedStreamNew m_cout, m_cerr, m_clog;
@@ -5220,7 +5261,7 @@ namespace Catch {
* Works by replacing the file descriptors numbered 1 and 2
* with an open temporary file.
*/
class FileRedirect : public OutputRedirect {
class FileRedirect final : public OutputRedirect {
TempFile m_outFile, m_errFile;
int m_originalOut = -1;
int m_originalErr = -1;
@@ -6023,6 +6064,8 @@ namespace Catch {
auto getGenerator() const -> GeneratorBasePtr const& override {
return m_generator;
}
bool isFilteredImpl() const override { return m_isFiltered; }
};
} // namespace
}
@@ -6136,6 +6179,25 @@ namespace Catch {
}
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
void pushScopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) );
}
void popScopedMessage( unsigned int messageId ) {
Detail::g_messageHolder().removeMessage( messageId );
}
void emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) );
}
void addUnscopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) );
}
bool lastAssertionPassed() { return Detail::g_lastAssertionPassed; }
} // namespace Detail
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
@@ -6269,14 +6331,13 @@ namespace Catch {
Detail::g_lastAssertionPassed = true;
} else if (!result.succeeded()) {
Detail::g_lastAssertionPassed = false;
if (result.isOk()) {
}
else if( m_activeTestCase->getTestCaseInfo().okToFail() ) // Read from a shared state established before the threads could start, this is fine
if (result.isOk()) {}
else if( m_activeTestCase->getTestCaseInfo().okToFail() ) { // Read from a shared state established before the threads could start, this is fine
m_atomicAssertionCount.failedButOk++;
else
} else {
m_atomicAssertionCount.failed++;
}
else {
}
} else {
Detail::g_lastAssertionPassed = true;
}
@@ -6349,17 +6410,6 @@ namespace Catch {
SourceLineInfo lineInfo,
Generators::GeneratorBasePtr&& generator ) {
// TBD: Do we want to avoid the warning if the generator is filtered?
if ( m_config->warnAboutInfiniteGenerators() &&
!generator->isFinite() ) {
// We want the semantics of `FAIL()`, but we inline it
// to avoid issues with conditionally prefixed macros
INTERNAL_CATCH_MSG( "FAIL",
Catch::ResultWas::ExplicitFailure,
Catch::ResultDisposition::Normal,
"GENERATE() would run infinitely" );
}
auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo );
auto& currentTracker = m_trackerContext.currentTracker();
assert(
@@ -6372,11 +6422,24 @@ namespace Catch {
m_trackerContext,
&currentTracker,
CATCH_MOVE( generator ) );
auto ret = newTracker.get();
// The warning shouldn't fire if the generator is infinite, **but** filtered down.
if ( m_config->warnAboutInfiniteGenerators() &&
!newTracker->m_generator->isFinite() &&
!newTracker->isFiltered() ) {
// We want the semantics of `FAIL()`, but we inline it
// to avoid issues with conditionally prefixed macros
INTERNAL_CATCH_MSG( "FAIL",
Catch::ResultWas::ExplicitFailure,
Catch::ResultDisposition::Normal,
"GENERATE() would run infinitely" );
}
auto returnPtr = newTracker.get();
currentTracker.addChild( CATCH_MOVE( newTracker ) );
ret->open();
return ret;
returnPtr->open();
return returnPtr;
}
bool RunContext::testForMissingAssertions(Counts& assertions) {
@@ -6458,7 +6521,7 @@ namespace Catch {
// and since IResultCapture::getLastResult is deprecated,
// we will leave it as is, until it is finally removed.
Detail::LockGuard _( m_assertionMutex );
return &(*m_lastResult);
return &*m_lastResult;
}
void RunContext::exceptionEarlyReported() {
@@ -6532,10 +6595,6 @@ namespace Catch {
m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
}
bool RunContext::lastAssertionPassed() {
return Detail::g_lastAssertionPassed;
}
void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) {
// We want to save the line info for better experience with unexpected assertions
Detail::g_lastKnownLineInfo = lineInfo;
@@ -6714,7 +6773,7 @@ namespace Catch {
}
void RunContext::populateReaction( AssertionReaction& reaction,
bool has_normal_disposition ) {
bool has_normal_disposition ) const {
reaction.shouldDebugBreak = m_shouldDebugBreak;
reaction.shouldThrow = aborting() || has_normal_disposition;
}
@@ -6765,22 +6824,6 @@ namespace Catch {
}
}
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) );
}
void IResultCapture::popScopedMessage( unsigned int messageId ) {
Detail::g_messageHolder().removeMessage( messageId );
}
void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) );
}
void IResultCapture::addUnscopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) );
}
void seedRng(IConfig const& config) {
sharedRng().seed(config.rngSeed());
}
@@ -7112,10 +7155,11 @@ namespace Catch {
TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
auto it = m_registry.find( alias );
if( it != m_registry.end() )
return &(it->second);
else
if ( it != m_registry.end() ) {
return &it->second;
} else {
return nullptr;
}
}
std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
@@ -7478,6 +7522,28 @@ namespace TestCaseTracking {
m_ctx.setCurrentTracker( this );
}
bool SectionTracker::isFilteredImpl() const {
// TBD: This is currently _very_ similar to the block in `isComplete`.
// Is this neccessarily that way, or just accident of current semantics?
const size_t filterIndex =
m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth;
if ( filterIndex < m_filterRef->size() ) {
// 1) New style filter must explicitly target section
if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type !=
PathFilter::For::Section ) {
return true;
}
// 2) Both style filters must match the trimmed name exactly
if ( m_trimmed_name !=
StringRef( ( *m_filterRef )[filterIndex].filter ) ) {
return true;
}
}
return false;
}
SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name)))
@@ -7562,7 +7628,7 @@ namespace Catch {
void throw_test_failure_exception() {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
throw TestFailureException{};
throw TestFailureException{}; //NOLINT(bugprone-std-exception-baseclass)
#else
CATCH_ERROR( "Test failure requires aborting test!" );
#endif
@@ -7570,7 +7636,7 @@ namespace Catch {
void throw_test_skip_exception() {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
throw Catch::TestSkipException();
throw Catch::TestSkipException(); //NOLINT(bugprone-std-exception-baseclass)
#else
CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
#endif
@@ -8024,8 +8090,8 @@ namespace Catch {
m_it--;
}
// Skip back over UTF-8 continuation bytes to the leading byte
while ( isUtf8ContinuationByte( *m_it ) ) {
assert( m_it != m_string->begin() );
while ( m_it != m_string->begin() &&
isUtf8ContinuationByte( *m_it ) ) {
m_it--;
}
}
@@ -8674,13 +8740,13 @@ namespace Catch {
namespace Matchers {
std::string MatcherUntypedBase::toString() const {
if (m_cachedToString.empty()) {
m_cachedToString = describe();
}
return m_cachedToString;
return describe();
}
MatcherUntypedBase::~MatcherUntypedBase() = default;
std::string MatcherUntypedBase::describe() const {
using namespace std::string_literals;
return "Undescribed matcher"s;
}
} // namespace Matchers
} // namespace Catch
@@ -8701,14 +8767,6 @@ namespace Matchers {
return sstr.str();
}
IsEmptyMatcher IsEmpty() {
return {};
}
HasSizeMatcher SizeIs(std::size_t sz) {
return HasSizeMatcher{ sz };
}
} // end namespace Matchers
} // end namespace Catch
@@ -8962,17 +9020,13 @@ std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc
namespace Catch {
namespace Matchers {
std::string AllTrueMatcher::describe() const { return "contains only true"; }
AllTrueMatcher AllTrue() { return AllTrueMatcher{}; }
std::string AllTrueMatcher::describe() const { return "contains only true"; }
std::string NoneTrueMatcher::describe() const { return "contains no true"; }
NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; }
std::string AnyTrueMatcher::describe() const { return "contains at least one true"; }
AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; }
} // namespace Matchers
} // namespace Catch
@@ -9085,7 +9139,11 @@ namespace Matchers {
namespace Catch {
namespace Matchers {
MatcherGenericBase::~MatcherGenericBase() = default;
std::string MatcherGenericBase::describe() const {
using namespace std::string_literals;
return "Undescribed generic matcher"s;
}
namespace Detail {
@@ -10108,9 +10166,8 @@ namespace Catch {
bool operator()(
Detail::unique_ptr<CumulativeReporterBase::SectionNode> const&
node ) const {
return (
( node->stats.sectionInfo.name == m_other.name ) &&
( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) );
return node->stats.sectionInfo.name == m_other.name
&& node->stats.sectionInfo.lineInfo == m_other.lineInfo;
}
void operator=( BySectionInfo const& ) = delete;
@@ -10464,7 +10521,7 @@ namespace Catch {
}
// minimum whitespace to pad tag counts, possibly overwritten below
size_t maxTagCountLen = 2;
int maxTagCountLen = 2;
// determine necessary padding for tag count column
if ( ! tags.empty() ) {
@@ -10479,7 +10536,7 @@ namespace Catch {
// more padding necessary for 3+ digits
if (maxTagCount >= 100) {
auto numDigits = 1 + std::floor( std::log10( maxTagCount ) );
maxTagCountLen = static_cast<size_t>( numDigits );
maxTagCountLen = static_cast<int>( numDigits );
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8,7 +8,7 @@
project(
'catch2',
'cpp',
version: '3.14.0', # CML version placeholder, don't delete
version: '3.15.3', # CML version placeholder, don't delete
license: 'BSL-1.0',
meson_version: '>=0.54.1',
)
+2 -3
View File
@@ -87,7 +87,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_decomposer.hpp
${SOURCES_DIR}/internal/catch_deprecation_macro.hpp
${SOURCES_DIR}/internal/catch_enforce.hpp
${SOURCES_DIR}/internal/catch_enum_values_registry.hpp
${SOURCES_DIR}/internal/catch_enum_info.hpp
${SOURCES_DIR}/internal/catch_errno_guard.hpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.hpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.hpp
@@ -181,7 +181,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_debugger.cpp
${SOURCES_DIR}/internal/catch_decomposer.cpp
${SOURCES_DIR}/internal/catch_enforce.cpp
${SOURCES_DIR}/internal/catch_enum_values_registry.cpp
${SOURCES_DIR}/internal/catch_enum_info.cpp
${SOURCES_DIR}/internal/catch_errno_guard.cpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.cpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp
@@ -227,7 +227,6 @@ set(INTERFACE_HEADERS
${SOURCES_DIR}/interfaces/catch_interfaces_all.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_capture.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_config.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_enum_values_registry.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_exception.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp
@@ -36,11 +36,11 @@ 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;
@@ -41,7 +41,7 @@ namespace Catch {
callable& operator=(callable&&) = default;
};
template <typename Fun>
struct model : public callable {
struct model final : public callable {
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(fun_) {}
@@ -14,7 +14,7 @@
namespace Catch {
namespace Benchmark {
namespace Detail {
struct optimized_away_error : std::exception {
struct optimized_away_error final : std::exception {
const char* what() const noexcept override;
};
+1 -1
View File
@@ -68,7 +68,7 @@
#include <catch2/internal/catch_decomposer.hpp>
#include <catch2/internal/catch_deprecation_macro.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_errno_guard.hpp>
#include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
+6 -6
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();
IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
Detail::pushScopedMessage( CATCH_MOVE( info ) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
@@ -31,7 +31,7 @@ namespace Catch {
}
ScopedMessage::~ScopedMessage() {
if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); }
}
@@ -103,7 +103,7 @@ namespace Catch {
assert( m_captured == m_messages.size() );
if ( m_isScoped ) {
for ( auto const& message : m_messages ) {
IResultCapture::popScopedMessage( message.sequence );
Detail::popScopedMessage( message.sequence );
}
}
}
@@ -112,9 +112,9 @@ namespace Catch {
assert( index < m_messages.size() );
m_messages[index].message += value;
if ( m_isScoped ) {
IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
} else {
IResultCapture::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
}
m_captured++;
}
+15 -3
View File
@@ -13,16 +13,28 @@
#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 {
@@ -112,7 +124,7 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
Catch::Detail::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
-5
View File
@@ -15,7 +15,6 @@
#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>
@@ -72,9 +71,6 @@ namespace Catch {
CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
#endif
}
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
return m_enumValuesRegistry;
}
private:
TestRegistry m_testCaseRegistry;
@@ -82,7 +78,6 @@ namespace Catch {
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry;
StartupExceptionRegistry m_exceptionRegistry;
Detail::EnumValuesRegistry m_enumValuesRegistry;
};
}
+1
View File
@@ -21,6 +21,7 @@
#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>
+21 -13
View File
@@ -18,7 +18,8 @@
#include <catch2/internal/catch_config_wchar.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_void_type.hpp>
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
#include <string_view>
@@ -383,7 +384,10 @@ namespace Catch {
}
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
#if defined( CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER ) && \
defined( CATCH_CONFIG_CPP17_OPTIONAL ) && \
/* P3168 turned optional into a range, making this ambigous with the range support */ \
!defined( __cpp_lib_optional_range_support )
#include <optional>
namespace Catch {
template<typename T>
@@ -630,17 +634,21 @@ struct ratio_string<std::milli> {
};
}
#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 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 ) ) ); \
} \
}; \
}
#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
+3 -1
View File
@@ -22,7 +22,7 @@ namespace Catch {
class ExceptionTranslatorRegistrar {
template<typename T>
class ExceptionTranslator : public IExceptionTranslator {
class ExceptionTranslator final : public IExceptionTranslator {
public:
constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) )
@@ -41,6 +41,8 @@ namespace Catch {
return m_translateFunction( ex );
}
#else
(void)it;
(void)itEnd;
return "You should never get here!";
#endif
}
+1 -1
View File
@@ -36,7 +36,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 14, 0, "", 0 );
static Version version( 3, 15, 3, "", 0 );
return version;
}
+2 -2
View File
@@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 14
#define CATCH_VERSION_PATCH 0
#define CATCH_VERSION_MINOR 15
#define CATCH_VERSION_PATCH 3
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
+2 -3
View File
@@ -95,7 +95,7 @@ namespace Generators {
void skipToNthElementImpl( std::size_t n ) override {
if ( n >= m_values.size() ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
"Could not jump to Nth element: not enough elements" );
}
m_idx = n;
}
@@ -179,8 +179,7 @@ namespace Generators {
bool isFinite() const override {
for (auto const& gen : m_generators) {
if (!gen.isFinite()) { return false;
}
if (!gen.isFinite()) { return false; }
}
return true;
}
@@ -27,7 +27,7 @@ namespace Generators {
void skipToNthElementImpl( std::size_t n ) override {
if ( n >= m_target ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
"Could not jump to Nth element: not enough elements" );
}
m_generator.skipToNthElement( n );
@@ -24,7 +24,6 @@
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/interfaces/catch_interfaces_exception.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
@@ -22,8 +22,6 @@ namespace Catch {
struct AssertionInfo;
struct SectionInfo;
struct SectionEndInfo;
struct MessageInfo;
struct MessageBuilder;
struct Counts;
struct AssertionReaction;
struct SourceLineInfo;
@@ -63,11 +61,6 @@ namespace Catch {
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
virtual void benchmarkFailed( StringRef error ) = 0;
static void pushScopedMessage( MessageInfo&& message );
static void popScopedMessage( unsigned int messageId );
static void addUnscopedMessage( MessageInfo&& message );
static void emplaceUnscopedMessage( MessageBuilder&& builder );
virtual void handleFatalErrorCondition( StringRef message ) = 0;
virtual void handleExpr
@@ -93,9 +86,6 @@ namespace Catch {
ResultWas::OfType resultType,
AssertionReaction &reaction ) = 0;
virtual bool lastAssertionPassed() = 0;
// Deprecated, do not use:
virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0;
@@ -64,7 +64,7 @@ namespace Catch {
class IStream;
struct PathFilter;
class IConfig : public Detail::NonCopyable {
class IConfig : Detail::NonCopyable {
public:
virtual ~IConfig();
@@ -1,47 +0,0 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#include <catch2/internal/catch_stringref.hpp>
#include <vector>
namespace Catch {
namespace Detail {
struct EnumInfo {
StringRef m_name;
std::vector<std::pair<int, StringRef>> m_values;
~EnumInfo();
StringRef lookup( int value ) const;
};
} // namespace Detail
class IMutableEnumValuesRegistry {
public:
virtual ~IMutableEnumValuesRegistry(); // = default;
virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
template<typename E>
Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
std::vector<int> intValues;
intValues.reserve( values.size() );
for( auto enumValue : values )
intValues.push_back( static_cast<int>( enumValue ) );
return registerEnum( enumName, allEnums, intValues );
}
};
} // Catch
#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
@@ -28,7 +28,7 @@ namespace Catch {
bool isValid = next();
if ( !isValid ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
"Could not jump to Nth element: not enough elements" );
}
}
}
@@ -53,7 +53,6 @@ namespace Catch {
virtual void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) = 0;
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
virtual void registerStartupException() noexcept = 0;
virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
};
IRegistryHub const& getRegistryHub();
@@ -6,21 +6,28 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_debugger.hpp>
#include <catch2/internal/catch_run_context.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
namespace Catch {
void AssertionHandler::finishIncomplete() {
m_resultCapture.handleIncomplete( m_assertionInfo );
}
AssertionHandler::AssertionHandler
( StringRef macroName,
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
m_resultCapture( getResultCapture() )
m_resultCapture( static_cast<RunContext&>(getResultCapture()) )
{
m_resultCapture.notifyAssertionStarted( m_assertionInfo );
}
@@ -10,12 +10,13 @@
#include <catch2/catch_assertion_info.hpp>
#include <catch2/internal/catch_decomposer.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <string>
namespace Catch {
class RunContext;
struct AssertionReaction {
bool shouldDebugBreak = false;
bool shouldThrow = false;
@@ -26,7 +27,12 @@ namespace Catch {
AssertionInfo m_assertionInfo;
AssertionReaction m_reaction;
bool m_completed = false;
IResultCapture& m_resultCapture;
// Since all uses are hidden in the .cpp file, we can directly use
// the final type and avoid going through virtual dispatch, without
// massive compilation time overhead.
RunContext& m_resultCapture;
void finishIncomplete();
public:
AssertionHandler
@@ -35,9 +41,9 @@ namespace Catch {
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition );
~AssertionHandler() {
if ( !m_completed ) {
m_resultCapture.handleIncomplete( m_assertionInfo );
}
// We want the common fast path inlinable, and the virtual
// dispatch in a function in single TU.
if ( !m_completed ) { finishIncomplete(); }
}
@@ -27,16 +27,24 @@
#include <catch2/internal/catch_platform.hpp>
#include <catch2/catch_user_config.hpp>
#ifdef __cplusplus
#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define CATCH_CPP17_OR_GREATER
#endif
# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define CATCH_CPP17_OR_GREATER
# endif
# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
# define CATCH_CPP20_OR_GREATER
# endif
#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
# define CATCH_CPP20_OR_GREATER
#endif
// Matchers are only constexpr-able in C++20
#if defined( CATCH_CPP20_OR_GREATER ) && \
defined( __cpp_constexpr_dynamic_alloc ) && \
__cpp_constexpr_dynamic_alloc >= 201907L && \
/* GCC < 13 define the feature macro, but compiler bugs stop us from using it */ \
( !defined( __GNUC__ ) || __GNUC__ >= 13 || defined(__clang__) )
# define CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED
# define CATCH_DESTRUCTOR_CONSTEXPR constexpr
#else
# define CATCH_DESTRUCTOR_CONSTEXPR
#endif
// Only GCC compiler should be used in this block, so other compilers trying to
+2 -2
View File
@@ -193,7 +193,7 @@ namespace Catch {
void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
template<typename LhsT, typename RhsT>
class BinaryExpr : public ITransientExpression {
class BinaryExpr final : public ITransientExpression {
LhsT m_lhs;
StringRef m_op;
RhsT m_rhs;
@@ -269,7 +269,7 @@ namespace Catch {
};
template<typename LhsT>
class UnaryExpr : public ITransientExpression {
class UnaryExpr final : public ITransientExpression {
LhsT m_lhs;
void streamReconstructedExpression( std::ostream &os ) const override {
@@ -5,15 +5,14 @@
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <cassert>
namespace Catch {
IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
namespace Detail {
namespace {
@@ -39,9 +38,7 @@ namespace Catch {
return parsed;
}
EnumInfo::~EnumInfo() = default;
StringRef EnumInfo::lookup( int value ) const {
StringRef EnumInfo::lookup( int64_t value ) const {
for( auto const& valueToName : m_values ) {
if( valueToName.first == value )
return valueToName.second;
@@ -49,25 +46,20 @@ namespace Catch {
return "{** unexpected enum value **}"_sr;
}
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
auto enumInfo = Catch::Detail::make_unique<EnumInfo>();
enumInfo->m_name = enumName;
enumInfo->m_values.reserve( values.size() );
EnumInfo makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int64_t> const& values ) {
EnumInfo enumInfo;
enumInfo.m_name = enumName;
enumInfo.m_values.reserve( values.size() );
const auto valueNames = Catch::Detail::parseEnums( allValueNames );
assert( valueNames.size() == values.size() );
std::size_t i = 0;
for( auto value : values )
enumInfo->m_values.emplace_back(value, valueNames[i++]);
for (size_t i = 0; i < values.size(); ++i) {
enumInfo.m_values.emplace_back( values[i], valueNames[i] );
}
return enumInfo;
}
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
return *m_enumInfos.back();
}
} // Detail
} // Catch
+48
View File
@@ -0,0 +1,48 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_ENUM_INFO_HPP_INCLUDED
#define CATCH_ENUM_INFO_HPP_INCLUDED
#include <catch2/internal/catch_stringref.hpp>
#include <cstdint>
#include <utility>
#include <vector>
namespace Catch {
namespace Detail {
struct EnumInfo {
StringRef m_name;
std::vector<std::pair<int64_t, StringRef>> m_values;
StringRef lookup( int64_t value ) const;
};
EnumInfo makeEnumInfo( StringRef enumName,
StringRef allValueNames,
std::vector<int64_t> const& values );
template <typename E>
Detail::EnumInfo makeEnumInfo( StringRef enumName,
StringRef allEnums,
std::initializer_list<E> values ) {
static_assert( sizeof( int64_t ) >= sizeof( E ),
"Cannot serialize enum to int64_t" );
std::vector<int64_t> intValues;
intValues.reserve( values.size() );
for ( auto enumValue : values )
intValues.push_back( static_cast<int64_t>( enumValue ) );
return makeEnumInfo( enumName, allEnums, intValues );
}
std::vector<StringRef> parseEnums( StringRef enums );
} // namespace Detail
} // namespace Catch
#endif // CATCH_ENUM_INFO_HPP_INCLUDED
@@ -1,36 +0,0 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <vector>
namespace Catch {
namespace Detail {
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
class EnumValuesRegistry : public IMutableEnumValuesRegistry {
std::vector<Catch::Detail::unique_ptr<EnumInfo>> m_enumInfos;
EnumInfo const& registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values) override;
};
std::vector<StringRef> parseEnums( StringRef enums );
} // Detail
} // Catch
#endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
@@ -15,7 +15,7 @@
namespace Catch {
class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
class ExceptionTranslatorRegistry final : public IExceptionTranslatorRegistry {
public:
~ExceptionTranslatorRegistry() override;
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
+65 -16
View File
@@ -7,14 +7,31 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_polyfills.hpp>
#include <catch2/internal/catch_unreachable.hpp>
#include <cmath>
#include <locale>
namespace Catch {
namespace {
static bool needsEscape( char c ) {
return c == '"' || c == '\\' || c == '\b' || c == '\f' ||
c == '\n' || c == '\r' || c == '\t';
struct EscapeLUT {
bool escape[256] = {};
constexpr EscapeLUT() {
escape[static_cast<unsigned char>( '"' )] = true;
escape[static_cast<unsigned char>( '\\' )] = true;
escape[static_cast<unsigned char>( '\b' )] = true;
escape[static_cast<unsigned char>( '\f' )] = true;
escape[static_cast<unsigned char>( '\n' )] = true;
escape[static_cast<unsigned char>( '\r' )] = true;
escape[static_cast<unsigned char>( '\t' )] = true;
}
};
static constexpr EscapeLUT escapeLUT{};
static constexpr bool needsEscape( char c ) {
return escapeLUT.escape[static_cast<unsigned char>( c )];
}
static Catch::StringRef makeEscapeStringRef( char c ) {
@@ -126,7 +143,10 @@ namespace Catch {
JsonValueWriter::JsonValueWriter( std::ostream& os,
std::uint64_t indent_level ):
m_os{ os }, m_indent_level{ indent_level } {}
m_os{ os }, m_indent_level{ indent_level } {
// We use C locale so that writing of numerical values is locale-independent.
m_sstream.imbue( std::locale::classic() );
}
JsonObjectWriter JsonValueWriter::writeObject() && {
return JsonObjectWriter{ m_os, m_indent_level };
@@ -140,26 +160,55 @@ namespace Catch {
writeImpl( value, true );
}
void JsonValueWriter::write( float value ) && {
writeFloatingPoint( value );
}
void JsonValueWriter::write( double value ) && {
writeFloatingPoint( value );
}
void JsonValueWriter::write( bool value ) && {
writeImpl( value ? "true"_sr : "false"_sr, false );
}
template <typename T>
void JsonValueWriter::writeFloatingPoint( T value ) {
if ( Catch::isnan( value ) ) {
writeImpl( "NaN"_sr, false );
} else if ( std::isinf( value ) ) {
writeImpl( value < 0 ? "-Infinity"_sr : "Infinity"_sr, false );
} else {
m_sstream << value;
writeImpl( m_sstream.str(), false );
}
}
void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
if ( quote ) { m_os << '"'; }
size_t current_start = 0;
for ( size_t i = 0; i < value.size(); ++i ) {
if ( needsEscape( value[i] ) ) {
if ( current_start < i ) {
m_os << value.substr( current_start, i - current_start );
// Escaping only makes sense for actual strings, which are passed
// with `quote == true`. Non-quoted values are things like bools
// and numbers, which cannot create inputs that need escaping.
if ( !quote ) {
m_os << value;
} else {
m_os << '"';
size_t current_start = 0;
for ( size_t i = 0; i < value.size(); ++i ) {
if ( needsEscape( value[i] ) ) {
if ( current_start < i ) {
m_os
<< value.substr( current_start, i - current_start );
}
m_os << makeEscapeStringRef( value[i] );
current_start = i + 1;
}
m_os << makeEscapeStringRef( value[i] );
current_start = i + 1;
}
if ( current_start < value.size() ) {
m_os << value.substr( current_start,
value.size() - current_start );
}
m_os << '"';
}
if ( current_start < value.size() ) {
m_os << value.substr( current_start, value.size() - current_start );
}
if ( quote ) { m_os << '"'; }
}
} // namespace Catch
+8
View File
@@ -39,11 +39,19 @@ namespace Catch {
writeImpl( value, !std::is_arithmetic<T>::value );
}
void write( StringRef value ) &&;
void write( float value ) &&;
void write( double value ) &&;
void write( bool value ) &&;
private:
void writeImpl( StringRef value, bool quote );
// Helper to deal with non-finite floating point values, which
// are not standard JSON, but we use JS/Python/etc. approach of
// emitting `NaN`, `Infinity`, `-Infinity` as number(like).
template <typename T>
void writeFloatingPoint( T value );
// Without this SFINAE, this overload is a better match
// for `std::string`, `char const*`, `char const[N]` args.
// While it would still work, it would cause code bloat
@@ -33,7 +33,7 @@ namespace Catch {
namespace {
//! A no-op implementation, used if no reporter wants output
//! redirection.
class NoopRedirect : public OutputRedirect {
class NoopRedirect final : public OutputRedirect {
void activateImpl() override {}
void deactivateImpl() override {}
std::string getStdout() override { return {}; }
@@ -70,7 +70,7 @@ namespace Catch {
* Redirects the `std::cout`, `std::cerr`, `std::clog` streams,
* but does not touch the actual `stdout`/`stderr` file descriptors.
*/
class StreamRedirect : public OutputRedirect {
class StreamRedirect final : public OutputRedirect {
ReusableStringStream m_redirectedOut, m_redirectedErr;
RedirectedStreamNew m_cout, m_cerr, m_clog;
@@ -181,7 +181,7 @@ namespace Catch {
* Works by replacing the file descriptors numbered 1 and 2
* with an open temporary file.
*/
class FileRedirect : public OutputRedirect {
class FileRedirect final : public OutputRedirect {
TempFile m_outFile, m_errFile;
int m_originalOut = -1;
int m_originalErr = -1;
+45 -42
View File
@@ -11,6 +11,7 @@
#include <catch2/generators/catch_generators_throw.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_context.hpp>
@@ -195,6 +196,8 @@ namespace Catch {
auto getGenerator() const -> GeneratorBasePtr const& override {
return m_generator;
}
bool isFilteredImpl() const override { return m_isFiltered; }
};
} // namespace
}
@@ -308,6 +311,25 @@ namespace Catch {
}
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
void pushScopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) );
}
void popScopedMessage( unsigned int messageId ) {
Detail::g_messageHolder().removeMessage( messageId );
}
void emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) );
}
void addUnscopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) );
}
bool lastAssertionPassed() { return Detail::g_lastAssertionPassed; }
} // namespace Detail
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
@@ -441,14 +463,13 @@ namespace Catch {
Detail::g_lastAssertionPassed = true;
} else if (!result.succeeded()) {
Detail::g_lastAssertionPassed = false;
if (result.isOk()) {
}
else if( m_activeTestCase->getTestCaseInfo().okToFail() ) // Read from a shared state established before the threads could start, this is fine
if (result.isOk()) {}
else if( m_activeTestCase->getTestCaseInfo().okToFail() ) { // Read from a shared state established before the threads could start, this is fine
m_atomicAssertionCount.failedButOk++;
else
} else {
m_atomicAssertionCount.failed++;
}
else {
}
} else {
Detail::g_lastAssertionPassed = true;
}
@@ -521,17 +542,6 @@ namespace Catch {
SourceLineInfo lineInfo,
Generators::GeneratorBasePtr&& generator ) {
// TBD: Do we want to avoid the warning if the generator is filtered?
if ( m_config->warnAboutInfiniteGenerators() &&
!generator->isFinite() ) {
// We want the semantics of `FAIL()`, but we inline it
// to avoid issues with conditionally prefixed macros
INTERNAL_CATCH_MSG( "FAIL",
Catch::ResultWas::ExplicitFailure,
Catch::ResultDisposition::Normal,
"GENERATE() would run infinitely" );
}
auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo );
auto& currentTracker = m_trackerContext.currentTracker();
assert(
@@ -544,11 +554,24 @@ namespace Catch {
m_trackerContext,
&currentTracker,
CATCH_MOVE( generator ) );
auto ret = newTracker.get();
// The warning shouldn't fire if the generator is infinite, **but** filtered down.
if ( m_config->warnAboutInfiniteGenerators() &&
!newTracker->m_generator->isFinite() &&
!newTracker->isFiltered() ) {
// We want the semantics of `FAIL()`, but we inline it
// to avoid issues with conditionally prefixed macros
INTERNAL_CATCH_MSG( "FAIL",
Catch::ResultWas::ExplicitFailure,
Catch::ResultDisposition::Normal,
"GENERATE() would run infinitely" );
}
auto returnPtr = newTracker.get();
currentTracker.addChild( CATCH_MOVE( newTracker ) );
ret->open();
return ret;
returnPtr->open();
return returnPtr;
}
bool RunContext::testForMissingAssertions(Counts& assertions) {
@@ -630,7 +653,7 @@ namespace Catch {
// and since IResultCapture::getLastResult is deprecated,
// we will leave it as is, until it is finally removed.
Detail::LockGuard _( m_assertionMutex );
return &(*m_lastResult);
return &*m_lastResult;
}
void RunContext::exceptionEarlyReported() {
@@ -704,10 +727,6 @@ namespace Catch {
m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
}
bool RunContext::lastAssertionPassed() {
return Detail::g_lastAssertionPassed;
}
void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) {
// We want to save the line info for better experience with unexpected assertions
Detail::g_lastKnownLineInfo = lineInfo;
@@ -886,7 +905,7 @@ namespace Catch {
}
void RunContext::populateReaction( AssertionReaction& reaction,
bool has_normal_disposition ) {
bool has_normal_disposition ) const {
reaction.shouldDebugBreak = m_shouldDebugBreak;
reaction.shouldThrow = aborting() || has_normal_disposition;
}
@@ -937,22 +956,6 @@ namespace Catch {
}
}
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) );
}
void IResultCapture::popScopedMessage( unsigned int messageId ) {
Detail::g_messageHolder().removeMessage( messageId );
}
void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) );
}
void IResultCapture::addUnscopedMessage( MessageInfo&& message ) {
Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) );
}
void seedRng(IConfig const& config) {
sharedRng().seed(config.rngSeed());
}
+1 -3
View File
@@ -102,8 +102,6 @@ namespace Catch {
void handleFatalErrorCondition( StringRef message ) override;
bool lastAssertionPassed() override;
public:
// !TBD We need to do this another way!
bool aborting() const;
@@ -125,7 +123,7 @@ namespace Catch {
ITransientExpression const *expr,
bool negated );
void populateReaction( AssertionReaction& reaction, bool has_normal_disposition );
void populateReaction( AssertionReaction& reaction, bool has_normal_disposition ) const;
// Creates dummy info for unexpected exceptions/fatal errors,
// where we do not have the access to one, but we still need
+1 -1
View File
@@ -20,7 +20,7 @@ namespace Catch {
template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT>
class Singleton : SingletonImplT, public ISingleton {
class Singleton final : SingletonImplT, public ISingleton {
static auto getInternal() -> Singleton* {
static Singleton* s_instance = nullptr;
@@ -16,10 +16,11 @@ namespace Catch {
TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
auto it = m_registry.find( alias );
if( it != m_registry.end() )
return &(it->second);
else
if ( it != m_registry.end() ) {
return &it->second;
} else {
return nullptr;
}
}
std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
@@ -17,7 +17,7 @@
namespace Catch {
struct SourceLineInfo;
class TagAliasRegistry : public ITagAliasRegistry {
class TagAliasRegistry final : public ITagAliasRegistry {
public:
~TagAliasRegistry() override;
TagAlias const* find( std::string const& alias ) const override;
@@ -245,42 +245,42 @@
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
#endif
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
namespace {\
template<typename TestType> \
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
void test();\
};\
namespace {\
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
INTERNAL_CATCH_TYPE_GEN \
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
template<typename...Types>\
struct TestNameClass{\
void reg_tests(){\
std::size_t index = 0;\
using expander = std::size_t[];\
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */ \
}\
};\
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(Catch::Detail::priority_tag<1>{})), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
TestInit t;\
t.reg_tests();\
return 0;\
}(); \
}\
}\
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
void TestName<TestType>::test()
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
void test();\
};\
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
INTERNAL_CATCH_TYPE_GEN \
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
template<typename...Types>\
struct TestNameClass{\
void reg_tests(){\
std::size_t index = 0;\
using expander = std::size_t[];\
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */ \
}\
};\
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(Catch::Detail::priority_tag<1>{})), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
TestInit t;\
t.reg_tests();\
return 0;\
}(); \
}\
}\
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
void TestName<TestType>::test()
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
@@ -298,37 +298,37 @@
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
#endif
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
namespace {\
template<typename TestType> \
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
void test();\
};\
namespace {\
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
INTERNAL_CATCH_TYPE_GEN\
template<typename...Types>\
struct TestNameClass{\
void reg_tests(){\
size_t index = 0;\
using expander = size_t[];\
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
}\
};\
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
using TestInit = typename convert<TestNameClass, TmplList>::type;\
TestInit t;\
t.reg_tests();\
return 0;\
}(); \
}}\
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
void TestName<TestType>::test()
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
INTERNAL_CATCH_TYPE_GEN\
template<typename...Types>\
struct TestNameClass{\
void reg_tests(){\
size_t index = 0;\
using expander = size_t[];\
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
}\
};\
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
using TestInit = typename convert<TestNameClass, TmplList>::type;\
TestInit t;\
t.reg_tests();\
return 0;\
}(); \
}}\
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
void TestName<TestType>::test()
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, TmplList )
@@ -28,7 +28,7 @@ namespace Catch {
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
class TestRegistry : public ITestCaseRegistry {
class TestRegistry final : public ITestCaseRegistry {
public:
void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker );
@@ -167,6 +167,28 @@ namespace TestCaseTracking {
m_ctx.setCurrentTracker( this );
}
bool SectionTracker::isFilteredImpl() const {
// TBD: This is currently _very_ similar to the block in `isComplete`.
// Is this neccessarily that way, or just accident of current semantics?
const size_t filterIndex =
m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth;
if ( filterIndex < m_filterRef->size() ) {
// 1) New style filter must explicitly target section
if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type !=
PathFilter::For::Section ) {
return true;
}
// 2) Both style filters must match the trimmed name exactly
if ( m_trimmed_name !=
StringRef( ( *m_filterRef )[filterIndex].filter ) ) {
return true;
}
}
return false;
}
SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name)))
@@ -12,6 +12,7 @@
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_path_filter.hpp>
#include <string>
#include <vector>
@@ -81,6 +82,8 @@ namespace TestCaseTracking {
using Children = std::vector<ITrackerPtr>;
virtual bool isFilteredImpl() const = 0;
protected:
enum CycleState {
NotStarted,
@@ -178,6 +181,20 @@ namespace TestCaseTracking {
* for internal debug checks.
*/
virtual bool isGeneratorTracker() const;
/**
* Returns true if the concrete tracker instance has a filter that applies to it.
*/
bool isFiltered() const {
// Fast path: are there even filters for tracker in this position?
const size_t filter_depth =
m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth;
if ( m_filterRef->size() <= filter_depth ) { return false; }
// Slow path: If there are filters, ask the concrete tracker.
// This handles things like match-all filters for that tracker.
return isFilteredImpl();
}
};
class TrackerContext {
@@ -233,6 +250,8 @@ namespace TestCaseTracking {
// to not own the name, the name still has to outlive the `ITracker` parent, so
// this should still be safe.
StringRef m_trimmed_name;
bool isFilteredImpl() const override;
public:
SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
@@ -14,7 +14,7 @@ namespace Catch {
void throw_test_failure_exception() {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
throw TestFailureException{};
throw TestFailureException{}; //NOLINT(bugprone-std-exception-baseclass)
#else
CATCH_ERROR( "Test failure requires aborting test!" );
#endif
@@ -22,7 +22,7 @@ namespace Catch {
void throw_test_skip_exception() {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
throw Catch::TestSkipException();
throw Catch::TestSkipException(); //NOLINT(bugprone-std-exception-baseclass)
#else
CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
#endif
@@ -11,10 +11,16 @@
#include <catch2/catch_user_config.hpp>
#include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
namespace Catch {
namespace Detail {
// Defined in catch_run_context.cpp, where the thread-local data lives.
bool lastAssertionPassed();
}
}
// We need this suppression to leak, because it took until GCC 10
// for the front end to handle local suppression via _Pragma properly
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9
@@ -57,12 +63,12 @@
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
if( Catch::getResultCapture().lastAssertionPassed() )
if( Catch::Detail::lastAssertionPassed() )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \
INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
if( !Catch::getResultCapture().lastAssertionPassed() )
if( !Catch::Detail::lastAssertionPassed() )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \
+3 -3
View File
@@ -29,7 +29,7 @@
namespace Catch {
template<typename C>
class TestInvokerAsMethod : public ITestInvoker {
class TestInvokerAsMethod final : public ITestInvoker {
void (C::*m_testAsMethod)();
public:
constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept:
@@ -49,7 +49,7 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
}
template <typename C>
class TestInvokerFixture : public ITestInvoker {
class TestInvokerFixture final : public ITestInvoker {
void ( C::*m_testAsMethod )() const;
Detail::unique_ptr<C> m_fixture = nullptr;
@@ -124,7 +124,7 @@ struct AutoReg : Detail::NonCopyable {
namespace Catch {
namespace Detail {
struct DummyUse {
DummyUse( void ( * )( int ), Catch::NameAndTags const& );
DummyUse( void ( * )( int ), Catch::NameAndTags const& ) noexcept;
};
} // namespace Detail
} // namespace Catch
+2 -2
View File
@@ -148,8 +148,8 @@ namespace Catch {
m_it--;
}
// Skip back over UTF-8 continuation bytes to the leading byte
while ( isUtf8ContinuationByte( *m_it ) ) {
assert( m_it != m_string->begin() );
while ( m_it != m_string->begin() &&
isUtf8ContinuationByte( *m_it ) ) {
m_it--;
}
}
+5 -5
View File
@@ -13,13 +13,13 @@ namespace Catch {
namespace Matchers {
std::string MatcherUntypedBase::toString() const {
if (m_cachedToString.empty()) {
m_cachedToString = describe();
}
return m_cachedToString;
return describe();
}
MatcherUntypedBase::~MatcherUntypedBase() = default;
std::string MatcherUntypedBase::describe() const {
using namespace std::string_literals;
return "Undescribed matcher"s;
}
} // namespace Matchers
} // namespace Catch
+38 -6
View File
@@ -20,10 +20,10 @@ namespace Matchers {
class MatcherUntypedBase {
public:
MatcherUntypedBase() = default;
constexpr MatcherUntypedBase() = default;
MatcherUntypedBase(MatcherUntypedBase const&) = default;
MatcherUntypedBase(MatcherUntypedBase&&) = default;
constexpr MatcherUntypedBase(MatcherUntypedBase const&) = default;
constexpr MatcherUntypedBase(MatcherUntypedBase&&) = default;
MatcherUntypedBase& operator = (MatcherUntypedBase const&) = delete;
MatcherUntypedBase& operator = (MatcherUntypedBase&&) = delete;
@@ -31,9 +31,9 @@ namespace Matchers {
std::string toString() const;
protected:
virtual ~MatcherUntypedBase(); // = default;
virtual std::string describe() const = 0;
mutable std::string m_cachedToString;
CATCH_DESTRUCTOR_CONSTEXPR virtual ~MatcherUntypedBase() = default;
//! Should be overridden, but we provide default "undescribed" impl
virtual std::string describe() const;
};
@@ -215,6 +215,19 @@ namespace Matchers {
#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
#define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) \
static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \
CATCH_SUCCEED( #matcher ".match( " #arg " )" )
#define CATCH_STATIC_CHECK_THAT( arg, matcher ) \
static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \
CATCH_SUCCEED( #matcher ".match( " #arg " )" )
#else
#define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) CATCH_REQUIRE_THAT( arg, matcher )
#define CATCH_STATIC_CHECK_THAT( arg, matcher ) CATCH_CHECK_THAT( arg, matcher )
#endif
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
@@ -226,6 +239,9 @@ namespace Matchers {
#define CATCH_CHECK_THAT( arg, matcher ) (void)(0)
#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
#define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) (void)(0)
#define CATCH_STATIC_CHECK_THAT( arg, matcher ) (void)(0)
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
@@ -237,6 +253,19 @@ namespace Matchers {
#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
#define STATIC_REQUIRE_THAT( arg, matcher ) \
static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \
SUCCEED( #matcher ".match( " #arg " )" )
#define STATIC_CHECK_THAT( arg, matcher ) \
static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \
SUCCEED( #matcher ".match( " #arg " )" )
#else
#define STATIC_REQUIRE_THAT( arg, matcher ) REQUIRE_THAT( arg, matcher )
#define STATIC_CHECK_THAT( arg, matcher ) CHECK_THAT( arg, matcher )
#endif
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
@@ -248,6 +277,9 @@ namespace Matchers {
#define CHECK_THAT( arg, matcher ) (void)(0)
#define REQUIRE_THAT( arg, matcher ) (void)(0)
#define STATIC_REQUIRE_THAT( arg, matcher ) (void)(0)
#define STATIC_CHECK_THAT( arg, matcher ) (void)(0)
#endif // end of user facing macro declarations
#endif // CATCH_MATCHERS_HPP_INCLUDED
@@ -22,13 +22,5 @@ namespace Matchers {
return sstr.str();
}
IsEmptyMatcher IsEmpty() {
return {};
}
HasSizeMatcher SizeIs(std::size_t sz) {
return HasSizeMatcher{ sz };
}
} // end namespace Matchers
} // end namespace Catch
@@ -18,7 +18,7 @@ namespace Catch {
class IsEmptyMatcher final : public MatcherGenericBase {
public:
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
using Catch::Detail::empty;
#else
@@ -33,12 +33,12 @@ namespace Catch {
class HasSizeMatcher final : public MatcherGenericBase {
std::size_t m_target_size;
public:
explicit HasSizeMatcher(std::size_t target_size):
constexpr explicit HasSizeMatcher(std::size_t target_size):
m_target_size(target_size)
{}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
using Catch::Detail::size;
#else
@@ -54,12 +54,12 @@ namespace Catch {
class SizeMatchesMatcher final : public MatcherGenericBase {
Matcher m_matcher;
public:
explicit SizeMatchesMatcher(Matcher m):
constexpr explicit SizeMatchesMatcher(Matcher m):
m_matcher(CATCH_MOVE(m))
{}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
#if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS)
using Catch::Detail::size;
#else
@@ -75,11 +75,13 @@ namespace Catch {
//! Creates a matcher that accepts empty ranges/containers
IsEmptyMatcher IsEmpty();
inline CATCH_DESTRUCTOR_CONSTEXPR IsEmptyMatcher IsEmpty() { return {}; }
//! Creates a matcher that accepts ranges/containers with specific size
HasSizeMatcher SizeIs(std::size_t sz);
inline CATCH_DESTRUCTOR_CONSTEXPR HasSizeMatcher SizeIs( std::size_t sz ) {
return HasSizeMatcher{ sz };
}
template <typename Matcher>
std::enable_if_t<Detail::is_matcher_v<Matcher>,
constexpr std::enable_if_t<Detail::is_matcher_v<Matcher>,
SizeMatchesMatcher<Matcher>> SizeIs(Matcher&& m) {
return SizeMatchesMatcher<Matcher>{CATCH_FORWARD(m)};
}
@@ -23,7 +23,7 @@ namespace Catch {
Equality m_eq;
public:
template <typename T2, typename Equality2>
ContainsElementMatcher(T2&& target, Equality2&& predicate):
constexpr ContainsElementMatcher(T2&& target, Equality2&& predicate):
m_desired(CATCH_FORWARD(target)),
m_eq(CATCH_FORWARD(predicate))
{}
@@ -33,7 +33,7 @@ namespace Catch {
}
template <typename RangeLike>
bool match( RangeLike&& rng ) const {
constexpr bool match( RangeLike&& rng ) const {
for ( auto&& elem : rng ) {
if ( m_eq( elem, m_desired ) ) { return true; }
}
@@ -49,12 +49,12 @@ namespace Catch {
// Note that we do a copy+move to avoid having to SFINAE this
// constructor (and also avoid some perfect forwarding failure
// cases)
ContainsMatcherMatcher(Matcher matcher):
constexpr ContainsMatcherMatcher(Matcher matcher):
m_matcher(CATCH_MOVE(matcher))
{}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (m_matcher.match(elem)) {
return true;
@@ -74,14 +74,14 @@ namespace Catch {
* Uses `std::equal_to` to do the comparison
*/
template <typename T>
std::enable_if_t<!Detail::is_matcher_v<T>,
constexpr std::enable_if_t<!Detail::is_matcher_v<T>,
ContainsElementMatcher<T, std::equal_to<>>> Contains(T&& elem) {
return { CATCH_FORWARD(elem), std::equal_to<>{} };
}
//! Creates a matcher that checks whether a range contains element matching a matcher
template <typename Matcher>
std::enable_if_t<Detail::is_matcher_v<Matcher>,
constexpr std::enable_if_t<Detail::is_matcher_v<Matcher>,
ContainsMatcherMatcher<Matcher>> Contains(Matcher&& matcher) {
return { CATCH_FORWARD(matcher) };
}
@@ -92,7 +92,7 @@ namespace Catch {
* Uses `eq` to do the comparisons, the element is provided on the rhs
*/
template <typename T, typename Equality>
ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
constexpr ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) };
}
@@ -9,16 +9,12 @@
namespace Catch {
namespace Matchers {
std::string AllTrueMatcher::describe() const { return "contains only true"; }
AllTrueMatcher AllTrue() { return AllTrueMatcher{}; }
std::string AllTrueMatcher::describe() const { return "contains only true"; }
std::string NoneTrueMatcher::describe() const { return "contains no true"; }
NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; }
std::string AnyTrueMatcher::describe() const { return "contains at least one true"; }
AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; }
} // namespace Matchers
} // namespace Catch
@@ -18,7 +18,7 @@ namespace Catch {
class AllMatchMatcher final : public MatcherGenericBase {
Matcher m_matcher;
public:
AllMatchMatcher(Matcher matcher):
constexpr AllMatchMatcher(Matcher matcher):
m_matcher(CATCH_MOVE(matcher))
{}
@@ -27,7 +27,7 @@ namespace Catch {
}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (!m_matcher.match(elem)) {
return false;
@@ -42,7 +42,7 @@ namespace Catch {
class NoneMatchMatcher final : public MatcherGenericBase {
Matcher m_matcher;
public:
NoneMatchMatcher(Matcher matcher):
constexpr NoneMatchMatcher(Matcher matcher):
m_matcher(CATCH_MOVE(matcher))
{}
@@ -51,7 +51,7 @@ namespace Catch {
}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (m_matcher.match(elem)) {
return false;
@@ -66,7 +66,7 @@ namespace Catch {
class AnyMatchMatcher final : public MatcherGenericBase {
Matcher m_matcher;
public:
AnyMatchMatcher(Matcher matcher):
constexpr AnyMatchMatcher(Matcher matcher):
m_matcher(CATCH_MOVE(matcher))
{}
@@ -75,7 +75,7 @@ namespace Catch {
}
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (m_matcher.match(elem)) {
return true;
@@ -91,7 +91,7 @@ namespace Catch {
std::string describe() const override;
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (!elem) {
return false;
@@ -107,7 +107,7 @@ namespace Catch {
std::string describe() const override;
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (elem) {
return false;
@@ -123,7 +123,7 @@ namespace Catch {
std::string describe() const override;
template <typename RangeLike>
bool match(RangeLike&& rng) const {
constexpr bool match(RangeLike&& rng) const {
for (auto&& elem : rng) {
if (elem) {
return true;
@@ -133,32 +133,35 @@ namespace Catch {
}
};
// Creates a matcher that checks whether all elements in a range match a matcher
//! Creates a matcher that checks whether all elements in a range match a matcher
template <typename Matcher>
AllMatchMatcher<Matcher> AllMatch(Matcher&& matcher) {
constexpr AllMatchMatcher<Matcher> AllMatch(Matcher&& matcher) {
return { CATCH_FORWARD(matcher) };
}
// Creates a matcher that checks whether no element in a range matches a matcher.
//! Creates a matcher that checks whether no element in a range matches a matcher.
template <typename Matcher>
NoneMatchMatcher<Matcher> NoneMatch(Matcher&& matcher) {
constexpr NoneMatchMatcher<Matcher> NoneMatch(Matcher&& matcher) {
return { CATCH_FORWARD(matcher) };
}
// Creates a matcher that checks whether any element in a range matches a matcher.
//! Creates a matcher that checks whether any element in a range matches a matcher.
template <typename Matcher>
AnyMatchMatcher<Matcher> AnyMatch(Matcher&& matcher) {
constexpr AnyMatchMatcher<Matcher> AnyMatch(Matcher&& matcher) {
return { CATCH_FORWARD(matcher) };
}
// Creates a matcher that checks whether all elements in a range are true
AllTrueMatcher AllTrue();
//! Creates a matcher that checks whether all elements in a range are true
inline CATCH_DESTRUCTOR_CONSTEXPR
AllTrueMatcher AllTrue() { return AllTrueMatcher{}; }
// Creates a matcher that checks whether no element in a range is true
NoneTrueMatcher NoneTrue();
//! Creates a matcher that checks whether no element in a range is true
inline CATCH_DESTRUCTOR_CONSTEXPR
NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; }
// Creates a matcher that checks whether any element in a range is true
AnyTrueMatcher AnyTrue();
//! Creates a matcher that checks whether any element in a range is true
inline CATCH_DESTRUCTOR_CONSTEXPR
AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; }
}
}
@@ -9,7 +9,11 @@
namespace Catch {
namespace Matchers {
MatcherGenericBase::~MatcherGenericBase() = default;
std::string MatcherGenericBase::describe() const {
using namespace std::string_literals;
return "Undescribed generic matcher"s;
}
namespace Detail {
@@ -22,12 +22,12 @@
namespace Catch {
namespace Matchers {
class MatcherGenericBase : public MatcherUntypedBase {
std::string describe() const override;
public:
MatcherGenericBase() = default;
~MatcherGenericBase() override; // = default;
constexpr MatcherGenericBase() = default;
MatcherGenericBase(MatcherGenericBase const&) = default;
MatcherGenericBase(MatcherGenericBase&&) = default;
constexpr MatcherGenericBase(MatcherGenericBase const&) = default;
constexpr MatcherGenericBase(MatcherGenericBase&&) = default;
MatcherGenericBase& operator=(MatcherGenericBase const&) = delete;
MatcherGenericBase& operator=(MatcherGenericBase&&) = delete;
@@ -36,7 +36,9 @@ namespace Matchers {
namespace Detail {
template<std::size_t N, std::size_t M>
std::array<void const*, N + M> array_cat(std::array<void const*, N> && lhs, std::array<void const*, M> && rhs) {
constexpr std::array<void const*, N + M>
array_cat( std::array<void const*, N>&& lhs,
std::array<void const*, M>&& rhs ) {
std::array<void const*, N + M> arr{};
std::copy_n(lhs.begin(), N, arr.begin());
std::copy_n(rhs.begin(), M, arr.begin() + N);
@@ -44,7 +46,8 @@ namespace Matchers {
}
template<std::size_t N>
std::array<void const*, N+1> array_cat(std::array<void const*, N> && lhs, void const* rhs) {
constexpr std::array<void const*, N + 1>
array_cat( std::array<void const*, N>&& lhs, void const* rhs ) {
std::array<void const*, N+1> arr{};
std::copy_n(lhs.begin(), N, arr.begin());
arr[N] = rhs;
@@ -52,7 +55,8 @@ namespace Matchers {
}
template<std::size_t N>
std::array<void const*, N+1> array_cat(void const* lhs, std::array<void const*, N> && rhs) {
constexpr std::array<void const*, N + 1>
array_cat( void const* lhs, std::array<void const*, N>&& rhs ) {
std::array<void const*, N + 1> arr{ {lhs} };
std::copy_n(rhs.begin(), N, arr.begin() + 1);
return arr;
@@ -75,23 +79,31 @@ namespace Matchers {
template<std::size_t N, typename Arg>
bool match_all_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
constexpr bool match_all_of( Arg&&,
std::array<void const*, N> const&,
std::index_sequence<> ) {
return true;
}
template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
bool match_all_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
constexpr bool match_all_of( Arg&& arg,
std::array<void const*, N> const& matchers,
std::index_sequence<Idx, Indices...> ) {
return static_cast<T const*>(matchers[Idx])->match(arg) && match_all_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
}
template<std::size_t N, typename Arg>
bool match_any_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) {
constexpr bool match_any_of( Arg&&,
std::array<void const*, N> const&,
std::index_sequence<> ) {
return false;
}
template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices>
bool match_any_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) {
constexpr bool match_any_of( Arg&& arg,
std::array<void const*, N> const& matchers,
std::index_sequence<Idx, Indices...> ) {
return static_cast<T const*>(matchers[Idx])->match(arg) || match_any_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{});
}
@@ -112,15 +124,18 @@ namespace Matchers {
public:
MatchAllOfGeneric(MatchAllOfGeneric const&) = delete;
MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete;
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
constexpr MatchAllOfGeneric( MatchAllOfGeneric&& ) = default;
constexpr MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
constexpr MatchAllOfGeneric(
MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND )
: m_matchers{ {std::addressof(matchers)...} } {}
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
constexpr explicit MatchAllOfGeneric(
std::array<void const*, sizeof...( MatcherTs )> matchers ):
m_matchers{ matchers } {}
template<typename Arg>
bool match(Arg&& arg) const {
constexpr bool match( Arg&& arg ) const {
return match_all_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
}
@@ -136,7 +151,7 @@ namespace Matchers {
//! Avoids type nesting for `GenericAllOf && GenericAllOf` case
template<typename... MatchersRHS>
friend
constexpr friend
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
@@ -145,7 +160,8 @@ namespace Matchers {
//! Avoids type nesting for `GenericAllOf && some matcher` case
template<typename MatcherRHS>
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
constexpr friend std::enable_if_t<
is_matcher_v<MatcherRHS>,
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
@@ -154,7 +170,8 @@ namespace Matchers {
//! Avoids type nesting for `some matcher && GenericAllOf` case
template<typename MatcherLHS>
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
constexpr friend std::enable_if_t<
is_matcher_v<MatcherLHS>,
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
@@ -168,15 +185,18 @@ namespace Matchers {
public:
MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete;
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete;
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
constexpr MatchAnyOfGeneric( MatchAnyOfGeneric&& ) = default;
constexpr MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
constexpr MatchAnyOfGeneric(
MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND )
: m_matchers{ {std::addressof(matchers)...} } {}
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
constexpr explicit MatchAnyOfGeneric(
std::array<void const*, sizeof...( MatcherTs )> matchers ):
m_matchers{ matchers } {}
template<typename Arg>
bool match(Arg&& arg) const {
constexpr bool match( Arg&& arg ) const {
return match_any_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{});
}
@@ -192,7 +212,8 @@ namespace Matchers {
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
template<typename... MatchersRHS>
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
constexpr friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>
operator||(
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
@@ -200,7 +221,8 @@ namespace Matchers {
//! Avoids type nesting for `GenericAnyOf || some matcher` case
template<typename MatcherRHS>
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
constexpr friend std::enable_if_t<
is_matcher_v<MatcherRHS>,
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
@@ -209,7 +231,8 @@ namespace Matchers {
//! Avoids type nesting for `some matcher || GenericAnyOf` case
template<typename MatcherLHS>
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
constexpr friend std::enable_if_t<
is_matcher_v<MatcherLHS>,
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND) {
@@ -225,14 +248,15 @@ namespace Matchers {
public:
MatchNotOfGeneric(MatchNotOfGeneric const&) = delete;
MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete;
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
constexpr MatchNotOfGeneric( MatchNotOfGeneric&& ) = default;
constexpr MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND)
constexpr explicit MatchNotOfGeneric(
MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND )
: m_matcher{matcher} {}
template<typename Arg>
bool match(Arg&& arg) const {
constexpr bool match( Arg&& arg ) const {
return !m_matcher.match(arg);
}
@@ -241,7 +265,7 @@ namespace Matchers {
}
//! Negating negation can just unwrap and return underlying matcher
friend MatcherT const&
constexpr friend MatcherT const&
operator!( MatchNotOfGeneric<MatcherT> const& matcher
CATCH_ATTR_LIFETIMEBOUND ) {
return matcher.m_matcher;
@@ -252,14 +276,18 @@ namespace Matchers {
// compose only generic matchers
template<typename MatcherLHS, typename MatcherRHS>
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
constexpr std::enable_if_t<
Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>,
Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
}
template<typename MatcherLHS, typename MatcherRHS>
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
constexpr std::enable_if_t<
Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>,
Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
@@ -267,7 +295,8 @@ namespace Matchers {
//! Wrap provided generic matcher in generic negator
template<typename MatcherT>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
constexpr std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>,
Detail::MatchNotOfGeneric<MatcherT>>
operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
}
@@ -29,7 +29,7 @@ namespace Catch {
#endif
template<typename ArgT, typename MatcherT>
class MatchExpr : public ITransientExpression {
class MatchExpr final : public ITransientExpression {
ArgT && m_arg;
MatcherT const& m_matcher;
public:
+2 -3
View File
@@ -65,7 +65,6 @@ internal_headers = [
'interfaces/catch_interfaces_all.hpp',
'interfaces/catch_interfaces_capture.hpp',
'interfaces/catch_interfaces_config.hpp',
'interfaces/catch_interfaces_enum_values_registry.hpp',
'interfaces/catch_interfaces_exception.hpp',
'interfaces/catch_interfaces_generatortracker.hpp',
'interfaces/catch_interfaces_registry_hub.hpp',
@@ -95,7 +94,7 @@ internal_headers = [
'internal/catch_decomposer.hpp',
'internal/catch_deprecation_macro.hpp',
'internal/catch_enforce.hpp',
'internal/catch_enum_values_registry.hpp',
'internal/catch_enum_info.hpp',
'internal/catch_errno_guard.hpp',
'internal/catch_exception_translator_registry.hpp',
'internal/catch_fatal_condition_handler.hpp',
@@ -224,7 +223,7 @@ internal_sources = files(
'internal/catch_debugger.cpp',
'internal/catch_decomposer.cpp',
'internal/catch_enforce.cpp',
'internal/catch_enum_values_registry.cpp',
'internal/catch_enum_info.cpp',
'internal/catch_errno_guard.cpp',
'internal/catch_exception_translator_registry.cpp',
'internal/catch_fatal_condition_handler.cpp',
@@ -20,9 +20,8 @@ namespace Catch {
bool operator()(
Detail::unique_ptr<CumulativeReporterBase::SectionNode> const&
node ) const {
return (
( node->stats.sectionInfo.name == m_other.name ) &&
( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) );
return node->stats.sectionInfo.name == m_other.name
&& node->stats.sectionInfo.lineInfo == m_other.lineInfo;
}
void operator=( BySectionInfo const& ) = delete;
@@ -199,7 +199,7 @@ namespace Catch {
}
// minimum whitespace to pad tag counts, possibly overwritten below
size_t maxTagCountLen = 2;
int maxTagCountLen = 2;
// determine necessary padding for tag count column
if ( ! tags.empty() ) {
@@ -214,7 +214,7 @@ namespace Catch {
// more padding necessary for 3+ digits
if (maxTagCount >= 100) {
auto numDigits = 1 + std::floor( std::log10( maxTagCount ) );
maxTagCountLen = static_cast<size_t>( numDigits );
maxTagCountLen = static_cast<int>( numDigits );
}
}
+1
View File
@@ -62,6 +62,7 @@ cc_test(
"SelfTest/UsageTests/Exception.tests.cpp",
"SelfTest/UsageTests/Generators.tests.cpp",
"SelfTest/UsageTests/Matchers.tests.cpp",
"SelfTest/UsageTests/MatchersConstexpr.tests.cpp",
"SelfTest/UsageTests/MatchersRanges.tests.cpp",
"SelfTest/UsageTests/Message.tests.cpp",
"SelfTest/UsageTests/Misc.tests.cpp",
+16 -1
View File
@@ -124,8 +124,9 @@ set(TEST_SOURCES
${SELF_TEST_DIR}/UsageTests/ToStringWhich.tests.cpp
${SELF_TEST_DIR}/UsageTests/Tricky.tests.cpp
${SELF_TEST_DIR}/UsageTests/VariadicMacros.tests.cpp
${SELF_TEST_DIR}/UsageTests/MatchersRanges.tests.cpp
${SELF_TEST_DIR}/UsageTests/Matchers.tests.cpp
${SELF_TEST_DIR}/UsageTests/MatchersConstexpr.tests.cpp
${SELF_TEST_DIR}/UsageTests/MatchersRanges.tests.cpp
)
set(TEST_HEADERS
@@ -624,6 +625,20 @@ if(CATCH_ENABLE_CMAKE_HELPER_TESTS)
COST 240
LABELS "uses-python"
)
add_test(NAME "CMakeHelper::PrepareCommand"
COMMAND
"${CMAKE_COMMAND}"
"-DCATCH_ADD_TESTS_SCRIPT=${CATCH_DIR}/extras/CatchAddTests.cmake"
-P "${CMAKE_CURRENT_LIST_DIR}/TestScripts/DiscoverTests/TestPrepareCommand.cmake"
)
add_test(NAME "CMakeHelper::DecomposeJsonArray"
COMMAND
"${CMAKE_COMMAND}"
"-DCATCH_ADD_TESTS_SCRIPT=${CATCH_DIR}/extras/CatchAddTests.cmake"
-P "${CMAKE_CURRENT_LIST_DIR}/TestScripts/DiscoverTests/TestDecomposeJsonArray.cmake"
)
endif()
foreach(reporterName # "Automake" - the simple .trs format does not support any kind of comments/metadata
+81 -25
View File
@@ -174,7 +174,7 @@ target_compile_definitions(DeferredStaticChecks PRIVATE "CATCH_CONFIG_RUNTIME_ST
add_test(NAME DeferredStaticChecks COMMAND DeferredStaticChecks -r compact)
set_tests_properties(DeferredStaticChecks
PROPERTIES
PASS_REGULAR_EXPRESSION "test cases: 1 \\| 1 failed\nassertions: 3 \\| 3 failed"
PASS_REGULAR_EXPRESSION "test cases: 1 \\| 1 failed\nassertions: 3 \\| 3 failed;test cases: 1 \\| 1 failed\nassertions: 4 \\| 4 failed"
)
add_executable(MixingClearedAndUnclearedMessages ${TESTS_DIR}/X06-MixingClearedAndUnclearedMessages.cpp)
@@ -526,29 +526,18 @@ set_tests_properties(TestSpecs::SkippingAllTestsFails
WILL_FAIL ON
)
set(EXTRA_TEST_BINARIES
AllSkipped
PrefixedMacros
DisabledMacros
DisabledExceptions-DefaultHandler
DisabledExceptions-CustomHandler
FallbackStringifier
DisableStringification
PartialTestCaseEvents
DuplicatedTestCases-SameNameAndTags
DuplicatedTestCases-SameNameDifferentTags
DuplicatedTestCases-DuplicatedTestCaseMethods
NoTests
ListenersGetEventsBeforeReporters
MixingClearedAndUnclearedMessages
# DebugBreakMacros
)
add_executable(FastCompileMacros ${TESTS_DIR}/X07-FastCompileMacros.cpp)
target_link_libraries(FastCompileMacros PRIVATE Catch2_buildall_interface)
target_compile_definitions(FastCompileMacros PRIVATE CATCH_CONFIG_FAST_COMPILE)
# Notice that we are modifying EXTRA_TEST_BINARIES destructively, do not
# use it after this point!
list(FILTER EXTRA_TEST_BINARIES EXCLUDE REGEX "DisabledExceptions.*")
list(APPEND CATCH_TEST_TARGETS ${EXTRA_TEST_BINARIES})
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
add_test(
NAME CompileConfiguration::FastCompile
COMMAND $<TARGET_FILE:FastCompileMacros>
)
set_tests_properties(CompileConfiguration::FastCompile
PROPERTIES
PASS_REGULAR_EXPRESSION "test cases: 6 \\| 1 passed \\| 1 failed \\| 4 failed as expected\nassertions: 13 \\| 6 passed \\| 2 failed \\| 5 failed as expected"
)
# This sets up a one-off executable that compiles against the amalgamated
# files, and then runs it for a super simple check that the amalgamated
@@ -594,12 +583,79 @@ add_executable(InfiniteGenerators ${TESTS_DIR}/X95-InfiniteGenerators.cpp)
target_link_libraries(InfiniteGenerators PRIVATE Catch2::Catch2WithMain)
add_test(
NAME Warnings::InfiniteGenerators
NAME Warnings::InfiniteGenerators::NoFilterWarns
COMMAND $<TARGET_FILE:InfiniteGenerators> --warn InfiniteGenerators
)
set_tests_properties(Warnings::InfiniteGenerators
set_tests_properties(Warnings::InfiniteGenerators::NoFilterWarns
PROPERTIES
# One test case fails with infinite generator, but the other one runs
PASS_REGULAR_EXPRESSION "test cases: 2 \\| 1 passed \\| 1 failed"
TIMEOUT 5
)
add_test(
NAME Warnings::InfiniteGenerators::MatchAllFilterWarns
COMMAND $<TARGET_FILE:InfiniteGenerators>
--warn InfiniteGenerators
--path-filter g:*
)
set_tests_properties(Warnings::InfiniteGenerators::MatchAllFilterWarns
PROPERTIES
# One test case fails with infinite generator, but the other one runs
PASS_REGULAR_EXPRESSION "test cases: 2 \\| 1 passed \\| 1 failed"
TIMEOUT 5
)
add_test(
NAME Warnings::InfiniteGenerators::MatchOneFilterDoesntWarn
COMMAND $<TARGET_FILE:InfiniteGenerators>
--warn InfiniteGenerators
--path-filter g:1
)
set_tests_properties(Warnings::InfiniteGenerators::MatchOneFilterDoesntWarn
PROPERTIES
# One test case fails with infinite generator, but the other one runs
PASS_REGULAR_EXPRESSION "All tests passed \\(1 assertion in 2 test cases\\)"
TIMEOUT 5
)
add_test(
NAME Warnings::InfiniteGenerators::SectionFilterWarns
COMMAND $<TARGET_FILE:InfiniteGenerators>
--warn InfiniteGenerators
--section FooBarBaz
)
set_tests_properties(Warnings::InfiniteGenerators::SectionFilterWarns
PROPERTIES
# One test case fails with infinite generator, but the other one runs
PASS_REGULAR_EXPRESSION "test cases: 2 \\| 1 passed \\| 1 failed"
TIMEOUT 5
)
set(EXTRA_TEST_BINARIES
AllSkipped
PrefixedMacros
DisabledMacros
DisabledExceptions-DefaultHandler
DisabledExceptions-CustomHandler
FallbackStringifier
DisableStringification
PartialTestCaseEvents
DuplicatedTestCases-SameNameAndTags
DuplicatedTestCases-SameNameDifferentTags
DuplicatedTestCases-DuplicatedTestCaseMethods
NoTests
ListenersGetEventsBeforeReporters
MixingClearedAndUnclearedMessages
FastCompileMacros
InfiniteGenerators
ThreadSafetyTests
# DebugBreakMacros
)
# Notice that we are modifying EXTRA_TEST_BINARIES destructively, do not
# use it after this point!
list(FILTER EXTRA_TEST_BINARIES EXCLUDE REGEX "DisabledExceptions.*")
list(APPEND CATCH_TEST_TARGETS ${EXTRA_TEST_BINARIES})
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
-1
View File
@@ -3,7 +3,6 @@ yet:
CATCH_CONFIG_COUNTER // Use __COUNTER__ to generate unique names for test cases
CATCH_CONFIG_WINDOWS_SEH // Enable SEH handling on Windows
CATCH_CONFIG_FAST_COMPILE // Sacrifices some (rather minor) features for compilation speed
CATCH_CONFIG_POSIX_SIGNALS // Enable handling POSIX signals
CATCH_CONFIG_WINDOWS_CRTDBG // Enable leak checking using Windows's CRT Debug Heap
CATCH_CONFIG_DEFAULT_REPORTER
+2
View File
@@ -53,6 +53,8 @@ TEST_CASE( "Disabled Macros" ) {
REQUIRE_THAT( 1,
Catch::Matchers::Predicate( []( int ) { return false; } ) );
BENCHMARK( "Disabled benchmark" ) { REQUIRE( 1 == 2 ); };
STATIC_REQUIRE_THAT( 1, Catch::Matchers::Predicate( []( int ) { return false; } ) );
}
struct DisabledFixture {};
@@ -12,10 +12,36 @@
*/
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_templated.hpp>
#if defined( CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED )
namespace {
struct MatchNoneMatcher final : public Catch::Matchers::MatcherGenericBase {
public:
template <typename Any>
constexpr bool match( Any&& ) const {
return false;
}
std::string describe() const override {
using namespace std::string_literals;
return "Matches anything"s;
}
};
constexpr MatchNoneMatcher MatchNone() { return MatchNoneMatcher(); }
} // namespace
#endif
TEST_CASE("Deferred static checks") {
STATIC_CHECK(1 == 2);
STATIC_CHECK_FALSE(1 != 2);
#if defined(CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED)
STATIC_CHECK_THAT(1, MatchNone());
#endif
// This last assertion must be executed too
CHECK(1 == 2);
}
@@ -0,0 +1,63 @@
// 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
* Test that various basic macros work with CATCH_CONFIG_FAST_COMPILE.
*
* Note that the current checking is rather loose. We check that the
* macros compile, and that the test cases (don't) fail as they are
* supposed to.
*/
#include <catch2/catch_test_macros.hpp>
#include <stdexcept>
namespace {
[[noreturn]]
static void throws() {
throw std::runtime_error{ "sup" };
}
static void doesnt_throw() {}
[[noreturn]]
static int throws_i() {
throw std::runtime_error{ "sup" };
}
} // namespace
TEST_CASE( "Passing macros work" ) {
REQUIRE( 1 != 2 );
CHECK( 2 == 2 );
REQUIRE_THROWS( throws() );
REQUIRE_NOTHROW( doesnt_throw() );
}
TEST_CASE( "Failing macros work", "[!shouldfail]" ) {
CHECK( 1 != 2 );
CHECK( 2 == 2 );
CHECK( 3 == 2 );
}
TEST_CASE( "Failing NOTHROW works", "[!shouldfail]" ) {
REQUIRE_NOTHROW( throws() );
}
TEST_CASE( "Failing THROW works", "[!shouldfail]" ) {
REQUIRE_THROWS( doesnt_throw() );
}
TEST_CASE( "Unexpected exception in REQUIRE gets inverted properly",
"[!shouldfail]" ) {
REQUIRE( throws_i() == 1 );
}
TEST_CASE( "Unexpected exception in REQUIRE fails properly" ) {
REQUIRE( throws_i() == 2 );
}
@@ -33,6 +33,7 @@ namespace {
TEST_CASE() {
auto _ = GENERATE( make_infinite_generator() );
(void)_;
}
TEST_CASE() {
@@ -144,6 +144,7 @@ Nor would this
:test-result: SKIP Empty generators can SKIP in constructor
:test-result: PASS Empty stream name opens cout stream
:test-result: FAIL EndsWith string matcher
:test-result: PASS Enum backed by larger underlying type
:test-result: PASS Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM
:test-result: PASS Enums in namespaces can quickly have stringification enabled using CATCH_REGISTER_ENUM
:test-result: PASS Epsilon only applies to Approx's value
@@ -189,6 +190,9 @@ Nor would this
:test-result: PASS Inequality checks that should succeed
:test-result: PASS JsonWriter
:test-result: PASS JsonWriter escapes characters in strings properly
:test-result: PASS JsonWriter serializes non-finite FP using Python spelling - double
:test-result: PASS JsonWriter serializes non-finite FP using Python spelling - float
:test-result: PASS JsonWriter serializes numbers independently of the global locale
:test-result: PASS Lambdas in assertions
:test-result: PASS Less-than inequalities with different epsilons
:test-result: PASS ManuallyRegistered
@@ -142,6 +142,7 @@
:test-result: SKIP Empty generators can SKIP in constructor
:test-result: PASS Empty stream name opens cout stream
:test-result: FAIL EndsWith string matcher
:test-result: PASS Enum backed by larger underlying type
:test-result: PASS Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM
:test-result: PASS Enums in namespaces can quickly have stringification enabled using CATCH_REGISTER_ENUM
:test-result: PASS Epsilon only applies to Approx's value
@@ -187,6 +188,9 @@
:test-result: PASS Inequality checks that should succeed
:test-result: PASS JsonWriter
:test-result: PASS JsonWriter escapes characters in strings properly
:test-result: PASS JsonWriter serializes non-finite FP using Python spelling - double
:test-result: PASS JsonWriter serializes non-finite FP using Python spelling - float
:test-result: PASS JsonWriter serializes numbers independently of the global locale
:test-result: PASS Lambdas in assertions
:test-result: PASS Less-than inequalities with different epsilons
:test-result: PASS ManuallyRegistered
@@ -576,15 +576,18 @@ Approx.tests.cpp:<line number>: passed: 101.000001 != Approx(100).epsilon(0.01)
Approx.tests.cpp:<line number>: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 )
Message.tests.cpp:<line number>: passed: true with 1 message: 'a'
Message.tests.cpp:<line number>: failed: false with 1 message: 'b'
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(0) == "Value1" for: Value1 == "Value1"
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(1) == "Value2" for: Value2 == "Value2"
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **}
EnumToString.tests.cpp:<line number>: passed: enumInfo.lookup(0) == "Value1" for: Value1 == "Value1"
EnumToString.tests.cpp:<line number>: passed: enumInfo.lookup(1) == "Value2" for: Value2 == "Value2"
EnumToString.tests.cpp:<line number>: passed: enumInfo.lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **}
==
"{** unexpected enum value **}"
Skip.tests.cpp:<line number>: skipped: 'This generator is empty'
Stream.tests.cpp:<line number>: passed: Catch::makeStream( "" )->isConsole() for: true
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring"
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
EnumToString.tests.cpp:<line number>: passed: stringify( LargeEnum::Flag1 ) == "Flag1" for: "Flag1" == "Flag1"
EnumToString.tests.cpp:<line number>: passed: stringify( LargeEnum::Flag2 ) == "Flag2" for: "Flag2" == "Flag2"
EnumToString.tests.cpp:<line number>: passed: stringify( LargeEnum::Flag5 ) == "Flag5" for: "Flag5" == "Flag5"
EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value1 ) == "Value1" for: "Value1" == "Value1"
EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value2 ) == "Value2" for: "Value2" == "Value2"
EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value3 ) == "Value3" for: "Value3" == "Value3"
@@ -1245,6 +1248,26 @@ Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\n\"" for: ""\n"" ==
Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\r\"" for: ""\r"" == ""\r""
Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\t\"" for: ""\t"" == ""\t""
Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n""
Json.tests.cpp:<line number>: passed: sstream.str() == "NaN" for: "NaN" == "NaN"
Json.tests.cpp:<line number>: passed: sstream.str() == "Infinity" for: "Infinity" == "Infinity"
Json.tests.cpp:<line number>: passed: sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
Json.tests.cpp:<line number>: passed: sstream.str() == "NaN" for: "NaN" == "NaN"
Json.tests.cpp:<line number>: passed: sstream.str() == "Infinity" for: "Infinity" == "Infinity"
Json.tests.cpp:<line number>: passed: sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
Json.tests.cpp:<line number>: passed: sstream.str(), ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"int\": 1234567," ) && ContainsSubstring( "\"bool-1\": true," ) && ContainsSubstring( "\"bool-2\": false," ) && ContainsSubstring( "\"array\": [\n 2.5,\n 1234567\n ]\n}" ) for: "{
"double": 1.5,
"int": 1234567,
"bool-1": true,
"bool-2": false,
"array": [
2.5,
1234567
]
}" ( contains: ""double": 1.5," and contains: ""int": 1234567," and contains: ""bool-1": true," and contains: ""bool-2": false," and contains: ""array": [
2.5,
1234567
]
}" )
Compilation.tests.cpp:<line number>: passed: []() { return true; }() for: true
Approx.tests.cpp:<line number>: passed: d <= Approx( 1.24 ) for: 1.22999999999999998
<=
@@ -2755,13 +2778,13 @@ Tricky.tests.cpp:<line number>: passed: ptr.get() == 0 for: 0 == 0
ToStringPair.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" for: "{ { 42, "Arthur" }, { "Ford", 24 } }"
==
"{ { 42, "Arthur" }, { "Ford", 24 } }"
ToString.tests.cpp:<line number>: passed: parseEnums( "" ), Equals( std::vector<Catch::StringRef>{} ) for: { } Equals: { }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: passed: parseEnums( "Value1" ), Equals( std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: passed: parseEnums( "EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2"} ) for: { Value1, Value2 } Equals: { Value1, Value2 }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "" ), Equals( std::vector<Catch::StringRef>{} ) for: { } Equals: { }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "Value1" ), Equals( std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2"} ) for: { Value1, Value2 } Equals: { Value1, Value2 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
Tricky.tests.cpp:<line number>: passed: p == 0 for: 0 == 0
Message.tests.cpp:<line number>: passed: true with 1 message: 'this MAY be seen IF info is printed for passing assertions'
Message.tests.cpp:<line number>: failed: false with 2 messages: 'this SHOULD be seen' and 'this SHOULD also be seen'
@@ -2997,7 +3020,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
test cases: 450 | 330 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2413 | 2212 passed | 158 failed | 43 failed as expected
test cases: 454 | 334 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2423 | 2222 passed | 158 failed | 43 failed as expected
@@ -574,15 +574,18 @@ Approx.tests.cpp:<line number>: passed: 101.000001 != Approx(100).epsilon(0.01)
Approx.tests.cpp:<line number>: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 )
Message.tests.cpp:<line number>: passed: true with 1 message: 'a'
Message.tests.cpp:<line number>: failed: false with 1 message: 'b'
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(0) == "Value1" for: Value1 == "Value1"
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(1) == "Value2" for: Value2 == "Value2"
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **}
EnumToString.tests.cpp:<line number>: passed: enumInfo.lookup(0) == "Value1" for: Value1 == "Value1"
EnumToString.tests.cpp:<line number>: passed: enumInfo.lookup(1) == "Value2" for: Value2 == "Value2"
EnumToString.tests.cpp:<line number>: passed: enumInfo.lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **}
==
"{** unexpected enum value **}"
Skip.tests.cpp:<line number>: skipped: 'This generator is empty'
Stream.tests.cpp:<line number>: passed: Catch::makeStream( "" )->isConsole() for: true
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring"
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
EnumToString.tests.cpp:<line number>: passed: stringify( LargeEnum::Flag1 ) == "Flag1" for: "Flag1" == "Flag1"
EnumToString.tests.cpp:<line number>: passed: stringify( LargeEnum::Flag2 ) == "Flag2" for: "Flag2" == "Flag2"
EnumToString.tests.cpp:<line number>: passed: stringify( LargeEnum::Flag5 ) == "Flag5" for: "Flag5" == "Flag5"
EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value1 ) == "Value1" for: "Value1" == "Value1"
EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value2 ) == "Value2" for: "Value2" == "Value2"
EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value3 ) == "Value3" for: "Value3" == "Value3"
@@ -1243,6 +1246,26 @@ Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\n\"" for: ""\n"" ==
Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\r\"" for: ""\r"" == ""\r""
Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\t\"" for: ""\t"" == ""\t""
Json.tests.cpp:<line number>: passed: sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n""
Json.tests.cpp:<line number>: passed: sstream.str() == "NaN" for: "NaN" == "NaN"
Json.tests.cpp:<line number>: passed: sstream.str() == "Infinity" for: "Infinity" == "Infinity"
Json.tests.cpp:<line number>: passed: sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
Json.tests.cpp:<line number>: passed: sstream.str() == "NaN" for: "NaN" == "NaN"
Json.tests.cpp:<line number>: passed: sstream.str() == "Infinity" for: "Infinity" == "Infinity"
Json.tests.cpp:<line number>: passed: sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
Json.tests.cpp:<line number>: passed: sstream.str(), ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"int\": 1234567," ) && ContainsSubstring( "\"bool-1\": true," ) && ContainsSubstring( "\"bool-2\": false," ) && ContainsSubstring( "\"array\": [\n 2.5,\n 1234567\n ]\n}" ) for: "{
"double": 1.5,
"int": 1234567,
"bool-1": true,
"bool-2": false,
"array": [
2.5,
1234567
]
}" ( contains: ""double": 1.5," and contains: ""int": 1234567," and contains: ""bool-1": true," and contains: ""bool-2": false," and contains: ""array": [
2.5,
1234567
]
}" )
Compilation.tests.cpp:<line number>: passed: []() { return true; }() for: true
Approx.tests.cpp:<line number>: passed: d <= Approx( 1.24 ) for: 1.22999999999999998
<=
@@ -2744,13 +2767,13 @@ Tricky.tests.cpp:<line number>: passed: ptr.get() == 0 for: 0 == 0
ToStringPair.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" for: "{ { 42, "Arthur" }, { "Ford", 24 } }"
==
"{ { 42, "Arthur" }, { "Ford", 24 } }"
ToString.tests.cpp:<line number>: passed: parseEnums( "" ), Equals( std::vector<Catch::StringRef>{} ) for: { } Equals: { }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: passed: parseEnums( "Value1" ), Equals( std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: passed: parseEnums( "EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2"} ) for: { Value1, Value2 } Equals: { Value1, Value2 }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
ToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "" ), Equals( std::vector<Catch::StringRef>{} ) for: { } Equals: { }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "Value1" ), Equals( std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) for: { Value1 } Equals: { Value1 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2"} ) for: { Value1, Value2 } Equals: { Value1, Value2 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
EnumToString.tests.cpp:<line number>: passed: parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) for: { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
Tricky.tests.cpp:<line number>: passed: p == 0 for: 0 == 0
Message.tests.cpp:<line number>: passed: true with 1 message: 'this MAY be seen IF info is printed for passing assertions'
Message.tests.cpp:<line number>: failed: false with 2 messages: 'this SHOULD be seen' and 'this SHOULD also be seen'
@@ -2986,7 +3009,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
test cases: 450 | 330 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2413 | 2212 passed | 158 failed | 43 failed as expected
test cases: 454 | 334 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2423 | 2222 passed | 158 failed | 43 failed as expected
@@ -1743,6 +1743,6 @@ due to unexpected exception with message:
Why would you throw a std::string?
===============================================================================
test cases: 450 | 348 passed | 76 failed | 7 skipped | 19 failed as expected
assertions: 2391 | 2212 passed | 136 failed | 43 failed as expected
test cases: 454 | 352 passed | 76 failed | 7 skipped | 19 failed as expected
assertions: 2401 | 2222 passed | 136 failed | 43 failed as expected
+137 -19
View File
@@ -4241,21 +4241,21 @@ with message:
-------------------------------------------------------------------------------
Directly creating an EnumInfo
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo->lookup(0) == "Value1" )
EnumToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo.lookup(0) == "Value1" )
with expansion:
Value1 == "Value1"
ToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo->lookup(1) == "Value2" )
EnumToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo.lookup(1) == "Value2" )
with expansion:
Value2 == "Value2"
ToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo->lookup(3) == "{** unexpected enum value **}" )
EnumToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo.lookup(3) == "{** unexpected enum value **}" )
with expansion:
{** unexpected enum value **}
==
@@ -4299,6 +4299,27 @@ with expansion:
"this string contains 'abc' as a substring" ends with: "this" (case
insensitive)
-------------------------------------------------------------------------------
Enum backed by larger underlying type
-------------------------------------------------------------------------------
EnumToString.tests.cpp:<line number>
...............................................................................
EnumToString.tests.cpp:<line number>: PASSED:
REQUIRE( stringify( LargeEnum::Flag1 ) == "Flag1" )
with expansion:
"Flag1" == "Flag1"
EnumToString.tests.cpp:<line number>: PASSED:
REQUIRE( stringify( LargeEnum::Flag2 ) == "Flag2" )
with expansion:
"Flag2" == "Flag2"
EnumToString.tests.cpp:<line number>: PASSED:
REQUIRE( stringify( LargeEnum::Flag5 ) == "Flag5" )
with expansion:
"Flag5" == "Flag5"
-------------------------------------------------------------------------------
Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM
-------------------------------------------------------------------------------
@@ -8198,6 +8219,103 @@ Json.tests.cpp:<line number>: PASSED:
with expansion:
""\\/\t\r\n"" == ""\\/\t\r\n""
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - double
NaN
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "NaN" )
with expansion:
"NaN" == "NaN"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - double
Pos nf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "Infinity" )
with expansion:
"Infinity" == "Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - double
Neg inf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "-Infinity" )
with expansion:
"-Infinity" == "-Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - float
NaN
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "NaN" )
with expansion:
"NaN" == "NaN"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - float
Pos nf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "Infinity" )
with expansion:
"Infinity" == "Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - float
Neg inf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "-Infinity" )
with expansion:
"-Infinity" == "-Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes numbers independently of the global locale
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( sstream.str(), ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"int\": 1234567," ) && ContainsSubstring( "\"bool-1\": true," ) && ContainsSubstring( "\"bool-2\": false," ) && ContainsSubstring( "\"array\": [\n 2.5,\n 1234567\n ]\n}" ) )
with expansion:
"{
"double": 1.5,
"int": 1234567,
"bool-1": true,
"bool-2": false,
"array": [
2.5,
1234567
]
}" ( contains: ""double": 1.5," and contains: ""int": 1234567," and contains:
""bool-1": true," and contains: ""bool-2": false," and contains: ""array": [
2.5,
1234567
]
}" )
-------------------------------------------------------------------------------
Lambdas in assertions
-------------------------------------------------------------------------------
@@ -18383,10 +18501,10 @@ with expansion:
parseEnums
No enums
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "" ), Equals( std::vector<Catch::StringRef>{} ) )
with expansion:
{ } Equals: { }
@@ -18395,20 +18513,20 @@ with expansion:
parseEnums
One enum value
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) )
with expansion:
{ Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "Value1" ), Equals( std::vector<Catch::StringRef>{"Value1"} ) )
with expansion:
{ Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) )
with expansion:
{ Value1 } Equals: { Value1 }
@@ -18417,20 +18535,20 @@ with expansion:
parseEnums
Multiple enum values
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2"} ) )
with expansion:
{ Value1, Value2 } Equals: { Value1, Value2 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) )
with expansion:
{ Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) )
with expansion:
{ Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
@@ -20113,6 +20231,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 450 | 330 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2413 | 2212 passed | 158 failed | 43 failed as expected
test cases: 454 | 334 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2423 | 2222 passed | 158 failed | 43 failed as expected
@@ -4239,21 +4239,21 @@ with message:
-------------------------------------------------------------------------------
Directly creating an EnumInfo
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo->lookup(0) == "Value1" )
EnumToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo.lookup(0) == "Value1" )
with expansion:
Value1 == "Value1"
ToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo->lookup(1) == "Value2" )
EnumToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo.lookup(1) == "Value2" )
with expansion:
Value2 == "Value2"
ToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo->lookup(3) == "{** unexpected enum value **}" )
EnumToString.tests.cpp:<line number>: PASSED:
CHECK( enumInfo.lookup(3) == "{** unexpected enum value **}" )
with expansion:
{** unexpected enum value **}
==
@@ -4297,6 +4297,27 @@ with expansion:
"this string contains 'abc' as a substring" ends with: "this" (case
insensitive)
-------------------------------------------------------------------------------
Enum backed by larger underlying type
-------------------------------------------------------------------------------
EnumToString.tests.cpp:<line number>
...............................................................................
EnumToString.tests.cpp:<line number>: PASSED:
REQUIRE( stringify( LargeEnum::Flag1 ) == "Flag1" )
with expansion:
"Flag1" == "Flag1"
EnumToString.tests.cpp:<line number>: PASSED:
REQUIRE( stringify( LargeEnum::Flag2 ) == "Flag2" )
with expansion:
"Flag2" == "Flag2"
EnumToString.tests.cpp:<line number>: PASSED:
REQUIRE( stringify( LargeEnum::Flag5 ) == "Flag5" )
with expansion:
"Flag5" == "Flag5"
-------------------------------------------------------------------------------
Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM
-------------------------------------------------------------------------------
@@ -8196,6 +8217,103 @@ Json.tests.cpp:<line number>: PASSED:
with expansion:
""\\/\t\r\n"" == ""\\/\t\r\n""
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - double
NaN
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "NaN" )
with expansion:
"NaN" == "NaN"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - double
Pos nf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "Infinity" )
with expansion:
"Infinity" == "Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - double
Neg inf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "-Infinity" )
with expansion:
"-Infinity" == "-Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - float
NaN
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "NaN" )
with expansion:
"NaN" == "NaN"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - float
Pos nf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "Infinity" )
with expansion:
"Infinity" == "Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes non-finite FP using Python spelling - float
Neg inf
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE( sstream.str() == "-Infinity" )
with expansion:
"-Infinity" == "-Infinity"
-------------------------------------------------------------------------------
JsonWriter serializes numbers independently of the global locale
-------------------------------------------------------------------------------
Json.tests.cpp:<line number>
...............................................................................
Json.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( sstream.str(), ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"int\": 1234567," ) && ContainsSubstring( "\"bool-1\": true," ) && ContainsSubstring( "\"bool-2\": false," ) && ContainsSubstring( "\"array\": [\n 2.5,\n 1234567\n ]\n}" ) )
with expansion:
"{
"double": 1.5,
"int": 1234567,
"bool-1": true,
"bool-2": false,
"array": [
2.5,
1234567
]
}" ( contains: ""double": 1.5," and contains: ""int": 1234567," and contains:
""bool-1": true," and contains: ""bool-2": false," and contains: ""array": [
2.5,
1234567
]
}" )
-------------------------------------------------------------------------------
Lambdas in assertions
-------------------------------------------------------------------------------
@@ -18372,10 +18490,10 @@ with expansion:
parseEnums
No enums
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "" ), Equals( std::vector<Catch::StringRef>{} ) )
with expansion:
{ } Equals: { }
@@ -18384,20 +18502,20 @@ with expansion:
parseEnums
One enum value
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) )
with expansion:
{ Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "Value1" ), Equals( std::vector<Catch::StringRef>{"Value1"} ) )
with expansion:
{ Value1 } Equals: { Value1 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "EnumName::Value1" ), Equals(std::vector<Catch::StringRef>{"Value1"} ) )
with expansion:
{ Value1 } Equals: { Value1 }
@@ -18406,20 +18524,20 @@ with expansion:
parseEnums
Multiple enum values
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
EnumToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2"} ) )
with expansion:
{ Value1, Value2 } Equals: { Value1, Value2 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) )
with expansion:
{ Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
ToString.tests.cpp:<line number>: PASSED:
EnumToString.tests.cpp:<line number>: PASSED:
CHECK_THAT( parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector<Catch::StringRef>{"Value1", "Value2", "Value3"} ) )
with expansion:
{ Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
@@ -20102,6 +20220,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 450 | 330 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2413 | 2212 passed | 158 failed | 43 failed as expected
test cases: 454 | 334 passed | 96 failed | 6 skipped | 18 failed as expected
assertions: 2423 | 2222 passed | 158 failed | 43 failed as expected
+11 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact
>
<testsuite name="<exe-name>" errors="17" failures="141" skipped="12" tests="2425" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="141" skipped="12" tests="2435" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="random-seed" value="1"/>
<property name="filters" value="&quot;*&quot; ~[!nonportable] ~[!benchmark] ~[approvals]"/>
@@ -506,6 +506,7 @@ with expansion:
at Matchers.tests.cpp:<line number>
</failure>
</testcase>
<testcase classname="<exe-name>.global" name="Enum backed by larger underlying type" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Enums in namespaces can quickly have stringification enabled using CATCH_REGISTER_ENUM" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Epsilon only applies to Approx's value" time="{duration}" status="run"/>
@@ -773,6 +774,15 @@ at Condition.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="JsonWriter escapes characters in strings properly/carriage return in a string is escaped" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter escapes characters in strings properly/tab in a string is escaped" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter escapes characters in strings properly/combination of characters is escaped" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double/NaN" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double/Pos nf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double/Neg inf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float/NaN" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float/Pos nf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float/Neg inf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes numbers independently of the global locale" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Lambdas in assertions" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Less-than inequalities with different epsilons" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="ManuallyRegistered" time="{duration}" status="run"/>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="<exe-name>" errors="17" failures="141" skipped="12" tests="2425" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="141" skipped="12" tests="2435" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="random-seed" value="1"/>
<property name="filters" value="&quot;*&quot; ~[!nonportable] ~[!benchmark] ~[approvals]"/>
@@ -505,6 +505,7 @@ with expansion:
at Matchers.tests.cpp:<line number>
</failure>
</testcase>
<testcase classname="<exe-name>.global" name="Enum backed by larger underlying type" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Enums in namespaces can quickly have stringification enabled using CATCH_REGISTER_ENUM" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Epsilon only applies to Approx's value" time="{duration}" status="run"/>
@@ -772,6 +773,15 @@ at Condition.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="JsonWriter escapes characters in strings properly/carriage return in a string is escaped" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter escapes characters in strings properly/tab in a string is escaped" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter escapes characters in strings properly/combination of characters is escaped" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double/NaN" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double/Pos nf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - double/Neg inf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float/NaN" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float/Pos nf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes non-finite FP using Python spelling - float/Neg inf" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="JsonWriter serializes numbers independently of the global locale" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Lambdas in assertions" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Less-than inequalities with different epsilons" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="ManuallyRegistered" time="{duration}" status="run"/>
@@ -262,6 +262,15 @@ at AssertionHandler.tests.cpp:<line number>
<testCase name="JsonWriter escapes characters in strings properly/carriage return in a string is escaped" duration="{duration}"/>
<testCase name="JsonWriter escapes characters in strings properly/tab in a string is escaped" duration="{duration}"/>
<testCase name="JsonWriter escapes characters in strings properly/combination of characters is escaped" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double/NaN" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double/Pos nf" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double/Neg inf" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float/NaN" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float/Pos nf" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float/Neg inf" duration="{duration}"/>
<testCase name="JsonWriter serializes numbers independently of the global locale" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/Parse.tests.cpp">
<testCase name="Parse uints" duration="{duration}"/>
@@ -422,16 +431,11 @@ at AssertionHandler.tests.cpp:<line number>
<testCase name="Parsing tags with non-alphabetical characters is pass-through" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp">
<testCase name="Directly creating an EnumInfo" duration="{duration}"/>
<testCase name="Exception thrown inside stringify does not fail the test" duration="{duration}"/>
<testCase name="Range type with sentinel" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - char" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - signed char" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - unsigned char" duration="{duration}"/>
<testCase name="parseEnums" duration="{duration}"/>
<testCase name="parseEnums/No enums" duration="{duration}"/>
<testCase name="parseEnums/One enum value" duration="{duration}"/>
<testCase name="parseEnums/Multiple enum values" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp">
<testCase name="Upcasting special member functions" duration="{duration}"/>
@@ -1079,8 +1083,14 @@ at Decomposition.tests.cpp:<line number>
</testCase>
</file>
<file path="tests/<exe-name>/UsageTests/EnumToString.tests.cpp">
<testCase name="Directly creating an EnumInfo" duration="{duration}"/>
<testCase name="Enum backed by larger underlying type" duration="{duration}"/>
<testCase name="Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM" duration="{duration}"/>
<testCase name="Enums in namespaces can quickly have stringification enabled using CATCH_REGISTER_ENUM" duration="{duration}"/>
<testCase name="parseEnums" duration="{duration}"/>
<testCase name="parseEnums/No enums" duration="{duration}"/>
<testCase name="parseEnums/One enum value" duration="{duration}"/>
<testCase name="parseEnums/Multiple enum values" duration="{duration}"/>
<testCase name="toString(enum class w/operator&lt;&lt;)" duration="{duration}"/>
<testCase name="toString(enum class)" duration="{duration}"/>
<testCase name="toString(enum w/operator&lt;&lt;)" duration="{duration}"/>
@@ -261,6 +261,15 @@ at AssertionHandler.tests.cpp:<line number>
<testCase name="JsonWriter escapes characters in strings properly/carriage return in a string is escaped" duration="{duration}"/>
<testCase name="JsonWriter escapes characters in strings properly/tab in a string is escaped" duration="{duration}"/>
<testCase name="JsonWriter escapes characters in strings properly/combination of characters is escaped" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double/NaN" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double/Pos nf" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - double/Neg inf" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float/NaN" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float/Pos nf" duration="{duration}"/>
<testCase name="JsonWriter serializes non-finite FP using Python spelling - float/Neg inf" duration="{duration}"/>
<testCase name="JsonWriter serializes numbers independently of the global locale" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/Parse.tests.cpp">
<testCase name="Parse uints" duration="{duration}"/>
@@ -421,16 +430,11 @@ at AssertionHandler.tests.cpp:<line number>
<testCase name="Parsing tags with non-alphabetical characters is pass-through" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp">
<testCase name="Directly creating an EnumInfo" duration="{duration}"/>
<testCase name="Exception thrown inside stringify does not fail the test" duration="{duration}"/>
<testCase name="Range type with sentinel" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - char" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - signed char" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - unsigned char" duration="{duration}"/>
<testCase name="parseEnums" duration="{duration}"/>
<testCase name="parseEnums/No enums" duration="{duration}"/>
<testCase name="parseEnums/One enum value" duration="{duration}"/>
<testCase name="parseEnums/Multiple enum values" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp">
<testCase name="Upcasting special member functions" duration="{duration}"/>
@@ -1078,8 +1082,14 @@ at Decomposition.tests.cpp:<line number>
</testCase>
</file>
<file path="tests/<exe-name>/UsageTests/EnumToString.tests.cpp">
<testCase name="Directly creating an EnumInfo" duration="{duration}"/>
<testCase name="Enum backed by larger underlying type" duration="{duration}"/>
<testCase name="Enums can quickly have stringification enabled using CATCH_REGISTER_ENUM" duration="{duration}"/>
<testCase name="Enums in namespaces can quickly have stringification enabled using CATCH_REGISTER_ENUM" duration="{duration}"/>
<testCase name="parseEnums" duration="{duration}"/>
<testCase name="parseEnums/No enums" duration="{duration}"/>
<testCase name="parseEnums/One enum value" duration="{duration}"/>
<testCase name="parseEnums/Multiple enum values" duration="{duration}"/>
<testCase name="toString(enum class w/operator&lt;&lt;)" duration="{duration}"/>
<testCase name="toString(enum class)" duration="{duration}"/>
<testCase name="toString(enum w/operator&lt;&lt;)" duration="{duration}"/>

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