mirror of
https://github.com/catchorg/Catch2.git
synced 2026-08-25 14:53:28 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
317ac1ed4c | ||
|
|
fc5bc1fa8b | ||
|
|
499442d7ef | ||
|
|
9a2b2ee8d6 | ||
|
|
ef3a728265 | ||
|
|
69da66ee73 | ||
|
|
45f9ea1e79 | ||
|
|
9915f7250d | ||
|
|
f9dfb10315 | ||
|
|
fdfc07e571 | ||
|
|
8582805f54 | ||
|
|
0aeb818520 | ||
|
|
97ec4e8e2e | ||
|
|
0136276e15 | ||
|
|
630840c500 | ||
|
|
4cde128517 | ||
|
|
0b4d7a5a16 | ||
|
|
a0eba50e9d | ||
|
|
8b08d4d795 | ||
|
|
1079da4c5f | ||
|
|
60c8b87829 | ||
|
|
64a551e2e7 | ||
|
|
2b971368dd | ||
|
|
f5db82a5c4 | ||
|
|
dcbb2d5d84 | ||
|
|
1a9625a6e7 | ||
|
|
bb8873ccd7 | ||
|
|
46bdf1a04c | ||
|
|
c267251e70 | ||
|
|
0cc833ea95 | ||
|
|
8494e2dce4 | ||
|
|
eb431764b4 | ||
|
|
ae5d271da2 | ||
|
|
a15f718c82 | ||
|
|
191fa38c9b | ||
|
|
dd94b9a780 | ||
|
|
9f7c9f6872 | ||
|
|
919385f704 | ||
|
|
15d52830ee | ||
|
|
9ec44dd62b | ||
|
|
675f9eaeb1 |
+1
-1
@@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
endif()
|
||||
|
||||
project(Catch2
|
||||
VERSION 3.15.1 # CML version placeholder, don't delete
|
||||
VERSION 3.16.0 # CML version placeholder, don't delete
|
||||
LANGUAGES CXX
|
||||
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
|
||||
DESCRIPTION "A modern, C++-native, unit test framework."
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright Catch2 Authors
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at
|
||||
# https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import statistics
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
TEMPLATE = os.path.join(HERE, "listing_template.json")
|
||||
SHIM = os.path.join(HERE, "copy_shim.cmake")
|
||||
COUNTS = [1, 10, 500, 1000, 2000, 4000, 8000, 16000]
|
||||
#COUNTS = [1, 10, 100]
|
||||
REPEATS = 5
|
||||
|
||||
def load_template(path: str) -> dict:
|
||||
with open(path, encoding="utf-8") as f:
|
||||
catch_out = json.load(f)
|
||||
if 'listings' not in catch_out or 'tests' not in catch_out['listings']:
|
||||
sys.exit(f"Template '{path}' does not contain the expected 'listings.tests' data")
|
||||
|
||||
return catch_out
|
||||
|
||||
|
||||
def synthesize_listing(template_doc: dict, count: int, file, with_tags: bool) -> str:
|
||||
"""Writes JSON listing with exactly `count` test cases into `file`.
|
||||
|
||||
The template entries are cycled through, and each name is made unique by
|
||||
appending an index, so that every registered CTest test has a distinct name.
|
||||
"""
|
||||
# To avoid messing up the template tests by deletion, we have
|
||||
# to take a deepcopy before the changes.
|
||||
doc_copy = copy.deepcopy(template_doc)
|
||||
template_tests = doc_copy['listings']['tests']
|
||||
if not with_tags:
|
||||
for test in template_tests:
|
||||
del test['tags']
|
||||
num_original = len(template_tests)
|
||||
|
||||
out_tests = []
|
||||
for i in range(count):
|
||||
entry = template_tests[i % num_original].copy()
|
||||
entry["name"] = f"{entry['name']} #{i // num_original}"
|
||||
out_tests.append(entry)
|
||||
|
||||
doc_copy['listings']['tests'] = out_tests
|
||||
|
||||
# indent=2 matches Catch2's pretty printing
|
||||
json.dump(doc_copy, file, indent=2)
|
||||
file.write('\n')
|
||||
|
||||
|
||||
def build_command(args, listing_path: str, ctest_file: str, add_tags: bool) -> list[str]:
|
||||
# The executor needs to be a CMake list, so it has to be separated by ';'
|
||||
executor = ";".join(['cmake', f"-DBENCH_LISTING={listing_path}", "-P", SHIM, "--"])
|
||||
return [
|
||||
'cmake',
|
||||
"-DTEST_TARGET=benchmark",
|
||||
f"-DTEST_EXECUTABLE={listing_path}",
|
||||
f"-DTEST_EXECUTOR={executor}",
|
||||
f"-DTEST_WORKING_DIR={os.path.dirname(ctest_file)}",
|
||||
f"-DCTEST_FILE={ctest_file}",
|
||||
f"-DADD_TAGS_AS_LABELS={'TRUE' if add_tags else 'FALSE'}",
|
||||
"-P",
|
||||
args.script,
|
||||
]
|
||||
|
||||
|
||||
def run_once(cmd: list[str]) -> float:
|
||||
start = time.monotonic()
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
elapsed = time.monotonic() - start
|
||||
if result.returncode != 0:
|
||||
sys.stderr.write("Benchmark invocation failed:\n")
|
||||
sys.stderr.write(f" cmd: {' '.join(cmd)}\n")
|
||||
sys.stderr.write(f" stdout: {result.stdout}\n")
|
||||
sys.stderr.write(f" stderr: {result.stderr}\n")
|
||||
sys.exit(1)
|
||||
return elapsed
|
||||
|
||||
|
||||
def verify_output(ctest_file: str, count: int, add_tags: bool):
|
||||
"""Sanity-check the generated CTest script and return its add_test count."""
|
||||
with open(ctest_file, encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
add_test_lines = sum(1 for line in content.splitlines() if line.startswith("add_test("))
|
||||
if add_test_lines != count:
|
||||
sys.exit(
|
||||
f"Sanity check failed: expected {count} add_test() lines, "
|
||||
f"found {add_test_lines} in {ctest_file}"
|
||||
)
|
||||
if add_tags and count > 0 and "LABELS" not in content:
|
||||
sys.exit(
|
||||
f"Sanity check failed: ADD_TAGS_AS_LABELS was on but no LABELS found "
|
||||
f"in {ctest_file}"
|
||||
)
|
||||
|
||||
|
||||
def cmake_version() -> str:
|
||||
stdout = subprocess.run(
|
||||
['cmake', "--version"], capture_output=True, text=True, check=True
|
||||
).stdout
|
||||
match = re.match("cmake version (.+)", stdout)
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def print_table(counts, tag_modes, results):
|
||||
timing_by_ct = {(count, tags): median_time for (count, tags, median_time) in results}
|
||||
|
||||
headers = ["N"]
|
||||
if False in tag_modes:
|
||||
headers.append("tags:off")
|
||||
if True in tag_modes:
|
||||
headers.append("tags:on")
|
||||
|
||||
def fmt_cell(count, tags_on):
|
||||
median_time = timing_by_ct[(count, tags_on)]
|
||||
return f"{median_time:8.1f} ms"
|
||||
|
||||
# Header
|
||||
row = f"{headers[0]:>6}"
|
||||
if False in tag_modes:
|
||||
row += f" {headers[1]:>14}"
|
||||
if True in tag_modes:
|
||||
row += f" {headers[2]:>14}"
|
||||
|
||||
print()
|
||||
print("-" * len(row))
|
||||
print(row)
|
||||
print("-" * len(row))
|
||||
|
||||
for count in counts:
|
||||
row = f"{count:>6}"
|
||||
if False in tag_modes:
|
||||
row += f" {fmt_cell(count, False):>14}"
|
||||
if True in tag_modes:
|
||||
row += f" {fmt_cell(count, True):>14}"
|
||||
print(row)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Benchmark the CMake-side cost of catch_discover_tests."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--script",
|
||||
required=True,
|
||||
help="Path to the CatchAddTests.cmake to benchmark.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists(args.script):
|
||||
sys.exit(f"Script to benchmark '{args.script}' does not exist")
|
||||
if not os.path.exists(SHIM):
|
||||
sys.exit(f"Copy-shim '{SHIM}' does not exist")
|
||||
|
||||
tag_modes = [False, True]
|
||||
|
||||
template_doc = load_template(TEMPLATE)
|
||||
|
||||
print(f"Benchmarking: {os.path.abspath(args.script)}")
|
||||
print(f"CMake version: {cmake_version()}")
|
||||
print(f"Ns: {COUNTS}")
|
||||
print(f"Repeats: {REPEATS}")
|
||||
print()
|
||||
|
||||
|
||||
results = [] # (count, tags_on, median_ms)
|
||||
with tempfile.TemporaryDirectory() as workdir:
|
||||
os.makedirs(workdir, exist_ok=True)
|
||||
for count in COUNTS:
|
||||
listing_path = os.path.join(workdir, f"listing-{count}.json")
|
||||
|
||||
for add_tags in tag_modes:
|
||||
with open(listing_path, "w", encoding="utf-8") as f:
|
||||
synthesize_listing(template_doc, count, f, add_tags)
|
||||
mode = "on" if add_tags else "off"
|
||||
ctest_file = os.path.join(workdir, f"ctest-{count}-{mode}.cmake")
|
||||
cmd = build_command(args, listing_path, ctest_file, add_tags)
|
||||
|
||||
# Untimed warmup run (populates any filesystem/OS caches).
|
||||
run_once(cmd)
|
||||
verify_output(ctest_file, count, add_tags)
|
||||
|
||||
timings = [run_once(cmd) for _ in range(REPEATS)]
|
||||
median_ms = statistics.median(timings) * 1000.0
|
||||
results.append((count, add_tags, median_ms))
|
||||
print(f'N = {count} done in ~{len(tag_modes) * sum(timings):.2f} s')
|
||||
|
||||
print_table(COUNTS, tag_modes, results)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -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,85 @@
|
||||
{
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "benchmark-template",
|
||||
"catch2-version": "3.15.2"
|
||||
},
|
||||
"listings": {
|
||||
"tests": [
|
||||
{
|
||||
"name": "Comparing function pointers",
|
||||
"tags": [
|
||||
"function pointer",
|
||||
"Tricky"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Testing checked-if 4",
|
||||
"tags": [
|
||||
"!shouldfail",
|
||||
"checked-if"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "count_equidistant_floats - double",
|
||||
"tags": [
|
||||
"approvals",
|
||||
"distance",
|
||||
"floating-point"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Usage of AllTrue range matcher",
|
||||
"tags": [
|
||||
"matchers",
|
||||
"quantifiers",
|
||||
"templated"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Exception matchers that succeed",
|
||||
"tags": [
|
||||
"!throws",
|
||||
"exceptions",
|
||||
"matchers"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - float",
|
||||
"class-name": "Template_Fixture",
|
||||
"tags": [
|
||||
"class",
|
||||
"template"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Approximate PI",
|
||||
"tags": [
|
||||
"Approx",
|
||||
"PI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TextFlow::Column respects width setting",
|
||||
"tags": [
|
||||
"approvals",
|
||||
"column",
|
||||
"TextFlow"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Generators internals",
|
||||
"tags": [
|
||||
"generators",
|
||||
"internals"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Mayfail test case with nested sections",
|
||||
"tags": [
|
||||
"!mayfail"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+37
-1
@@ -56,9 +56,9 @@ _Note that we redirect the output to `/dev/null` to reduce the overhead of the a
|
||||
|
||||
TODO:
|
||||
* Start empty binary (set up cost base)
|
||||
* Start binary with X (100/1k/10k) tests (test registration cost)
|
||||
* Section tracking
|
||||
|
||||
|
||||
## Compilation benchmarks
|
||||
|
||||
As tests are often iterated upon and relinked, the compilation cost of
|
||||
@@ -79,3 +79,39 @@ hyperfine --warmup 2 --parameter-list version old,vas --prepare 'find ~/benches/
|
||||
|
||||
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.
|
||||
|
||||
@@ -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>
|
||||
@@ -202,8 +202,13 @@ as many times as you want, e.g. `--reporter xml::out=someFile.xml` or
|
||||
|
||||
The keys must either be prefixed by "X", in which case they are not parsed
|
||||
by Catch2 and are only passed down to the reporter, or one of options
|
||||
hardcoded into Catch2. Currently there are only 2,
|
||||
["out"](#sending-output-to-a-file), and ["colour-mode"](#colour-mode).
|
||||
hardcoded into Catch2. Currently there are 3 supported options:
|
||||
|
||||
* ["out"](#sending-output-to-a-file)
|
||||
* ["colour-mode"](#colour-mode)
|
||||
* ["verbosity"](#output-verbosity)
|
||||
|
||||
> Support for per-reporter verbosity option was added in Catch2 3.16.0
|
||||
|
||||
_Note that the reporter might still check the X-prefixed options for
|
||||
validity, and throw an error if they are wrong._
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
# Release notes
|
||||
**Contents**<br>
|
||||
[3.16.0](#3160)<br>
|
||||
[3.15.3](#3153)<br>
|
||||
[3.15.2](#3152)<br>
|
||||
[3.15.1](#3151)<br>
|
||||
[3.15.0](#3150)<br>
|
||||
[3.14.0](#3140)<br>
|
||||
@@ -76,6 +79,67 @@
|
||||
[Even Older versions](#even-older-versions)<br>
|
||||
|
||||
|
||||
|
||||
## 3.16.0
|
||||
|
||||
### Fixes
|
||||
* Multiple fixes in `catch_discover_tests`:
|
||||
* Fixed `<target>_TESTS` variable accumulating JSON fragments alongside test names.
|
||||
* This was introduced during the refactoring in last release.
|
||||
* Fixed `<target>_TESTS` variable from `catch_discover_tests` not escaping test names to be properly parsed by CMake.
|
||||
* This means that e.g. test names with semicolons will not be split into multiple partial test names.
|
||||
* This bug has existed since the first version of the script.
|
||||
* Fixed `TEST_PREFIX`/`TEST_SUFFIX` args having leading/trailing whitespace stripped.
|
||||
* Added workaround for Clang 20-21 compile error with `TEMPLATE_PRODUCT_TEST_CASE` (#3115, #3173)
|
||||
|
||||
### Improvements
|
||||
* Verbosity option is now handled per reporter.
|
||||
* The standalone `--verbosity` flag is propagated to all reporters as default, just like `--colour-mode`.
|
||||
* The JSON reporter considers verbosity when listing tests.
|
||||
* Another set of performance improvements for `catch_discover_tests` performance
|
||||
* The newest version can register about 3k tests in 1 second, up from 1k previously.
|
||||
* The initial `TEST_CASE` registration is slightly faster.
|
||||
* Reduced overhead from first instantiating `BENCHMARK` machinery in a TU.
|
||||
* Improved compilation speed when multiple templated test case macros use the same types.
|
||||
* Rewrote implementation of the string matchers
|
||||
* Case-sensitive matching (the default) is significantly faster.
|
||||
* Case-insensitive matching is faster in most cases.
|
||||
* Added optimizer barrier to calls into benchmarks without return values
|
||||
* This limits the optimizations compiler can perform **between calls** into the benchmarked function, improving the accuracy.
|
||||
* Added warning that checks for using sharding without deterministic test order (#3186)
|
||||
|
||||
|
||||
## 3.15.3
|
||||
|
||||
### Fixes
|
||||
* The JSON reporter's number handling is locale-independent. (#3176)
|
||||
* Removed leftover debug message from `catch_discover_tests` (#3177)
|
||||
* Fixed typo in the "Could not jump to the Nth element" exception message (#3181)
|
||||
|
||||
### Improvements
|
||||
* `catch_discover_tests` registers tests in deterministic (alphabetical) order.
|
||||
* `catch_discover_tests` has been rewritten to be massively faster.
|
||||
* Preparing the actual CTest script is significantly faster.
|
||||
* Parsing the JSON test array is significantly faster.
|
||||
* Running without `ADD_TAGS_AS_LABELS` set is 10-15% faster (as opposed to being the same speed) (#3169)
|
||||
* The new implementation is about 4x-5x faster, so registering 500 tests now takes ~350ms (down from 1.1s).
|
||||
* JSON writing is faster
|
||||
* Small improvement in writing non-string values
|
||||
* ~6-40% improvement in writing string values that do not need escaping
|
||||
* ~1-6% improvement in writing string values that do need escaping
|
||||
* Better support for infs and NaNs in JSON output
|
||||
|
||||
|
||||
## 3.15.2
|
||||
|
||||
### Fixes
|
||||
* Fixed `--warn InfiniteGenerators` triggering even if the generator was limited to specific element via path filtering.
|
||||
* Fixed `-Wunused-parameter` triggering in `-fnoexceptions` builds.
|
||||
|
||||
### Improvements
|
||||
* `catch_discover_tests` can handle cases where the binary prints out non-Catch2 output due to global constructors (#3162)
|
||||
|
||||
|
||||
## 3.15.1
|
||||
|
||||
### Fixes
|
||||
|
||||
+4
-4
@@ -43,13 +43,13 @@ them write into different destinations. The two main uses of this are
|
||||
|
||||
Specifying multiple reporter looks like this:
|
||||
```
|
||||
--reporter JUnit::out=result-junit.xml --reporter console::out=-::colour-mode=ansi
|
||||
--reporter JUnit::out=result-junit.xml --reporter console::out=-::colour-mode=ansi::verbosity=quiet
|
||||
```
|
||||
|
||||
This tells Catch2 to use two reporters, `JUnit` reporter that writes
|
||||
its machine-readable XML output to file `result-junit.xml`, and the
|
||||
`console` reporter that writes its user-friendly output to stdout and
|
||||
uses ANSI colour codes for colouring the output.
|
||||
`console` reporter that writes its user-friendly output to stdout, uses
|
||||
ANSI colour codes for colouring the output and is set to "quiet" verbosity.
|
||||
|
||||
Using multiple reporters (or one reporter and one-or-more [event
|
||||
listeners](event-listeners.md#top)) can have surprisingly complex semantics
|
||||
@@ -110,7 +110,7 @@ passing and failing assertions.
|
||||
|
||||
_Generally we recommend that if you override a member function from either
|
||||
of the bases, you call into the base's implementation first. This is not
|
||||
necessarily in all cases, but it is safer and easier._
|
||||
necessary in all cases, but it is safer and easier._
|
||||
|
||||
|
||||
Writing your own reporter then looks like this:
|
||||
|
||||
+17
-6
@@ -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
|
||||
@@ -58,6 +58,11 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
directory property. The set of discovered tests is made accessible to such a
|
||||
script via the ``<target>_TESTS`` variable.
|
||||
|
||||
Note that ``<target>_TESTS`` variable contains test names with brackets
|
||||
("[", "]") escaped into ASCII char 2, 3 respectively, to work around CMake's
|
||||
list parsing rules. You have to unescape them back for each element to get
|
||||
the original names.
|
||||
|
||||
The options are:
|
||||
|
||||
``target``
|
||||
@@ -67,7 +72,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 +151,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.
|
||||
|
||||
@@ -220,8 +231,8 @@ function(catch_discover_tests TARGET)
|
||||
-D "TEST_SPEC=${_TEST_SPEC}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "TEST_PREFIX='${_TEST_PREFIX}'"
|
||||
-D "TEST_SUFFIX='${_TEST_SUFFIX}'"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "TEST_REPORTER=${_REPORTER}"
|
||||
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
|
||||
@@ -266,8 +277,8 @@ function(catch_discover_tests TARGET)
|
||||
" TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n"
|
||||
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
|
||||
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
|
||||
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
|
||||
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
|
||||
" TEST_PREFIX" " [==['" "${_TEST_PREFIX}" "']==]" "\n"
|
||||
" TEST_SUFFIX" " [==['" "${_TEST_SUFFIX}" "']==]" "\n"
|
||||
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
|
||||
" TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n"
|
||||
" TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n"
|
||||
|
||||
+350
-55
@@ -1,23 +1,220 @@
|
||||
# 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)
|
||||
set(_args "")
|
||||
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
|
||||
math(EXPR _last_arg ${ARGC}-1)
|
||||
foreach(_n RANGE 1 ${_last_arg})
|
||||
set(_arg "${ARGV${_n}}")
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
set(_args "${_args} ${_arg}")
|
||||
# Because natively using CMake's JSON processing for arrays leads to quadratic
|
||||
# running times, we do terrible hack and split JSON array by object boundary
|
||||
# + commas and try to reconstruct valid JSON objects. During this, we need
|
||||
# to replace CMake characters that could be in the test name/tags with
|
||||
# placeholder, so it doesn't affect CMake's processing of the strings/lists
|
||||
# we create during the parsing.
|
||||
#
|
||||
# We use 0x01, 0x02, 0x03, and 0x04 as placeholder bytes, as those cannot
|
||||
# exist in JSON unescaped.
|
||||
#
|
||||
# 0x01 <=> ';' (CMake list separator)
|
||||
# 0x02 <-> '[' (opens a CMake bracket-argument context)
|
||||
# 0x03 <-> ']' (closes a CMake bracket-argument context)
|
||||
# 0x04 == element boundary marker used while splitting the tests array
|
||||
#
|
||||
string(ASCII 1 _SemicolonEscape)
|
||||
string(ASCII 2 _OpenBracketEscape)
|
||||
string(ASCII 3 _CloseBracketEscape)
|
||||
string(ASCII 4 _BoundaryEscape)
|
||||
|
||||
|
||||
# As far as I can tell, the only way of having unclosed '[' or ']' in the
|
||||
# list of test names we define for CTest, is to escape them into completely
|
||||
# different characters. Otherwise, they will break parsing of unrelated
|
||||
# semicolons (inserted by CMake after parsing the list from the user-facing
|
||||
# space separated format). This means that the consumers of the list have
|
||||
# to unescape them back, but that's CMake :v
|
||||
#
|
||||
# 0x02 <-> '['
|
||||
# 0x03 <-> ']'
|
||||
#
|
||||
string(ASCII 2 _OpenBracketListingEscape)
|
||||
string(ASCII 3 _CloseBracketListingEscape)
|
||||
|
||||
|
||||
# Placeholder bytes in the listing would break our parsing hack, so
|
||||
# we check they don't exist. They shouldn't exist in valid JSON, but
|
||||
# the reporter might not be escaping them properly.
|
||||
function(validate_input_noescapes listing_var)
|
||||
foreach(byte "${_SemicolonEscape}" "${_BoundaryEscape}" "${_OpenBracketEscape}" "${_CloseBracketEscape}")
|
||||
string(FIND "${${listing_var}}" "${byte}" found)
|
||||
if(NOT found EQUAL -1)
|
||||
message(FATAL_ERROR
|
||||
"The test listing contains raw control byte (0x01-0x04) which should not "
|
||||
"be there. This means either bad escaping in JSON reporter, or corrupted file. "
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Replaces relevant characters with their placeholders, see the top of this file.
|
||||
# Modifies argument `var` in place.
|
||||
function(magic_escape_chars var)
|
||||
set(value "${${var}}")
|
||||
string(REPLACE ";" "${_SemicolonEscape}" value "${value}")
|
||||
string(REPLACE "[" "${_OpenBracketEscape}" value "${value}")
|
||||
string(REPLACE "]" "${_CloseBracketEscape}" value "${value}")
|
||||
set(${var} "${value}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Turns placeholders back into original characters, see the top of this file.
|
||||
# Modifies argument `var` in place.
|
||||
function(magic_unescape_chars var)
|
||||
set(value "${${var}}")
|
||||
string(REPLACE "${_SemicolonEscape}" ";" value "${value}")
|
||||
string(REPLACE "${_OpenBracketEscape}" "[" value "${value}")
|
||||
string(REPLACE "${_CloseBracketEscape}" "]" value "${value}")
|
||||
set(${var} "${value}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Abuses knowledge of Catch2's JSON reporter output for listing tests to
|
||||
# split JSON array of the test listings into a CMake list of strings,
|
||||
# with each element being the JSON string of one array entry.
|
||||
#
|
||||
# This avoids the terrible quadratic running time of using CMake's JSON
|
||||
# support to parse the JSON reporter output "properly", where the whole
|
||||
# JSON array of tests is parsed again for every element. Instead, we can
|
||||
# use the CMake's API to only parse the individual test's objects, which
|
||||
# are usually small and only have to be reparsed fixed number of times
|
||||
# (once for test names, once for labels).
|
||||
#
|
||||
# We process the string representing the JSON array by splitting it on
|
||||
# `}<ws>*,<ws>*{` and then checking for each chunk whether it is a valid
|
||||
# JSON object representing Catch2's test. If not (e.g. because we split
|
||||
# on the presence of `}<ws>*,<ws>*{` inside a test name), then we append
|
||||
# the next chunk to the current one and check again. And again, until
|
||||
# we get back to a valid JSON.
|
||||
#
|
||||
# Note that to support passing the object strings back from the function,
|
||||
# they will still contain the placeholders and need to be unescaped before
|
||||
# further processing (e.g. sending them into CMake's JSON parsing API).
|
||||
function(split_json_array json_array_var out_var)
|
||||
# We have to pass the input by var name to avoid CMake processing
|
||||
# the input as an arg.
|
||||
set(json_in "${${json_array_var}}")
|
||||
|
||||
# Strip the array brackets at the start and end of the JSON array.
|
||||
# Must happen before we escape the other [] instances below from the
|
||||
# actual array data.
|
||||
string(REGEX REPLACE "^[ \t\r\n]*\\[" "" json_in "${json_in}")
|
||||
string(REGEX REPLACE "\\][ \t\r\n]*$" "" json_in "${json_in}")
|
||||
|
||||
magic_escape_chars(json_in)
|
||||
|
||||
# We need to keep the whitespace around comma around, so that if we
|
||||
# split inside the test object, we can reconstruct it losslessly.
|
||||
string(REGEX REPLACE "(}[ \t\r\n]*)[,]([ \t\r\n]*{)" "\\1${_BoundaryEscape}\\2" json_in "${json_in}")
|
||||
|
||||
# We escaped all list separators above, so now we can turn the JSON
|
||||
# string into a CMake list of fragments in single pass.
|
||||
string(REPLACE "${_BoundaryEscape}" ";" fragments "${json_in}")
|
||||
|
||||
# And now we have to reconstruct the actual JSON structure from fragments.
|
||||
set(array_elements "")
|
||||
set(accumulator "")
|
||||
foreach(next_fragment IN LISTS fragments)
|
||||
if(accumulator)
|
||||
set(accumulator "${accumulator},${next_fragment}")
|
||||
else()
|
||||
set(accumulator "${next_fragment}")
|
||||
endif()
|
||||
|
||||
# Because the fragments (might) contain invalid JSON characters due
|
||||
# to escaping, we have to unescape it before checking if we can parse it.
|
||||
set(maybe_json "${accumulator}")
|
||||
magic_unescape_chars(maybe_json)
|
||||
string(JSON unused ERROR_VARIABLE err GET "${maybe_json}" "name")
|
||||
if(err STREQUAL "NOTFOUND")
|
||||
list(APPEND array_elements "${accumulator}")
|
||||
set(accumulator "")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${out_var} "${array_elements}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Prepare (a part of) command with bracketed arguments and return it via `out_var`.
|
||||
#
|
||||
# This allows the registration script to escape parts of the test script
|
||||
# only once, instead of escaping the unchanged arguments over and over again.
|
||||
function(prepare_command_fragment out_var)
|
||||
set(_args "")
|
||||
math(EXPR _last_arg ${ARGC}-1)
|
||||
if(_last_arg GREATER_EQUAL 1)
|
||||
foreach(_n RANGE 1 ${_last_arg})
|
||||
set(_arg "${ARGV${_n}}")
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
set(_args "${_args} ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
set(${out_var} "${_args}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Generates random filename in the temp folder.
|
||||
# Temp folder is retrieved by checking env vars from various platforms.
|
||||
function(make_temp_file_path OUT_VARIABLE FALLBACK_PATH)
|
||||
set(TEMP_DIR "")
|
||||
set(ENV_VARS
|
||||
# From XDG base dir specification
|
||||
XDG_RUNTIME_DIR
|
||||
# From POSIX standard
|
||||
TMPDIR
|
||||
# From Windows
|
||||
TMP
|
||||
TEMP
|
||||
)
|
||||
|
||||
foreach(var ${ENV_VARS})
|
||||
if(DEFINED ENV{${var}} AND NOT "$ENV{${var}}" STREQUAL "")
|
||||
set(TEMP_DIR "$ENV{${var}}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# If all checks fail, we use the fallback path
|
||||
if(TEMP_DIR STREQUAL "")
|
||||
set(TEMP_DIR "${FALLBACK_PATH}")
|
||||
endif()
|
||||
|
||||
file(TO_CMAKE_PATH "${TEMP_DIR}" TEMP_DIR)
|
||||
|
||||
# Generate the random file name
|
||||
string(RANDOM LENGTH 8 RAND_ID)
|
||||
set(FINAL_TEMP_PATH "${TEMP_DIR}/Catch2-test-listing.${RAND_ID}.json")
|
||||
|
||||
set(${OUT_VARIABLE} "${FINAL_TEMP_PATH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Computes the path of the test-list file based on the path for the main
|
||||
# CTest script file (passed in `CTEST_FILE`).
|
||||
#
|
||||
# It works by replacing the `_tests` part of the original name with
|
||||
# `_test-list`, or failing that, it appends `-list.cmake` instead.
|
||||
#
|
||||
# <base>_tests.cmake -> <base>_test-list.cmake
|
||||
# <base>_tests-Debug.cmake -> <base>_test-list-Debug.cmake
|
||||
function(make_test_list_file_path CTEST_FILE OUT_VARIABLE)
|
||||
if("${CTEST_FILE}" MATCHES "_tests(.*)\\.cmake$")
|
||||
string(REGEX REPLACE "_tests(.*)\\.cmake$" "_test-list\\1.cmake" list_file "${CTEST_FILE}")
|
||||
else()
|
||||
set(list_file "${CTEST_FILE}-list.cmake")
|
||||
endif()
|
||||
set(${OUT_VARIABLE} "${list_file}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(catch_discover_tests_impl)
|
||||
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
@@ -26,9 +223,22 @@ function(catch_discover_tests_impl)
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
# We write the list of all discovered tests into a separate file
|
||||
make_test_list_file_path("${_CTEST_FILE}" _CTEST_LIST_FILE)
|
||||
|
||||
# We periodically append to the output files below, so we have to ensure
|
||||
# that it is empty at the start, or we get duplicated test scripts.
|
||||
file(REMOVE "${_CTEST_FILE}")
|
||||
file(REMOVE "${_CTEST_LIST_FILE}")
|
||||
|
||||
# Size (in Bytes) at which the intermediate `script` var is dumped to file.
|
||||
set(_WriteToFileThreshold 50000)
|
||||
|
||||
set(add_tags "${_ADD_TAGS_AS_LABELS}")
|
||||
set(prefix "${_TEST_PREFIX}")
|
||||
set(suffix "${_TEST_SUFFIX}")
|
||||
# TEST_{PREFIX,SUFFIX} is enclosed in single quotes to keep ensure
|
||||
# leading/trailing whitespace isn't trimmed by CMake's argument passing.
|
||||
string(REGEX REPLACE "^'(.*)'$" "\\1" prefix "${_TEST_PREFIX}")
|
||||
string(REGEX REPLACE "^'(.*)'$" "\\1" suffix "${_TEST_SUFFIX}")
|
||||
set(spec ${_TEST_SPEC})
|
||||
set(extra_args ${_TEST_EXTRA_ARGS})
|
||||
set(properties ${_TEST_PROPERTIES})
|
||||
@@ -42,6 +252,10 @@ function(catch_discover_tests_impl)
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
# Holds the list of all registered test names **as a string**, not list.
|
||||
# This avoids issue of CMake removing semicolon escapes even inside
|
||||
# bracket-quoted strings.
|
||||
set(_test_names)
|
||||
|
||||
if(WIN32)
|
||||
set(dl_paths_variable_name PATH)
|
||||
@@ -72,8 +286,19 @@ function(catch_discover_tests_impl)
|
||||
set(ENV{DYLD_FRAMEWORK_PATH} "${paths}")
|
||||
endif()
|
||||
|
||||
make_temp_file_path(listing_output_path "${_TEST_WORKING_DIR}")
|
||||
|
||||
set(_Verbosity "quiet")
|
||||
if (add_tags)
|
||||
set(_Verbosity "normal")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --reporter json
|
||||
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec}
|
||||
--list-tests
|
||||
--reporter "json::verbosity=${_Verbosity}"
|
||||
--out "${listing_output_path}"
|
||||
--order lex # Make sure the output order, and thus test registration order, is consistent across runs.
|
||||
OUTPUT_VARIABLE listing_output
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
@@ -86,6 +311,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}")
|
||||
@@ -136,26 +366,87 @@ function(catch_discover_tests_impl)
|
||||
|
||||
# Parse JSON output for list of tests/class names/tags
|
||||
string(JSON version GET "${listing_output}" "version")
|
||||
if(NOT version STREQUAL "1")
|
||||
if(NOT version STREQUAL "2")
|
||||
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")
|
||||
file(WRITE "${_CTEST_FILE}" "")
|
||||
if(NOT tests)
|
||||
# Still emit an (empty) test list file and have the main script include
|
||||
# it, so that consumers relying on the `${_TEST_LIST}` variable and on the
|
||||
# include structure get consistent behavior regardless of test count.
|
||||
file(WRITE "${_CTEST_LIST_FILE}" "set(${_TEST_LIST})\n")
|
||||
file(WRITE "${_CTEST_FILE}" "include(\"${_CTEST_LIST_FILE}\")\n")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# CMake's foreach-RANGE is inclusive, so we have to subtract 1
|
||||
math(EXPR num_tests "${num_tests} - 1")
|
||||
|
||||
foreach(idx RANGE ${num_tests})
|
||||
string(JSON single_test GET ${test_listing} ${idx})
|
||||
string(JSON test_tags GET "${single_test}" "tags")
|
||||
# The 'set(VAR` header for the test list has to be written separately,
|
||||
# so that each test name can be appended file without further processing.
|
||||
set(test_names "set(${_TEST_LIST}")
|
||||
|
||||
# Most of the commands/arguments in the CTest script are identical
|
||||
# for every test registered with one `catch_discover_tests` call.
|
||||
# To avoid repeating the work in escaping them, we escape them before
|
||||
# the per-test loop and reuse the escaped fragments.
|
||||
#
|
||||
# `add_test` calls are
|
||||
# add_test(<name><exec><exe><escaped_name><extra_args><reporter><out_dir>)
|
||||
# Of these, <exec>,<exe>,<extra_args>, and <reporter> are the same between
|
||||
# all tests.
|
||||
prepare_command_fragment(_exec_exe_fragment
|
||||
${_TEST_EXECUTOR}
|
||||
"${_TEST_EXECUTABLE}"
|
||||
)
|
||||
prepare_command_fragment(_args_reporter_fragment
|
||||
${extra_args}
|
||||
"${reporter_arg}"
|
||||
)
|
||||
|
||||
# `set_tests_properties` calls are
|
||||
# set_tests_properties(<name> PROPERTIES WORKING_DIRECTORY <dir> <properties>)
|
||||
# set_tests_properties(<name> PROPERTIES ENVIRONMENT_MODIFICATION <env_mod>)
|
||||
# Of these, only the <name> changes between tests.
|
||||
prepare_command_fragment(_properties_fragment
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
# Env modification is optional, so we prepare it in a separate command
|
||||
if(environment_modifications)
|
||||
prepare_command_fragment(_env_modification_fragment
|
||||
PROPERTIES
|
||||
ENVIRONMENT_MODIFICATION "${environment_modifications}"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Each element in the tests is JSON-string representing one test object.
|
||||
# We have to parse it and then turn it into CTest script commands.
|
||||
foreach(single_test IN LISTS tests)
|
||||
# Because appending to the same string in CMake has quadratic runtime,
|
||||
# we flush the script into the file periodically to avoid the worst case.
|
||||
string(LENGTH "${script}" script_len)
|
||||
if (script_len GREATER _WriteToFileThreshold)
|
||||
file(APPEND "${_CTEST_FILE}" "${script}")
|
||||
set(script "")
|
||||
endif()
|
||||
string(LENGTH "${test_names}" names_len)
|
||||
if (names_len GREATER _WriteToFileThreshold)
|
||||
file(APPEND "${_CTEST_LIST_FILE}" "${test_names}")
|
||||
set(test_names "")
|
||||
endif()
|
||||
|
||||
# The elements are still escaped and contain JSON-invalid characters,
|
||||
# they have to be unescaped before parsing them as JSON.
|
||||
magic_unescape_chars(single_test)
|
||||
if(add_tags)
|
||||
string(JSON test_tags GET "${single_test}" "tags")
|
||||
endif()
|
||||
string(JSON plain_name GET "${single_test}" "name")
|
||||
|
||||
# Escape characters in test case names that would be parsed by Catch2
|
||||
@@ -170,22 +461,15 @@ function(catch_discover_tests_impl)
|
||||
set(output_dir_arg "--out ${output_dir}/${output_prefix}${escaped_name_clean}${output_suffix}")
|
||||
endif()
|
||||
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${plain_name}${suffix}"
|
||||
${_TEST_EXECUTOR}
|
||||
"${_TEST_EXECUTABLE}"
|
||||
"${escaped_name}"
|
||||
${extra_args}
|
||||
"${reporter_arg}"
|
||||
"${output_dir_arg}"
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${plain_name}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
set(full_name "${prefix}${plain_name}${suffix}")
|
||||
prepare_command_fragment(_full_name_fragment "${full_name}")
|
||||
prepare_command_fragment(_escaped_name_fragment "${escaped_name}")
|
||||
prepare_command_fragment(_outdir_fragment "${output_dir_arg}")
|
||||
|
||||
string(APPEND script
|
||||
"add_test(${_full_name_fragment}${_exec_exe_fragment}${_escaped_name_fragment}${_args_reporter_fragment}${_outdir_fragment})\n")
|
||||
string(APPEND script
|
||||
"set_tests_properties(${_full_name_fragment}${_properties_fragment})\n")
|
||||
|
||||
if(add_tags)
|
||||
string(JSON num_tags LENGTH "${test_tags}")
|
||||
@@ -205,33 +489,44 @@ function(catch_discover_tests_impl)
|
||||
list(APPEND tag_list "${a_tag}")
|
||||
endforeach()
|
||||
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${plain_name}${suffix}"
|
||||
prepare_command_fragment(_labels_fragment
|
||||
PROPERTIES
|
||||
LABELS "${tag_list}"
|
||||
)
|
||||
string(APPEND script "set_tests_properties(${_full_name_fragment}${_labels_fragment})\n")
|
||||
endif()
|
||||
endif(add_tags)
|
||||
|
||||
if(environment_modifications)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${plain_name}${suffix}"
|
||||
PROPERTIES
|
||||
ENVIRONMENT_MODIFICATION "${environment_modifications}")
|
||||
string(APPEND script "set_tests_properties(${_full_name_fragment}${_env_modification_fragment})\n")
|
||||
endif()
|
||||
|
||||
list(APPEND tests "${prefix}${plain_name}${suffix}")
|
||||
# The test name has to be escaped using the same rules as prepare_command
|
||||
# uses for the arguments, so that it keeps being single element in list
|
||||
# even with weird characters and semicolons.
|
||||
set(full_list_name "${prefix}${plain_name}${suffix}")
|
||||
string(REPLACE ";" "\\;" full_list_name "${full_list_name}")
|
||||
string(REPLACE "[" "${_OpenBracketListingEscape}" full_list_name "${full_list_name}")
|
||||
string(REPLACE "]" "${_CloseBracketListingEscape}" full_list_name "${full_list_name}")
|
||||
if(full_list_name MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
# The space before the start of quote is important, so that we get
|
||||
# space-separated list in the final file.
|
||||
string(APPEND test_names " [==[${full_list_name}]==]")
|
||||
else()
|
||||
string(APPEND test_names " ${full_list_name}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
add_command(set ${_TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${_CTEST_FILE}" "${script}")
|
||||
# Write any test names leftovers we have
|
||||
file(APPEND "${_CTEST_LIST_FILE}" "${test_names})\n")
|
||||
# Write any main script leftovers we have, and append the include of test names
|
||||
file(APPEND "${_CTEST_FILE}" "${script}")
|
||||
file(APPEND "${_CTEST_FILE}" "include(\"${_CTEST_LIST_FILE}\")\n")
|
||||
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}
|
||||
|
||||
+317
-91
@@ -6,8 +6,8 @@
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
// Catch v3.15.1
|
||||
// Generated: 2026-06-14 10:51:56.053498
|
||||
// Catch v3.16.0
|
||||
// Generated: 2026-08-25 09:29:23.172704
|
||||
// ----------------------------------------------------------
|
||||
// This file is an amalgamation of multiple different files.
|
||||
// You probably shouldn't edit it directly.
|
||||
@@ -40,6 +40,54 @@
|
||||
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
|
||||
Environment measure_environment_default() {
|
||||
return Detail::measure_environment<default_clock>();
|
||||
}
|
||||
|
||||
ExecutionPlan prepare_default( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
// This mirrors Benchmark::prepare<default_clock>(), but with the
|
||||
// clock fixed so it is instantiated once here in the library.
|
||||
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time = std::max(
|
||||
min_time,
|
||||
std::chrono::duration_cast<decltype( min_time )>(
|
||||
cfg.benchmarkWarmupTime() ) );
|
||||
auto&& test = Detail::run_for_at_least<default_clock>(
|
||||
std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
|
||||
int new_iters = static_cast<int>(
|
||||
std::ceil( min_time * test.iterations / test.elapsed ) );
|
||||
return { new_iters,
|
||||
test.elapsed / test.iterations * new_iters *
|
||||
cfg.benchmarkSamples(),
|
||||
CATCH_MOVE( fun ),
|
||||
std::chrono::duration_cast<FDuration>(
|
||||
cfg.benchmarkWarmupTime() ),
|
||||
Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
std::vector<FDuration> run_plan_default( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env ) {
|
||||
return plan.run<default_clock>( cfg, env );
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
@@ -795,6 +843,7 @@ namespace Catch {
|
||||
return lhs.name == rhs.name &&
|
||||
lhs.outputFilename == rhs.outputFilename &&
|
||||
lhs.colourMode == rhs.colourMode &&
|
||||
lhs.verbosity == rhs.verbosity &&
|
||||
lhs.customOptions == rhs.customOptions;
|
||||
}
|
||||
|
||||
@@ -863,6 +912,7 @@ namespace Catch {
|
||||
reporterSpec.outputFile() ? *reporterSpec.outputFile()
|
||||
: data.defaultOutputFilename,
|
||||
reporterSpec.colourMode().valueOr( data.defaultColourMode ),
|
||||
reporterSpec.verbosity().valueOr( data.verbosity ),
|
||||
reporterSpec.customOptions() } );
|
||||
}
|
||||
}
|
||||
@@ -913,6 +963,7 @@ namespace Catch {
|
||||
double Config::minDuration() const { return m_data.minDuration; }
|
||||
TestRunOrder Config::runOrder() const { return m_data.runOrder; }
|
||||
uint32_t Config::rngSeed() const { return m_data.rngSeed; }
|
||||
bool Config::rngSeedWasFixed() const { return m_data.rngSeedWasFixed; }
|
||||
unsigned int Config::shardCount() const { return m_data.shardCount; }
|
||||
unsigned int Config::shardIndex() const { return m_data.shardIndex; }
|
||||
ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; }
|
||||
@@ -938,7 +989,7 @@ namespace Catch {
|
||||
|
||||
if ( bazelOutputFile ) {
|
||||
m_data.reporterSpecifications.push_back(
|
||||
{ "junit", std::string( bazelOutputFile ), {}, {} } );
|
||||
{ "junit", std::string( bazelOutputFile ), {}, {}, {} } );
|
||||
}
|
||||
|
||||
const auto bazelTestSpec = Detail::getEnv( "TESTBRIDGE_TEST_ONLY" );
|
||||
@@ -977,6 +1028,7 @@ namespace Catch {
|
||||
<< bazelRandomSeed << "') as proper seed.\n";
|
||||
} else {
|
||||
m_data.rngSeed = *parsedSeed;
|
||||
m_data.rngSeedWasFixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1162,6 +1214,10 @@ namespace Catch {
|
||||
#endif
|
||||
}
|
||||
|
||||
ITestCaseRegistry& getMutableTestCaseRegistry() override {
|
||||
return m_testCaseRegistry;
|
||||
}
|
||||
|
||||
private:
|
||||
TestRegistry m_testCaseRegistry;
|
||||
ReporterRegistry m_reporterRegistry;
|
||||
@@ -1217,6 +1273,7 @@ namespace Catch {
|
||||
ReporterConfig( config,
|
||||
makeStream( spec.outputFilename ),
|
||||
spec.colourMode,
|
||||
spec.verbosity,
|
||||
spec.customOptions ) );
|
||||
}
|
||||
|
||||
@@ -1233,6 +1290,7 @@ namespace Catch {
|
||||
ReporterConfig( config,
|
||||
makeStream( reporterSpec.outputFilename ),
|
||||
reporterSpec.colourMode,
|
||||
reporterSpec.verbosity,
|
||||
reporterSpec.customOptions ) ) );
|
||||
}
|
||||
|
||||
@@ -1303,9 +1361,7 @@ namespace Catch {
|
||||
};
|
||||
|
||||
void applyFilenamesAsTags() {
|
||||
for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) {
|
||||
testInfo->addFilenameTag();
|
||||
}
|
||||
getMutableRegistryHub().getMutableTestCaseRegistry().enableFilenameTags();
|
||||
}
|
||||
|
||||
// Creates empty file at path. The path must be writable, we do not
|
||||
@@ -1511,6 +1567,19 @@ namespace Catch {
|
||||
CATCH_TRY {
|
||||
config(); // Force config to be constructed
|
||||
|
||||
if ( m_config->shardCount() > 1 &&
|
||||
m_config->runOrder() == TestRunOrder::Randomized &&
|
||||
!m_config->rngSeedWasFixed() ) {
|
||||
Catch::cerr()
|
||||
<< "Warning: using sharding (--shard-count) with random "
|
||||
"order (--order rand, the default) and without a fixed "
|
||||
"numeric --rng-seed does not guarantee disjoint coverage "
|
||||
"between shard invocations. Pass the same numeric "
|
||||
"--rng-seed to every shard, or use --order decl or "
|
||||
"--order lex instead.\n"
|
||||
<< std::flush;
|
||||
}
|
||||
|
||||
// We need to retrieve potential Bazel config with the full Config
|
||||
// constructor, so we have to create the guard file after it is created.
|
||||
setUpGuardFile( m_config->getExitGuardFilePath() );
|
||||
@@ -2394,7 +2463,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
Version const& libraryVersion() {
|
||||
static Version version( 3, 15, 1, "", 0 );
|
||||
static Version version( 3, 16, 0, "", 0 );
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -2541,7 +2610,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" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2591,10 +2660,12 @@ namespace Catch {
|
||||
IConfig const* _fullConfig,
|
||||
Detail::unique_ptr<IStream> _stream,
|
||||
ColourMode colourMode,
|
||||
Verbosity verbosity,
|
||||
std::map<std::string, std::string> customOptions ):
|
||||
m_stream( CATCH_MOVE(_stream) ),
|
||||
m_fullConfig( _fullConfig ),
|
||||
m_colourMode( colourMode ),
|
||||
m_verbosity( verbosity ),
|
||||
m_customOptions( CATCH_MOVE( customOptions ) ) {}
|
||||
|
||||
Detail::unique_ptr<IStream> ReporterConfig::takeStream() && {
|
||||
@@ -2603,6 +2674,7 @@ namespace Catch {
|
||||
}
|
||||
IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; }
|
||||
ColourMode ReporterConfig::colourMode() const { return m_colourMode; }
|
||||
Verbosity ReporterConfig::verbosity() const { return m_verbosity; }
|
||||
|
||||
std::map<std::string, std::string> const&
|
||||
ReporterConfig::customOptions() const {
|
||||
@@ -3297,9 +3369,11 @@ namespace Catch {
|
||||
auto const setRngSeed = [&]( std::string const& seed ) {
|
||||
if( seed == "time" ) {
|
||||
config.rngSeed = generateRandomSeed(GenerateFrom::Time);
|
||||
config.rngSeedWasFixed = false;
|
||||
return ParserResult::ok(ParseResultType::Matched);
|
||||
} else if (seed == "random-device") {
|
||||
config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice);
|
||||
config.rngSeedWasFixed = false;
|
||||
return ParserResult::ok(ParseResultType::Matched);
|
||||
}
|
||||
|
||||
@@ -3310,6 +3384,7 @@ namespace Catch {
|
||||
return ParserResult::runtimeError( "Could not parse '" + seed + "' as seed" );
|
||||
}
|
||||
config.rngSeed = *parsedSeed;
|
||||
config.rngSeedWasFixed = true;
|
||||
return ParserResult::ok( ParseResultType::Matched );
|
||||
};
|
||||
auto const setDefaultColourMode = [&]( std::string const& colourMode ) {
|
||||
@@ -4678,12 +4753,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 ) {
|
||||
@@ -4795,7 +4886,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 };
|
||||
@@ -4809,26 +4903,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
|
||||
@@ -5705,6 +5828,18 @@ namespace Catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Verbosity> stringToVerbosity( StringRef verbosity ) {
|
||||
if (verbosity == "quiet") { return Verbosity::Quiet;
|
||||
} else if ( verbosity == "normal" ) {
|
||||
return Verbosity::Normal;
|
||||
} else if ( verbosity == "high" ) {
|
||||
return Verbosity::High;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
|
||||
@@ -5712,6 +5847,7 @@ namespace Catch {
|
||||
return lhs.m_name == rhs.m_name &&
|
||||
lhs.m_outputFileName == rhs.m_outputFileName &&
|
||||
lhs.m_colourMode == rhs.m_colourMode &&
|
||||
lhs.m_verbosity == rhs.m_verbosity &&
|
||||
lhs.m_customOptions == rhs.m_customOptions;
|
||||
}
|
||||
|
||||
@@ -5723,6 +5859,7 @@ namespace Catch {
|
||||
std::map<std::string, std::string> kvPairs;
|
||||
Optional<std::string> outputFileName;
|
||||
Optional<ColourMode> colourMode;
|
||||
Optional<Verbosity> verbosity;
|
||||
|
||||
// First part is always reporter name, so we skip it
|
||||
for ( size_t i = 1; i < parts.size(); ++i ) {
|
||||
@@ -5760,6 +5897,12 @@ namespace Catch {
|
||||
if ( !colourMode ) {
|
||||
return {};
|
||||
}
|
||||
} else if ( key == "verbosity" ) {
|
||||
// Duplicated key
|
||||
if ( verbosity ) { return {}; }
|
||||
verbosity = Detail::stringToVerbosity( value );
|
||||
// Parsing failed
|
||||
if ( !verbosity ) { return {}; }
|
||||
} else {
|
||||
// Unrecognized option
|
||||
return {};
|
||||
@@ -5769,6 +5912,7 @@ namespace Catch {
|
||||
return ReporterSpec{ CATCH_MOVE( parts[0] ),
|
||||
CATCH_MOVE( outputFileName ),
|
||||
CATCH_MOVE( colourMode ),
|
||||
CATCH_MOVE( verbosity),
|
||||
CATCH_MOVE( kvPairs ) };
|
||||
}
|
||||
|
||||
@@ -5776,10 +5920,12 @@ ReporterSpec::ReporterSpec(
|
||||
std::string name,
|
||||
Optional<std::string> outputFileName,
|
||||
Optional<ColourMode> colourMode,
|
||||
Optional<Verbosity> verbosity,
|
||||
std::map<std::string, std::string> customOptions ):
|
||||
m_name( CATCH_MOVE( name ) ),
|
||||
m_outputFileName( CATCH_MOVE( outputFileName ) ),
|
||||
m_colourMode( CATCH_MOVE( colourMode ) ),
|
||||
m_verbosity( CATCH_MOVE( verbosity ) ),
|
||||
m_customOptions( CATCH_MOVE( customOptions ) ) {}
|
||||
|
||||
} // namespace Catch
|
||||
@@ -6016,6 +6162,8 @@ namespace Catch {
|
||||
auto getGenerator() const -> GeneratorBasePtr const& override {
|
||||
return m_generator;
|
||||
}
|
||||
|
||||
bool isFilteredImpl() const override { return m_isFiltered; }
|
||||
};
|
||||
} // namespace
|
||||
}
|
||||
@@ -6360,17 +6508,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(
|
||||
@@ -6383,11 +6520,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) {
|
||||
@@ -7182,6 +7332,9 @@ namespace Catch {
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
// Picked small-ish number at random
|
||||
static size_t kInitialTestCount = 120;
|
||||
|
||||
static void enforceNoDuplicateTestCases(
|
||||
std::vector<TestCaseHandle> const& tests ) {
|
||||
auto testInfoCmp = []( TestCaseInfo const* lhs,
|
||||
@@ -7283,17 +7436,26 @@ namespace Catch {
|
||||
return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
|
||||
}
|
||||
|
||||
|
||||
TestRegistry::TestRegistry() {
|
||||
// We pre-reserve some reasonable number of tests to avoid the
|
||||
// initial geometric growth churning during test registration.
|
||||
m_handles.reserve( kInitialTestCount );
|
||||
m_test_infos.reserve( kInitialTestCount );
|
||||
m_invokers.reserve( kInitialTestCount );
|
||||
}
|
||||
TestRegistry::~TestRegistry() = default;
|
||||
|
||||
void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
|
||||
m_handles.emplace_back(testInfo.get(), testInvoker.get());
|
||||
m_viewed_test_infos.push_back(testInfo.get());
|
||||
m_owned_test_infos.push_back(CATCH_MOVE(testInfo));
|
||||
m_test_infos.push_back(CATCH_MOVE(testInfo));
|
||||
m_invokers.push_back(CATCH_MOVE(testInvoker));
|
||||
}
|
||||
|
||||
std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const {
|
||||
return m_viewed_test_infos;
|
||||
void TestRegistry::enableFilenameTags() {
|
||||
for (auto& info : m_test_infos) {
|
||||
info->addFilenameTag();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const {
|
||||
@@ -7470,6 +7632,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)))
|
||||
@@ -8961,66 +9145,101 @@ namespace Catch {
|
||||
#include <regex>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
constexpr StringRef caseSensitivitySuffix( CaseSensitive caseSensitivity ) {
|
||||
return caseSensitivity == CaseSensitive::Yes
|
||||
? StringRef{}
|
||||
: " (case insensitive)"_sr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace Matchers {
|
||||
|
||||
CasedString::CasedString( std::string const& str, CaseSensitive caseSensitivity )
|
||||
: m_caseSensitivity( caseSensitivity ),
|
||||
m_str( adjustString( str ) )
|
||||
{}
|
||||
std::string CasedString::adjustString( std::string const& str ) const {
|
||||
return m_caseSensitivity == CaseSensitive::No
|
||||
? toLower( str )
|
||||
: str;
|
||||
}
|
||||
StringRef CasedString::caseSensitivitySuffix() const {
|
||||
return m_caseSensitivity == CaseSensitive::Yes
|
||||
? StringRef()
|
||||
: " (case insensitive)"_sr;
|
||||
}
|
||||
StringMatcherBase::StringMatcherBase( std::string target,
|
||||
StringRef operation,
|
||||
CaseSensitive caseSensitivity ):
|
||||
m_target( CATCH_MOVE( target ) ),
|
||||
m_operation( operation ),
|
||||
m_caseSensitivity( caseSensitivity ) {}
|
||||
|
||||
|
||||
StringMatcherBase::StringMatcherBase( StringRef operation, CasedString const& comparator )
|
||||
: m_comparator( comparator ),
|
||||
m_operation( operation ) {
|
||||
}
|
||||
|
||||
std::string StringMatcherBase::describe() const {
|
||||
std::string description;
|
||||
description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
|
||||
m_comparator.caseSensitivitySuffix().size());
|
||||
description.reserve(5 + m_operation.size() + m_target.size() +
|
||||
caseSensitivitySuffix(m_caseSensitivity).size());
|
||||
description += m_operation;
|
||||
description += ": \"";
|
||||
description += m_comparator.m_str;
|
||||
description += m_target;
|
||||
description += '"';
|
||||
description += m_comparator.caseSensitivitySuffix();
|
||||
description += caseSensitivitySuffix(m_caseSensitivity);
|
||||
return description;
|
||||
}
|
||||
|
||||
StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals"_sr, comparator ) {}
|
||||
|
||||
StringEqualsMatcher::StringEqualsMatcher( std::string comparator, CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "equals"_sr, caseSensitivity ) {}
|
||||
|
||||
bool StringEqualsMatcher::match( std::string const& source ) const {
|
||||
return m_comparator.adjustString( source ) == m_comparator.m_str;
|
||||
if (m_caseSensitivity == CaseSensitive::Yes) {
|
||||
return m_target == source;
|
||||
}
|
||||
if (m_target.size() != source.size()) { return false; }
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
return eq( m_target, source );
|
||||
}
|
||||
|
||||
|
||||
StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains"_sr, comparator ) {}
|
||||
StringContainsMatcher::StringContainsMatcher(
|
||||
std::string comparator, CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "contains"_sr, caseSensitivity ) {}
|
||||
|
||||
bool StringContainsMatcher::match( std::string const& source ) const {
|
||||
return contains( m_comparator.adjustString( source ), m_comparator.m_str );
|
||||
if ( m_caseSensitivity == CaseSensitive::Yes ) {
|
||||
return contains( source, m_target );
|
||||
}
|
||||
if ( source.size() < m_target.size() ) { return false; }
|
||||
StringRef as_ref( source );
|
||||
// The worst case of this is O(m*n), which is terrible, BUT:
|
||||
// * The average case is much better, the worst case only happens rarely
|
||||
// * We can implement BMH/other better searchers later if it matters
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
for (size_t i = 0; i < source.size(); ++i) {
|
||||
const auto substr = as_ref.substr( i, m_target.size() );
|
||||
bool found = eq( substr, m_target );
|
||||
if ( found ) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with"_sr, comparator ) {}
|
||||
StartsWithMatcher::StartsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "starts with"_sr, caseSensitivity ) {}
|
||||
|
||||
bool StartsWithMatcher::match( std::string const& source ) const {
|
||||
return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
|
||||
if ( m_caseSensitivity == CaseSensitive::Yes ) {
|
||||
return startsWith( source, m_target );
|
||||
}
|
||||
if (source.size() < m_target.size()) { return false; }
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
return eq(
|
||||
StringRef( source ).substr( 0, m_target.size() ), m_target );
|
||||
}
|
||||
|
||||
|
||||
EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with"_sr, comparator ) {}
|
||||
EndsWithMatcher::EndsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "ends with"_sr, caseSensitivity ) {}
|
||||
|
||||
bool EndsWithMatcher::match( std::string const& source ) const {
|
||||
return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
|
||||
if ( m_caseSensitivity == CaseSensitive::Yes ) {
|
||||
return endsWith( source, m_target );
|
||||
}
|
||||
if ( source.size() < m_target.size() ) { return false; }
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
const size_t start_point = source.size() - m_target.size();
|
||||
return eq( StringRef( source ).substr( start_point, m_target.size() ), m_target );
|
||||
}
|
||||
|
||||
|
||||
@@ -9041,21 +9260,21 @@ namespace Matchers {
|
||||
}
|
||||
|
||||
|
||||
StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return StringEqualsMatcher( CasedString( str, caseSensitivity) );
|
||||
StringEqualsMatcher Equals( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return StringEqualsMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return StringContainsMatcher( CasedString( str, caseSensitivity) );
|
||||
StringContainsMatcher ContainsSubstring( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return StringContainsMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return EndsWithMatcher( CasedString( str, caseSensitivity) );
|
||||
EndsWithMatcher EndsWith( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return EndsWithMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return StartsWithMatcher( CasedString( str, caseSensitivity) );
|
||||
StartsWithMatcher StartsWith( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return StartsWithMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
|
||||
RegexMatcher Matches(std::string const& regex, CaseSensitive caseSensitivity) {
|
||||
return RegexMatcher(regex, caseSensitivity);
|
||||
RegexMatcher Matches(std::string regex, CaseSensitive caseSensitivity) {
|
||||
return RegexMatcher( CATCH_MOVE( regex ), caseSensitivity );
|
||||
}
|
||||
|
||||
} // namespace Matchers
|
||||
@@ -9157,6 +9376,7 @@ namespace Catch {
|
||||
m_wrapped_stream( CATCH_MOVE(config).takeStream() ),
|
||||
m_stream( m_wrapped_stream->stream() ),
|
||||
m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.get() ) ),
|
||||
m_verbosity( config.verbosity() ),
|
||||
m_customOptions( config.customOptions() )
|
||||
{}
|
||||
|
||||
@@ -9164,12 +9384,12 @@ namespace Catch {
|
||||
|
||||
void ReporterBase::listReporters(
|
||||
std::vector<ReporterDescription> const& descriptions ) {
|
||||
defaultListReporters( m_stream, descriptions, m_config->verbosity() );
|
||||
defaultListReporters( m_stream, descriptions, m_verbosity );
|
||||
}
|
||||
|
||||
void ReporterBase::listListeners(
|
||||
std::vector<ListenerDescription> const& descriptions ) {
|
||||
defaultListListeners( m_stream, descriptions, m_config->verbosity() );
|
||||
defaultListListeners( m_stream, descriptions, m_verbosity );
|
||||
}
|
||||
|
||||
void ReporterBase::listTests(std::vector<TestCaseHandle> const& tests) {
|
||||
@@ -9177,11 +9397,11 @@ namespace Catch {
|
||||
m_colour.get(),
|
||||
tests,
|
||||
m_config->hasTestFilters(),
|
||||
m_config->verbosity());
|
||||
m_verbosity);
|
||||
}
|
||||
|
||||
void ReporterBase::listTags(std::vector<TagInfo> const& tags) {
|
||||
defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_config->verbosity() );
|
||||
defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_verbosity );
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
@@ -10631,6 +10851,8 @@ namespace Catch {
|
||||
|
||||
namespace Catch {
|
||||
namespace {
|
||||
static size_t kJsonOutputVersion = 2;
|
||||
|
||||
void writeSourceInfo( JsonObjectWriter& writer,
|
||||
SourceLineInfo const& sourceInfo ) {
|
||||
auto source_location_writer =
|
||||
@@ -10673,7 +10895,7 @@ namespace Catch {
|
||||
m_writers.emplace( Writer::Object );
|
||||
auto& writer = m_objectWriters.top();
|
||||
|
||||
writer.write( "version"_sr ).write( 1 );
|
||||
writer.write( "version"_sr ).write( kJsonOutputVersion );
|
||||
|
||||
{
|
||||
auto metadata_writer = writer.write( "metadata"_sr ).writeObject();
|
||||
@@ -10959,14 +11181,18 @@ namespace Catch {
|
||||
auto const& info = test.getTestCaseInfo();
|
||||
|
||||
desc_writer.write( "name"_sr ).write( info.name );
|
||||
desc_writer.write( "class-name"_sr ).write( info.className );
|
||||
{
|
||||
if (!info.className.empty()) {
|
||||
desc_writer.write( "class-name"_sr ).write( info.className );
|
||||
}
|
||||
if ( m_verbosity >= Verbosity::Normal ) {
|
||||
auto tag_writer = desc_writer.write( "tags"_sr ).writeArray();
|
||||
for ( auto const& tag : info.tags ) {
|
||||
tag_writer.write( tag.original );
|
||||
}
|
||||
}
|
||||
writeSourceInfo( desc_writer, info.lineInfo );
|
||||
if ( m_verbosity >= Verbosity::High) {
|
||||
writeSourceInfo( desc_writer, info.lineInfo );
|
||||
}
|
||||
}
|
||||
}
|
||||
void JsonReporter::listTags( std::vector<TagInfo> const& tags ) {
|
||||
|
||||
+254
-90
@@ -6,8 +6,8 @@
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
// Catch v3.15.1
|
||||
// Generated: 2026-06-14 10:51:55.600632
|
||||
// Catch v3.16.0
|
||||
// Generated: 2026-08-25 09:29:22.652020
|
||||
// ----------------------------------------------------------
|
||||
// This file is an amalgamation of multiple different files.
|
||||
// You probably shouldn't edit it directly.
|
||||
@@ -1317,6 +1317,8 @@ 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 ITestCaseRegistry& getMutableTestCaseRegistry() = 0;
|
||||
};
|
||||
|
||||
IRegistryHub const& getRegistryHub();
|
||||
@@ -1506,7 +1508,7 @@ namespace Catch {
|
||||
} // namespace Detail
|
||||
#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
|
||||
|
||||
#if defined(_MSVC_VER)
|
||||
#if defined(_MSC_VER)
|
||||
#pragma optimize("", off)
|
||||
#elif defined(__IAR_SYSTEMS_ICC__)
|
||||
// For IAR the pragma only affects the following function
|
||||
@@ -1543,6 +1545,13 @@ namespace Catch {
|
||||
template <typename Fn, typename... Args>
|
||||
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
|
||||
CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...);
|
||||
// In the non-void case, we pass the result through `deoptimize_value`
|
||||
// to force the compiler to keep it. We have no return value here,
|
||||
// but add an optimizer barrier (ideally a memory clobber) to force
|
||||
// the _side effects_ of the loop be visible (e.g. writes to globals).
|
||||
// Note that writes to benchmark-locals can be optimized away, as
|
||||
// we would expect in normal code.
|
||||
Detail::optimizer_barrier();
|
||||
}
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
@@ -2159,9 +2168,94 @@ namespace Catch {
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename Clock>
|
||||
ExecutionPlan prepare( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
auto min_time =
|
||||
env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time =
|
||||
std::max( min_time,
|
||||
std::chrono::duration_cast<decltype( min_time )>(
|
||||
cfg.benchmarkWarmupTime() ) );
|
||||
auto&& test = Detail::run_for_at_least<Clock>(
|
||||
std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
|
||||
int new_iters = static_cast<int>(
|
||||
std::ceil( min_time * test.iterations / test.elapsed ) );
|
||||
return { new_iters,
|
||||
test.elapsed / test.iterations * new_iters *
|
||||
cfg.benchmarkSamples(),
|
||||
CATCH_MOVE( fun ),
|
||||
std::chrono::duration_cast<FDuration>(
|
||||
cfg.benchmarkWarmupTime() ),
|
||||
Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
// These are wrappers for their respective function templated
|
||||
// over `default_clock`. This allows outlining the usual use
|
||||
// of the template into single TU and save on compilation costs.
|
||||
|
||||
Environment measure_environment_default();
|
||||
ExecutionPlan prepare_default( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun );
|
||||
std::vector<FDuration> run_plan_default( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env );
|
||||
|
||||
template <typename Clock>
|
||||
std::enable_if_t<std::is_same<Clock, default_clock>::value,
|
||||
Environment>
|
||||
measureEnvironmentDispatch() {
|
||||
return measure_environment_default();
|
||||
}
|
||||
template <typename Clock>
|
||||
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
|
||||
Environment>
|
||||
measureEnvironmentDispatch() {
|
||||
return measure_environment<Clock>();
|
||||
}
|
||||
|
||||
template <typename Clock>
|
||||
std::enable_if_t<std::is_same<Clock, default_clock>::value,
|
||||
std::vector<FDuration>>
|
||||
runPlanDispatch( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env ) {
|
||||
return run_plan_default( plan, cfg, env );
|
||||
}
|
||||
template <typename Clock>
|
||||
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
|
||||
std::vector<FDuration>>
|
||||
runPlanDispatch( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env ) {
|
||||
return plan.template run<Clock>( cfg, env );
|
||||
}
|
||||
|
||||
template <typename Clock>
|
||||
std::enable_if_t<std::is_same<Clock, default_clock>::value,
|
||||
ExecutionPlan>
|
||||
prepareDispatch( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
return prepare_default( cfg, env, CATCH_MOVE( fun ) );
|
||||
}
|
||||
template <typename Clock>
|
||||
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
|
||||
ExecutionPlan>
|
||||
prepareDispatch( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
return prepare<Clock>( cfg, env, CATCH_MOVE( fun ) );
|
||||
}
|
||||
} // namespace Detail
|
||||
|
||||
struct Benchmark {
|
||||
Benchmark(std::string&& benchmarkName)
|
||||
: name(CATCH_MOVE(benchmarkName)) {}
|
||||
@@ -2170,27 +2264,18 @@ namespace Catch {
|
||||
Benchmark(std::string&& benchmarkName , FUN &&func)
|
||||
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
|
||||
|
||||
template <typename Clock>
|
||||
ExecutionPlan prepare(const IConfig &cfg, Environment env) {
|
||||
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
|
||||
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
|
||||
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
|
||||
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
template <typename Clock = default_clock>
|
||||
void run() {
|
||||
static_assert( Clock::is_steady,
|
||||
"Benchmarking clock should be steady" );
|
||||
auto const* cfg = getCurrentContext().getConfig();
|
||||
|
||||
auto env = Detail::measure_environment<Clock>();
|
||||
auto env = Detail::measureEnvironmentDispatch<Clock>();
|
||||
|
||||
getResultCapture().benchmarkPreparing(name);
|
||||
CATCH_TRY{
|
||||
auto plan = user_code([&] {
|
||||
return prepare<Clock>(*cfg, env);
|
||||
return Detail::prepareDispatch<Clock>( *cfg, env, CATCH_MOVE(fun) );
|
||||
});
|
||||
|
||||
BenchmarkInfo info {
|
||||
@@ -2206,7 +2291,7 @@ namespace Catch {
|
||||
getResultCapture().benchmarkStarting(info);
|
||||
|
||||
auto samples = user_code([&] {
|
||||
return plan.template run<Clock>(*cfg, env);
|
||||
return Detail::runPlanDispatch<Clock>( plan, *cfg, env );
|
||||
});
|
||||
|
||||
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
|
||||
@@ -3736,6 +3821,7 @@ namespace Catch {
|
||||
std::vector<std::string> splitReporterSpec( StringRef reporterSpec );
|
||||
|
||||
Optional<ColourMode> stringToColourMode( StringRef colourMode );
|
||||
Optional<Verbosity> stringToVerbosity( StringRef verbosity );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3750,6 +3836,7 @@ namespace Catch {
|
||||
std::string m_name;
|
||||
Optional<std::string> m_outputFileName;
|
||||
Optional<ColourMode> m_colourMode;
|
||||
Optional<Verbosity> m_verbosity;
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
|
||||
friend bool operator==( ReporterSpec const& lhs,
|
||||
@@ -3764,6 +3851,7 @@ namespace Catch {
|
||||
std::string name,
|
||||
Optional<std::string> outputFileName,
|
||||
Optional<ColourMode> colourMode,
|
||||
Optional<Verbosity> verbosity,
|
||||
std::map<std::string, std::string> customOptions );
|
||||
|
||||
std::string const& name() const { return m_name; }
|
||||
@@ -3774,13 +3862,15 @@ namespace Catch {
|
||||
|
||||
Optional<ColourMode> const& colourMode() const { return m_colourMode; }
|
||||
|
||||
Optional<Verbosity> const& verbosity() const { return m_verbosity; }
|
||||
|
||||
std::map<std::string, std::string> const& customOptions() const {
|
||||
return m_customOptions;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses provided reporter spec string into
|
||||
* Parses provided reporter spec string into actual `ReporterSpec`
|
||||
*
|
||||
* Returns empty optional on errors, e.g.
|
||||
* * field that is not first and not a key+value pair
|
||||
@@ -3813,6 +3903,7 @@ namespace Catch {
|
||||
std::string name;
|
||||
std::string outputFilename;
|
||||
ColourMode colourMode;
|
||||
Verbosity verbosity;
|
||||
std::map<std::string, std::string> customOptions;
|
||||
friend bool operator==( ProcessedReporterSpec const& lhs,
|
||||
ProcessedReporterSpec const& rhs );
|
||||
@@ -3840,6 +3931,7 @@ namespace Catch {
|
||||
|
||||
int abortAfter = -1;
|
||||
uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default);
|
||||
bool rngSeedWasFixed = false;
|
||||
|
||||
unsigned int shardCount = 1;
|
||||
unsigned int shardIndex = 0;
|
||||
@@ -3911,6 +4003,7 @@ namespace Catch {
|
||||
double minDuration() const override;
|
||||
TestRunOrder runOrder() const override;
|
||||
uint32_t rngSeed() const override;
|
||||
bool rngSeedWasFixed() const;
|
||||
unsigned int shardCount() const override;
|
||||
unsigned int shardIndex() const override;
|
||||
ColourMode defaultColourMode() const override;
|
||||
@@ -6624,6 +6717,51 @@ namespace Catch {
|
||||
struct priority_tag : priority_tag<N - 1> {};
|
||||
template <>
|
||||
struct priority_tag<0> {};
|
||||
|
||||
// This is a bunch of helpers for the templated test case handling.
|
||||
// They should live elsewhere in the long run, but as an in-between
|
||||
// step we toss them all here.
|
||||
template <typename...> struct TypeList {};
|
||||
template <typename... Ts>
|
||||
constexpr auto get_wrapper( priority_tag<1> ) noexcept -> TypeList<Ts...> { return {}; }
|
||||
template <template <typename...> class...> struct TemplateTypeList {};
|
||||
// Clang 20 and 21 cannot handle an explicitly specified all-pack
|
||||
// template-template parameter here ("conflicting deduction" regression,
|
||||
// llvm/llvm-project#130778; fixed for Clang 22).
|
||||
// Remove get_template_wrapper once Clang 21 is no longer supported.
|
||||
template <template <typename...> class C, template <typename...> class... Cs>
|
||||
constexpr auto get_template_wrapper( priority_tag<1> ) noexcept -> TemplateTypeList<C, Cs...> { return {}; }
|
||||
|
||||
template <typename...>
|
||||
struct append;
|
||||
template <typename T>
|
||||
struct append<T> { using type = T; };
|
||||
template <template <typename...> class L1, typename... E1, template <typename...> class L2, typename... E2, typename... Rest>
|
||||
struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1..., E2...>, Rest...>::type; };
|
||||
template <template <typename...> class L1, typename... E1, typename... Rest>
|
||||
struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };
|
||||
|
||||
template <template <typename...> class, typename>
|
||||
struct convert;
|
||||
template <template <typename...> class Final, template <typename...> class List, typename... Ts>
|
||||
struct convert<Final, List<Ts...>> { using type = typename append<Final<>, TypeList<Ts>...>::type; };
|
||||
|
||||
// These are helpers for the PRODUCT templated test cases.
|
||||
// Note that the _SIG macros (for NTTPs) also use specializations
|
||||
// of these, but they have to use their own instances due to needing
|
||||
// per-sig specializations and we have to keep these in their own
|
||||
// unnamed namespace.
|
||||
template <typename...>
|
||||
struct rewrap;
|
||||
template <template <typename...> class Container, template <typename...> class List, typename... elems>
|
||||
struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };
|
||||
template <template <typename...> class Container, template <typename...> class List, class... Elems, typename... Elements>
|
||||
struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };
|
||||
|
||||
template <template <typename...> class, typename...>
|
||||
struct create;
|
||||
template <template <typename...> class Final, template <typename...> class... Containers, typename... Types>
|
||||
struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6710,46 +6848,42 @@ namespace Catch {
|
||||
|
||||
#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
|
||||
|
||||
#define INTERNAL_CATCH_TYPE_GEN\
|
||||
template<typename...> struct TypeList {};\
|
||||
template<typename... Ts>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TypeList<Ts...> { return {}; }\
|
||||
template<template<typename...> class...> struct TemplateTypeList{};\
|
||||
template<template<typename...> class...Cs>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TemplateTypeList<Cs...> { return {}; }\
|
||||
template<typename...>\
|
||||
struct append;\
|
||||
template<typename...>\
|
||||
struct rewrap;\
|
||||
template<template<typename...> class, typename...>\
|
||||
struct create;\
|
||||
template<template<typename...> class, typename>\
|
||||
struct convert;\
|
||||
\
|
||||
template<typename T> \
|
||||
struct append<T> { using type = T; };\
|
||||
template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
|
||||
struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
|
||||
template< template<typename...> class L1, typename...E1, typename...Rest>\
|
||||
struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
|
||||
#define INTERNAL_CATCH_TYPE_GEN \
|
||||
/* We moved these into a central location and no longer create them
|
||||
in each templated test's unnamed namespace, but we pull them in
|
||||
with using to avoid qualifying all the references. */ \
|
||||
using Catch::Detail::TypeList; \
|
||||
using Catch::Detail::get_wrapper; \
|
||||
using Catch::Detail::TemplateTypeList; \
|
||||
using Catch::Detail::get_template_wrapper; \
|
||||
using Catch::Detail::append; \
|
||||
using Catch::Detail::convert;
|
||||
|
||||
// This stamps out the per test case specializations of wrapper handlers
|
||||
// for _SIG (NTTP) macros inside their own namespace, so they can add
|
||||
// their required specializations
|
||||
#define INTERNAL_CATCH_NTTP_1( signature, ... ) \
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class C, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
|
||||
constexpr auto get_template_wrapper(Catch::Detail::priority_tag<0>) noexcept -> NttpTemplateTypeList<C, Cs...> { return {}; }
|
||||
|
||||
// This stamps out the per test case specializations of type-product
|
||||
// machinery for the NTTP product macros.
|
||||
#define INTERNAL_CATCH_NTTP_REWRAP_1( signature, ... ) \
|
||||
template<typename...> \
|
||||
struct rewrap; \
|
||||
template<template<typename...> class, typename...> \
|
||||
struct create; \
|
||||
\
|
||||
template< template<typename...> class Container, template<typename...> class List, typename...elems>\
|
||||
struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
|
||||
template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
|
||||
struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
|
||||
\
|
||||
template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
|
||||
struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
|
||||
template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
|
||||
struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_1(signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
|
||||
\
|
||||
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
|
||||
@@ -6826,7 +6960,9 @@ namespace Catch {
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_NTTP_0
|
||||
#define INTERNAL_CATCH_NTTP_0_REWRAP using Catch::Detail::rewrap; using Catch::Detail::create;
|
||||
#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
|
||||
#define INTERNAL_CATCH_NTTP_REWRAP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0_REWRAP)
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
|
||||
@@ -6836,7 +6972,9 @@ namespace Catch {
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
|
||||
#else
|
||||
#define INTERNAL_CATCH_NTTP_0(signature)
|
||||
#define INTERNAL_CATCH_NTTP_0_REWRAP(signature) using Catch::Detail::rewrap; using Catch::Detail::create;
|
||||
#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_NTTP_REWRAP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1,INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_0_REWRAP)( __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
|
||||
@@ -6961,6 +7099,7 @@ namespace Catch {
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
|
||||
INTERNAL_CATCH_TYPE_GEN \
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
|
||||
INTERNAL_CATCH_NTTP_REWRAP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
|
||||
template<typename... Types> \
|
||||
struct TestName { \
|
||||
void reg_tests() { \
|
||||
@@ -6973,7 +7112,7 @@ namespace Catch {
|
||||
} \
|
||||
}; \
|
||||
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
||||
using TestInit = typename create<TestName, 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; \
|
||||
using TestInit = typename create<TestName, decltype(get_template_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; \
|
||||
@@ -7057,7 +7196,7 @@ namespace Catch {
|
||||
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||
TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
|
||||
return 0;\
|
||||
}();\
|
||||
}();\
|
||||
}\
|
||||
}\
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||
@@ -7093,6 +7232,7 @@ namespace Catch {
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
|
||||
INTERNAL_CATCH_TYPE_GEN \
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
INTERNAL_CATCH_NTTP_REWRAP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
template<typename...Types>\
|
||||
struct TestNameClass{\
|
||||
void reg_tests(){\
|
||||
@@ -7105,7 +7245,7 @@ namespace Catch {
|
||||
}\
|
||||
};\
|
||||
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;\
|
||||
using TestInit = typename create<TestNameClass, decltype(get_template_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;\
|
||||
@@ -7484,6 +7624,8 @@ namespace Catch {
|
||||
return m_translateFunction( ex );
|
||||
}
|
||||
#else
|
||||
(void)it;
|
||||
(void)itEnd;
|
||||
return "You should never get here!";
|
||||
#endif
|
||||
}
|
||||
@@ -7569,8 +7711,8 @@ namespace Catch {
|
||||
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||
|
||||
#define CATCH_VERSION_MAJOR 3
|
||||
#define CATCH_VERSION_MINOR 15
|
||||
#define CATCH_VERSION_PATCH 1
|
||||
#define CATCH_VERSION_MINOR 16
|
||||
#define CATCH_VERSION_PATCH 0
|
||||
|
||||
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||
|
||||
@@ -7826,7 +7968,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;
|
||||
}
|
||||
@@ -7910,8 +8052,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;
|
||||
}
|
||||
@@ -8010,7 +8151,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 );
|
||||
@@ -9158,6 +9299,7 @@ namespace Catch {
|
||||
ReporterConfig( IConfig const* _fullConfig,
|
||||
Detail::unique_ptr<IStream> _stream,
|
||||
ColourMode colourMode,
|
||||
Verbosity verbosity,
|
||||
std::map<std::string, std::string> customOptions );
|
||||
|
||||
ReporterConfig( ReporterConfig&& ) = default;
|
||||
@@ -9167,12 +9309,14 @@ namespace Catch {
|
||||
Detail::unique_ptr<IStream> takeStream() &&;
|
||||
IConfig const* fullConfig() const;
|
||||
ColourMode colourMode() const;
|
||||
Verbosity verbosity() const;
|
||||
std::map<std::string, std::string> const& customOptions() const;
|
||||
|
||||
private:
|
||||
Detail::unique_ptr<IStream> m_stream;
|
||||
IConfig const* m_fullConfig;
|
||||
ColourMode m_colourMode;
|
||||
Verbosity m_verbosity;
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
};
|
||||
|
||||
@@ -9425,8 +9569,7 @@ namespace Catch {
|
||||
class ITestCaseRegistry {
|
||||
public:
|
||||
virtual ~ITestCaseRegistry(); // = default
|
||||
// TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
|
||||
virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
|
||||
virtual void enableFilenameTags() = 0;
|
||||
virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
|
||||
virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
|
||||
};
|
||||
@@ -10308,11 +10451,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
|
||||
@@ -10657,6 +10808,8 @@ namespace TestCaseTracking {
|
||||
|
||||
using Children = std::vector<ITrackerPtr>;
|
||||
|
||||
virtual bool isFilteredImpl() const = 0;
|
||||
|
||||
protected:
|
||||
enum CycleState {
|
||||
NotStarted,
|
||||
@@ -10754,6 +10907,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 {
|
||||
@@ -10809,6 +10976,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 );
|
||||
|
||||
@@ -11263,20 +11432,20 @@ namespace Catch {
|
||||
class TestRegistry final : public ITestCaseRegistry {
|
||||
public:
|
||||
void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker );
|
||||
void enableFilenameTags() override;
|
||||
|
||||
std::vector<TestCaseInfo*> const& getAllInfos() const override;
|
||||
std::vector<TestCaseHandle> const& getAllTests() const override;
|
||||
std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const override;
|
||||
|
||||
TestRegistry();
|
||||
~TestRegistry() override; // = default
|
||||
|
||||
private:
|
||||
std::vector<Detail::unique_ptr<TestCaseInfo>> m_owned_test_infos;
|
||||
// Keeps a materialized vector for `getAllInfos`.
|
||||
// We should get rid of that eventually (see interface note)
|
||||
std::vector<TestCaseInfo*> m_viewed_test_infos;
|
||||
|
||||
// Owns the test infos for handles
|
||||
std::vector<Detail::unique_ptr<TestCaseInfo>> m_test_infos;
|
||||
// Owns the test invokers for handles
|
||||
std::vector<Detail::unique_ptr<ITestInvoker>> m_invokers;
|
||||
|
||||
std::vector<TestCaseHandle> m_handles;
|
||||
mutable TestRunOrder m_currentSortOrder = TestRunOrder::Declared;
|
||||
mutable std::vector<TestCaseHandle> m_sortedFunctions;
|
||||
@@ -13274,44 +13443,40 @@ namespace Catch {
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
|
||||
struct CasedString {
|
||||
CasedString( std::string const& str, CaseSensitive caseSensitivity );
|
||||
std::string adjustString( std::string const& str ) const;
|
||||
StringRef caseSensitivitySuffix() const;
|
||||
|
||||
CaseSensitive m_caseSensitivity;
|
||||
std::string m_str;
|
||||
};
|
||||
|
||||
class StringMatcherBase : public MatcherBase<std::string> {
|
||||
protected:
|
||||
CasedString m_comparator;
|
||||
std::string m_target;
|
||||
StringRef m_operation;
|
||||
CaseSensitive m_caseSensitivity;
|
||||
|
||||
StringMatcherBase( std::string target,
|
||||
StringRef operation,
|
||||
CaseSensitive caseSensitivity );
|
||||
public:
|
||||
StringMatcherBase( StringRef operation,
|
||||
CasedString const& comparator );
|
||||
std::string describe() const override;
|
||||
};
|
||||
|
||||
class StringEqualsMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
StringEqualsMatcher( CasedString const& comparator );
|
||||
StringEqualsMatcher( std::string comparator, CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
class StringContainsMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
StringContainsMatcher( CasedString const& comparator );
|
||||
StringContainsMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
class StartsWithMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
StartsWithMatcher( CasedString const& comparator );
|
||||
StartsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
class EndsWithMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
EndsWithMatcher( CasedString const& comparator );
|
||||
EndsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
|
||||
@@ -13326,15 +13491,15 @@ namespace Matchers {
|
||||
};
|
||||
|
||||
//! Creates matcher that accepts strings that are exactly equal to `str`
|
||||
StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
StringEqualsMatcher Equals( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings that contain `str`
|
||||
StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
StringContainsMatcher ContainsSubstring( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings that _end_ with `str`
|
||||
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
EndsWithMatcher EndsWith( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings that _start_ with `str`
|
||||
StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
StartsWithMatcher StartsWith( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings matching `regex`
|
||||
RegexMatcher Matches( std::string const& regex, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
RegexMatcher Matches( std::string regex, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
@@ -13588,6 +13753,8 @@ namespace Catch {
|
||||
std::ostream& m_stream;
|
||||
//! Colour implementation this reporter was configured for
|
||||
Detail::unique_ptr<ColourImpl> m_colour;
|
||||
//! Verbosity configured for this reporter
|
||||
Verbosity m_verbosity;
|
||||
//! The custom reporter options user passed down to the reporter
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
|
||||
@@ -14179,9 +14346,6 @@ namespace Catch {
|
||||
std::stack<Writer> m_writers{};
|
||||
|
||||
bool m_startedListing = false;
|
||||
|
||||
// std::size_t m_sectionDepth = 0;
|
||||
// std::size_t m_sectionStarted = 0;
|
||||
};
|
||||
} // namespace Catch
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
project(
|
||||
'catch2',
|
||||
'cpp',
|
||||
version: '3.15.1', # CML version placeholder, don't delete
|
||||
version: '3.16.0', # CML version placeholder, don't delete
|
||||
license: 'BSL-1.0',
|
||||
meson_version: '>=0.54.1',
|
||||
)
|
||||
|
||||
@@ -32,6 +32,7 @@ set(BENCHMARK_HEADERS
|
||||
${SOURCES_DIR}/benchmark/detail/catch_timing.hpp
|
||||
)
|
||||
set(BENCHMARK_SOURCES
|
||||
${SOURCES_DIR}/benchmark/catch_benchmark.cpp
|
||||
${SOURCES_DIR}/benchmark/catch_chronometer.cpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_analyse.cpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.cpp
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <catch2/benchmark/catch_benchmark.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
|
||||
Environment measure_environment_default() {
|
||||
return Detail::measure_environment<default_clock>();
|
||||
}
|
||||
|
||||
ExecutionPlan prepare_default( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
// This mirrors Benchmark::prepare<default_clock>(), but with the
|
||||
// clock fixed so it is instantiated once here in the library.
|
||||
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time = std::max(
|
||||
min_time,
|
||||
std::chrono::duration_cast<decltype( min_time )>(
|
||||
cfg.benchmarkWarmupTime() ) );
|
||||
auto&& test = Detail::run_for_at_least<default_clock>(
|
||||
std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
|
||||
int new_iters = static_cast<int>(
|
||||
std::ceil( min_time * test.iterations / test.elapsed ) );
|
||||
return { new_iters,
|
||||
test.elapsed / test.iterations * new_iters *
|
||||
cfg.benchmarkSamples(),
|
||||
CATCH_MOVE( fun ),
|
||||
std::chrono::duration_cast<FDuration>(
|
||||
cfg.benchmarkWarmupTime() ),
|
||||
Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
std::vector<FDuration> run_plan_default( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env ) {
|
||||
return plan.run<default_clock>( cfg, env );
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
@@ -33,9 +33,94 @@
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename Clock>
|
||||
ExecutionPlan prepare( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
auto min_time =
|
||||
env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time =
|
||||
std::max( min_time,
|
||||
std::chrono::duration_cast<decltype( min_time )>(
|
||||
cfg.benchmarkWarmupTime() ) );
|
||||
auto&& test = Detail::run_for_at_least<Clock>(
|
||||
std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
|
||||
int new_iters = static_cast<int>(
|
||||
std::ceil( min_time * test.iterations / test.elapsed ) );
|
||||
return { new_iters,
|
||||
test.elapsed / test.iterations * new_iters *
|
||||
cfg.benchmarkSamples(),
|
||||
CATCH_MOVE( fun ),
|
||||
std::chrono::duration_cast<FDuration>(
|
||||
cfg.benchmarkWarmupTime() ),
|
||||
Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
// These are wrappers for their respective function templated
|
||||
// over `default_clock`. This allows outlining the usual use
|
||||
// of the template into single TU and save on compilation costs.
|
||||
|
||||
Environment measure_environment_default();
|
||||
ExecutionPlan prepare_default( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun );
|
||||
std::vector<FDuration> run_plan_default( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env );
|
||||
|
||||
template <typename Clock>
|
||||
std::enable_if_t<std::is_same<Clock, default_clock>::value,
|
||||
Environment>
|
||||
measureEnvironmentDispatch() {
|
||||
return measure_environment_default();
|
||||
}
|
||||
template <typename Clock>
|
||||
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
|
||||
Environment>
|
||||
measureEnvironmentDispatch() {
|
||||
return measure_environment<Clock>();
|
||||
}
|
||||
|
||||
template <typename Clock>
|
||||
std::enable_if_t<std::is_same<Clock, default_clock>::value,
|
||||
std::vector<FDuration>>
|
||||
runPlanDispatch( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env ) {
|
||||
return run_plan_default( plan, cfg, env );
|
||||
}
|
||||
template <typename Clock>
|
||||
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
|
||||
std::vector<FDuration>>
|
||||
runPlanDispatch( ExecutionPlan const& plan,
|
||||
const IConfig& cfg,
|
||||
Environment env ) {
|
||||
return plan.template run<Clock>( cfg, env );
|
||||
}
|
||||
|
||||
template <typename Clock>
|
||||
std::enable_if_t<std::is_same<Clock, default_clock>::value,
|
||||
ExecutionPlan>
|
||||
prepareDispatch( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
return prepare_default( cfg, env, CATCH_MOVE( fun ) );
|
||||
}
|
||||
template <typename Clock>
|
||||
std::enable_if_t<!std::is_same<Clock, default_clock>::value,
|
||||
ExecutionPlan>
|
||||
prepareDispatch( const IConfig& cfg,
|
||||
Environment env,
|
||||
BenchmarkFunction&& fun ) {
|
||||
return prepare<Clock>( cfg, env, CATCH_MOVE( fun ) );
|
||||
}
|
||||
} // namespace Detail
|
||||
|
||||
struct Benchmark {
|
||||
Benchmark(std::string&& benchmarkName)
|
||||
: name(CATCH_MOVE(benchmarkName)) {}
|
||||
@@ -44,27 +129,18 @@ namespace Catch {
|
||||
Benchmark(std::string&& benchmarkName , FUN &&func)
|
||||
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
|
||||
|
||||
template <typename Clock>
|
||||
ExecutionPlan prepare(const IConfig &cfg, Environment env) {
|
||||
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
|
||||
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
|
||||
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
|
||||
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
template <typename Clock = default_clock>
|
||||
void run() {
|
||||
static_assert( Clock::is_steady,
|
||||
"Benchmarking clock should be steady" );
|
||||
auto const* cfg = getCurrentContext().getConfig();
|
||||
|
||||
auto env = Detail::measure_environment<Clock>();
|
||||
auto env = Detail::measureEnvironmentDispatch<Clock>();
|
||||
|
||||
getResultCapture().benchmarkPreparing(name);
|
||||
CATCH_TRY{
|
||||
auto plan = user_code([&] {
|
||||
return prepare<Clock>(*cfg, env);
|
||||
return Detail::prepareDispatch<Clock>( *cfg, env, CATCH_MOVE(fun) );
|
||||
});
|
||||
|
||||
BenchmarkInfo info {
|
||||
@@ -80,7 +156,7 @@ namespace Catch {
|
||||
getResultCapture().benchmarkStarting(info);
|
||||
|
||||
auto samples = user_code([&] {
|
||||
return plan.template run<Clock>(*cfg, env);
|
||||
return Detail::runPlanDispatch<Clock>( plan, *cfg, env );
|
||||
});
|
||||
|
||||
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Catch {
|
||||
} // namespace Detail
|
||||
#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
|
||||
|
||||
#if defined(_MSVC_VER)
|
||||
#if defined(_MSC_VER)
|
||||
#pragma optimize("", off)
|
||||
#elif defined(__IAR_SYSTEMS_ICC__)
|
||||
// For IAR the pragma only affects the following function
|
||||
@@ -71,6 +71,13 @@ namespace Catch {
|
||||
template <typename Fn, typename... Args>
|
||||
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
|
||||
CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...);
|
||||
// In the non-void case, we pass the result through `deoptimize_value`
|
||||
// to force the compiler to keep it. We have no return value here,
|
||||
// but add an optimizer barrier (ideally a memory clobber) to force
|
||||
// the _side effects_ of the loop be visible (e.g. writes to globals).
|
||||
// Note that writes to benchmark-locals can be optimized away, as
|
||||
// we would expect in normal code.
|
||||
Detail::optimizer_barrier();
|
||||
}
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace Catch {
|
||||
return lhs.name == rhs.name &&
|
||||
lhs.outputFilename == rhs.outputFilename &&
|
||||
lhs.colourMode == rhs.colourMode &&
|
||||
lhs.verbosity == rhs.verbosity &&
|
||||
lhs.customOptions == rhs.customOptions;
|
||||
}
|
||||
|
||||
@@ -157,6 +158,7 @@ namespace Catch {
|
||||
reporterSpec.outputFile() ? *reporterSpec.outputFile()
|
||||
: data.defaultOutputFilename,
|
||||
reporterSpec.colourMode().valueOr( data.defaultColourMode ),
|
||||
reporterSpec.verbosity().valueOr( data.verbosity ),
|
||||
reporterSpec.customOptions() } );
|
||||
}
|
||||
}
|
||||
@@ -207,6 +209,7 @@ namespace Catch {
|
||||
double Config::minDuration() const { return m_data.minDuration; }
|
||||
TestRunOrder Config::runOrder() const { return m_data.runOrder; }
|
||||
uint32_t Config::rngSeed() const { return m_data.rngSeed; }
|
||||
bool Config::rngSeedWasFixed() const { return m_data.rngSeedWasFixed; }
|
||||
unsigned int Config::shardCount() const { return m_data.shardCount; }
|
||||
unsigned int Config::shardIndex() const { return m_data.shardIndex; }
|
||||
ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; }
|
||||
@@ -232,7 +235,7 @@ namespace Catch {
|
||||
|
||||
if ( bazelOutputFile ) {
|
||||
m_data.reporterSpecifications.push_back(
|
||||
{ "junit", std::string( bazelOutputFile ), {}, {} } );
|
||||
{ "junit", std::string( bazelOutputFile ), {}, {}, {} } );
|
||||
}
|
||||
|
||||
const auto bazelTestSpec = Detail::getEnv( "TESTBRIDGE_TEST_ONLY" );
|
||||
@@ -271,6 +274,7 @@ namespace Catch {
|
||||
<< bazelRandomSeed << "') as proper seed.\n";
|
||||
} else {
|
||||
m_data.rngSeed = *parsedSeed;
|
||||
m_data.rngSeedWasFixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Catch {
|
||||
std::string name;
|
||||
std::string outputFilename;
|
||||
ColourMode colourMode;
|
||||
Verbosity verbosity;
|
||||
std::map<std::string, std::string> customOptions;
|
||||
friend bool operator==( ProcessedReporterSpec const& lhs,
|
||||
ProcessedReporterSpec const& rhs );
|
||||
@@ -62,6 +63,7 @@ namespace Catch {
|
||||
|
||||
int abortAfter = -1;
|
||||
uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default);
|
||||
bool rngSeedWasFixed = false;
|
||||
|
||||
unsigned int shardCount = 1;
|
||||
unsigned int shardIndex = 0;
|
||||
@@ -133,6 +135,7 @@ namespace Catch {
|
||||
double minDuration() const override;
|
||||
TestRunOrder runOrder() const override;
|
||||
uint32_t rngSeed() const override;
|
||||
bool rngSeedWasFixed() const;
|
||||
unsigned int shardCount() const override;
|
||||
unsigned int shardIndex() const override;
|
||||
ColourMode defaultColourMode() const override;
|
||||
|
||||
@@ -72,6 +72,10 @@ namespace Catch {
|
||||
#endif
|
||||
}
|
||||
|
||||
ITestCaseRegistry& getMutableTestCaseRegistry() override {
|
||||
return m_testCaseRegistry;
|
||||
}
|
||||
|
||||
private:
|
||||
TestRegistry m_testCaseRegistry;
|
||||
ReporterRegistry m_reporterRegistry;
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Catch {
|
||||
ReporterConfig( config,
|
||||
makeStream( spec.outputFilename ),
|
||||
spec.colourMode,
|
||||
spec.verbosity,
|
||||
spec.customOptions ) );
|
||||
}
|
||||
|
||||
@@ -68,6 +69,7 @@ namespace Catch {
|
||||
ReporterConfig( config,
|
||||
makeStream( reporterSpec.outputFilename ),
|
||||
reporterSpec.colourMode,
|
||||
reporterSpec.verbosity,
|
||||
reporterSpec.customOptions ) ) );
|
||||
}
|
||||
|
||||
@@ -138,9 +140,7 @@ namespace Catch {
|
||||
};
|
||||
|
||||
void applyFilenamesAsTags() {
|
||||
for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) {
|
||||
testInfo->addFilenameTag();
|
||||
}
|
||||
getMutableRegistryHub().getMutableTestCaseRegistry().enableFilenameTags();
|
||||
}
|
||||
|
||||
// Creates empty file at path. The path must be writable, we do not
|
||||
@@ -346,6 +346,19 @@ namespace Catch {
|
||||
CATCH_TRY {
|
||||
config(); // Force config to be constructed
|
||||
|
||||
if ( m_config->shardCount() > 1 &&
|
||||
m_config->runOrder() == TestRunOrder::Randomized &&
|
||||
!m_config->rngSeedWasFixed() ) {
|
||||
Catch::cerr()
|
||||
<< "Warning: using sharding (--shard-count) with random "
|
||||
"order (--order rand, the default) and without a fixed "
|
||||
"numeric --rng-seed does not guarantee disjoint coverage "
|
||||
"between shard invocations. Pass the same numeric "
|
||||
"--rng-seed to every shard, or use --order decl or "
|
||||
"--order lex instead.\n"
|
||||
<< std::flush;
|
||||
}
|
||||
|
||||
// We need to retrieve potential Bazel config with the full Config
|
||||
// constructor, so we have to create the guard file after it is created.
|
||||
setUpGuardFile( m_config->getExitGuardFilePath() );
|
||||
|
||||
@@ -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, 15, 1, "", 0 );
|
||||
static Version version( 3, 16, 0, "", 0 );
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||
|
||||
#define CATCH_VERSION_MAJOR 3
|
||||
#define CATCH_VERSION_MINOR 15
|
||||
#define CATCH_VERSION_PATCH 1
|
||||
#define CATCH_VERSION_MINOR 16
|
||||
#define CATCH_VERSION_PATCH 0
|
||||
|
||||
#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 );
|
||||
|
||||
@@ -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,6 +53,8 @@ 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 ITestCaseRegistry& getMutableTestCaseRegistry() = 0;
|
||||
};
|
||||
|
||||
IRegistryHub const& getRegistryHub();
|
||||
|
||||
@@ -19,10 +19,12 @@ namespace Catch {
|
||||
IConfig const* _fullConfig,
|
||||
Detail::unique_ptr<IStream> _stream,
|
||||
ColourMode colourMode,
|
||||
Verbosity verbosity,
|
||||
std::map<std::string, std::string> customOptions ):
|
||||
m_stream( CATCH_MOVE(_stream) ),
|
||||
m_fullConfig( _fullConfig ),
|
||||
m_colourMode( colourMode ),
|
||||
m_verbosity( verbosity ),
|
||||
m_customOptions( CATCH_MOVE( customOptions ) ) {}
|
||||
|
||||
Detail::unique_ptr<IStream> ReporterConfig::takeStream() && {
|
||||
@@ -31,6 +33,7 @@ namespace Catch {
|
||||
}
|
||||
IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; }
|
||||
ColourMode ReporterConfig::colourMode() const { return m_colourMode; }
|
||||
Verbosity ReporterConfig::verbosity() const { return m_verbosity; }
|
||||
|
||||
std::map<std::string, std::string> const&
|
||||
ReporterConfig::customOptions() const {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <catch2/catch_test_run_info.hpp>
|
||||
#include <catch2/catch_totals.hpp>
|
||||
#include <catch2/catch_assertion_result.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/internal/catch_message_info.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
@@ -36,6 +37,7 @@ namespace Catch {
|
||||
ReporterConfig( IConfig const* _fullConfig,
|
||||
Detail::unique_ptr<IStream> _stream,
|
||||
ColourMode colourMode,
|
||||
Verbosity verbosity,
|
||||
std::map<std::string, std::string> customOptions );
|
||||
|
||||
ReporterConfig( ReporterConfig&& ) = default;
|
||||
@@ -45,12 +47,14 @@ namespace Catch {
|
||||
Detail::unique_ptr<IStream> takeStream() &&;
|
||||
IConfig const* fullConfig() const;
|
||||
ColourMode colourMode() const;
|
||||
Verbosity verbosity() const;
|
||||
std::map<std::string, std::string> const& customOptions() const;
|
||||
|
||||
private:
|
||||
Detail::unique_ptr<IStream> m_stream;
|
||||
IConfig const* m_fullConfig;
|
||||
ColourMode m_colourMode;
|
||||
Verbosity m_verbosity;
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace Catch {
|
||||
class ITestCaseRegistry {
|
||||
public:
|
||||
virtual ~ITestCaseRegistry(); // = default
|
||||
// TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
|
||||
virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
|
||||
virtual void enableFilenameTags() = 0;
|
||||
virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
|
||||
virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
|
||||
};
|
||||
|
||||
@@ -75,9 +75,11 @@ namespace Catch {
|
||||
auto const setRngSeed = [&]( std::string const& seed ) {
|
||||
if( seed == "time" ) {
|
||||
config.rngSeed = generateRandomSeed(GenerateFrom::Time);
|
||||
config.rngSeedWasFixed = false;
|
||||
return ParserResult::ok(ParseResultType::Matched);
|
||||
} else if (seed == "random-device") {
|
||||
config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice);
|
||||
config.rngSeedWasFixed = false;
|
||||
return ParserResult::ok(ParseResultType::Matched);
|
||||
}
|
||||
|
||||
@@ -88,6 +90,7 @@ namespace Catch {
|
||||
return ParserResult::runtimeError( "Could not parse '" + seed + "' as seed" );
|
||||
}
|
||||
config.rngSeed = *parsedSeed;
|
||||
config.rngSeedWasFixed = true;
|
||||
return ParserResult::ok( ParseResultType::Matched );
|
||||
};
|
||||
auto const setDefaultColourMode = [&]( std::string const& colourMode ) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define CATCH_PREPROCESSOR_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_preprocessor_remove_parens.hpp>
|
||||
#include <catch2/internal/catch_meta.hpp>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work
|
||||
@@ -22,6 +23,51 @@ namespace Catch {
|
||||
struct priority_tag : priority_tag<N - 1> {};
|
||||
template <>
|
||||
struct priority_tag<0> {};
|
||||
|
||||
// This is a bunch of helpers for the templated test case handling.
|
||||
// They should live elsewhere in the long run, but as an in-between
|
||||
// step we toss them all here.
|
||||
template <typename...> struct TypeList {};
|
||||
template <typename... Ts>
|
||||
constexpr auto get_wrapper( priority_tag<1> ) noexcept -> TypeList<Ts...> { return {}; }
|
||||
template <template <typename...> class...> struct TemplateTypeList {};
|
||||
// Clang 20 and 21 cannot handle an explicitly specified all-pack
|
||||
// template-template parameter here ("conflicting deduction" regression,
|
||||
// llvm/llvm-project#130778; fixed for Clang 22).
|
||||
// Remove get_template_wrapper once Clang 21 is no longer supported.
|
||||
template <template <typename...> class C, template <typename...> class... Cs>
|
||||
constexpr auto get_template_wrapper( priority_tag<1> ) noexcept -> TemplateTypeList<C, Cs...> { return {}; }
|
||||
|
||||
template <typename...>
|
||||
struct append;
|
||||
template <typename T>
|
||||
struct append<T> { using type = T; };
|
||||
template <template <typename...> class L1, typename... E1, template <typename...> class L2, typename... E2, typename... Rest>
|
||||
struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1..., E2...>, Rest...>::type; };
|
||||
template <template <typename...> class L1, typename... E1, typename... Rest>
|
||||
struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };
|
||||
|
||||
template <template <typename...> class, typename>
|
||||
struct convert;
|
||||
template <template <typename...> class Final, template <typename...> class List, typename... Ts>
|
||||
struct convert<Final, List<Ts...>> { using type = typename append<Final<>, TypeList<Ts>...>::type; };
|
||||
|
||||
// These are helpers for the PRODUCT templated test cases.
|
||||
// Note that the _SIG macros (for NTTPs) also use specializations
|
||||
// of these, but they have to use their own instances due to needing
|
||||
// per-sig specializations and we have to keep these in their own
|
||||
// unnamed namespace.
|
||||
template <typename...>
|
||||
struct rewrap;
|
||||
template <template <typename...> class Container, template <typename...> class List, typename... elems>
|
||||
struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };
|
||||
template <template <typename...> class Container, template <typename...> class List, class... Elems, typename... Elements>
|
||||
struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };
|
||||
|
||||
template <template <typename...> class, typename...>
|
||||
struct create;
|
||||
template <template <typename...> class Final, template <typename...> class... Containers, typename... Types>
|
||||
struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,46 +154,42 @@ namespace Catch {
|
||||
|
||||
#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
|
||||
|
||||
#define INTERNAL_CATCH_TYPE_GEN\
|
||||
template<typename...> struct TypeList {};\
|
||||
template<typename... Ts>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TypeList<Ts...> { return {}; }\
|
||||
template<template<typename...> class...> struct TemplateTypeList{};\
|
||||
template<template<typename...> class...Cs>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TemplateTypeList<Cs...> { return {}; }\
|
||||
template<typename...>\
|
||||
struct append;\
|
||||
template<typename...>\
|
||||
struct rewrap;\
|
||||
template<template<typename...> class, typename...>\
|
||||
struct create;\
|
||||
template<template<typename...> class, typename>\
|
||||
struct convert;\
|
||||
\
|
||||
template<typename T> \
|
||||
struct append<T> { using type = T; };\
|
||||
template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
|
||||
struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
|
||||
template< template<typename...> class L1, typename...E1, typename...Rest>\
|
||||
struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
|
||||
#define INTERNAL_CATCH_TYPE_GEN \
|
||||
/* We moved these into a central location and no longer create them
|
||||
in each templated test's unnamed namespace, but we pull them in
|
||||
with using to avoid qualifying all the references. */ \
|
||||
using Catch::Detail::TypeList; \
|
||||
using Catch::Detail::get_wrapper; \
|
||||
using Catch::Detail::TemplateTypeList; \
|
||||
using Catch::Detail::get_template_wrapper; \
|
||||
using Catch::Detail::append; \
|
||||
using Catch::Detail::convert;
|
||||
|
||||
// This stamps out the per test case specializations of wrapper handlers
|
||||
// for _SIG (NTTP) macros inside their own namespace, so they can add
|
||||
// their required specializations
|
||||
#define INTERNAL_CATCH_NTTP_1( signature, ... ) \
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class C, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
|
||||
constexpr auto get_template_wrapper(Catch::Detail::priority_tag<0>) noexcept -> NttpTemplateTypeList<C, Cs...> { return {}; }
|
||||
|
||||
// This stamps out the per test case specializations of type-product
|
||||
// machinery for the NTTP product macros.
|
||||
#define INTERNAL_CATCH_NTTP_REWRAP_1( signature, ... ) \
|
||||
template<typename...> \
|
||||
struct rewrap; \
|
||||
template<template<typename...> class, typename...> \
|
||||
struct create; \
|
||||
\
|
||||
template< template<typename...> class Container, template<typename...> class List, typename...elems>\
|
||||
struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
|
||||
template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
|
||||
struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
|
||||
\
|
||||
template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
|
||||
struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
|
||||
template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
|
||||
struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_1(signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
|
||||
constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
|
||||
\
|
||||
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
|
||||
@@ -224,7 +266,9 @@ namespace Catch {
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_NTTP_0
|
||||
#define INTERNAL_CATCH_NTTP_0_REWRAP using Catch::Detail::rewrap; using Catch::Detail::create;
|
||||
#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
|
||||
#define INTERNAL_CATCH_NTTP_REWRAP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_REWRAP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0_REWRAP)
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
|
||||
@@ -234,7 +278,9 @@ namespace Catch {
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
|
||||
#else
|
||||
#define INTERNAL_CATCH_NTTP_0(signature)
|
||||
#define INTERNAL_CATCH_NTTP_0_REWRAP(signature) using Catch::Detail::rewrap; using Catch::Detail::create;
|
||||
#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_NTTP_REWRAP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_REWRAP_1,INTERNAL_CATCH_NTTP_REWRAP_1, INTERNAL_CATCH_NTTP_0_REWRAP)( __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
|
||||
|
||||
@@ -93,6 +93,18 @@ namespace Catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Verbosity> stringToVerbosity( StringRef verbosity ) {
|
||||
if (verbosity == "quiet") { return Verbosity::Quiet;
|
||||
} else if ( verbosity == "normal" ) {
|
||||
return Verbosity::Normal;
|
||||
} else if ( verbosity == "high" ) {
|
||||
return Verbosity::High;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
|
||||
@@ -100,6 +112,7 @@ namespace Catch {
|
||||
return lhs.m_name == rhs.m_name &&
|
||||
lhs.m_outputFileName == rhs.m_outputFileName &&
|
||||
lhs.m_colourMode == rhs.m_colourMode &&
|
||||
lhs.m_verbosity == rhs.m_verbosity &&
|
||||
lhs.m_customOptions == rhs.m_customOptions;
|
||||
}
|
||||
|
||||
@@ -111,6 +124,7 @@ namespace Catch {
|
||||
std::map<std::string, std::string> kvPairs;
|
||||
Optional<std::string> outputFileName;
|
||||
Optional<ColourMode> colourMode;
|
||||
Optional<Verbosity> verbosity;
|
||||
|
||||
// First part is always reporter name, so we skip it
|
||||
for ( size_t i = 1; i < parts.size(); ++i ) {
|
||||
@@ -148,6 +162,12 @@ namespace Catch {
|
||||
if ( !colourMode ) {
|
||||
return {};
|
||||
}
|
||||
} else if ( key == "verbosity" ) {
|
||||
// Duplicated key
|
||||
if ( verbosity ) { return {}; }
|
||||
verbosity = Detail::stringToVerbosity( value );
|
||||
// Parsing failed
|
||||
if ( !verbosity ) { return {}; }
|
||||
} else {
|
||||
// Unrecognized option
|
||||
return {};
|
||||
@@ -157,6 +177,7 @@ namespace Catch {
|
||||
return ReporterSpec{ CATCH_MOVE( parts[0] ),
|
||||
CATCH_MOVE( outputFileName ),
|
||||
CATCH_MOVE( colourMode ),
|
||||
CATCH_MOVE( verbosity),
|
||||
CATCH_MOVE( kvPairs ) };
|
||||
}
|
||||
|
||||
@@ -164,10 +185,12 @@ ReporterSpec::ReporterSpec(
|
||||
std::string name,
|
||||
Optional<std::string> outputFileName,
|
||||
Optional<ColourMode> colourMode,
|
||||
Optional<Verbosity> verbosity,
|
||||
std::map<std::string, std::string> customOptions ):
|
||||
m_name( CATCH_MOVE( name ) ),
|
||||
m_outputFileName( CATCH_MOVE( outputFileName ) ),
|
||||
m_colourMode( CATCH_MOVE( colourMode ) ),
|
||||
m_verbosity( CATCH_MOVE( verbosity ) ),
|
||||
m_customOptions( CATCH_MOVE( customOptions ) ) {}
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Catch {
|
||||
std::vector<std::string> splitReporterSpec( StringRef reporterSpec );
|
||||
|
||||
Optional<ColourMode> stringToColourMode( StringRef colourMode );
|
||||
Optional<Verbosity> stringToVerbosity( StringRef verbosity );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,7 @@ namespace Catch {
|
||||
std::string m_name;
|
||||
Optional<std::string> m_outputFileName;
|
||||
Optional<ColourMode> m_colourMode;
|
||||
Optional<Verbosity> m_verbosity;
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
|
||||
friend bool operator==( ReporterSpec const& lhs,
|
||||
@@ -53,6 +55,7 @@ namespace Catch {
|
||||
std::string name,
|
||||
Optional<std::string> outputFileName,
|
||||
Optional<ColourMode> colourMode,
|
||||
Optional<Verbosity> verbosity,
|
||||
std::map<std::string, std::string> customOptions );
|
||||
|
||||
std::string const& name() const { return m_name; }
|
||||
@@ -63,13 +66,15 @@ namespace Catch {
|
||||
|
||||
Optional<ColourMode> const& colourMode() const { return m_colourMode; }
|
||||
|
||||
Optional<Verbosity> const& verbosity() const { return m_verbosity; }
|
||||
|
||||
std::map<std::string, std::string> const& customOptions() const {
|
||||
return m_customOptions;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses provided reporter spec string into
|
||||
* Parses provided reporter spec string into actual `ReporterSpec`
|
||||
*
|
||||
* Returns empty optional on errors, e.g.
|
||||
* * field that is not first and not a key+value pair
|
||||
|
||||
@@ -196,6 +196,8 @@ namespace Catch {
|
||||
auto getGenerator() const -> GeneratorBasePtr const& override {
|
||||
return m_generator;
|
||||
}
|
||||
|
||||
bool isFilteredImpl() const override { return m_isFiltered; }
|
||||
};
|
||||
} // namespace
|
||||
}
|
||||
@@ -540,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(
|
||||
@@ -563,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) {
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
|
||||
INTERNAL_CATCH_TYPE_GEN \
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
|
||||
INTERNAL_CATCH_NTTP_REWRAP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
|
||||
template<typename... Types> \
|
||||
struct TestName { \
|
||||
void reg_tests() { \
|
||||
@@ -139,7 +140,7 @@
|
||||
} \
|
||||
}; \
|
||||
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
||||
using TestInit = typename create<TestName, 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; \
|
||||
using TestInit = typename create<TestName, decltype(get_template_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; \
|
||||
@@ -223,7 +224,7 @@
|
||||
static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||
TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
|
||||
return 0;\
|
||||
}();\
|
||||
}();\
|
||||
}\
|
||||
}\
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||
@@ -259,6 +260,7 @@
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
|
||||
INTERNAL_CATCH_TYPE_GEN \
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
INTERNAL_CATCH_NTTP_REWRAP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
template<typename...Types>\
|
||||
struct TestNameClass{\
|
||||
void reg_tests(){\
|
||||
@@ -271,7 +273,7 @@
|
||||
}\
|
||||
};\
|
||||
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;\
|
||||
using TestInit = typename create<TestNameClass, decltype(get_template_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;\
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
// Picked small-ish number at random
|
||||
static size_t kInitialTestCount = 120;
|
||||
|
||||
static void enforceNoDuplicateTestCases(
|
||||
std::vector<TestCaseHandle> const& tests ) {
|
||||
auto testInfoCmp = []( TestCaseInfo const* lhs,
|
||||
@@ -123,17 +126,26 @@ namespace Catch {
|
||||
return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
|
||||
}
|
||||
|
||||
|
||||
TestRegistry::TestRegistry() {
|
||||
// We pre-reserve some reasonable number of tests to avoid the
|
||||
// initial geometric growth churning during test registration.
|
||||
m_handles.reserve( kInitialTestCount );
|
||||
m_test_infos.reserve( kInitialTestCount );
|
||||
m_invokers.reserve( kInitialTestCount );
|
||||
}
|
||||
TestRegistry::~TestRegistry() = default;
|
||||
|
||||
void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
|
||||
m_handles.emplace_back(testInfo.get(), testInvoker.get());
|
||||
m_viewed_test_infos.push_back(testInfo.get());
|
||||
m_owned_test_infos.push_back(CATCH_MOVE(testInfo));
|
||||
m_test_infos.push_back(CATCH_MOVE(testInfo));
|
||||
m_invokers.push_back(CATCH_MOVE(testInvoker));
|
||||
}
|
||||
|
||||
std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const {
|
||||
return m_viewed_test_infos;
|
||||
void TestRegistry::enableFilenameTags() {
|
||||
for (auto& info : m_test_infos) {
|
||||
info->addFilenameTag();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const {
|
||||
|
||||
@@ -31,20 +31,20 @@ namespace Catch {
|
||||
class TestRegistry final : public ITestCaseRegistry {
|
||||
public:
|
||||
void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker );
|
||||
void enableFilenameTags() override;
|
||||
|
||||
std::vector<TestCaseInfo*> const& getAllInfos() const override;
|
||||
std::vector<TestCaseHandle> const& getAllTests() const override;
|
||||
std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const override;
|
||||
|
||||
TestRegistry();
|
||||
~TestRegistry() override; // = default
|
||||
|
||||
private:
|
||||
std::vector<Detail::unique_ptr<TestCaseInfo>> m_owned_test_infos;
|
||||
// Keeps a materialized vector for `getAllInfos`.
|
||||
// We should get rid of that eventually (see interface note)
|
||||
std::vector<TestCaseInfo*> m_viewed_test_infos;
|
||||
|
||||
// Owns the test infos for handles
|
||||
std::vector<Detail::unique_ptr<TestCaseInfo>> m_test_infos;
|
||||
// Owns the test invokers for handles
|
||||
std::vector<Detail::unique_ptr<ITestInvoker>> m_invokers;
|
||||
|
||||
std::vector<TestCaseHandle> m_handles;
|
||||
mutable TestRunOrder m_currentSortOrder = TestRunOrder::Declared;
|
||||
mutable std::vector<TestCaseHandle> m_sortedFunctions;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -9,70 +9,106 @@
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/catch_tostring.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
constexpr StringRef caseSensitivitySuffix( CaseSensitive caseSensitivity ) {
|
||||
return caseSensitivity == CaseSensitive::Yes
|
||||
? StringRef{}
|
||||
: " (case insensitive)"_sr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace Matchers {
|
||||
|
||||
CasedString::CasedString( std::string const& str, CaseSensitive caseSensitivity )
|
||||
: m_caseSensitivity( caseSensitivity ),
|
||||
m_str( adjustString( str ) )
|
||||
{}
|
||||
std::string CasedString::adjustString( std::string const& str ) const {
|
||||
return m_caseSensitivity == CaseSensitive::No
|
||||
? toLower( str )
|
||||
: str;
|
||||
}
|
||||
StringRef CasedString::caseSensitivitySuffix() const {
|
||||
return m_caseSensitivity == CaseSensitive::Yes
|
||||
? StringRef()
|
||||
: " (case insensitive)"_sr;
|
||||
}
|
||||
StringMatcherBase::StringMatcherBase( std::string target,
|
||||
StringRef operation,
|
||||
CaseSensitive caseSensitivity ):
|
||||
m_target( CATCH_MOVE( target ) ),
|
||||
m_operation( operation ),
|
||||
m_caseSensitivity( caseSensitivity ) {}
|
||||
|
||||
|
||||
StringMatcherBase::StringMatcherBase( StringRef operation, CasedString const& comparator )
|
||||
: m_comparator( comparator ),
|
||||
m_operation( operation ) {
|
||||
}
|
||||
|
||||
std::string StringMatcherBase::describe() const {
|
||||
std::string description;
|
||||
description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
|
||||
m_comparator.caseSensitivitySuffix().size());
|
||||
description.reserve(5 + m_operation.size() + m_target.size() +
|
||||
caseSensitivitySuffix(m_caseSensitivity).size());
|
||||
description += m_operation;
|
||||
description += ": \"";
|
||||
description += m_comparator.m_str;
|
||||
description += m_target;
|
||||
description += '"';
|
||||
description += m_comparator.caseSensitivitySuffix();
|
||||
description += caseSensitivitySuffix(m_caseSensitivity);
|
||||
return description;
|
||||
}
|
||||
|
||||
StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals"_sr, comparator ) {}
|
||||
|
||||
StringEqualsMatcher::StringEqualsMatcher( std::string comparator, CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "equals"_sr, caseSensitivity ) {}
|
||||
|
||||
bool StringEqualsMatcher::match( std::string const& source ) const {
|
||||
return m_comparator.adjustString( source ) == m_comparator.m_str;
|
||||
if (m_caseSensitivity == CaseSensitive::Yes) {
|
||||
return m_target == source;
|
||||
}
|
||||
if (m_target.size() != source.size()) { return false; }
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
return eq( m_target, source );
|
||||
}
|
||||
|
||||
|
||||
StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains"_sr, comparator ) {}
|
||||
StringContainsMatcher::StringContainsMatcher(
|
||||
std::string comparator, CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "contains"_sr, caseSensitivity ) {}
|
||||
|
||||
bool StringContainsMatcher::match( std::string const& source ) const {
|
||||
return contains( m_comparator.adjustString( source ), m_comparator.m_str );
|
||||
if ( m_caseSensitivity == CaseSensitive::Yes ) {
|
||||
return contains( source, m_target );
|
||||
}
|
||||
if ( source.size() < m_target.size() ) { return false; }
|
||||
StringRef as_ref( source );
|
||||
// The worst case of this is O(m*n), which is terrible, BUT:
|
||||
// * The average case is much better, the worst case only happens rarely
|
||||
// * We can implement BMH/other better searchers later if it matters
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
for (size_t i = 0; i < source.size(); ++i) {
|
||||
const auto substr = as_ref.substr( i, m_target.size() );
|
||||
bool found = eq( substr, m_target );
|
||||
if ( found ) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with"_sr, comparator ) {}
|
||||
StartsWithMatcher::StartsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "starts with"_sr, caseSensitivity ) {}
|
||||
|
||||
bool StartsWithMatcher::match( std::string const& source ) const {
|
||||
return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
|
||||
if ( m_caseSensitivity == CaseSensitive::Yes ) {
|
||||
return startsWith( source, m_target );
|
||||
}
|
||||
if (source.size() < m_target.size()) { return false; }
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
return eq(
|
||||
StringRef( source ).substr( 0, m_target.size() ), m_target );
|
||||
}
|
||||
|
||||
|
||||
EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with"_sr, comparator ) {}
|
||||
EndsWithMatcher::EndsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity ):
|
||||
StringMatcherBase( CATCH_MOVE( comparator ), "ends with"_sr, caseSensitivity ) {}
|
||||
|
||||
bool EndsWithMatcher::match( std::string const& source ) const {
|
||||
return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
|
||||
if ( m_caseSensitivity == CaseSensitive::Yes ) {
|
||||
return endsWith( source, m_target );
|
||||
}
|
||||
if ( source.size() < m_target.size() ) { return false; }
|
||||
Catch::Detail::CaseInsensitiveEqualTo eq;
|
||||
const size_t start_point = source.size() - m_target.size();
|
||||
return eq( StringRef( source ).substr( start_point, m_target.size() ), m_target );
|
||||
}
|
||||
|
||||
|
||||
@@ -93,21 +129,21 @@ namespace Matchers {
|
||||
}
|
||||
|
||||
|
||||
StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return StringEqualsMatcher( CasedString( str, caseSensitivity) );
|
||||
StringEqualsMatcher Equals( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return StringEqualsMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return StringContainsMatcher( CasedString( str, caseSensitivity) );
|
||||
StringContainsMatcher ContainsSubstring( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return StringContainsMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return EndsWithMatcher( CasedString( str, caseSensitivity) );
|
||||
EndsWithMatcher EndsWith( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return EndsWithMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity ) {
|
||||
return StartsWithMatcher( CasedString( str, caseSensitivity) );
|
||||
StartsWithMatcher StartsWith( std::string str, CaseSensitive caseSensitivity ) {
|
||||
return StartsWithMatcher( CATCH_MOVE( str ), caseSensitivity );
|
||||
}
|
||||
|
||||
RegexMatcher Matches(std::string const& regex, CaseSensitive caseSensitivity) {
|
||||
return RegexMatcher(regex, caseSensitivity);
|
||||
RegexMatcher Matches(std::string regex, CaseSensitive caseSensitivity) {
|
||||
return RegexMatcher( CATCH_MOVE( regex ), caseSensitivity );
|
||||
}
|
||||
|
||||
} // namespace Matchers
|
||||
|
||||
@@ -17,44 +17,40 @@
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
|
||||
struct CasedString {
|
||||
CasedString( std::string const& str, CaseSensitive caseSensitivity );
|
||||
std::string adjustString( std::string const& str ) const;
|
||||
StringRef caseSensitivitySuffix() const;
|
||||
|
||||
CaseSensitive m_caseSensitivity;
|
||||
std::string m_str;
|
||||
};
|
||||
|
||||
class StringMatcherBase : public MatcherBase<std::string> {
|
||||
protected:
|
||||
CasedString m_comparator;
|
||||
std::string m_target;
|
||||
StringRef m_operation;
|
||||
CaseSensitive m_caseSensitivity;
|
||||
|
||||
StringMatcherBase( std::string target,
|
||||
StringRef operation,
|
||||
CaseSensitive caseSensitivity );
|
||||
public:
|
||||
StringMatcherBase( StringRef operation,
|
||||
CasedString const& comparator );
|
||||
std::string describe() const override;
|
||||
};
|
||||
|
||||
class StringEqualsMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
StringEqualsMatcher( CasedString const& comparator );
|
||||
StringEqualsMatcher( std::string comparator, CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
class StringContainsMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
StringContainsMatcher( CasedString const& comparator );
|
||||
StringContainsMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
class StartsWithMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
StartsWithMatcher( CasedString const& comparator );
|
||||
StartsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
class EndsWithMatcher final : public StringMatcherBase {
|
||||
public:
|
||||
EndsWithMatcher( CasedString const& comparator );
|
||||
EndsWithMatcher( std::string comparator,
|
||||
CaseSensitive caseSensitivity );
|
||||
bool match( std::string const& source ) const override;
|
||||
};
|
||||
|
||||
@@ -69,15 +65,15 @@ namespace Matchers {
|
||||
};
|
||||
|
||||
//! Creates matcher that accepts strings that are exactly equal to `str`
|
||||
StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
StringEqualsMatcher Equals( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings that contain `str`
|
||||
StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
StringContainsMatcher ContainsSubstring( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings that _end_ with `str`
|
||||
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
EndsWithMatcher EndsWith( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings that _start_ with `str`
|
||||
StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
StartsWithMatcher StartsWith( std::string str, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
//! Creates matcher that accepts strings matching `regex`
|
||||
RegexMatcher Matches( std::string const& regex, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
RegexMatcher Matches( std::string regex, CaseSensitive caseSensitivity = CaseSensitive::Yes );
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
||||
@@ -47,6 +47,7 @@ benchmark_headers = [
|
||||
]
|
||||
|
||||
benchmark_sources = files(
|
||||
'benchmark/catch_benchmark.cpp',
|
||||
'benchmark/catch_chronometer.cpp',
|
||||
'benchmark/detail/catch_analyse.cpp',
|
||||
'benchmark/detail/catch_benchmark_function.cpp',
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Catch {
|
||||
m_wrapped_stream( CATCH_MOVE(config).takeStream() ),
|
||||
m_stream( m_wrapped_stream->stream() ),
|
||||
m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.get() ) ),
|
||||
m_verbosity( config.verbosity() ),
|
||||
m_customOptions( config.customOptions() )
|
||||
{}
|
||||
|
||||
@@ -26,12 +27,12 @@ namespace Catch {
|
||||
|
||||
void ReporterBase::listReporters(
|
||||
std::vector<ReporterDescription> const& descriptions ) {
|
||||
defaultListReporters( m_stream, descriptions, m_config->verbosity() );
|
||||
defaultListReporters( m_stream, descriptions, m_verbosity );
|
||||
}
|
||||
|
||||
void ReporterBase::listListeners(
|
||||
std::vector<ListenerDescription> const& descriptions ) {
|
||||
defaultListListeners( m_stream, descriptions, m_config->verbosity() );
|
||||
defaultListListeners( m_stream, descriptions, m_verbosity );
|
||||
}
|
||||
|
||||
void ReporterBase::listTests(std::vector<TestCaseHandle> const& tests) {
|
||||
@@ -39,11 +40,11 @@ namespace Catch {
|
||||
m_colour.get(),
|
||||
tests,
|
||||
m_config->hasTestFilters(),
|
||||
m_config->verbosity());
|
||||
m_verbosity);
|
||||
}
|
||||
|
||||
void ReporterBase::listTags(std::vector<TagInfo> const& tags) {
|
||||
defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_config->verbosity() );
|
||||
defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_verbosity );
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Catch {
|
||||
std::ostream& m_stream;
|
||||
//! Colour implementation this reporter was configured for
|
||||
Detail::unique_ptr<ColourImpl> m_colour;
|
||||
//! Verbosity configured for this reporter
|
||||
Verbosity m_verbosity;
|
||||
//! The custom reporter options user passed down to the reporter
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
namespace Catch {
|
||||
namespace {
|
||||
static size_t kJsonOutputVersion = 2;
|
||||
|
||||
void writeSourceInfo( JsonObjectWriter& writer,
|
||||
SourceLineInfo const& sourceInfo ) {
|
||||
auto source_location_writer =
|
||||
@@ -58,7 +60,7 @@ namespace Catch {
|
||||
m_writers.emplace( Writer::Object );
|
||||
auto& writer = m_objectWriters.top();
|
||||
|
||||
writer.write( "version"_sr ).write( 1 );
|
||||
writer.write( "version"_sr ).write( kJsonOutputVersion );
|
||||
|
||||
{
|
||||
auto metadata_writer = writer.write( "metadata"_sr ).writeObject();
|
||||
@@ -344,14 +346,18 @@ namespace Catch {
|
||||
auto const& info = test.getTestCaseInfo();
|
||||
|
||||
desc_writer.write( "name"_sr ).write( info.name );
|
||||
desc_writer.write( "class-name"_sr ).write( info.className );
|
||||
{
|
||||
if (!info.className.empty()) {
|
||||
desc_writer.write( "class-name"_sr ).write( info.className );
|
||||
}
|
||||
if ( m_verbosity >= Verbosity::Normal ) {
|
||||
auto tag_writer = desc_writer.write( "tags"_sr ).writeArray();
|
||||
for ( auto const& tag : info.tags ) {
|
||||
tag_writer.write( tag.original );
|
||||
}
|
||||
}
|
||||
writeSourceInfo( desc_writer, info.lineInfo );
|
||||
if ( m_verbosity >= Verbosity::High) {
|
||||
writeSourceInfo( desc_writer, info.lineInfo );
|
||||
}
|
||||
}
|
||||
}
|
||||
void JsonReporter::listTags( std::vector<TagInfo> const& tags ) {
|
||||
|
||||
@@ -85,9 +85,6 @@ namespace Catch {
|
||||
std::stack<Writer> m_writers{};
|
||||
|
||||
bool m_startedListing = false;
|
||||
|
||||
// std::size_t m_sectionDepth = 0;
|
||||
// std::size_t m_sectionStarted = 0;
|
||||
};
|
||||
} // namespace Catch
|
||||
|
||||
|
||||
+60
-13
@@ -184,6 +184,13 @@ set_tests_properties(List::Tests::Output PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "[0-9]+ test cases"
|
||||
FAIL_REGULAR_EXPRESSION "Hidden Test"
|
||||
)
|
||||
|
||||
add_test(NAME VerbosityIsPerReporter COMMAND $<TARGET_FILE:SelfTest> --list-tests --verbosity high --reporter console::verbosity=quiet)
|
||||
set_tests_properties(VerbosityIsPerReporter
|
||||
PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION "\.cpp"
|
||||
)
|
||||
|
||||
# This should be equivalent to the old --list-test-names-only and be usable
|
||||
# with --input-file.
|
||||
add_test(NAME List::Tests::Quiet COMMAND $<TARGET_FILE:SelfTest> --list-tests --verbosity quiet)
|
||||
@@ -193,6 +200,7 @@ set_tests_properties(List::Tests::Quiet PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\"#1905 -- test spec parser properly clears internal state between compound tests\"[\r\n]"
|
||||
FAIL_REGULAR_EXPRESSION "[ \t]\"#1905 -- test spec parser properly clears internal state between compound tests\""
|
||||
)
|
||||
|
||||
add_test(NAME List::Tests::ExitCode COMMAND $<TARGET_FILE:SelfTest> --list-tests --verbosity high)
|
||||
add_test(NAME List::Tests::XmlOutput COMMAND $<TARGET_FILE:SelfTest> --list-tests --verbosity high -r xml)
|
||||
set_tests_properties(List::Tests::XmlOutput PROPERTIES
|
||||
@@ -200,6 +208,21 @@ set_tests_properties(List::Tests::XmlOutput PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION "[0-9]+ test cases"
|
||||
)
|
||||
|
||||
add_test(NAME List::Tests::Json::Normal COMMAND $<TARGET_FILE:SelfTest> --list-tests -r json)
|
||||
set_tests_properties(List::Tests::Json::Normal PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\"tags\": \\["
|
||||
FAIL_REGULAR_EXPRESSION "\"source-location\": {"
|
||||
)
|
||||
add_test(NAME List::Tests::Json::Quiet COMMAND $<TARGET_FILE:SelfTest> --list-tests -r json::verbosity=quiet)
|
||||
set_tests_properties(List::Tests::Json::Quiet PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\"name\": \""
|
||||
FAIL_REGULAR_EXPRESSION "\"tags\": \\["
|
||||
)
|
||||
add_test(NAME List::Tests::Json::High COMMAND $<TARGET_FILE:SelfTest> --list-tests -r json::verbosity=high)
|
||||
set_tests_properties(List::Tests::Json::High PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\"source-location\": {"
|
||||
)
|
||||
|
||||
add_test(NAME List::Tags::Output COMMAND $<TARGET_FILE:SelfTest> --list-tags)
|
||||
set_tests_properties(List::Tags::Output PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "[0-9]+ tags"
|
||||
@@ -210,6 +233,11 @@ set_tests_properties(List::Tags::XmlOutput PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "<Count>18</Count>"
|
||||
FAIL_REGULAR_EXPRESSION "[0-9]+ tags"
|
||||
)
|
||||
add_test(NAME List::Tags::JsonOutput COMMAND $<TARGET_FILE:SelfTest> --list-tags -r json)
|
||||
set_tests_properties(List::Tags::JsonOutput PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\"count\": 18"
|
||||
FAIL_REGULAR_EXPRESSION "[0-9]+ tags"
|
||||
)
|
||||
|
||||
add_test(NAME List::Reporters::Output COMMAND $<TARGET_FILE:SelfTest> --list-reporters)
|
||||
set_tests_properties(List::Reporters::Output PROPERTIES PASS_REGULAR_EXPRESSION "Available reporters:")
|
||||
@@ -219,6 +247,12 @@ set_tests_properties(List::Reporters::XmlOutput PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "<Name>compact</Name>"
|
||||
FAIL_REGULAR_EXPRESSION "Available reporters:"
|
||||
)
|
||||
add_test(NAME List::Reporters::JsonOutput COMMAND $<TARGET_FILE:SelfTest> --list-reporters -r json)
|
||||
set_tests_properties(List::Reporters::JsonOutput PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\"name\": \"compact\""
|
||||
FAIL_REGULAR_EXPRESSION "Available reporters:"
|
||||
)
|
||||
|
||||
|
||||
add_test(NAME List::Listeners::Output
|
||||
COMMAND
|
||||
@@ -243,6 +277,18 @@ set_tests_properties(List::Listeners::XmlOutput
|
||||
PASS_REGULAR_EXPRESSION "<RegisteredListeners>"
|
||||
FAIL_REGULAR_EXPRESSION "Registered listeners:"
|
||||
)
|
||||
add_test(NAME List::Listeners::JsonOutput
|
||||
COMMAND
|
||||
$<TARGET_FILE:SelfTest>
|
||||
--list-listeners
|
||||
--reporter json
|
||||
)
|
||||
set_tests_properties(List::Listeners::JsonOutput
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\"listeners\": \\["
|
||||
FAIL_REGULAR_EXPRESSION "Registered listeners:"
|
||||
)
|
||||
|
||||
|
||||
add_test(NAME NoAssertions COMMAND $<TARGET_FILE:SelfTest> -w NoAssertions "An empty test with no assertions")
|
||||
set_tests_properties(NoAssertions PROPERTIES PASS_REGULAR_EXPRESSION "No assertions in test case")
|
||||
@@ -440,19 +486,6 @@ set_tests_properties("Benchmarking::SkipBenchmarkMacros"
|
||||
FAIL_REGULAR_EXPRESSION "benchmark name"
|
||||
)
|
||||
|
||||
add_test(NAME "Benchmarking::FailureReporting::OptimizedOut"
|
||||
COMMAND
|
||||
$<TARGET_FILE:SelfTest> "Failing benchmarks" -c "empty" -r xml
|
||||
# This test only makes sense with the optimizer being enabled when
|
||||
# the tests are being compiled.
|
||||
CONFIGURATIONS Release
|
||||
)
|
||||
set_tests_properties("Benchmarking::FailureReporting::OptimizedOut"
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "could not measure benchmark\, maybe it was optimized away"
|
||||
FAIL_REGULAR_EXPRESSION "successes=\"1\""
|
||||
)
|
||||
|
||||
add_test(NAME "Benchmarking::FailureReporting::ThrowingBenchmark"
|
||||
COMMAND
|
||||
$<TARGET_FILE:SelfTest> "Failing benchmarks" -c "throw" -r xml
|
||||
@@ -625,6 +658,20 @@ if(CATCH_ENABLE_CMAKE_HELPER_TESTS)
|
||||
COST 240
|
||||
LABELS "uses-python"
|
||||
)
|
||||
|
||||
add_test(NAME "CMakeHelper::PrepareCommandFragment"
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}"
|
||||
"-DCATCH_ADD_TESTS_SCRIPT=${CATCH_DIR}/extras/CatchAddTests.cmake"
|
||||
-P "${CMAKE_CURRENT_LIST_DIR}/TestScripts/DiscoverTests/TestPrepareCommandFragment.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
|
||||
|
||||
+111
-24
@@ -21,6 +21,37 @@ set_tests_properties(TestSharding::OverlyLargeShardIndex
|
||||
PASS_REGULAR_EXPRESSION "The shard count \\(5\\) must be greater than the shard index \\(5\\)"
|
||||
)
|
||||
|
||||
set(CATCH_SHARDING_WARNING_REGEX "Warning: using sharding .* with random order")
|
||||
|
||||
add_test(
|
||||
NAME TestSharding::WarningOnRandomOrderWithoutFixedSeed
|
||||
COMMAND $<TARGET_FILE:SelfTest> --shard-index 0 --shard-count 2 --list-tests
|
||||
)
|
||||
set_tests_properties(TestSharding::WarningOnRandomOrderWithoutFixedSeed
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "${CATCH_SHARDING_WARNING_REGEX}"
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME TestSharding::NoWarningOnRandomOrderWithFixedSeed
|
||||
COMMAND $<TARGET_FILE:SelfTest> --shard-index 0 --shard-count 2 --rng-seed 12345 --list-tests
|
||||
)
|
||||
set_tests_properties(TestSharding::NoWarningOnRandomOrderWithFixedSeed
|
||||
PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION "${CATCH_SHARDING_WARNING_REGEX}"
|
||||
)
|
||||
|
||||
foreach(shardOrder decl lex)
|
||||
add_test(
|
||||
NAME TestSharding::NoWarningOn${shardOrder}OrderWithoutFixedSeed
|
||||
COMMAND $<TARGET_FILE:SelfTest> --shard-index 0 --shard-count 2 --order ${shardOrder} --list-tests
|
||||
)
|
||||
set_tests_properties(TestSharding::NoWarningOn${shardOrder}OrderWithoutFixedSeed
|
||||
PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION "${CATCH_SHARDING_WARNING_REGEX}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# The MinDuration reporting tests do not need separate compilation, but
|
||||
# they have non-trivial execution time, so they are categorized as
|
||||
# extra tests, so that they are run less.
|
||||
@@ -526,29 +557,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 +614,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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -190,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
|
||||
|
||||
@@ -188,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
|
||||
|
||||
@@ -631,7 +631,7 @@ Condition.tests.cpp:<line number>: passed: x == Approx( 1.3 ) for: 1.30000000000
|
||||
==
|
||||
Approx( 1.30000000000000004 )
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
|
||||
ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(WhatException{}) == "This exception has overridden what() method" for: "This exception has overridden what() method"
|
||||
@@ -654,11 +654,11 @@ Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedExcep
|
||||
Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, MessageMatches( !StartsWith( "::what" ) ) for: DerivedException::what matches "not starts with: "::what""
|
||||
Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 2 ), SpecialException, MessageMatches( StartsWith( "Special" ) ) for: SpecialException::what matches "starts with: "Special""
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expected exception" (case insensitive)
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), StartsWith( "expected" ) for: "expected exception" starts with: "expected"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), EndsWith( "exception" ) for: "expected exception" ends with: "exception"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "exCept" (case insensitive)
|
||||
ToString.tests.cpp:<line number>: passed: tos == tos for: { stringification failed with an exception: "Invalid" }
|
||||
==
|
||||
{ stringification failed with an exception: "Invalid" }
|
||||
@@ -1248,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
|
||||
<=
|
||||
@@ -1511,9 +1531,9 @@ CmdLine.tests.cpp:<line number>: passed: config.noThrow == false for: false == f
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications.empty() for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: !(cfg.hasTestFilters()) for: !false
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getReporterSpecs().size() == 1 for: 1 == 1
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getProcessedReporterSpecs().size() == 1 for: 1 == 1
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.hasTestFilters() for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.testSpec().matches(*fakeTestCase("notIncluded")) == false for: false == false
|
||||
@@ -1527,21 +1547,21 @@ CmdLine.tests.cpp:<line number>: passed: cfg.hasTestFilters() for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.testSpec().matches(*fakeTestCase("test1")) == false for: false == false
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.testSpec().matches(*fakeTestCase("alwaysIncluded")) for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: !result for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: result.errorMessage(), ContainsSubstring("Unrecognized reporter") for: "Unrecognized reporter, 'unsupported'. Check available with --list-reporters" contains: "Unrecognized reporter"
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "junit::out=output-junit.xml" }) for: {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "console" }) for: {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: !result for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: result.errorMessage(), ContainsSubstring("Only one reporter may have unspecified output file.") for: "Only one reporter may have unspecified output file." contains: "Only one reporter may have unspecified output file."
|
||||
CmdLine.tests.cpp:<line number>: passed: cli.parse({"test", "-b"}) for: {?}
|
||||
@@ -1676,7 +1696,7 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fa
|
||||
" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: console'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fakeTag"s) for: "{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -1693,7 +1713,7 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fak
|
||||
]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fake reporter"s) for: "{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -1708,7 +1728,7 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fak
|
||||
]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -1718,14 +1738,9 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fa
|
||||
"tests": [
|
||||
{
|
||||
"name": "fake test name",
|
||||
"class-name": "",
|
||||
"tags": [
|
||||
"fakeTestTag"
|
||||
],
|
||||
"source-location": {
|
||||
"filename": "fake-file.cpp",
|
||||
"line": 123456789
|
||||
}
|
||||
]
|
||||
}
|
||||
]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
@@ -1897,11 +1912,11 @@ ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify(arr) =
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "string" ) for: "this string contains 'abc' as a substring" contains: "string"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "string", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "string" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "abc" ) for: "this string contains 'abc' as a substring" contains: "abc"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "aBC" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "this" ) for: "this string contains 'abc' as a substring" starts with: "this"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "THIS" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( "substring" ) for: "this string contains 'abc' as a substring" ends with: "substring"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case insensitive)
|
||||
String.tests.cpp:<line number>: passed: empty.empty() for: true
|
||||
String.tests.cpp:<line number>: passed: empty.size() == 0 for: 0 == 0
|
||||
String.tests.cpp:<line number>: passed: std::strcmp( empty.data(), "" ) == 0 for: 0 == 0
|
||||
@@ -3000,7 +3015,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: 451 | 331 passed | 96 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2416 | 2215 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
|
||||
|
||||
|
||||
|
||||
@@ -629,7 +629,7 @@ Condition.tests.cpp:<line number>: passed: x == Approx( 1.3 ) for: 1.30000000000
|
||||
==
|
||||
Approx( 1.30000000000000004 )
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
|
||||
ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(WhatException{}) == "This exception has overridden what() method" for: "This exception has overridden what() method"
|
||||
@@ -652,11 +652,11 @@ Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedExcep
|
||||
Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, MessageMatches( !StartsWith( "::what" ) ) for: DerivedException::what matches "not starts with: "::what""
|
||||
Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 2 ), SpecialException, MessageMatches( StartsWith( "Special" ) ) for: SpecialException::what matches "starts with: "Special""
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expected exception" (case insensitive)
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), StartsWith( "expected" ) for: "expected exception" starts with: "expected"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), EndsWith( "exception" ) for: "expected exception" ends with: "exception"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "exCept" (case insensitive)
|
||||
ToString.tests.cpp:<line number>: passed: tos == tos for: { stringification failed with an exception: "Invalid" }
|
||||
==
|
||||
{ stringification failed with an exception: "Invalid" }
|
||||
@@ -1246,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
|
||||
<=
|
||||
@@ -1509,9 +1529,9 @@ CmdLine.tests.cpp:<line number>: passed: config.noThrow == false for: false == f
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications.empty() for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: !(cfg.hasTestFilters()) for: !false
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getReporterSpecs().size() == 1 for: 1 == 1
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getProcessedReporterSpecs().size() == 1 for: 1 == 1
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} } for: {?} == {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.hasTestFilters() for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.testSpec().matches(*fakeTestCase("notIncluded")) == false for: false == false
|
||||
@@ -1525,21 +1545,21 @@ CmdLine.tests.cpp:<line number>: passed: cfg.hasTestFilters() for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.testSpec().matches(*fakeTestCase("test1")) == false for: false == false
|
||||
CmdLine.tests.cpp:<line number>: passed: cfg.testSpec().matches(*fakeTestCase("alwaysIncluded")) for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: !result for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: result.errorMessage(), ContainsSubstring("Unrecognized reporter") for: "Unrecognized reporter, 'unsupported'. Check available with --list-reporters" contains: "Unrecognized reporter"
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
CmdLine.tests.cpp:<line number>: passed: cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "junit::out=output-junit.xml" }) for: {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "console" }) for: {?}
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
CmdLine.tests.cpp:<line number>: passed: !result for: true
|
||||
CmdLine.tests.cpp:<line number>: passed: result.errorMessage(), ContainsSubstring("Only one reporter may have unspecified output file.") for: "Only one reporter may have unspecified output file." contains: "Only one reporter may have unspecified output file."
|
||||
CmdLine.tests.cpp:<line number>: passed: cli.parse({"test", "-b"}) for: {?}
|
||||
@@ -1674,7 +1694,7 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fa
|
||||
" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: console'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fakeTag"s) for: "{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -1691,7 +1711,7 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fak
|
||||
]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fake reporter"s) for: "{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -1706,7 +1726,7 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring("fak
|
||||
]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -1716,14 +1736,9 @@ Reporters.tests.cpp:<line number>: passed: listingString, ContainsSubstring( "fa
|
||||
"tests": [
|
||||
{
|
||||
"name": "fake test name",
|
||||
"class-name": "",
|
||||
"tags": [
|
||||
"fakeTestTag"
|
||||
],
|
||||
"source-location": {
|
||||
"filename": "fake-file.cpp",
|
||||
"line": 123456789
|
||||
}
|
||||
]
|
||||
}
|
||||
]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON'
|
||||
Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
|
||||
@@ -1890,11 +1905,11 @@ ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify(arr) =
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "string" ) for: "this string contains 'abc' as a substring" contains: "string"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "string", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "string" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "abc" ) for: "this string contains 'abc' as a substring" contains: "abc"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "aBC" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "this" ) for: "this string contains 'abc' as a substring" starts with: "this"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "THIS" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( "substring" ) for: "this string contains 'abc' as a substring" ends with: "substring"
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case insensitive)
|
||||
String.tests.cpp:<line number>: passed: empty.empty() for: true
|
||||
String.tests.cpp:<line number>: passed: empty.size() == 0 for: 0 == 0
|
||||
String.tests.cpp:<line number>: passed: std::strcmp( empty.data(), "" ) == 0 for: 0 == 0
|
||||
@@ -2989,7 +3004,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: 451 | 331 passed | 96 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2416 | 2215 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: 451 | 349 passed | 76 failed | 7 skipped | 19 failed as expected
|
||||
assertions: 2394 | 2215 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
|
||||
|
||||
|
||||
@@ -4521,7 +4521,7 @@ Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" equals: "this string contains
|
||||
'abc' as a substring" (case insensitive)
|
||||
'ABC' as a substring" (case insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Equals string matcher
|
||||
@@ -4679,7 +4679,7 @@ Exception.tests.cpp:<line number>
|
||||
Exception.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THROWS_WITH( thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"expected exception" equals: "expected exception" (case insensitive)
|
||||
"expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception messages can be tested for
|
||||
@@ -4706,7 +4706,7 @@ with expansion:
|
||||
Exception.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THROWS_WITH( thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"expected exception" contains: "except" (case insensitive)
|
||||
"expected exception" contains: "exCept" (case insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception thrown inside stringify does not fail the test
|
||||
@@ -8219,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
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -9880,7 +9977,7 @@ with expansion:
|
||||
1 == 1
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
CHECK( cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} } )
|
||||
CHECK( cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} } )
|
||||
with expansion:
|
||||
{?} == {?}
|
||||
|
||||
@@ -9890,7 +9987,7 @@ with expansion:
|
||||
1 == 1
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
CHECK( cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} } )
|
||||
CHECK( cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} } )
|
||||
with expansion:
|
||||
{?} == {?}
|
||||
|
||||
@@ -9994,7 +10091,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10016,7 +10113,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10038,7 +10135,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10079,7 +10176,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10101,7 +10198,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10122,7 +10219,7 @@ with expansion:
|
||||
{?}
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
|
||||
@@ -10141,7 +10238,7 @@ with expansion:
|
||||
{?}
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
|
||||
@@ -11136,7 +11233,7 @@ Reporters.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THAT( listingString, ContainsSubstring("fakeTag"s) )
|
||||
with expansion:
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -11176,7 +11273,7 @@ Reporters.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THAT( listingString, ContainsSubstring("fake reporter"s) )
|
||||
with expansion:
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -11214,7 +11311,7 @@ Reporters.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) )
|
||||
with expansion:
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -11224,14 +11321,9 @@ with expansion:
|
||||
"tests": [
|
||||
{
|
||||
"name": "fake test name",
|
||||
"class-name": "",
|
||||
"tags": [
|
||||
"fakeTestTag"
|
||||
],
|
||||
"source-location": {
|
||||
"filename": "fake-file.cpp",
|
||||
"line": 123456789
|
||||
}
|
||||
]
|
||||
}
|
||||
]" ( contains: "fake test name" and contains: "fakeTestTag" )
|
||||
with message:
|
||||
@@ -12103,7 +12195,7 @@ with expansion:
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" contains: "abc" (case
|
||||
"this string contains 'abc' as a substring" contains: "aBC" (case
|
||||
insensitive)
|
||||
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
@@ -12114,7 +12206,7 @@ with expansion:
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" starts with: "this" (case
|
||||
"this string contains 'abc' as a substring" starts with: "THIS" (case
|
||||
insensitive)
|
||||
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
@@ -12125,7 +12217,7 @@ with expansion:
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" ends with: " substring" (case
|
||||
"this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case
|
||||
insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -20134,6 +20226,6 @@ Misc.tests.cpp:<line number>
|
||||
Misc.tests.cpp:<line number>: PASSED:
|
||||
|
||||
===============================================================================
|
||||
test cases: 451 | 331 passed | 96 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2416 | 2215 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
|
||||
|
||||
|
||||
@@ -4519,7 +4519,7 @@ Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" equals: "this string contains
|
||||
'abc' as a substring" (case insensitive)
|
||||
'ABC' as a substring" (case insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Equals string matcher
|
||||
@@ -4677,7 +4677,7 @@ Exception.tests.cpp:<line number>
|
||||
Exception.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THROWS_WITH( thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"expected exception" equals: "expected exception" (case insensitive)
|
||||
"expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception messages can be tested for
|
||||
@@ -4704,7 +4704,7 @@ with expansion:
|
||||
Exception.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THROWS_WITH( thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"expected exception" contains: "except" (case insensitive)
|
||||
"expected exception" contains: "exCept" (case insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception thrown inside stringify does not fail the test
|
||||
@@ -8217,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
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -9878,7 +9975,7 @@ with expansion:
|
||||
1 == 1
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
CHECK( cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} } )
|
||||
CHECK( cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} } )
|
||||
with expansion:
|
||||
{?} == {?}
|
||||
|
||||
@@ -9888,7 +9985,7 @@ with expansion:
|
||||
1 == 1
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
CHECK( cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} } )
|
||||
CHECK( cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} } )
|
||||
with expansion:
|
||||
{?} == {?}
|
||||
|
||||
@@ -9992,7 +10089,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10014,7 +10111,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10036,7 +10133,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10077,7 +10174,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10099,7 +10196,7 @@ with message:
|
||||
result.errorMessage() := ""
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?} } == { {?} }
|
||||
with message:
|
||||
@@ -10120,7 +10217,7 @@ with expansion:
|
||||
{?}
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
|
||||
@@ -10139,7 +10236,7 @@ with expansion:
|
||||
{?}
|
||||
|
||||
CmdLine.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } } )
|
||||
REQUIRE( config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } } )
|
||||
with expansion:
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
|
||||
@@ -11134,7 +11231,7 @@ Reporters.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THAT( listingString, ContainsSubstring("fakeTag"s) )
|
||||
with expansion:
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -11174,7 +11271,7 @@ Reporters.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THAT( listingString, ContainsSubstring("fake reporter"s) )
|
||||
with expansion:
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -11212,7 +11309,7 @@ Reporters.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) )
|
||||
with expansion:
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -11222,14 +11319,9 @@ with expansion:
|
||||
"tests": [
|
||||
{
|
||||
"name": "fake test name",
|
||||
"class-name": "",
|
||||
"tags": [
|
||||
"fakeTestTag"
|
||||
],
|
||||
"source-location": {
|
||||
"filename": "fake-file.cpp",
|
||||
"line": 123456789
|
||||
}
|
||||
]
|
||||
}
|
||||
]" ( contains: "fake test name" and contains: "fakeTestTag" )
|
||||
with message:
|
||||
@@ -12096,7 +12188,7 @@ with expansion:
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" contains: "abc" (case
|
||||
"this string contains 'abc' as a substring" contains: "aBC" (case
|
||||
insensitive)
|
||||
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
@@ -12107,7 +12199,7 @@ with expansion:
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" starts with: "this" (case
|
||||
"this string contains 'abc' as a substring" starts with: "THIS" (case
|
||||
insensitive)
|
||||
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
@@ -12118,7 +12210,7 @@ with expansion:
|
||||
Matchers.tests.cpp:<line number>: PASSED:
|
||||
CHECK_THAT( testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) )
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" ends with: " substring" (case
|
||||
"this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case
|
||||
insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -20123,6 +20215,6 @@ Misc.tests.cpp:<line number>
|
||||
Misc.tests.cpp:<line number>: PASSED:
|
||||
|
||||
===============================================================================
|
||||
test cases: 451 | 331 passed | 96 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2416 | 2215 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="2428" 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]"/>
|
||||
@@ -774,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="2428" 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]"/>
|
||||
@@ -773,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}"/>
|
||||
|
||||
@@ -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}"/>
|
||||
|
||||
@@ -1131,7 +1131,7 @@ ok {test-number} - x == Approx( 1.3 ) for: 1.30000000000000027 == Approx( 1.3000
|
||||
# Equals
|
||||
ok {test-number} - testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
|
||||
# Equals
|
||||
ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" (case insensitive)
|
||||
# Equals string matcher
|
||||
not ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
|
||||
# Equals string matcher
|
||||
@@ -1169,7 +1169,7 @@ ok {test-number} - throwsSpecialException( 2 ), SpecialException, MessageMatches
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expected exception" (case insensitive)
|
||||
ok {test-number} - thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), StartsWith( "expected" ) for: "expected exception" starts with: "expected"
|
||||
# Exception messages can be tested for
|
||||
@@ -1177,7 +1177,7 @@ ok {test-number} - thisThrows(), EndsWith( "exception" ) for: "expected exceptio
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
|
||||
ok {test-number} - thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "exCept" (case insensitive)
|
||||
# Exception thrown inside stringify does not fail the test
|
||||
ok {test-number} - tos == tos for: { stringification failed with an exception: "Invalid" } == { stringification failed with an exception: "Invalid" }
|
||||
# Exceptions matchers
|
||||
@@ -2054,6 +2054,20 @@ ok {test-number} - sstream.str() == "\"\\r\"" for: ""\r"" == ""\r""
|
||||
ok {test-number} - sstream.str() == "\"\\t\"" for: ""\t"" == ""\t""
|
||||
# JsonWriter escapes characters in strings properly
|
||||
ok {test-number} - sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n""
|
||||
# JsonWriter serializes non-finite FP using Python spelling - double
|
||||
ok {test-number} - sstream.str() == "NaN" for: "NaN" == "NaN"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - double
|
||||
ok {test-number} - sstream.str() == "Infinity" for: "Infinity" == "Infinity"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - double
|
||||
ok {test-number} - sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - float
|
||||
ok {test-number} - sstream.str() == "NaN" for: "NaN" == "NaN"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - float
|
||||
ok {test-number} - sstream.str() == "Infinity" for: "Infinity" == "Infinity"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - float
|
||||
ok {test-number} - sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
|
||||
# JsonWriter serializes numbers independently of the global locale
|
||||
ok {test-number} - 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 ] }" )
|
||||
# Lambdas in assertions
|
||||
ok {test-number} - []() { return true; }() for: true
|
||||
# Less-than inequalities with different epsilons
|
||||
@@ -2501,11 +2515,11 @@ ok {test-number} - !(cfg.hasTestFilters()) for: !false
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getReporterSpecs().size() == 1 for: 1 == 1
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} } for: {?} == {?}
|
||||
ok {test-number} - cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} } for: {?} == {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getProcessedReporterSpecs().size() == 1 for: 1 == 1
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} } for: {?} == {?}
|
||||
ok {test-number} - cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} } for: {?} == {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?}
|
||||
# Process can be configured on command line
|
||||
@@ -2533,15 +2547,15 @@ ok {test-number} - cfg.testSpec().matches(*fakeTestCase("alwaysIncluded")) for:
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - !result for: true
|
||||
# Process can be configured on command line
|
||||
@@ -2549,19 +2563,19 @@ ok {test-number} - result.errorMessage(), ContainsSubstring("Unrecognized report
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "junit::out=output-junit.xml" }) for: {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "console" }) for: {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - !result for: true
|
||||
# Process can be configured on command line
|
||||
@@ -2753,15 +2767,15 @@ ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && Cont
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON'
|
||||
ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 2, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON'
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON'
|
||||
ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 2, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON'
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tests": [ { "name": "fake test name", "class-name": "", "tags": [ "fakeTestTag" ], "source-location": { "filename": "fake-file.cpp", "line": 123456789 } } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON'
|
||||
ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 2, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tests": [ { "name": "fake test name", "tags": [ "fakeTestTag" ] } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON'
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
@@ -2902,15 +2916,15 @@ ok {test-number} - testStringForMatching(), ContainsSubstring( "string", Catch::
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), ContainsSubstring( "abc" ) for: "this string contains 'abc' as a substring" contains: "abc"
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "aBC" (case insensitive)
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), StartsWith( "this" ) for: "this string contains 'abc' as a substring" starts with: "this"
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "THIS" (case insensitive)
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), EndsWith( "substring" ) for: "this string contains 'abc' as a substring" ends with: "substring"
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case insensitive)
|
||||
# StringRef
|
||||
ok {test-number} - empty.empty() for: true
|
||||
# StringRef
|
||||
@@ -4851,5 +4865,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
|
||||
ok {test-number} -
|
||||
# xmlentitycheck
|
||||
ok {test-number} -
|
||||
1..2428
|
||||
1..2435
|
||||
|
||||
|
||||
@@ -1129,7 +1129,7 @@ ok {test-number} - x == Approx( 1.3 ) for: 1.30000000000000027 == Approx( 1.3000
|
||||
# Equals
|
||||
ok {test-number} - testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
|
||||
# Equals
|
||||
ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" (case insensitive)
|
||||
# Equals string matcher
|
||||
not ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
|
||||
# Equals string matcher
|
||||
@@ -1167,7 +1167,7 @@ ok {test-number} - throwsSpecialException( 2 ), SpecialException, MessageMatches
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expected exception" (case insensitive)
|
||||
ok {test-number} - thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), StartsWith( "expected" ) for: "expected exception" starts with: "expected"
|
||||
# Exception messages can be tested for
|
||||
@@ -1175,7 +1175,7 @@ ok {test-number} - thisThrows(), EndsWith( "exception" ) for: "expected exceptio
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
|
||||
# Exception messages can be tested for
|
||||
ok {test-number} - thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
|
||||
ok {test-number} - thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "exCept" (case insensitive)
|
||||
# Exception thrown inside stringify does not fail the test
|
||||
ok {test-number} - tos == tos for: { stringification failed with an exception: "Invalid" } == { stringification failed with an exception: "Invalid" }
|
||||
# Exceptions matchers
|
||||
@@ -2052,6 +2052,20 @@ ok {test-number} - sstream.str() == "\"\\r\"" for: ""\r"" == ""\r""
|
||||
ok {test-number} - sstream.str() == "\"\\t\"" for: ""\t"" == ""\t""
|
||||
# JsonWriter escapes characters in strings properly
|
||||
ok {test-number} - sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n""
|
||||
# JsonWriter serializes non-finite FP using Python spelling - double
|
||||
ok {test-number} - sstream.str() == "NaN" for: "NaN" == "NaN"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - double
|
||||
ok {test-number} - sstream.str() == "Infinity" for: "Infinity" == "Infinity"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - double
|
||||
ok {test-number} - sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - float
|
||||
ok {test-number} - sstream.str() == "NaN" for: "NaN" == "NaN"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - float
|
||||
ok {test-number} - sstream.str() == "Infinity" for: "Infinity" == "Infinity"
|
||||
# JsonWriter serializes non-finite FP using Python spelling - float
|
||||
ok {test-number} - sstream.str() == "-Infinity" for: "-Infinity" == "-Infinity"
|
||||
# JsonWriter serializes numbers independently of the global locale
|
||||
ok {test-number} - 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 ] }" )
|
||||
# Lambdas in assertions
|
||||
ok {test-number} - []() { return true; }() for: true
|
||||
# Less-than inequalities with different epsilons
|
||||
@@ -2499,11 +2513,11 @@ ok {test-number} - !(cfg.hasTestFilters()) for: !false
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getReporterSpecs().size() == 1 for: 1 == 1
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} } for: {?} == {?}
|
||||
ok {test-number} - cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} } for: {?} == {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getProcessedReporterSpecs().size() == 1 for: 1 == 1
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} } for: {?} == {?}
|
||||
ok {test-number} - cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} } for: {?} == {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?}
|
||||
# Process can be configured on command line
|
||||
@@ -2531,15 +2545,15 @@ ok {test-number} - cfg.testSpec().matches(*fakeTestCase("alwaysIncluded")) for:
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - !result for: true
|
||||
# Process can be configured on command line
|
||||
@@ -2547,19 +2561,19 @@ ok {test-number} - result.errorMessage(), ContainsSubstring("Unrecognized report
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - result for: {?} with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } } for: { {?} } == { {?} } with 1 message: 'result.errorMessage() := ""'
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "junit::out=output-junit.xml" }) for: {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "console" }) for: {?}
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
ok {test-number} - config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } } for: { {?}, {?} } == { {?}, {?} }
|
||||
# Process can be configured on command line
|
||||
ok {test-number} - !result for: true
|
||||
# Process can be configured on command line
|
||||
@@ -2751,15 +2765,15 @@ ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && Cont
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON'
|
||||
ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 2, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON'
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON'
|
||||
ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 2, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON'
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tests": [ { "name": "fake test name", "class-name": "", "tags": [ "fakeTestTag" ], "source-location": { "filename": "fake-file.cpp", "line": 123456789 } } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON'
|
||||
ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 2, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "<version>" }, "listings": { "tests": [ { "name": "fake test name", "tags": [ "fakeTestTag" ] } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON'
|
||||
# Reporter's write listings to provided stream
|
||||
ok {test-number} - !(factories.empty()) for: !false
|
||||
# Reporter's write listings to provided stream
|
||||
@@ -2895,15 +2909,15 @@ ok {test-number} - testStringForMatching(), ContainsSubstring( "string", Catch::
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), ContainsSubstring( "abc" ) for: "this string contains 'abc' as a substring" contains: "abc"
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "aBC" (case insensitive)
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), StartsWith( "this" ) for: "this string contains 'abc' as a substring" starts with: "this"
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "THIS" (case insensitive)
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), EndsWith( "substring" ) for: "this string contains 'abc' as a substring" ends with: "substring"
|
||||
# String matchers
|
||||
ok {test-number} - testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
|
||||
ok {test-number} - testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case insensitive)
|
||||
# StringRef
|
||||
ok {test-number} - empty.empty() for: true
|
||||
# StringRef
|
||||
@@ -4840,5 +4854,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
|
||||
ok {test-number} -
|
||||
# xmlentitycheck
|
||||
ok {test-number} -
|
||||
1..2428
|
||||
1..2435
|
||||
|
||||
|
||||
@@ -470,6 +470,12 @@
|
||||
##teamcity[testFinished name='JsonWriter' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter escapes characters in strings properly']
|
||||
##teamcity[testFinished name='JsonWriter escapes characters in strings properly' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter serializes non-finite FP using Python spelling - double']
|
||||
##teamcity[testFinished name='JsonWriter serializes non-finite FP using Python spelling - double' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter serializes non-finite FP using Python spelling - float']
|
||||
##teamcity[testFinished name='JsonWriter serializes non-finite FP using Python spelling - float' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter serializes numbers independently of the global locale']
|
||||
##teamcity[testFinished name='JsonWriter serializes numbers independently of the global locale' duration="{duration}"]
|
||||
##teamcity[testStarted name='Lambdas in assertions']
|
||||
##teamcity[testFinished name='Lambdas in assertions' duration="{duration}"]
|
||||
##teamcity[testStarted name='Less-than inequalities with different epsilons']
|
||||
|
||||
@@ -470,6 +470,12 @@
|
||||
##teamcity[testFinished name='JsonWriter' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter escapes characters in strings properly']
|
||||
##teamcity[testFinished name='JsonWriter escapes characters in strings properly' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter serializes non-finite FP using Python spelling - double']
|
||||
##teamcity[testFinished name='JsonWriter serializes non-finite FP using Python spelling - double' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter serializes non-finite FP using Python spelling - float']
|
||||
##teamcity[testFinished name='JsonWriter serializes non-finite FP using Python spelling - float' duration="{duration}"]
|
||||
##teamcity[testStarted name='JsonWriter serializes numbers independently of the global locale']
|
||||
##teamcity[testFinished name='JsonWriter serializes numbers independently of the global locale' duration="{duration}"]
|
||||
##teamcity[testStarted name='Lambdas in assertions']
|
||||
##teamcity[testFinished name='Lambdas in assertions' duration="{duration}"]
|
||||
##teamcity[testStarted name='Less-than inequalities with different epsilons']
|
||||
|
||||
@@ -5102,7 +5102,7 @@ Approx( 1.30000000000000004 )
|
||||
testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
|
||||
"this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
@@ -5295,7 +5295,7 @@ Approx( 1.30000000000000004 )
|
||||
thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"expected exception" equals: "expected exception" (case insensitive)
|
||||
"expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
@@ -5330,7 +5330,7 @@ Approx( 1.30000000000000004 )
|
||||
thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"expected exception" contains: "except" (case insensitive)
|
||||
"expected exception" contains: "exCept" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="4" failures="0" expectedFailures="0" skipped="false"/>
|
||||
@@ -9894,6 +9894,102 @@ Approx( 3.14150000000000018 )
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="JsonWriter serializes non-finite FP using Python spelling - double" tags="[floating-point][JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Section name="NaN" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "NaN"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"NaN" == "NaN"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Pos nf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"Infinity" == "Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Neg inf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "-Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"-Infinity" == "-Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="JsonWriter serializes non-finite FP using Python spelling - float" tags="[floating-point][JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Section name="NaN" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "NaN"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"NaN" == "NaN"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Pos nf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"Infinity" == "Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Neg inf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "-Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"-Infinity" == "-Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="JsonWriter serializes numbers independently of the global locale" tags="[JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
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}" )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"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
|
||||
]
|
||||
}" )
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="Lambdas in assertions" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
|
||||
<Original>
|
||||
@@ -11880,7 +11976,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} }
|
||||
cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} == {?}
|
||||
@@ -11896,7 +11992,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} }
|
||||
cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} == {?}
|
||||
@@ -12036,7 +12132,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12064,7 +12160,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12092,7 +12188,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12142,7 +12238,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12170,7 +12266,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12193,7 +12289,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
@@ -12218,7 +12314,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
@@ -13323,7 +13419,7 @@ Approx( 0.98999999999999999 )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -13360,7 +13456,7 @@ Approx( 0.98999999999999999 )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -13395,7 +13491,7 @@ Approx( 0.98999999999999999 )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -13405,14 +13501,9 @@ Approx( 0.98999999999999999 )
|
||||
"tests": [
|
||||
{
|
||||
"name": "fake test name",
|
||||
"class-name": "",
|
||||
"tags": [
|
||||
"fakeTestTag"
|
||||
],
|
||||
"source-location": {
|
||||
"filename": "fake-file.cpp",
|
||||
"line": 123456789
|
||||
}
|
||||
]
|
||||
}
|
||||
]" ( contains: "fake test name" and contains: "fakeTestTag" )
|
||||
</Expanded>
|
||||
@@ -14266,7 +14357,7 @@ Message from section two
|
||||
testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" contains: "abc" (case insensitive)
|
||||
"this string contains 'abc' as a substring" contains: "aBC" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@@ -14282,7 +14373,7 @@ Message from section two
|
||||
testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" starts with: "this" (case insensitive)
|
||||
"this string contains 'abc' as a substring" starts with: "THIS" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@@ -14298,7 +14389,7 @@ Message from section two
|
||||
testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
|
||||
"this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
@@ -23385,6 +23476,6 @@ Approx( -1.95996398454005449 )
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<OverallResults successes="2215" failures="158" expectedFailures="43" skips="12"/>
|
||||
<OverallResultsCases successes="331" failures="96" expectedFailures="18" skips="6"/>
|
||||
<OverallResults successes="2222" failures="158" expectedFailures="43" skips="12"/>
|
||||
<OverallResultsCases successes="334" failures="96" expectedFailures="18" skips="6"/>
|
||||
</Catch2TestRun>
|
||||
|
||||
@@ -5102,7 +5102,7 @@ Approx( 1.30000000000000004 )
|
||||
testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
|
||||
"this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
@@ -5295,7 +5295,7 @@ Approx( 1.30000000000000004 )
|
||||
thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"expected exception" equals: "expected exception" (case insensitive)
|
||||
"expected exception" equals: "expecteD Exception" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
@@ -5330,7 +5330,7 @@ Approx( 1.30000000000000004 )
|
||||
thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"expected exception" contains: "except" (case insensitive)
|
||||
"expected exception" contains: "exCept" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="4" failures="0" expectedFailures="0" skipped="false"/>
|
||||
@@ -9894,6 +9894,102 @@ Approx( 3.14150000000000018 )
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="JsonWriter serializes non-finite FP using Python spelling - double" tags="[floating-point][JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Section name="NaN" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "NaN"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"NaN" == "NaN"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Pos nf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"Infinity" == "Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Neg inf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "-Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"-Infinity" == "-Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="JsonWriter serializes non-finite FP using Python spelling - float" tags="[floating-point][JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Section name="NaN" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "NaN"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"NaN" == "NaN"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Pos nf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"Infinity" == "Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<Section name="Neg inf" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
sstream.str() == "-Infinity"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"-Infinity" == "-Infinity"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="1" failures="0" expectedFailures="0" skipped="false"/>
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="JsonWriter serializes numbers independently of the global locale" tags="[JsonWriter]" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Json.tests.cpp" >
|
||||
<Original>
|
||||
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}" )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"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
|
||||
]
|
||||
}" )
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="Lambdas in assertions" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
|
||||
<Original>
|
||||
@@ -11880,7 +11976,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {} }
|
||||
cfg.getReporterSpecs()[0] == Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} == {?}
|
||||
@@ -11896,7 +11992,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, {} }
|
||||
cfg.getProcessedReporterSpecs()[0] == Catch::ProcessedReporterSpec{ expectedReporter, std::string{}, Catch::ColourMode::PlatformDefault, Catch::Verbosity::Normal, {} }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} == {?}
|
||||
@@ -12036,7 +12132,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "console", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12064,7 +12160,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12092,7 +12188,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "junit", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12142,7 +12238,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "out.txt"s, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12170,7 +12266,7 @@ Approx( 1.21999999999999997 )
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?} } == { {?} }
|
||||
@@ -12193,7 +12289,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "junit", "output-junit.xml"s, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "junit", "output-junit.xml"s, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
@@ -12218,7 +12314,7 @@ Approx( 1.21999999999999997 )
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
|
||||
<Original>
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {} }, { "console", {}, {}, {} } }
|
||||
config.reporterSpecifications == vec_Specs{ { "xml", "output.xml"s, {}, {}, {} }, { "console", {}, {}, {}, {} } }
|
||||
</Original>
|
||||
<Expanded>
|
||||
{ {?}, {?} } == { {?}, {?} }
|
||||
@@ -13323,7 +13419,7 @@ Approx( 0.98999999999999999 )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -13360,7 +13456,7 @@ Approx( 0.98999999999999999 )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -13395,7 +13491,7 @@ Approx( 0.98999999999999999 )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"metadata": {
|
||||
"name": "",
|
||||
"rng-seed": 1234,
|
||||
@@ -13405,14 +13501,9 @@ Approx( 0.98999999999999999 )
|
||||
"tests": [
|
||||
{
|
||||
"name": "fake test name",
|
||||
"class-name": "",
|
||||
"tags": [
|
||||
"fakeTestTag"
|
||||
],
|
||||
"source-location": {
|
||||
"filename": "fake-file.cpp",
|
||||
"line": 123456789
|
||||
}
|
||||
]
|
||||
}
|
||||
]" ( contains: "fake test name" and contains: "fakeTestTag" )
|
||||
</Expanded>
|
||||
@@ -14266,7 +14357,7 @@ Message from section two
|
||||
testStringForMatching(), ContainsSubstring( "aBC", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" contains: "abc" (case insensitive)
|
||||
"this string contains 'abc' as a substring" contains: "aBC" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@@ -14282,7 +14373,7 @@ Message from section two
|
||||
testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" starts with: "this" (case insensitive)
|
||||
"this string contains 'abc' as a substring" starts with: "THIS" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@@ -14298,7 +14389,7 @@ Message from section two
|
||||
testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No )
|
||||
</Original>
|
||||
<Expanded>
|
||||
"this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
|
||||
"this string contains 'abc' as a substring" ends with: " SuBsTrInG" (case insensitive)
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
@@ -23384,6 +23475,6 @@ Approx( -1.95996398454005449 )
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<OverallResults successes="2215" failures="158" expectedFailures="43" skips="12"/>
|
||||
<OverallResultsCases successes="331" failures="96" expectedFailures="18" skips="6"/>
|
||||
<OverallResults successes="2222" failures="158" expectedFailures="43" skips="12"/>
|
||||
<OverallResultsCases successes="334" failures="96" expectedFailures="18" skips="6"/>
|
||||
</Catch2TestRun>
|
||||
|
||||
@@ -58,12 +58,13 @@ TEST_CASE( "Process can be configured on command line", "[config][command-line]"
|
||||
|
||||
CHECK( cfg.getReporterSpecs().size() == 1 );
|
||||
CHECK( cfg.getReporterSpecs()[0] ==
|
||||
Catch::ReporterSpec{ expectedReporter, {}, {}, {} } );
|
||||
Catch::ReporterSpec{ expectedReporter, {}, {}, {}, {} } );
|
||||
CHECK( cfg.getProcessedReporterSpecs().size() == 1 );
|
||||
CHECK( cfg.getProcessedReporterSpecs()[0] ==
|
||||
Catch::ProcessedReporterSpec{ expectedReporter,
|
||||
std::string{},
|
||||
Catch::ColourMode::PlatformDefault,
|
||||
Catch::Verbosity::Normal,
|
||||
{} } );
|
||||
}
|
||||
|
||||
@@ -108,7 +109,7 @@ TEST_CASE( "Process can be configured on command line", "[config][command-line]"
|
||||
CHECK(result);
|
||||
|
||||
REQUIRE( config.reporterSpecifications ==
|
||||
vec_Specs{ { "console", {}, {}, {} } } );
|
||||
vec_Specs{ { "console", {}, {}, {}, {} } } );
|
||||
}
|
||||
SECTION("-r/xml") {
|
||||
auto result = cli.parse({"test", "-r", "xml"});
|
||||
@@ -116,7 +117,7 @@ TEST_CASE( "Process can be configured on command line", "[config][command-line]"
|
||||
CHECK(result);
|
||||
|
||||
REQUIRE( config.reporterSpecifications ==
|
||||
vec_Specs{ { "xml", {}, {}, {} } } );
|
||||
vec_Specs{ { "xml", {}, {}, {}, {} } } );
|
||||
}
|
||||
SECTION("--reporter/junit") {
|
||||
auto result = cli.parse({"test", "--reporter", "junit"});
|
||||
@@ -124,7 +125,7 @@ TEST_CASE( "Process can be configured on command line", "[config][command-line]"
|
||||
CHECK(result);
|
||||
|
||||
REQUIRE( config.reporterSpecifications ==
|
||||
vec_Specs{ { "junit", {}, {}, {} } } );
|
||||
vec_Specs{ { "junit", {}, {}, {}, {} } } );
|
||||
}
|
||||
SECTION("must match one of the available ones") {
|
||||
auto result = cli.parse({"test", "--reporter", "unsupported"});
|
||||
@@ -137,27 +138,27 @@ TEST_CASE( "Process can be configured on command line", "[config][command-line]"
|
||||
CAPTURE(result.errorMessage());
|
||||
CHECK(result);
|
||||
REQUIRE( config.reporterSpecifications ==
|
||||
vec_Specs{ { "console", "out.txt"s, {}, {} } } );
|
||||
vec_Specs{ { "console", "out.txt"s, {}, {}, {} } } );
|
||||
}
|
||||
SECTION("With Windows-like absolute path as output file") {
|
||||
auto result = cli.parse({ "test", "-r", "console::out=C:\\Temp\\out.txt" });
|
||||
CAPTURE(result.errorMessage());
|
||||
CHECK(result);
|
||||
REQUIRE( config.reporterSpecifications ==
|
||||
vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {} } } );
|
||||
vec_Specs{ { "console", "C:\\Temp\\out.txt"s, {}, {}, {} } } );
|
||||
}
|
||||
SECTION("Multiple reporters") {
|
||||
SECTION("All with output files") {
|
||||
CHECK(cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "junit::out=output-junit.xml" }));
|
||||
REQUIRE( config.reporterSpecifications ==
|
||||
vec_Specs{ { "xml", "output.xml"s, {}, {} },
|
||||
{ "junit", "output-junit.xml"s, {}, {} } } );
|
||||
vec_Specs{ { "xml", "output.xml"s, {}, {}, {} },
|
||||
{ "junit", "output-junit.xml"s, {}, {}, {} } } );
|
||||
}
|
||||
SECTION("Mixed output files and default output") {
|
||||
CHECK(cli.parse({ "test", "-r", "xml::out=output.xml", "-r", "console" }));
|
||||
REQUIRE( config.reporterSpecifications ==
|
||||
vec_Specs{ { "xml", "output.xml"s, {}, {} },
|
||||
{ "console", {}, {}, {} } } );
|
||||
vec_Specs{ { "xml", "output.xml"s, {}, {}, {} },
|
||||
{ "console", {}, {}, {}, {} } } );
|
||||
}
|
||||
SECTION("cannot have multiple reporters with default output") {
|
||||
auto result = cli.parse({ "test", "-r", "console", "-r", "xml::out=output.xml", "-r", "junit" });
|
||||
@@ -472,15 +473,34 @@ TEST_CASE( "Parse rng seed in different formats", "[approvals][cli][rng-seed]" )
|
||||
CAPTURE( seed_string );
|
||||
|
||||
auto result = cli.parse( { "tests", "--rng-seed", seed_string } );
|
||||
|
||||
REQUIRE( result );
|
||||
REQUIRE( config.rngSeed == seed_value );
|
||||
|
||||
Catch::Config cfg{config};
|
||||
REQUIRE( cfg.rngSeed() == seed_value );
|
||||
REQUIRE( cfg.rngSeedWasFixed() );
|
||||
}
|
||||
SECTION( "time seed is not considered fixed" ) {
|
||||
auto result = cli.parse( { "tests", "--rng-seed", "time" } );
|
||||
REQUIRE( result );
|
||||
|
||||
Catch::Config cfg{config};
|
||||
REQUIRE_FALSE( cfg.rngSeedWasFixed() );
|
||||
}
|
||||
SECTION( "random-device seed is not considered fixed" ) {
|
||||
auto result = cli.parse( { "tests", "--rng-seed", "random-device" } );
|
||||
REQUIRE( result );
|
||||
|
||||
Catch::Config cfg{config};
|
||||
REQUIRE_FALSE( cfg.rngSeedWasFixed() );
|
||||
}
|
||||
SECTION( "Error cases" ) {
|
||||
auto seed_string =
|
||||
GENERATE( "0xSEED", "999999999999", "08888", "BEEF", "123 456" );
|
||||
CAPTURE( seed_string );
|
||||
REQUIRE_FALSE( cli.parse( { "tests", "--rng-seed", seed_string } ) );
|
||||
|
||||
Catch::Config cfg{config};
|
||||
REQUIRE_FALSE( cfg.rngSeedWasFixed() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,20 @@ TEST_CASE( "Parsing colour mode", "[cli][colour][approvals]" ) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE( "Parsing verbosity", "[cli][colour][approvals]" ) {
|
||||
using Catch::Detail::stringToVerbosity;
|
||||
using Catch::Verbosity;
|
||||
SECTION( "Valid strings" ) {
|
||||
REQUIRE( stringToVerbosity( "quiet" ) == Verbosity::Quiet );
|
||||
REQUIRE( stringToVerbosity( "normal" ) == Verbosity::Normal );
|
||||
REQUIRE( stringToVerbosity( "high" ) == Verbosity::High );
|
||||
}
|
||||
SECTION( "Wrong strings" ) {
|
||||
REQUIRE_FALSE( stringToVerbosity( "QUIET" ) );
|
||||
REQUIRE_FALSE( stringToVerbosity( "medium" ) );
|
||||
REQUIRE_FALSE( stringToVerbosity( "vvv" ) );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing reporter specs", "[cli][reporter-spec][approvals]") {
|
||||
using Catch::parseReporterSpec;
|
||||
@@ -71,12 +85,13 @@ TEST_CASE("Parsing reporter specs", "[cli][reporter-spec][approvals]") {
|
||||
|
||||
SECTION( "Correct specs" ) {
|
||||
REQUIRE( parseReporterSpec( "someReporter" ) ==
|
||||
ReporterSpec( "someReporter"s, {}, {}, {} ) );
|
||||
ReporterSpec( "someReporter"s, {}, {}, {}, {} ) );
|
||||
REQUIRE( parseReporterSpec( "otherReporter::Xk=v::out=c:\\blah" ) ==
|
||||
ReporterSpec(
|
||||
"otherReporter"s, "c:\\blah"s, {}, { { "Xk"s, "v"s } } ) );
|
||||
"otherReporter"s, "c:\\blah"s, {}, {}, { { "Xk"s, "v"s } } ) );
|
||||
REQUIRE( parseReporterSpec( "diffReporter::Xk1=v1::Xk2==v2" ) ==
|
||||
ReporterSpec( "diffReporter",
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{ { "Xk1"s, "v1"s }, { "Xk2"s, "=v2"s } } ) );
|
||||
@@ -85,7 +100,15 @@ TEST_CASE("Parsing reporter specs", "[cli][reporter-spec][approvals]") {
|
||||
ReporterSpec( "Foo:bar:reporter",
|
||||
{},
|
||||
Catch::ColourMode::ANSI,
|
||||
{},
|
||||
{ { "Xk 1"s, "v 1"s }, { "Xk2"s, "v:3"s } } ) );
|
||||
REQUIRE(
|
||||
parseReporterSpec( "my:reporter::X1=V1::verbosity=high::X2=V2" ) ==
|
||||
ReporterSpec( "my:reporter",
|
||||
{},
|
||||
{},
|
||||
Catch::Verbosity::High,
|
||||
{ { "X1"s, "V1"s }, { "X2"s, "V2"s } } ) );
|
||||
}
|
||||
|
||||
SECTION( "Bad specs" ) {
|
||||
@@ -107,5 +130,9 @@ TEST_CASE("Parsing reporter specs", "[cli][reporter-spec][approvals]") {
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::Xa=" ) );
|
||||
// non-key value later field
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::Xab" ) );
|
||||
// Invalid verbosity value
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::verbosity=medium" ) );
|
||||
// Duplicated verbosity
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::verbosity=high::X1=X2::verbosity=high" ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,9 +429,6 @@ TEST_CASE("run benchmark", "[benchmark][approvals]") {
|
||||
}
|
||||
|
||||
TEST_CASE("Failing benchmarks", "[!benchmark][.approvals]") {
|
||||
SECTION("empty", "Benchmark that has been optimized away (because it is empty)") {
|
||||
BENCHMARK("Empty benchmark") {};
|
||||
}
|
||||
SECTION("throw", "Benchmark that throws an exception") {
|
||||
BENCHMARK("Throwing benchmark") {
|
||||
throw "just a plain literal, bleh";
|
||||
|
||||
@@ -7,11 +7,14 @@
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/benchmark/catch_benchmark.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/internal/catch_jsonwriter.hpp>
|
||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
@@ -20,6 +23,27 @@ namespace {
|
||||
return os << "custom";
|
||||
}
|
||||
|
||||
// Obviously wrong numpunct if it is actually used by the JSON writer.
|
||||
class TestNumpunct : public std::numpunct<char> {
|
||||
char do_decimal_point() const override { return '?'; }
|
||||
char do_thousands_sep() const override { return '!'; }
|
||||
std::string do_grouping() const override { return "\1"; }
|
||||
std::string do_truename() const override { return "real"; }
|
||||
std::string do_falsename() const override { return "fake"; }
|
||||
};
|
||||
|
||||
class LocaleGuard {
|
||||
std::locale m_previous_locale;
|
||||
|
||||
public:
|
||||
explicit LocaleGuard( std::locale const& locale ):
|
||||
m_previous_locale{ std::locale() } {
|
||||
std::locale::global( locale );
|
||||
}
|
||||
|
||||
~LocaleGuard() { std::locale::global( m_previous_locale ); }
|
||||
};
|
||||
|
||||
TEST_CASE( "JsonWriter", "[JSON][JsonWriter]" ) {
|
||||
|
||||
std::stringstream stream;
|
||||
@@ -152,6 +176,54 @@ TEST_CASE( "JsonWriter escapes characters in strings properly", "[JsonWriter]" )
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE( "JsonWriter serializes numbers independently of the global locale",
|
||||
"[JsonWriter]" ) {
|
||||
using Catch::Matchers::ContainsSubstring;
|
||||
|
||||
LocaleGuard locale_guard{ std::locale{ std::locale(), new TestNumpunct } };
|
||||
|
||||
std::stringstream sstream;
|
||||
{
|
||||
auto writer = Catch::JsonValueWriter{ sstream }.writeObject();
|
||||
writer.write( "double" ).write( 1.5 );
|
||||
writer.write( "int" ).write( 1234567 );
|
||||
writer.write( "bool-1" ).write( true );
|
||||
writer.write( "bool-2" ).write( false );
|
||||
writer.write( "array" ).writeArray().write( 2.5 ).write( 1234567 );
|
||||
}
|
||||
|
||||
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}" ) );
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE( "JsonWriter serializes non-finite FP using Python spelling",
|
||||
"[JsonWriter][floating-point]",
|
||||
float,
|
||||
double ) {
|
||||
std::stringstream sstream;
|
||||
|
||||
SECTION( "NaN" ) {
|
||||
Catch::JsonValueWriter{ sstream }.write(
|
||||
std::numeric_limits<TestType>::quiet_NaN() );
|
||||
REQUIRE( sstream.str() == "NaN" );
|
||||
}
|
||||
SECTION( "Pos nf" ) {
|
||||
Catch::JsonValueWriter{ sstream }.write(
|
||||
std::numeric_limits<TestType>::infinity() );
|
||||
REQUIRE( sstream.str() == "Infinity" );
|
||||
}
|
||||
SECTION( "Neg inf" ) {
|
||||
Catch::JsonValueWriter{ sstream }.write(
|
||||
-std::numeric_limits<TestType>::infinity() );
|
||||
REQUIRE( sstream.str() == "-Infinity" );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE( "JsonWriter benchmarks", "[JsonWriter][!benchmark]" ) {
|
||||
const auto input_length = GENERATE( as<size_t>{}, 10, 100, 10'000 );
|
||||
std::string test_input( input_length, 'a' );
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace {
|
||||
&config,
|
||||
Catch::Detail::make_unique<StringIStream>(),
|
||||
Catch::ColourMode::None,
|
||||
Catch::Verbosity::Normal,
|
||||
{} };
|
||||
}
|
||||
}
|
||||
@@ -114,7 +115,7 @@ TEST_CASE( "Reporter's write listings to provided stream", "[reporters]" ) {
|
||||
cfg_data.rngSeed = 1234;
|
||||
Catch::Config config( cfg_data );
|
||||
auto reporter = factory.second->create( Catch::ReporterConfig{
|
||||
&config, CATCH_MOVE( sstream ), Catch::ColourMode::None, {} } );
|
||||
&config, CATCH_MOVE( sstream ), Catch::ColourMode::None, Catch::Verbosity::Normal, {} } );
|
||||
|
||||
DYNAMIC_SECTION( factory.first << " reporter lists tags" ) {
|
||||
std::vector<Catch::TagInfo> tags(1);
|
||||
|
||||
@@ -15,6 +15,8 @@ if(CMAKE_VERSION GREATER_EQUAL 3.27)
|
||||
endif()
|
||||
catch_discover_tests(
|
||||
tests
|
||||
TEST_PREFIX " prefix "
|
||||
TEST_SUFFIX " suffix "
|
||||
ADD_TAGS_AS_LABELS
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
${extra_args}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
# Unit tests for `split_json_array` helper in `extras/CatchAddTests.cmake`.
|
||||
#
|
||||
# Yes, we are at the stage where the script helpers need unit tests.
|
||||
#
|
||||
# Run as
|
||||
# cmake -DCATCH_ADD_TESTS_SCRIPT=/path/to/extras/CatchAddTests.cmake \
|
||||
# -P TestDecomposeJsonArray.cmake
|
||||
|
||||
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
if(NOT DEFINED CATCH_ADD_TESTS_SCRIPT)
|
||||
message(FATAL_ERROR "Missing argument `CATCH_ADD_TESTS_SCRIPT`")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CATCH_ADD_TESTS_SCRIPT}")
|
||||
message(FATAL_ERROR "Cannot find CatchAddTests.cmake at '${CATCH_ADD_TESTS_SCRIPT}'")
|
||||
endif()
|
||||
|
||||
# Pull in the helper functions. Without `TEST_EXECUTABLE` being defined,
|
||||
# `catch_discover_tests_impl` is not called.
|
||||
include("${CATCH_ADD_TESTS_SCRIPT}")
|
||||
|
||||
set(_failures 0)
|
||||
|
||||
# Parses out test names from provided listings and returns them through `out_var`.
|
||||
# Semicolons in test names are escaped as `@SEMI@`.
|
||||
function(decomposed_names listing_var out_var)
|
||||
split_json_array(${listing_var} elements)
|
||||
set(names "")
|
||||
foreach(element IN LISTS elements)
|
||||
magic_unescape_chars(element)
|
||||
string(JSON name ERROR_VARIABLE err GET "${element}" "name")
|
||||
if(NOT err STREQUAL "NOTFOUND")
|
||||
set(${out_var} "PARSE-ERROR" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
string(REPLACE ";" "@SEMI@" name "${name}")
|
||||
list(APPEND names "${name}")
|
||||
endforeach()
|
||||
set(${out_var} "${names}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Assert that decomposing the provided listing returns the expected list
|
||||
# of (test) names.
|
||||
# Semicolons inside expected names must be escaped as `@SEMI@`.
|
||||
function(expect_names description listing_var expected_names)
|
||||
decomposed_names(${listing_var} actual_names)
|
||||
if(actual_names STREQUAL expected_names)
|
||||
message(" [PASS] ${description}")
|
||||
else()
|
||||
message(" [FAIL] ${description}")
|
||||
message(" expected: ${expected_names}")
|
||||
message(" actual: ${actual_names}")
|
||||
math(EXPR _n "${_failures} + 1")
|
||||
set(_failures "${_n}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Assert that the tags in one decomposed element are preserved exactly.
|
||||
# Semicolons in expected tags have to be escaped as `@SEMI@`.
|
||||
function(expect_tags description listing_var element_index expected_tags)
|
||||
split_json_array(${listing_var} elements)
|
||||
list(GET elements ${element_index} element)
|
||||
magic_unescape_chars(element)
|
||||
|
||||
string(JSON tags ERROR_VARIABLE err GET "${element}" "tags")
|
||||
if(NOT err STREQUAL "NOTFOUND")
|
||||
set(actual_tags "PARSE-ERROR")
|
||||
else()
|
||||
string(JSON tag_count LENGTH "${tags}")
|
||||
set(actual_tags "")
|
||||
if(tag_count GREATER 0)
|
||||
math(EXPR last_tag "${tag_count} - 1")
|
||||
foreach(tag_index RANGE ${last_tag})
|
||||
string(JSON tag GET "${tags}" ${tag_index})
|
||||
string(REPLACE ";" "@SEMI@" tag "${tag}")
|
||||
list(APPEND actual_tags "${tag}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(actual_tags STREQUAL expected_tags)
|
||||
message(" [PASS] ${description}")
|
||||
else()
|
||||
message(" [FAIL] ${description}")
|
||||
message(" expected: ${expected_tags}")
|
||||
message(" actual: ${actual_tags}")
|
||||
math(EXPR _n "${_failures} + 1")
|
||||
set(_failures "${_n}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Convenience for building a minimal-but-realistic (pretty-printed) listing.
|
||||
function(make_listing out_var)
|
||||
set(objects "")
|
||||
foreach(name IN LISTS ARGN)
|
||||
# Build each object by hand; the names passed in are already JSON-safe.
|
||||
string(APPEND objects
|
||||
" {\n"
|
||||
" \"class-name\" : \"\",\n"
|
||||
" \"name\" : \"${name}\",\n"
|
||||
" \"tags\" : [ \"[tag]\" ]\n"
|
||||
" },\n")
|
||||
endforeach()
|
||||
string(REGEX REPLACE ",\n$" "\n" objects "${objects}")
|
||||
set(${out_var} "[\n${objects}]" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
message(STATUS "Running split_json_array correctness tests")
|
||||
|
||||
# There are 2 main difficulties in the array decomposition that we need
|
||||
# to check for:
|
||||
# 1) Names/tags that contain the expected element boundary (`}<ws>*,<ws>*{`)
|
||||
# inside them, and thus are split into invalid JSON.
|
||||
# 2) Names/tags that contain CMake-relevant characters (e.g. semicolon,
|
||||
# which is list separator) and thus cause issues when processing the
|
||||
# string splits.
|
||||
|
||||
make_listing(listing "")
|
||||
expect_names("No tests" listing "")
|
||||
|
||||
make_listing(listing "plain")
|
||||
expect_names("Single test" listing "plain")
|
||||
|
||||
make_listing(listing "n1" "n2" "n3")
|
||||
expect_names("Multiple tests" listing "n1;n2;n3")
|
||||
|
||||
make_listing(listing "before },{ after" "second")
|
||||
expect_names("The element boundary in a test name" listing "before },{ after;second")
|
||||
|
||||
make_listing(listing "},{" "next")
|
||||
expect_names("Test name is just the boundary" listing "},{;next")
|
||||
|
||||
make_listing(listing "a},{b},{c" "x" "y},{z")
|
||||
expect_names("Test name has multiple boundaries" listing "a},{b},{c;x;y},{z")
|
||||
|
||||
# Listings with semicolons have to be built by hand, or CMake would mess
|
||||
# them up before decomposition.
|
||||
set(listing "[{\"name\":\"has;semicolon\",\"tags\":[]},{\"name\":\"and;another;one\",\"tags\":[]}]")
|
||||
expect_names("Test names with semicolons" listing "has@SEMI@semicolon;and@SEMI@another@SEMI@one")
|
||||
|
||||
set(listing "[{\"name\":\"C:\\\\path\\\\file\",\"tags\":[]},{\"name\":\"plain\",\"tags\":[]}]")
|
||||
expect_names("Test names with backslashes" listing "C:\\path\\file;plain")
|
||||
|
||||
set(listing "[{\"name\":\"compact1\",\"tags\":[]},{\"name\":\"compact2\",\"tags\":[]}]")
|
||||
expect_names("compact json" listing "compact1;compact2")
|
||||
|
||||
make_listing(listing "Then } , { we }\t,\t{ concatenate } , { them } ,{back},{" "second")
|
||||
expect_names("Whitespace around commas in test names survive split"
|
||||
listing "Then } , { we }\t,\t{ concatenate } , { them } ,{back},{;second")
|
||||
|
||||
make_listing(listing "}},{{" "second")
|
||||
expect_names("Doubled up boundary braces in names" listing "}},{{;second")
|
||||
|
||||
# Square brackets and array likes in the test names.
|
||||
set(listing "[{\"name\":\"Arrays [{},{}] wheee\",\"tags\":[\"also},{tags\",\"tag;with;semicolons\"]},{\"name\":\"n\",\"tags\":[]}]")
|
||||
expect_names("array-like substring in name" listing "Arrays [{},{}] wheee;n")
|
||||
expect_tags("boundary-like and semicolon tags are preserved" listing 0
|
||||
"also},{tags;tag@SEMI@with@SEMI@semicolons")
|
||||
expect_tags("empty tags are preserved" listing 1 "")
|
||||
|
||||
# Listings with square brackets have to be built by hand, or CMake would
|
||||
# mess them up before decomposition.
|
||||
set(listing "[{\"name\":\"[\",\"tags\":[]},{\"name\":\"middle\",\"tags\":[]},{\"name\":\"]\",\"tags\":[]}]")
|
||||
expect_names("unmatched square brackets" listing "[;middle;]")
|
||||
|
||||
set(listing "[{\"name\":\"a[b]c\",\"tags\":[]},{\"name\":\"[open\",\"tags\":[]},{\"name\":\"close]\",\"tags\":[]},{\"name\":\"[]\",\"tags\":[]}]")
|
||||
expect_names("Test names with mess of brackets" listing "a[b]c;[open;close];[]")
|
||||
|
||||
# Without careful handling, these could be evaluated as variables.
|
||||
make_listing(listing
|
||||
"curly \${NOT_A_VAR}"
|
||||
"env \$ENV{HOME}"
|
||||
"cache \$CACHE{FOO}"
|
||||
"genex \$<CONFIG>"
|
||||
"bare \$ and \$\$ and \${ unterminated")
|
||||
expect_names("Test names with dollars and various brackets (CMake vars)" listing
|
||||
"curly \${NOT_A_VAR};env \$ENV{HOME};cache \$CACHE{FOO};genex \$<CONFIG>;bare \$ and \$\$ and \${ unterminated")
|
||||
|
||||
# Listings with semicolons have to be built by hand, or CMake would mess
|
||||
# them up before decomposition.
|
||||
# This is just a huge mess of everything to see if anything shakes loose.
|
||||
set(listing "[{\"name\":\"\$ENV{X};[weird]{},{ mix \$<0:no> \${VAR} };,{ }},{{\",\"tags\":[]},{\"name\":\"after\",\"tags\":[]}]")
|
||||
expect_names("combined mega-case" listing
|
||||
"\$ENV{X}@SEMI@[weird]{},{ mix \$<0:no> \${VAR} }@SEMI@,{ }},{{;after")
|
||||
|
||||
if(_failures GREATER 0)
|
||||
message(FATAL_ERROR "${_failures} decomposition test(s) failed")
|
||||
endif()
|
||||
|
||||
message(STATUS "All split_json_array correctness tests passed")
|
||||
@@ -0,0 +1,61 @@
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
# Unit tests for `prepare_command_fragment` helper in `extras/CatchAddTests.cmake`.
|
||||
#
|
||||
# Yes, we are at the stage where the script helpers need unit tests.
|
||||
#
|
||||
# Run as
|
||||
# cmake -DCATCH_ADD_TESTS_SCRIPT=/path/to/extras/CatchAddTests.cmake \
|
||||
# -P TestPrepareCommandFragment.cmake
|
||||
|
||||
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
if(NOT DEFINED CATCH_ADD_TESTS_SCRIPT)
|
||||
message(FATAL_ERROR "Missing argument `CATCH_ADD_TESTS_SCRIPT`")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CATCH_ADD_TESTS_SCRIPT}")
|
||||
message(FATAL_ERROR "Cannot find CatchAddTests.cmake at '${CATCH_ADD_TESTS_SCRIPT}'")
|
||||
endif()
|
||||
|
||||
# Pull in the helper functions. Without `TEST_EXECUTABLE` being defined,
|
||||
# `catch_discover_tests_impl` is not called.
|
||||
include("${CATCH_ADD_TESTS_SCRIPT}")
|
||||
|
||||
set(_failures 0)
|
||||
|
||||
function(expect_equal description actual expected)
|
||||
if(actual STREQUAL expected)
|
||||
message(" [PASS] ${description}")
|
||||
else()
|
||||
message(" [FAIL] ${description}")
|
||||
message(" expected: [${expected}]")
|
||||
message(" actual: [${actual}]")
|
||||
math(EXPR _n "${_failures} + 1")
|
||||
set(_failures "${_n}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
prepare_command_fragment(test_fragment SimpleName /path/to/tests)
|
||||
expect_equal("Simple arg, no quotes" "${test_fragment}" " SimpleName /path/to/tests")
|
||||
|
||||
prepare_command_fragment(test_fragment "Name with spaces")
|
||||
expect_equal("Spaces in arg, needs quotes" "${test_fragment}" " [==[Name with spaces]==]")
|
||||
|
||||
prepare_command_fragment(test_fragment Foo PROPERTIES LABELS "tagA\;tagB\;tagC")
|
||||
expect_equal("semicolons in argument are kept and quoted"
|
||||
"${test_fragment}" " Foo PROPERTIES LABELS [==[tagA\;tagB\;tagC]==]")
|
||||
|
||||
set(test_fragment "PRE-EXISTING")
|
||||
prepare_command_fragment(test_fragment Foo PROPERTIES BAR baz)
|
||||
expect_equal("out var does no accumulate commands"
|
||||
"${test_fragment}" " Foo PROPERTIES BAR baz")
|
||||
|
||||
|
||||
|
||||
if(_failures GREATER 0)
|
||||
message(FATAL_ERROR "${_failures} prepare_command_fragment test(s) failed")
|
||||
else()
|
||||
message(STATUS "All prepare_command_fragment tests passed")
|
||||
endif()
|
||||
@@ -7,11 +7,13 @@
|
||||
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import tempfile
|
||||
from collections import namedtuple
|
||||
from typing import List
|
||||
|
||||
@@ -19,6 +21,11 @@ TestInfo = namedtuple('TestInfo', ['name', 'tags'])
|
||||
|
||||
cmake_version_regex = re.compile(r'cmake version (\d+)\.(\d+)\.(\d+)')
|
||||
|
||||
# Note that these both intentionally include preceding/trailing space,
|
||||
# which should not get stripped.
|
||||
CTEST_NAME_PREFIX = ' prefix '
|
||||
CTEST_NAME_SUFFIX = ' suffix '
|
||||
|
||||
def get_cmake_version():
|
||||
result = subprocess.run(['cmake', '--version'],
|
||||
capture_output = True,
|
||||
@@ -64,7 +71,6 @@ def build_project(sources_dir, output_base_path, catch2_path):
|
||||
return build_dir
|
||||
|
||||
|
||||
|
||||
def get_test_names(build_path: str) -> List[TestInfo]:
|
||||
# For now we assume that Windows builds are done using MSBuild under
|
||||
# Debug configuration. This means that we need to add "Debug" folder
|
||||
@@ -72,16 +78,21 @@ def get_test_names(build_path: str) -> List[TestInfo]:
|
||||
config_path = "Debug" if os.name == 'nt' else ""
|
||||
full_path = os.path.join(build_path, config_path, 'tests')
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
fname = f'{tmpdir}/listing-output.json'
|
||||
cmd = [full_path,
|
||||
'--list-tests',
|
||||
'--reporter', 'json',
|
||||
'--out', fname
|
||||
]
|
||||
result = subprocess.run(cmd,
|
||||
capture_output = False,
|
||||
check = True,
|
||||
text = True)
|
||||
with open(fname, mode='r', encoding='utf-8') as file:
|
||||
test_listing = json.load(file)
|
||||
|
||||
cmd = [full_path, '--reporter', 'json', '--list-tests']
|
||||
result = subprocess.run(cmd,
|
||||
capture_output = True,
|
||||
check = True,
|
||||
text = True)
|
||||
|
||||
test_listing = json.loads(result.stdout)
|
||||
|
||||
assert test_listing['version'] == 1
|
||||
assert test_listing['version'] == 2
|
||||
|
||||
tests = []
|
||||
for test in test_listing['listings']['tests']:
|
||||
@@ -91,15 +102,24 @@ def get_test_names(build_path: str) -> List[TestInfo]:
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
def get_ctest_listing(build_path):
|
||||
old_path = os.getcwd()
|
||||
os.chdir(build_path)
|
||||
|
||||
cmd = ['ctest', '-C', 'debug', '--show-only=json-v1']
|
||||
result = subprocess.run(cmd,
|
||||
capture_output = True,
|
||||
check = True,
|
||||
text = True)
|
||||
try:
|
||||
result = subprocess.run(cmd,
|
||||
capture_output = True,
|
||||
check = True,
|
||||
text = True)
|
||||
except subprocess.CalledProcessError as err:
|
||||
print('Error when getting output from CTest')
|
||||
print(f'cmd: {err.cmd}')
|
||||
print(f'stderr: {err.stderr}')
|
||||
print(f'stdout: {err.stdout}')
|
||||
exit(4)
|
||||
|
||||
os.chdir(old_path)
|
||||
return result.stdout
|
||||
|
||||
@@ -123,6 +143,7 @@ def extract_tests_from_ctest(ctest_output) -> List[TestInfo]:
|
||||
|
||||
return test_infos
|
||||
|
||||
|
||||
def check_DL_PATHS(ctest_output):
|
||||
ctest_response = json.loads(ctest_output)
|
||||
tests = ctest_response['tests']
|
||||
@@ -132,6 +153,98 @@ def check_DL_PATHS(ctest_output):
|
||||
if property['name'] == 'ENVIRONMENT_MODIFICATION':
|
||||
assert len(property['value']) == 2, f"The test provides 2 arguments to DL_PATHS, but instead found {len(property['value'])}"
|
||||
|
||||
|
||||
def add_test_list_extractor(build_path: str) -> str:
|
||||
# The actual CTest script file has one of two names:
|
||||
# * `<target>-<short-hash>_tests.cmake` on single-config generators
|
||||
# * `<target>-<short-hash>_tests-<Config>.cmake` on multi-config generators
|
||||
#
|
||||
# We know the target name (`tests`), so we glob for the hash part
|
||||
patterns = [
|
||||
os.path.join(build_path, 'tests-*_tests.cmake'),
|
||||
os.path.join(build_path, 'tests-*_tests-Debug.cmake'),
|
||||
]
|
||||
matches = []
|
||||
for pattern in patterns:
|
||||
matches.extend(glob.glob(pattern))
|
||||
if len(matches) != 1:
|
||||
print(f"Found {len(matches)} CTest files in '{build_path}'. Expected only 1.")
|
||||
exit(5)
|
||||
test_script_file = matches[0]
|
||||
basename = os.path.basename(test_script_file)
|
||||
extractor_fname = os.path.join(build_path, 'extractor.cmake')
|
||||
with open(extractor_fname, 'w') as f:
|
||||
f.write(fr"""
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
cmake_policy(VERSION 3.19...4.4)
|
||||
|
||||
# This dummies out the `add_test` and `set_tests_properties` commands
|
||||
# inside the CTest script, so we can include it during CMake script call.
|
||||
macro(add_test)
|
||||
endmacro()
|
||||
macro(set_tests_properties)
|
||||
endmacro()
|
||||
|
||||
include(${{CMAKE_CURRENT_LIST_DIR}}/{basename})
|
||||
|
||||
list(LENGTH tests_TESTS num_tests)
|
||||
message(STATUS "NUM TESTS: ${{num_tests}}")
|
||||
|
||||
# '[' and ']' were escaped into ASCII 2 and 3 respectively, we have to
|
||||
# unescape them back here. Note that this has to be done per-element,
|
||||
# or CMake's list parsing breaks (which is why they were escaped).
|
||||
string(ASCII 2 _LeftBracketListingEscape)
|
||||
string(ASCII 3 _RightBracketListingEscape)
|
||||
|
||||
foreach(test IN LISTS tests_TESTS)
|
||||
string(REPLACE "${{_LeftBracketListingEscape}}" "[" test "${{test}}")
|
||||
string(REPLACE "${{_RightBracketListingEscape}}" "]" test "${{test}}")
|
||||
string(REPLACE "\\" "\\\\" test "${{test}}")
|
||||
string(REPLACE "\r" "\\r" test "${{test}}")
|
||||
string(REPLACE "\n" "\\n" test "${{test}}")
|
||||
message(STATUS "TEST_NAME: ${{test}}")
|
||||
endforeach()
|
||||
""")
|
||||
return extractor_fname
|
||||
|
||||
|
||||
def extract_tests_list_from_ctest_script(build_path: str) -> List[str]:
|
||||
extractor = add_test_list_extractor(build_path)
|
||||
|
||||
cmd = ['cmake', '-P', extractor]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd,
|
||||
capture_output = True,
|
||||
check = True,
|
||||
text = True)
|
||||
except subprocess.CalledProcessError as err:
|
||||
print('Error when calling CTest test extractor')
|
||||
print(f'cmd: {err.cmd}')
|
||||
print(f'stderr: {err.stderr}')
|
||||
print(f'stdout: {err.stdout}')
|
||||
exit(4)
|
||||
|
||||
lines = [x for x in result.stdout.split('\n') if x.strip()]
|
||||
test_num_line = lines[0]
|
||||
test_lines = lines[1:]
|
||||
|
||||
test_num_prefix = '-- NUM TESTS: '
|
||||
assert test_num_prefix in test_num_line, test_num_line
|
||||
test_num_line = test_num_line[len(test_num_prefix):]
|
||||
num_tests = int(test_num_line)
|
||||
|
||||
assert num_tests == len(test_lines), len(test_lines)
|
||||
test_name_prefix = '-- TEST_NAME: '
|
||||
assert all(test_name_prefix in x for x in test_lines)
|
||||
test_names = [x[len(test_name_prefix):] for x in test_lines]
|
||||
|
||||
# Unescape the names, so that names with literal newlines have newlines in them again
|
||||
test_names = [x.encode('utf-8').decode('unicode-escape') for x in test_names]
|
||||
return test_names
|
||||
|
||||
|
||||
def escape_catch2_test_names(infos: List[TestInfo]):
|
||||
escaped = []
|
||||
for info in infos:
|
||||
@@ -141,6 +254,7 @@ def escape_catch2_test_names(infos: List[TestInfo]):
|
||||
escaped.append(TestInfo(name, info.tags))
|
||||
return escaped
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 3:
|
||||
print(f'Usage: {sys.argv[0]} path-to-catch2-cml output-path')
|
||||
@@ -151,7 +265,8 @@ if __name__ == '__main__':
|
||||
|
||||
build_path = build_project(sources_dir, output_base_path, catch2_path)
|
||||
|
||||
catch_test_names = escape_catch2_test_names(get_test_names(build_path))
|
||||
raw_catch_test_names = get_test_names(build_path)
|
||||
catch_test_names = escape_catch2_test_names(raw_catch_test_names)
|
||||
ctest_output = get_ctest_listing(build_path)
|
||||
ctest_test_names = extract_tests_from_ctest(ctest_output)
|
||||
|
||||
@@ -168,7 +283,20 @@ if __name__ == '__main__':
|
||||
if mismatched:
|
||||
print(f"Found {mismatched} mismatched tests catch test names and ctest test commands!")
|
||||
exit(1)
|
||||
print(f"{len(catch_test_names)} tests matched")
|
||||
print(f"{len(catch_test_names)} tests matched in CTest listing")
|
||||
|
||||
test_list_names = sorted(extract_tests_list_from_ctest_script(build_path))
|
||||
expected_names = sorted(CTEST_NAME_PREFIX + info.name + CTEST_NAME_SUFFIX for info in raw_catch_test_names)
|
||||
if test_list_names != expected_names:
|
||||
print("TEST_LIST variable (tests_TESTS) does not match Catch2 test listing!")
|
||||
for name in test_list_names:
|
||||
if name not in expected_names:
|
||||
print(f" TEST_LIST name '{name}' not in Catch2 listing")
|
||||
for name in expected_names:
|
||||
if name not in test_list_names:
|
||||
print(f" Catch2 name '{name}' not in TEST_LIST")
|
||||
exit(1)
|
||||
print(f"{len(test_list_names)} tests matched in TEST_LIST variable")
|
||||
|
||||
cmake_version = get_cmake_version()
|
||||
if cmake_version >= (3, 27):
|
||||
|
||||
@@ -8,6 +8,24 @@
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
|
||||
struct PrintsWhenConstructed {
|
||||
PrintsWhenConstructed() {
|
||||
std::cout << "Hello\n";
|
||||
std::cerr << "Holla\n";
|
||||
std::fprintf(stdout, "Hullo\n");
|
||||
std::fprintf(stderr, "Hillo\n");
|
||||
}
|
||||
};
|
||||
|
||||
static PrintsWhenConstructed instance;
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("@Script[C:\\EPM1A]=x;\"SCALA_ZERO:\"", "[script regressions]"){}
|
||||
TEST_CASE("Some test") {}
|
||||
TEST_CASE( "Let's have a test case with a long name. Longer. No, even longer. "
|
||||
@@ -21,3 +39,30 @@ public:
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(TestCaseFixture, "A test case as method", "[tagstagstags]") {}
|
||||
|
||||
TEST_CASE("Unclosed right ) parenthesis") {}
|
||||
TEST_CASE("Unclosed left ( parenthesis") {}
|
||||
|
||||
TEST_CASE( "Newlines\nAnd\rOther\n\tWhitespace", "[whitespace-going-wild]" ) {}
|
||||
TEST_CASE( "Escaped \\n newline and \\r other whitespace", "[whitespace-going-wild]") {}
|
||||
|
||||
// Some JSON-like and JSON-adjacent characters and substrings in the test names/tags
|
||||
// This serves to test that the parse-json-via-string-splitting hack in
|
||||
// catch_discover_tests works properly.
|
||||
TEST_CASE( "We split on variants of },{ in name" ) {}
|
||||
TEST_CASE( "Then }\t, { we }\t,\t{ concatenate } , { them } ,{back},{" ) {}
|
||||
TEST_CASE( "}},{{" ) {}
|
||||
TEST_CASE( "Arrays [{},{}] wheee", "[also},{tags]" ) {}
|
||||
TEST_CASE( "Let's add semicolon into the mix ;},{;},{};,{}" ) {}
|
||||
TEST_CASE( "[", "[unmatched-square-bracket]" ) {}
|
||||
TEST_CASE( "]", "[unmatched-square-bracket]" ) {}
|
||||
|
||||
// Some CMake-like special strings ($ as dereference) strings in test names.
|
||||
// This serves to test that the names of test cases are not evaluated
|
||||
// inside the catch_discover_tests.
|
||||
TEST_CASE( "Plain ${NOT_A_VAR} variable" ) {}
|
||||
TEST_CASE( "Env variable access $ENV{HOME}" ) {}
|
||||
TEST_CASE( "Cache check $CACHE{FOO}" ) {}
|
||||
TEST_CASE( "Also some generator exprs $<CONFIG> in $<1:yes> name" ) {}
|
||||
TEST_CASE( "Mess of bare $ $$ $$$ and unterminated $} ${ exprs" ) {}
|
||||
TEST_CASE( "$ENV{X};[weird]{},{ mix $<0:no> ${VAR} };,{ }},{{" ) {}
|
||||
|
||||
Reference in New Issue
Block a user