mirror of
https://github.com/catchorg/Catch2.git
synced 2026-08-06 13:44:22 +02:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b08d4d795 | |||
| 1079da4c5f | |||
| 60c8b87829 | |||
| 64a551e2e7 | |||
| 2b971368dd | |||
| f5db82a5c4 | |||
| dcbb2d5d84 | |||
| 1a9625a6e7 | |||
| bb8873ccd7 | |||
| 46bdf1a04c | |||
| c267251e70 | |||
| 0cc833ea95 | |||
| 8494e2dce4 | |||
| eb431764b4 | |||
| ae5d271da2 | |||
| a15f718c82 | |||
| 191fa38c9b | |||
| dd94b9a780 | |||
| 9f7c9f6872 | |||
| 919385f704 | |||
| 15d52830ee | |||
| 9ec44dd62b | |||
| 675f9eaeb1 | |||
| bcfb10e498 | |||
| b7e0310fbe | |||
| 6d4ea62200 | |||
| 195231c59a | |||
| 3a865e8c0f | |||
| 69e0473f6e | |||
| 6ee0826dca | |||
| 47ea57d74f | |||
| d838f88b9c | |||
| c267b6eb4d | |||
| 3cdae5faf0 | |||
| 651247c7f4 | |||
| 15b9393f0f | |||
| a18badd10f | |||
| 54af40652a | |||
| 5f47160bb2 | |||
| e83528c6eb | |||
| fd31a62547 | |||
| 4df8fee92d | |||
| 300c5d3eed | |||
| 11a96e186a | |||
| 10f62484bf | |||
| be2dfb45cc | |||
| 47200ddbee | |||
| e51dcdcc59 | |||
| 77eae8bd2a | |||
| 3ab0d7cef3 | |||
| e83218c2df | |||
| 548e14a8c8 |
@@ -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
@@ -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."
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
@@ -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
@@ -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 );
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
¤tTracker,
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+348
-253
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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__ )
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+10
-18
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
¤tTracker,
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, ... ) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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=""*" ~[!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=""*" ~[!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<<)" duration="{duration}"/>
|
||||
<testCase name="toString(enum class)" duration="{duration}"/>
|
||||
<testCase name="toString(enum w/operator<<)" 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<<)" duration="{duration}"/>
|
||||
<testCase name="toString(enum class)" duration="{duration}"/>
|
||||
<testCase name="toString(enum w/operator<<)" duration="{duration}"/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user