mirror of
https://github.com/catchorg/Catch2.git
synced 2025-09-11 16:05:40 +02:00
Compare commits
127 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2c869e17e4 | ||
![]() |
0ab11aa9b4 | ||
![]() |
7a6af7ba76 | ||
![]() |
fa096b26d1 | ||
![]() |
820b1f12bf | ||
![]() |
6070745cab | ||
![]() |
3d9e7db2e0 | ||
![]() |
cf55cfd76f | ||
![]() |
3701c2e2e6 | ||
![]() |
7dc7d77af2 | ||
![]() |
06bc20cf37 | ||
![]() |
7a4beed6a6 | ||
![]() |
67b4ada6b0 | ||
![]() |
119569a67e | ||
![]() |
ab713894cc | ||
![]() |
69fc94d6f8 | ||
![]() |
49cd7c96b4 | ||
![]() |
e998d152cc | ||
![]() |
42a5903188 | ||
![]() |
c071f07e1a | ||
![]() |
53776a90cf | ||
![]() |
4511dc0c16 | ||
![]() |
e7c3bdb351 | ||
![]() |
9aab958667 | ||
![]() |
8cd58f75ec | ||
![]() |
d5a69cd400 | ||
![]() |
1d13d88833 | ||
![]() |
de0674c116 | ||
![]() |
3d7282c2bd | ||
![]() |
e5c0e3322d | ||
![]() |
dc8c8e957f | ||
![]() |
ba9193370b | ||
![]() |
7b70b11c23 | ||
![]() |
ab80277a86 | ||
![]() |
7e7ab0e28b | ||
![]() |
425957dc63 | ||
![]() |
d017f6d18f | ||
![]() |
91244d30a7 | ||
![]() |
62b3f6c3c2 | ||
![]() |
e7c26f09d1 | ||
![]() |
a22b7df46c | ||
![]() |
032068b889 | ||
![]() |
2aed6233cf | ||
![]() |
fb74bb133c | ||
![]() |
0b42ada60d | ||
![]() |
c424ca47f9 | ||
![]() |
52f3abadbb | ||
![]() |
53281b471f | ||
![]() |
03ffc1014c | ||
![]() |
87739ad3fe | ||
![]() |
0c27554af5 | ||
![]() |
11488e63b6 | ||
![]() |
820271bf24 | ||
![]() |
56d4510138 | ||
![]() |
c0d3a2e08f | ||
![]() |
2c3018a9d5 | ||
![]() |
9a6551b22b | ||
![]() |
800f1b1d3d | ||
![]() |
9cf5897a11 | ||
![]() |
6f32c67ea7 | ||
![]() |
7eea3ab245 | ||
![]() |
80af9ca687 | ||
![]() |
33286fdc37 | ||
![]() |
2f631bb808 | ||
![]() |
25cc09dcec | ||
![]() |
f9dce28e7d | ||
![]() |
b87caafd91 | ||
![]() |
bbbd5c4e08 | ||
![]() |
f41051f22a | ||
![]() |
e90d5a86e4 | ||
![]() |
dbc1295354 | ||
![]() |
f2cfc2b852 | ||
![]() |
c365ac392b | ||
![]() |
e640c3837a | ||
![]() |
b468d7cbff | ||
![]() |
7142d5a8c9 | ||
![]() |
1967feac49 | ||
![]() |
f0b7b0ca11 | ||
![]() |
4b1252547c | ||
![]() |
10067a47da | ||
![]() |
e340ab8db6 | ||
![]() |
ce2560ca95 | ||
![]() |
00347f1e79 | ||
![]() |
a5a2d08fbb | ||
![]() |
97602b248b | ||
![]() |
e28e162795 | ||
![]() |
90378f4a59 | ||
![]() |
84f8e806b8 | ||
![]() |
732e4b06db | ||
![]() |
0c43f98fa2 | ||
![]() |
bd703dd74b | ||
![]() |
99602787cd | ||
![]() |
bfb4ee1597 | ||
![]() |
31537c43d9 | ||
![]() |
96355da34e | ||
![]() |
71fce429af | ||
![]() |
d13e094598 | ||
![]() |
d30f1dda02 | ||
![]() |
3bce8ba14b | ||
![]() |
e680c4b9fb | ||
![]() |
f1e14a1168 | ||
![]() |
92ad9ee355 | ||
![]() |
e2862a8d71 | ||
![]() |
1161011dd0 | ||
![]() |
53a83e855e | ||
![]() |
9c741fe960 | ||
![]() |
979bbf03bb | ||
![]() |
33ce3f3953 | ||
![]() |
87a9424c9d | ||
![]() |
00cb0035c9 | ||
![]() |
6267b06089 | ||
![]() |
9837c35df1 | ||
![]() |
46066ede17 | ||
![]() |
6981783178 | ||
![]() |
08c8df1e3b | ||
![]() |
daeb5a87e6 | ||
![]() |
f2ee4f17ad | ||
![]() |
182fc3e46e | ||
![]() |
6b5b72651d | ||
![]() |
f45bb00351 | ||
![]() |
e02d9e788f | ||
![]() |
541f1ed1b3 | ||
![]() |
346723c9b6 | ||
![]() |
5a74fcc9c9 | ||
![]() |
9d5d719868 | ||
![]() |
02f13cf95a | ||
![]() |
43428c6093 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
patreon: horenmar
|
15
.travis.yml
15
.travis.yml
@@ -276,7 +276,7 @@ matrix:
|
||||
- "3.7"
|
||||
dist: xenial
|
||||
install:
|
||||
- pip install conan==1.10.2 conan-package-tools
|
||||
- pip install conan conan-package-tools
|
||||
env:
|
||||
- CONAN_GCC_VERSIONS=8
|
||||
- CONAN_DOCKER_IMAGE=conanio/gcc8
|
||||
@@ -301,10 +301,19 @@ before_script:
|
||||
# Regenerate single header file, so it is tested in the examples...
|
||||
- python scripts/generateSingleHeader.py
|
||||
|
||||
- |
|
||||
if [[ ${CPP17} -eq 1 ]]; then
|
||||
export CPP_STANDARD=17
|
||||
elif [[ ${CPP14} -eq 1 ]]; then
|
||||
export CPP_STANDARD=14
|
||||
else
|
||||
export CPP_STANDARD=11
|
||||
fi
|
||||
|
||||
# Use Debug builds for running Valgrind and building examples
|
||||
- cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_CPP17=${CPP17} -DCATCH_USE_VALGRIND=${VALGRIND} -DCATCH_BUILD_EXAMPLES=${EXAMPLES} -DCATCH_ENABLE_COVERAGE=${COVERAGE} -DCATCH_BUILD_EXTRA_TESTS=${EXTRAS}
|
||||
- cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DCATCH_USE_VALGRIND=${VALGRIND} -DCATCH_BUILD_EXAMPLES=${EXAMPLES} -DCATCH_ENABLE_COVERAGE=${COVERAGE} -DCATCH_BUILD_EXTRA_TESTS=${EXTRAS} -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF
|
||||
# Don't bother with release build for coverage build
|
||||
- cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14} -DUSE_CPP17=${CPP17}
|
||||
- cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF
|
||||
|
||||
|
||||
script:
|
||||
|
@@ -6,7 +6,11 @@ if(NOT DEFINED PROJECT_NAME)
|
||||
set(NOT_SUBPROJECT ON)
|
||||
endif()
|
||||
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.7.2)
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.9.2)
|
||||
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||
endif()
|
||||
|
||||
# Provide path for scripts
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
|
||||
@@ -132,9 +136,11 @@ if (NOT_SUBPROJECT)
|
||||
#
|
||||
# CMake does not provide a direct customization point for this in
|
||||
# `write_basic_package_version_file`, but it can be accomplished
|
||||
# indirectly by temporarily undefining `CMAKE_SIZEOF_VOID_P`.
|
||||
# indirectly by temporarily redefining `CMAKE_SIZEOF_VOID_P` to an
|
||||
# empty string. Note that just undefining the variable could be
|
||||
# insufficient in cases where the variable was already in CMake cache
|
||||
set(CATCH2_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
|
||||
unset(CMAKE_SIZEOF_VOID_P)
|
||||
set(CMAKE_SIZEOF_VOID_P "")
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake"
|
||||
COMPATIBILITY
|
||||
|
@@ -5,11 +5,11 @@
|
||||
[](https://travis-ci.org/catchorg/Catch2)
|
||||
[](https://ci.appveyor.com/project/catchorg/catch2)
|
||||
[](https://codecov.io/gh/catchorg/Catch2)
|
||||
[](https://wandbox.org/permlink/rsEsNO9M0flb5NlQ)
|
||||
[](https://wandbox.org/permlink/8YrGVqYqqSC4Sc5R)
|
||||
[](https://discord.gg/4CWS9zD)
|
||||
|
||||
|
||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.7.2/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.9.2/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||
|
||||
## Catch2 is released!
|
||||
|
||||
@@ -19,9 +19,8 @@ before moving to Catch2. You might also like to read [this blog post](https://le
|
||||
|
||||
## What's the Catch?
|
||||
|
||||
Catch2 stands for C++ Automated Test Cases in a Header and is a
|
||||
multi-paradigm test framework for C++. which also supports Objective-C
|
||||
(and maybe C).
|
||||
Catch2 is a multi-paradigm test framework for C++. which also supports
|
||||
Objective-C (and maybe C).
|
||||
It is primarily distributed as a single header file, although certain
|
||||
extensions may require additional headers.
|
||||
|
||||
|
@@ -18,7 +18,7 @@ class CatchConan(ConanFile):
|
||||
cmake.definitions["BUILD_TESTING"] = "OFF"
|
||||
cmake.definitions["CATCH_INSTALL_DOCS"] = "OFF"
|
||||
cmake.definitions["CATCH_INSTALL_HELPERS"] = "ON"
|
||||
cmake.configure()
|
||||
cmake.configure(build_folder='build')
|
||||
cmake.install()
|
||||
|
||||
self.copy(pattern="LICENSE.txt", dst="licenses")
|
||||
|
@@ -22,39 +22,6 @@ function(add_command NAME)
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
macro(_add_catch_test_labels LINE)
|
||||
# convert to list of tags
|
||||
string(REPLACE "][" "]\\;[" tags ${line})
|
||||
|
||||
add_command(
|
||||
set_tests_properties "${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
LABELS "${tags}"
|
||||
)
|
||||
endmacro()
|
||||
|
||||
macro(_add_catch_test LINE)
|
||||
set(test ${line})
|
||||
# use escape commas to handle properly test cases with commans inside the name
|
||||
string(REPLACE "," "\\," test_name ${test})
|
||||
# ...and add to script
|
||||
add_command(
|
||||
add_test "${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
)
|
||||
|
||||
add_command(
|
||||
set_tests_properties "${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endmacro()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
@@ -62,7 +29,7 @@ if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
)
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
@@ -80,22 +47,30 @@ elseif(${result} LESS 0)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
set(test)
|
||||
set(tags_regex "(\\[([^\\[]*)\\])+$")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
# lines without leading whitespaces are catch output not tests
|
||||
if(${line} MATCHES "^[ \t]+")
|
||||
# strip leading spaces and tabs
|
||||
string(REGEX REPLACE "^[ \t]+" "" line ${line})
|
||||
|
||||
if(${line} MATCHES "${tags_regex}")
|
||||
_add_catch_test_labels(${line})
|
||||
else()
|
||||
_add_catch_test(${line})
|
||||
endif()
|
||||
endif()
|
||||
set(test ${line})
|
||||
# Escape characters in test case names that would be parsed by Catch2
|
||||
set(test_name ${test})
|
||||
foreach(char , [ ])
|
||||
string(REPLACE ${char} "\\${char}" test_name ${test_name})
|
||||
endforeach(char)
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
|
@@ -14,6 +14,7 @@ Writing tests:
|
||||
* [Event Listeners](event-listeners.md#top)
|
||||
* [Data Generators](generators.md#top)
|
||||
* [Other macros](other-macros.md#top)
|
||||
* [Micro benchmarking](benchmarks.md#top)
|
||||
|
||||
Fine tuning:
|
||||
* [Supplying your own main()](own-main.md#top)
|
||||
|
@@ -136,7 +136,7 @@ REQUIRE_THROWS_WITH( dismantleHal(), "My mind is going" );
|
||||
* **REQUIRE_THROWS_MATCHES(** _expression_, _exception type_, _matcher for given exception type_ **)** and
|
||||
* **CHECK_THROWS_MATCHES(** _expression_, _exception type_, _matcher for given exception type_ **)**
|
||||
|
||||
Expects that exception of _exception type_ is thrown and it matches provided matcher (see next section for Matchers).
|
||||
Expects that exception of _exception type_ is thrown and it matches provided matcher (see the [documentation for Matchers](matchers.md#top)).
|
||||
|
||||
|
||||
_Please note that the `THROW` family of assertions expects to be passed a single expression, not a statement or series of statements. If you want to check a more complicated sequence of operations, you can use a C++11 lambda function._
|
||||
|
256
docs/benchmarks.md
Normal file
256
docs/benchmarks.md
Normal file
@@ -0,0 +1,256 @@
|
||||
<a id="top"></a>
|
||||
# Authoring benchmarks
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
_Note that benchmarking support is disabled by default and to enable it,
|
||||
you need to define `CATCH_CONFIG_ENABLE_BENCHMARKING`. For more details,
|
||||
see the [compile-time configuration documentation](configuration.md#top)._
|
||||
|
||||
Writing benchmarks is not easy. Catch simplifies certain aspects but you'll
|
||||
always need to take care about various aspects. Understanding a few things about
|
||||
the way Catch runs your code will be very helpful when writing your benchmarks.
|
||||
|
||||
First off, let's go over some terminology that will be used throughout this
|
||||
guide.
|
||||
|
||||
- *User code*: user code is the code that the user provides to be measured.
|
||||
- *Run*: one run is one execution of the user code.
|
||||
- *Sample*: one sample is one data point obtained by measuring the time it takes
|
||||
to perform a certain number of runs. One sample can consist of more than one
|
||||
run if the clock available does not have enough resolution to accurately
|
||||
measure a single run. All samples for a given benchmark execution are obtained
|
||||
with the same number of runs.
|
||||
|
||||
## Execution procedure
|
||||
|
||||
Now I can explain how a benchmark is executed in Catch. There are three main
|
||||
steps, though the first does not need to be repeated for every benchmark.
|
||||
|
||||
1. *Environmental probe*: before any benchmarks can be executed, the clock's
|
||||
resolution is estimated. A few other environmental artifacts are also estimated
|
||||
at this point, like the cost of calling the clock function, but they almost
|
||||
never have any impact in the results.
|
||||
|
||||
2. *Estimation*: the user code is executed a few times to obtain an estimate of
|
||||
the amount of runs that should be in each sample. This also has the potential
|
||||
effect of bringing relevant code and data into the caches before the actual
|
||||
measurement starts.
|
||||
|
||||
3. *Measurement*: all the samples are collected sequentially by performing the
|
||||
number of runs estimated in the previous step for each sample.
|
||||
|
||||
This already gives us one important rule for writing benchmarks for Catch: the
|
||||
benchmarks must be repeatable. The user code will be executed several times, and
|
||||
the number of times it will be executed during the estimation step cannot be
|
||||
known beforehand since it depends on the time it takes to execute the code.
|
||||
User code that cannot be executed repeatedly will lead to bogus results or
|
||||
crashes.
|
||||
|
||||
## Benchmark specification
|
||||
|
||||
Benchmarks can be specified anywhere inside a Catch test case.
|
||||
There is a simple and a slightly more advanced version of the `BENCHMARK` macro.
|
||||
|
||||
Let's have a look how a naive Fibonacci implementation could be benchmarked:
|
||||
```c++
|
||||
std::uint64_t Fibonacci(std::uint64_t number) {
|
||||
return number < 2 ? 1 : Fibonacci(number - 1) + Fibonacci(number - 2);
|
||||
}
|
||||
```
|
||||
Now the most straight forward way to benchmark this function, is just adding a `BENCHMARK` macro to our test case:
|
||||
```c++
|
||||
TEST_CASE("Fibonacci") {
|
||||
CHECK(Fibonacci(0) == 1);
|
||||
// some more asserts..
|
||||
CHECK(Fibonacci(5) == 8);
|
||||
// some more asserts..
|
||||
|
||||
// now let's benchmark:
|
||||
BENCHMARK("Fibonacci 20") {
|
||||
return Fibonacci(20);
|
||||
};
|
||||
|
||||
BENCHMARK("Fibonacci 25") {
|
||||
return Fibonacci(25);
|
||||
};
|
||||
|
||||
BENCHMARK("Fibonacci 30") {
|
||||
return Fibonacci(30);
|
||||
};
|
||||
|
||||
BENCHMARK("Fibonacci 35") {
|
||||
return Fibonacci(35);
|
||||
};
|
||||
}
|
||||
```
|
||||
There's a few things to note:
|
||||
- As `BENCHMARK` expands to a lambda expression it is necessary to add a semicolon after
|
||||
the closing brace (as opposed to the first experimental version).
|
||||
- The `return` is a handy way to avoid the compiler optimizing away the benchmark code.
|
||||
|
||||
Running this already runs the benchmarks and outputs something similar to:
|
||||
```
|
||||
-------------------------------------------------------------------------------
|
||||
Fibonacci
|
||||
-------------------------------------------------------------------------------
|
||||
C:\path\to\Catch2\Benchmark.tests.cpp(10)
|
||||
...............................................................................
|
||||
benchmark name samples iterations estimated
|
||||
mean low mean high mean
|
||||
std dev low std dev high std dev
|
||||
-------------------------------------------------------------------------------
|
||||
Fibonacci 20 100 416439 83.2878 ms
|
||||
2 ns 2 ns 2 ns
|
||||
0 ns 0 ns 0 ns
|
||||
|
||||
Fibonacci 25 100 400776 80.1552 ms
|
||||
3 ns 3 ns 3 ns
|
||||
0 ns 0 ns 0 ns
|
||||
|
||||
Fibonacci 30 100 396873 79.3746 ms
|
||||
17 ns 17 ns 17 ns
|
||||
0 ns 0 ns 0 ns
|
||||
|
||||
Fibonacci 35 100 145169 87.1014 ms
|
||||
468 ns 464 ns 473 ns
|
||||
21 ns 15 ns 34 ns
|
||||
```
|
||||
|
||||
### Advanced benchmarking
|
||||
The simplest use case shown above, takes no arguments and just runs the user code that needs to be measured.
|
||||
However, if using the `BENCHMARK_ADVANCED` macro and adding a `Catch::Benchmark::Chronometer` argument after
|
||||
the macro, some advanced features are available. The contents of the simple benchmarks are invoked once per run,
|
||||
while the blocks of the advanced benchmarks are invoked exactly twice:
|
||||
once during the estimation phase, and another time during the execution phase.
|
||||
|
||||
```c++
|
||||
BENCHMARK("simple"){ return long_computation(); };
|
||||
|
||||
BENCHMARK_ADVANCED("advanced")(Catch::Benchmark::Chronometer meter) {
|
||||
set_up();
|
||||
meter.measure([] { return long_computation(); });
|
||||
};
|
||||
```
|
||||
|
||||
These advanced benchmarks no longer consist entirely of user code to be measured.
|
||||
In these cases, the code to be measured is provided via the
|
||||
`Catch::Benchmark::Chronometer::measure` member function. This allows you to set up any
|
||||
kind of state that might be required for the benchmark but is not to be included
|
||||
in the measurements, like making a vector of random integers to feed to a
|
||||
sorting algorithm.
|
||||
|
||||
A single call to `Catch::Benchmark::Chronometer::measure` performs the actual measurements
|
||||
by invoking the callable object passed in as many times as necessary. Anything
|
||||
that needs to be done outside the measurement can be done outside the call to
|
||||
`measure`.
|
||||
|
||||
The callable object passed in to `measure` can optionally accept an `int`
|
||||
parameter.
|
||||
|
||||
```c++
|
||||
meter.measure([](int i) { return long_computation(i); });
|
||||
```
|
||||
|
||||
If it accepts an `int` parameter, the sequence number of each run will be passed
|
||||
in, starting with 0. This is useful if you want to measure some mutating code,
|
||||
for example. The number of runs can be known beforehand by calling
|
||||
`Catch::Benchmark::Chronometer::runs`; with this one can set up a different instance to be
|
||||
mutated by each run.
|
||||
|
||||
```c++
|
||||
std::vector<std::string> v(meter.runs());
|
||||
std::fill(v.begin(), v.end(), test_string());
|
||||
meter.measure([&v](int i) { in_place_escape(v[i]); });
|
||||
```
|
||||
|
||||
Note that it is not possible to simply use the same instance for different runs
|
||||
and resetting it between each run since that would pollute the measurements with
|
||||
the resetting code.
|
||||
|
||||
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
|
||||
the same semantics as providing a callable to `meter.measure` with `int` argument:
|
||||
|
||||
```c++
|
||||
BENCHMARK("indexed", i){ return long_computation(i); };
|
||||
```
|
||||
|
||||
### Constructors and destructors
|
||||
|
||||
All of these tools give you a lot mileage, but there are two things that still
|
||||
need special handling: constructors and destructors. The problem is that if you
|
||||
use automatic objects they get destroyed by the end of the scope, so you end up
|
||||
measuring the time for construction and destruction together. And if you use
|
||||
dynamic allocation instead, you end up including the time to allocate memory in
|
||||
the measurements.
|
||||
|
||||
To solve this conundrum, Catch provides class templates that let you manually
|
||||
construct and destroy objects without dynamic allocation and in a way that lets
|
||||
you measure construction and destruction separately.
|
||||
|
||||
```c++
|
||||
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
|
||||
meter.measure([&](int i) { storage[i].construct("thing"); });
|
||||
})
|
||||
|
||||
BENCHMARK_ADVANCED("destroy", [](Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
|
||||
for(auto&& o : storage)
|
||||
o.construct("thing");
|
||||
meter.measure([&](int i) { storage[i].destruct(); });
|
||||
})
|
||||
```
|
||||
|
||||
`Catch::Benchmark::storage_for<T>` objects are just pieces of raw storage suitable for `T`
|
||||
objects. You can use the `Catch::Benchmark::storage_for::construct` member function to call a constructor and
|
||||
create an object in that storage. So if you want to measure the time it takes
|
||||
for a certain constructor to run, you can just measure the time it takes to run
|
||||
this function.
|
||||
|
||||
When the lifetime of a `Catch::Benchmark::storage_for<T>` object ends, if an actual object was
|
||||
constructed there it will be automatically destroyed, so nothing leaks.
|
||||
|
||||
If you want to measure a destructor, though, we need to use
|
||||
`Catch::Benchmark::destructable_object<T>`. These objects are similar to
|
||||
`Catch::Benchmark::storage_for<T>` in that construction of the `T` object is manual, but
|
||||
it does not destroy anything automatically. Instead, you are required to call
|
||||
the `Catch::Benchmark::destructable_object::destruct` member function, which is what you
|
||||
can use to measure the destruction time.
|
||||
|
||||
### The optimizer
|
||||
|
||||
Sometimes the optimizer will optimize away the very code that you want to
|
||||
measure. There are several ways to use results that will prevent the optimiser
|
||||
from removing them. You can use the `volatile` keyword, or you can output the
|
||||
value to standard output or to a file, both of which force the program to
|
||||
actually generate the value somehow.
|
||||
|
||||
Catch adds a third option. The values returned by any function provided as user
|
||||
code are guaranteed to be evaluated and not optimised out. This means that if
|
||||
your user code consists of computing a certain value, you don't need to bother
|
||||
with using `volatile` or forcing output. Just `return` it from the function.
|
||||
That helps with keeping the code in a natural fashion.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```c++
|
||||
// may measure nothing at all by skipping the long calculation since its
|
||||
// result is not used
|
||||
BENCHMARK("no return"){ long_calculation(); };
|
||||
|
||||
// the result of long_calculation() is guaranteed to be computed somehow
|
||||
BENCHMARK("with return"){ return long_calculation(); };
|
||||
```
|
||||
|
||||
However, there's no other form of control over the optimizer whatsoever. It is
|
||||
up to you to write a benchmark that actually measures what you want and doesn't
|
||||
just measure the time to do a whole bunch of nothing.
|
||||
|
||||
To sum up, there are two simple rules: whatever you would do in handwritten code
|
||||
to control optimization still works in Catch; and Catch makes return values
|
||||
from user code into observable effects that can't be optimized away.
|
||||
|
||||
<i>Adapted from nonius' documentation.</i>
|
@@ -20,7 +20,10 @@
|
||||
[Specify a seed for the Random Number Generator](#specify-a-seed-for-the-random-number-generator)<br>
|
||||
[Identify framework and version according to the libIdentify standard](#identify-framework-and-version-according-to-the-libidentify-standard)<br>
|
||||
[Wait for key before continuing](#wait-for-key-before-continuing)<br>
|
||||
[Specify multiples of clock resolution to run benchmarks for](#specify-multiples-of-clock-resolution-to-run-benchmarks-for)<br>
|
||||
[Specify the number of benchmark samples to collect](#specify-the-number-of-benchmark-samples-to-collect)<br>
|
||||
[Specify the number of resamples for bootstrapping](#specify-the-number-of-resamples-for-bootstrapping)<br>
|
||||
[Specify the confidence-interval for bootstrapping](#specify-the-confidence-interval-for-bootstrapping)<br>
|
||||
[Disable statistical analysis of collected benchmark samples](#disable-statistical-analysis-of-collected-benchmark-samples)<br>
|
||||
[Usage](#usage)<br>
|
||||
[Specify the section to run](#specify-the-section-to-run)<br>
|
||||
[Filenames as tags](#filenames-as-tags)<br>
|
||||
@@ -57,7 +60,10 @@ Click one of the following links to take you straight to that option - or scroll
|
||||
<a href="#rng-seed"> ` --rng-seed`</a><br />
|
||||
<a href="#libidentify"> ` --libidentify`</a><br />
|
||||
<a href="#wait-for-keypress"> ` --wait-for-keypress`</a><br />
|
||||
<a href="#benchmark-resolution-multiple"> ` --benchmark-resolution-multiple`</a><br />
|
||||
<a href="#benchmark-samples"> ` --benchmark-samples`</a><br />
|
||||
<a href="#benchmark-resamples"> ` --benchmark-resamples`</a><br />
|
||||
<a href="#benchmark-confidence-interval"> ` --benchmark-confidence-interval`</a><br />
|
||||
<a href="#benchmark-no-analysis"> ` --benchmark-no-analysis`</a><br />
|
||||
<a href="#use-colour"> ` --use-colour`</a><br />
|
||||
|
||||
</br>
|
||||
@@ -236,7 +242,7 @@ Test cases are ordered one of three ways:
|
||||
|
||||
|
||||
### decl
|
||||
Declaration order. The order the tests were originally declared in. Note that ordering between files is not guaranteed and is implementation dependent.
|
||||
Declaration order (this is the default order if no --order argument is provided). The order the tests were originally declared in. Note that ordering between files is not guaranteed and is implementation dependent.
|
||||
|
||||
### lex
|
||||
Lexicographically sorted. Tests are sorted, alpha-numerically, by name.
|
||||
@@ -250,7 +256,7 @@ Randomly sorted. Test names are sorted using ```std::random_shuffle()```. By def
|
||||
|
||||
Sets a seed for the random number generator using ```std::srand()```.
|
||||
If a number is provided this is used directly as the seed so the random pattern is repeatable.
|
||||
Alternatively if the keyword ```time``` is provided then the result of calling ```std::time(0)``` is used and so the pattern becomes unpredictable.
|
||||
Alternatively if the keyword ```time``` is provided then the result of calling ```std::time(0)``` is used and so the pattern becomes unpredictable. In some cases, you might need to pass the keyword ```time``` in double quotes instead of single quotes.
|
||||
|
||||
In either case the actual value for the seed is printed as part of Catch's output so if an issue is discovered that is sensitive to test ordering the ordering can be reproduced - even if it was originally seeded from ```std::time(0)```.
|
||||
|
||||
@@ -267,13 +273,48 @@ See [The LibIdentify repo for more information and examples](https://github.com/
|
||||
Will cause the executable to print a message and wait until the return/ enter key is pressed before continuing -
|
||||
either before running any tests, after running all tests - or both, depending on the argument.
|
||||
|
||||
<a id="benchmark-resolution-multiple"></a>
|
||||
## Specify multiples of clock resolution to run benchmarks for
|
||||
<pre>--benchmark-resolution-multiple <multiplier></pre>
|
||||
<a id="benchmark-samples"></a>
|
||||
## Specify the number of benchmark samples to collect
|
||||
<pre>--benchmark-samples <# of samples></pre>
|
||||
|
||||
When running benchmarks the clock resolution is estimated. Benchmarks are then run for exponentially increasing
|
||||
numbers of iterations until some multiple of the estimated resolution is exceed. By default that multiple is 100, but
|
||||
it can be overridden here.
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
When running benchmarks a number of "samples" is collected. This is the base data for later statistical analysis.
|
||||
Per sample a clock resolution dependent number of iterations of the user code is run, which is independent of the number of samples. Defaults to 100.
|
||||
|
||||
<a id="benchmark-resamples"></a>
|
||||
## Specify the number of resamples for bootstrapping
|
||||
<pre>--benchmark-resamples <# of resamples></pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
After the measurements are performed, statistical [bootstrapping] is performed
|
||||
on the samples. The number of resamples for that bootstrapping is configurable
|
||||
but defaults to 100000. Due to the bootstrapping it is possible to give
|
||||
estimates for the mean and standard deviation. The estimates come with a lower
|
||||
bound and an upper bound, and the confidence interval (which is configurable but
|
||||
defaults to 95%).
|
||||
|
||||
[bootstrapping]: http://en.wikipedia.org/wiki/Bootstrapping_%28statistics%29
|
||||
|
||||
<a id="benchmark-confidence-interval"></a>
|
||||
## Specify the confidence-interval for bootstrapping
|
||||
<pre>--benchmark-confidence-interval <confidence-interval></pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
The confidence-interval is used for statistical bootstrapping on the samples to
|
||||
calculate the upper and lower bounds of mean and standard deviation.
|
||||
Must be between 0 and 1 and defaults to 0.95.
|
||||
|
||||
<a id="benchmark-no-analysis"></a>
|
||||
## Disable statistical analysis of collected benchmark samples
|
||||
<pre>--benchmark-no-analysis</pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
When this flag is specified no bootstrapping or any other statistical analysis is performed.
|
||||
Instead the user code is only measured and the plain mean from the samples is reported.
|
||||
|
||||
<a id="usage"></a>
|
||||
## Usage
|
||||
|
@@ -127,8 +127,12 @@ Catch's selection, by defining either `CATCH_CONFIG_CPP11_TO_STRING` or
|
||||
## C++17 toggles
|
||||
|
||||
CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS // Use std::uncaught_exceptions instead of std::uncaught_exception
|
||||
CATCH_CONFIG_CPP17_STRING_VIEW // Provide StringMaker specialization for std::string_view
|
||||
CATCH_CONFIG_CPP17_VARIANT // Override C++17 detection for CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
|
||||
CATCH_CONFIG_CPP17_STRING_VIEW // Override std::string_view support detection(Catch provides a StringMaker specialization by default)
|
||||
CATCH_CONFIG_CPP17_VARIANT // Override std::variant support detection (checked by CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER)
|
||||
CATCH_CONFIG_CPP17_OPTIONAL // Override std::optional support detection (checked by CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER)
|
||||
CATCH_CONFIG_CPP17_BYTE // Override std::byte support detection (Catch provides a StringMaker specialization by default)
|
||||
|
||||
> `CATCH_CONFIG_CPP17_STRING_VIEW` was [introduced](https://github.com/catchorg/Catch2/issues/1376) in Catch 2.4.1.
|
||||
|
||||
Catch contains basic compiler/standard detection and attempts to use
|
||||
some C++17 features whenever appropriate. This automatic detection
|
||||
@@ -149,6 +153,8 @@ by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`.
|
||||
CATCH_CONFIG_DISABLE // Disables assertions and test case registration
|
||||
CATCH_CONFIG_WCHAR // Enables use of wchart_t
|
||||
CATCH_CONFIG_EXPERIMENTAL_REDIRECT // Enables the new (experimental) way of capturing stdout/stderr
|
||||
CATCH_CONFIG_ENABLE_BENCHMARKING // Enables the integrated benchmarking features (has a significant effect on compilation speed)
|
||||
CATCH_CONFIG_USE_ASYNC // Force parallel statistical processing of samples during benchmarking
|
||||
|
||||
Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support.
|
||||
|
||||
@@ -207,9 +213,14 @@ By default, Catch does not stringify some types from the standard library. This
|
||||
CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Provide StringMaker specialization for std::optional (on C++17)
|
||||
CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS // Defines all of the above
|
||||
|
||||
> `CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1380) in Catch 2.4.1.
|
||||
|
||||
> `CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1510) in Catch 2.6.0.
|
||||
|
||||
## Disabling exceptions
|
||||
|
||||
> Introduced in Catch 2.4.0.
|
||||
|
||||
By default, Catch2 uses exceptions to signal errors and to abort tests
|
||||
when an assertion from the `REQUIRE` family of assertions fails. We also
|
||||
provide an experimental support for disabling exceptions. Catch2 should
|
||||
|
@@ -1,6 +1,13 @@
|
||||
<a id="top"></a>
|
||||
# Contributing to Catch
|
||||
|
||||
**Contents**<br>
|
||||
[Branches](#branches)<br>
|
||||
[Directory structure](#directory-structure)<br>
|
||||
[Testing your changes](#testing-your-changes)<br>
|
||||
[Documenting your code](#documenting-your-code)<br>
|
||||
[Code constructs to watch out for](#code-constructs-to-watch-out-for)<br>
|
||||
|
||||
So you want to contribute something to Catch? That's great! Whether it's a bug fix, a new feature, support for
|
||||
additional compilers - or just a fix to the documentation - all contributions are very welcome and very much appreciated.
|
||||
Of course so are bug reports and other comments and questions.
|
||||
@@ -75,6 +82,59 @@ before you do so, you need to check that the introduced changes are indeed
|
||||
intentional.
|
||||
|
||||
|
||||
## Documenting your code
|
||||
|
||||
If you have added new feature to Catch2, it needs documentation, so that
|
||||
other people can use it as well. This section collects some technical
|
||||
information that you will need for updating Catch2's documentation, and
|
||||
possibly some generic advise as well.
|
||||
|
||||
First, the technicalities:
|
||||
|
||||
* We introduced version tags to the documentation, which show users in
|
||||
which version a specific feature was introduced. This means that newly
|
||||
written documentation should be tagged with a placeholder, that will
|
||||
be replaced with the actual version upon release. There are 2 styles
|
||||
of placeholders used through the documentation, you should pick one that
|
||||
fits your text better (if in doubt, take a look at the existing version
|
||||
tags for other features).
|
||||
* `> [Introduced](link-to-issue-or-PR) in Catch X.Y.Z` - this
|
||||
placeholder is usually used after a section heading
|
||||
* `> X (Y and Z) was [introduced](link-to-issue-or-PR) in Catch X.Y.Z`
|
||||
- this placeholder is used when you need to tag a subpart of something,
|
||||
e.g. list
|
||||
* Crosslinks to different pages should target the `top` anchor, like this
|
||||
`[link to contributing](contributing.md#top)`.
|
||||
* If you have introduced a new document, there is a simple template you
|
||||
should use. It provides you with the top anchor mentioned above, and also
|
||||
with a backlink to the top of the documentation:
|
||||
```markdown
|
||||
<a id="top"></a>
|
||||
# Cool feature
|
||||
|
||||
Text that explains how to use the cool feature.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
```
|
||||
* For pages with more than 4 subheadings, we provide a table of contents
|
||||
(ToC) at the top of the page. Because GitHub markdown does not support
|
||||
automatic generation of ToC, it has to be handled semi-manually. Thus,
|
||||
if you've added a new subheading to some page, you should add it to the
|
||||
ToC. This can be done either manually, or by running the
|
||||
`updateDocumentToC.py` script in the `scripts/` folder.
|
||||
|
||||
|
||||
Now, for the generic tips:
|
||||
* Usage examples are good
|
||||
* Don't be afraid to introduce new pages
|
||||
* Try to be reasonably consistent with the surrounding documentation
|
||||
|
||||
|
||||
|
||||
|
||||
## Code constructs to watch out for
|
||||
|
||||
This section is a (sadly incomplete) listing of various constructs that
|
||||
|
@@ -1,6 +1,8 @@
|
||||
<a id="top"></a>
|
||||
# Data Generators
|
||||
|
||||
> Introduced in Catch 2.6.0.
|
||||
|
||||
Data generators (also known as _data driven/parametrized test cases_)
|
||||
let you reuse the same set of assertions across different input values.
|
||||
In Catch2, this means that they respect the ordering and nesting
|
||||
@@ -49,6 +51,8 @@ a test case,
|
||||
* `RandomFloatGenerator<Float>` -- generates random Floats from range
|
||||
* `RangeGenerator<T>` -- generates all values inside a specific range
|
||||
|
||||
> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch 2.7.0.
|
||||
|
||||
The generators also have associated helper functions that infer their
|
||||
type, making their usage much nicer. These are
|
||||
|
||||
@@ -64,6 +68,7 @@ type, making their usage much nicer. These are
|
||||
* `range(start, end)` for `RangeGenerator<T>` with a step size of `1`
|
||||
* `range(start, end, step)` for `RangeGenerator<T>` with a custom step size
|
||||
|
||||
> `chunk()`, `random()` and both `range()` functions were introduced in Catch 2.7.0.
|
||||
|
||||
And can be used as shown in the example below to create a generator
|
||||
that returns 100 odd random number:
|
||||
@@ -96,6 +101,8 @@ scope and thus capturing references is dangerous. If you need to use
|
||||
variables inside the generator expression, make sure you thought through
|
||||
the lifetime implications and use `GENERATE_COPY` or `GENERATE_REF`.**
|
||||
|
||||
> `GENERATE_COPY` and `GENERATE_REF` were introduced in Catch 2.7.1.
|
||||
|
||||
You can also override the inferred type by using `as<type>` as the first
|
||||
argument to the macro. This can be useful when dealing with string literals,
|
||||
if you want them to come out as `std::string`:
|
||||
|
@@ -30,6 +30,8 @@ When the last `CHECK` fails in the "Bar" test case, then only one message will b
|
||||
|
||||
## Logging without local scope
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
|
||||
|
||||
`UNSCOPED_INFO` is similar to `INFO` with two key differences:
|
||||
|
||||
- Lifetime of an unscoped message is not tied to its own scope.
|
||||
@@ -104,6 +106,8 @@ This semicolon will be removed with next major version. It is highly advised to
|
||||
|
||||
**UNSCOPED_INFO(** _message expression_ **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
|
||||
|
||||
Similar to `INFO`, but messages are not limited to their own scope: They are removed from the buffer after each assertion, section or test case, whichever comes first.
|
||||
|
||||
**WARN(** _message expression_ **)**
|
||||
|
@@ -45,7 +45,16 @@ Each of the provided `std::string` matchers also takes an optional second argume
|
||||
|
||||
|
||||
### Vector matchers
|
||||
The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector.
|
||||
Catch2 currently provides 5 built-in matchers that work on `std::vector`.
|
||||
These are
|
||||
|
||||
* `Contains` which checks whether a specified vector is present in the result
|
||||
* `VectorContains` which checks whether a specified element is present in the result
|
||||
* `Equals` which checks whether the result is exactly equal (order matters) to a specific vector
|
||||
* `UnorderedEquals` which checks whether the result is equal to a specific vector under a permutation
|
||||
* `Approx` which checks whether the result is "approx-equal" (order matters, but comparison is done via `Approx`) to a specific vector
|
||||
> Approx matcher was [introduced](https://github.com/catchorg/Catch2/issues/1499) in Catch 2.7.2.
|
||||
|
||||
|
||||
### Floating point matchers
|
||||
The floating point matchers are `WithinULP` and `WithinAbs`. `WithinAbs` accepts floating point numbers that are within a certain margin of target. `WithinULP` performs an [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place)-based comparison of two floating point numbers and accepts them if they are less than certain number of ULPs apart.
|
||||
|
@@ -112,6 +112,9 @@ SpECTRE is a code for multi-scale, multi-physics problems in astrophysics and gr
|
||||
### [Standardese](https://github.com/foonathan/standardese)
|
||||
Standardese aims to be a nextgen Doxygen.
|
||||
|
||||
### [PopHead](https://github.com/SPC-Some-Polish-Coders/PopHead)
|
||||
A 2D, Zombie, RPG game which is being made on our own engine.
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -59,6 +59,8 @@ TEST_CASE( "SUCCEED showcase" ) {
|
||||
|
||||
* `STATIC_REQUIRE`
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1362) in Catch 2.4.2.
|
||||
|
||||
`STATIC_REQUIRE( expr )` is a macro that can be used the same way as a
|
||||
`static_assert`, but also registers the success with Catch2, so it is
|
||||
reported as a success at runtime. The whole check can also be deferred
|
||||
@@ -132,6 +134,8 @@ ANON_TEST_CASE() {
|
||||
|
||||
* `DYNAMIC_SECTION`
|
||||
|
||||
> Introduced in Catch 2.3.0.
|
||||
|
||||
`DYNAMIC_SECTION` is a `SECTION` where the user can use `operator<<` to
|
||||
create the final name for that section. This can be useful with e.g.
|
||||
generators, or when creating a `SECTION` dynamically, within a loop.
|
||||
|
@@ -2,6 +2,10 @@
|
||||
|
||||
# Release notes
|
||||
**Contents**<br>
|
||||
[2.9.2](#292)<br>
|
||||
[2.9.1](#291)<br>
|
||||
[2.9.0](#290)<br>
|
||||
[2.8.0](#280)<br>
|
||||
[2.7.2](#272)<br>
|
||||
[2.7.1](#271)<br>
|
||||
[2.7.0](#270)<br>
|
||||
@@ -24,6 +28,78 @@
|
||||
[Even Older versions](#even-older-versions)<br>
|
||||
|
||||
|
||||
## 2.9.2
|
||||
|
||||
### Fixes
|
||||
* `ChunkGenerator` can now be used with chunks of size 0 (#1671)
|
||||
* Nested subsections are now run properly when specific section is run via the `-c` argument (#1670, #1673)
|
||||
* Catch2 now consistently uses `_WIN32` to detect Windows platform (#1676)
|
||||
* `TEMPLATE_LIST_TEST_CASE` now support non-default constructible type lists (#1697)
|
||||
* Fixed a crash in the XMLReporter when a benchmark throws exception during warmup (#1706)
|
||||
* Fixed a possible infinite loop in CompactReporter (#1715)
|
||||
* Fixed `-w NoTests` returning 0 even when no tests were matched (#1449, #1683, #1684)
|
||||
* Fixed matcher compilation under Obj-C++ (#1661)
|
||||
|
||||
### Improvements
|
||||
* `RepeatGenerator` and `FixedValuesGenerator` now fail to compile when used with `bool` (#1692)
|
||||
* Previously they would fail at runtime.
|
||||
* Catch2 now supports Android's debug logging for its debug output (#1710)
|
||||
* Catch2 now detects and configures itself for the RTX platform (#1693)
|
||||
* You still need to pass `--benchmark-no-analysis` if you are using benchmarking under RTX
|
||||
* Removed a "storage class is not first" warning when compiling Catch2 with PGI compiler (#1717)
|
||||
|
||||
### Miscellaneous
|
||||
* Documentation now contains indication when a specific feature was introduced (#1695)
|
||||
* These start with Catch2 v2.3.0, (a bit over a year ago).
|
||||
* `docs/contributing.md` has been updated to provide contributors guidance on how to add these to newly written documentation
|
||||
* Various other documentation improvements
|
||||
* ToC fixes
|
||||
* Documented `--order` and `--rng-seed` command line options
|
||||
* Benchmarking documentation now clearly states that it requires opt-in
|
||||
* Documented `CATCH_CONFIG_CPP17_OPTIONAL` and `CATCH_CONFIG_CPP17_BYTE` macros
|
||||
* Properly documented built-in vector matchers
|
||||
* Improved `*_THROWS_MATCHES` documentation a bit
|
||||
* CMake config file is now arch-independent even if `CMAKE_SIZEOF_VOID_P` is in CMake cache (#1660)
|
||||
* `CatchAddTests` now properly escapes `[` and `]` in test names (#1634, #1698)
|
||||
|
||||
|
||||
## 2.9.1
|
||||
|
||||
### Fixes
|
||||
* Fix benchmarking compilation failure in files without `CATCH_CONFIG_EXTERNAL_INTERFACES` (or implementation)
|
||||
|
||||
## 2.9.0
|
||||
|
||||
### Improvements
|
||||
* The experimental benchmarking support has been replaced by integrating Nonius code (#1616)
|
||||
* This provides a much more featurefull micro-benchmarking support.
|
||||
* Due to the compilation cost, it is disabled by default. See the documentation for details.
|
||||
* As far as backwards compatibility is concerned, this feature is still considered experimental in that we might change the interface based on user feedback.
|
||||
* `WithinULP` matcher now shows the acceptable range (#1581)
|
||||
* Template test cases now support type lists (#1627)
|
||||
|
||||
|
||||
## 2.8.0
|
||||
|
||||
### Improvements
|
||||
* Templated test cases no longer check whether the provided types are unique (#1628)
|
||||
* This allows you to e.g. test over `uint32_t`, `uint64_t`, and `size_t` without compilation failing
|
||||
* The precision of floating point stringification can be modified by user (#1612, #1614)
|
||||
* We now provide `REGISTER_ENUM` convenience macro for generating `StringMaker` specializations for enums
|
||||
* See the "String conversion" documentation for details
|
||||
* Added new set of macros for template test cases that enables the use of NTTPs (#1531, #1609)
|
||||
* See "Test cases and sections" documentation for details
|
||||
|
||||
### Fixes
|
||||
* `UNSCOPED_INFO` macro now has a prefixed/disabled/prefixed+disabled versions (#1611)
|
||||
* Reporting errors at startup should no longer cause a segfault under certain circumstances (#1626)
|
||||
|
||||
|
||||
### Miscellaneous
|
||||
* CMake will now prevent you from attempting in-tree build (#1636, #1638)
|
||||
* Previously it would break with an obscure error message during the build step
|
||||
|
||||
|
||||
## 2.7.2
|
||||
|
||||
### Improvements
|
||||
@@ -49,7 +125,7 @@
|
||||
|
||||
### Improvements
|
||||
* Reporters now print out the filters applied to test cases (#1550, #1585)
|
||||
* Added `GENERATE_COPY` and `GENERATE_VAR` macros that can use variables inside the generator expression
|
||||
* Added `GENERATE_COPY` and `GENERATE_REF` macros that can use variables inside the generator expression
|
||||
* Because of the significant danger of lifetime issues, the default `GENERATE` macro still does not allow variables
|
||||
* The `map` generator helper now deduces the mapped return type (#1576)
|
||||
|
||||
|
@@ -51,14 +51,15 @@ After compiling `tests-main.cpp` once, it is enough to link it with separately c
|
||||
|
||||
```
|
||||
$ g++ tests-main.cpp -c
|
||||
$ g++ tests-main.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
$ g++ factorial.cpp -c
|
||||
$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
Passed 1 test case with 4 assertions.
|
||||
```
|
||||
|
||||
Now, the next time we change the file `tests-factorial.cpp` (say we add `REQUIRE( Factorial(0) == 1)`), it is enough to recompile the tests instead of recompiling main as well:
|
||||
|
||||
```
|
||||
$ g++ tests-main.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
|
||||
tests-factorial.cpp:11: failed: Factorial(0) == 1 for: 0 == 1
|
||||
Failed 1 test case, failed 1 assertion.
|
||||
```
|
||||
|
@@ -6,6 +6,7 @@
|
||||
[Tag aliases](#tag-aliases)<br>
|
||||
[BDD-style test cases](#bdd-style-test-cases)<br>
|
||||
[Type parametrised test cases](#type-parametrised-test-cases)<br>
|
||||
[Signature based parametrised test cases](#signature-based-parametrised-test-cases)<br>
|
||||
|
||||
While Catch fully supports the traditional, xUnit, style of class-based fixtures containing test case methods this is not the preferred style.
|
||||
|
||||
@@ -83,10 +84,13 @@ This macro maps onto ```TEST_CASE``` and works in the same way, except that the
|
||||
|
||||
These macros map onto ```SECTION```s except that the section names are the _something_s prefixed by "given: ", "when: " or "then: " respectively.
|
||||
|
||||
* **AND_GIVEN(** _something_ **)**
|
||||
* **AND_WHEN(** _something_ **)**
|
||||
* **AND_THEN(** _something_ **)**
|
||||
|
||||
Similar to ```WHEN``` and ```THEN``` except that the prefixes start with "and ". These are used to chain ```WHEN```s and ```THEN```s together.
|
||||
Similar to ```GIVEN```, ```WHEN``` and ```THEN``` except that the prefixes start with "and ". These are used to chain ```GIVEN```s, ```WHEN```s and ```THEN```s together.
|
||||
|
||||
> `AND_GIVEN` was [introduced](https://github.com/catchorg/Catch2/issues/1360) in Catch 2.4.0.
|
||||
|
||||
When any of these macros are used the console reporter recognises them and formats the test case header such that the Givens, Whens and Thens are aligned to aid readability.
|
||||
|
||||
@@ -95,11 +99,13 @@ Other than the additional prefixes and the formatting in the console reporter th
|
||||
## Type parametrised test cases
|
||||
|
||||
In addition to `TEST_CASE`s, Catch2 also supports test cases parametrised
|
||||
by types, in the form of `TEMPLATE_TEST_CASE` and
|
||||
`TEMPLATE_PRODUCT_TEST_CASE`.
|
||||
by types, in the form of `TEMPLATE_TEST_CASE`,
|
||||
`TEMPLATE_PRODUCT_TEST_CASE` and `TEMPLATE_LIST_TEST_CASE`.
|
||||
|
||||
* **TEMPLATE_TEST_CASE(** _test name_ , _tags_, _type1_, _type2_, ..., _typen_ **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1437) in Catch 2.5.0.
|
||||
|
||||
_test name_ and _tag_ are exactly the same as they are in `TEST_CASE`,
|
||||
with the difference that the tag string must be provided (however, it
|
||||
can be empty). _type1_ through _typen_ is the list of types for which
|
||||
@@ -150,6 +156,8 @@ TEMPLATE_TEST_CASE( "vectors can be sized and resized", "[vector][template]", in
|
||||
|
||||
* **TEMPLATE_PRODUCT_TEST_CASE(** _test name_ , _tags_, (_template-type1_, _template-type2_, ..., _template-typen_), (_template-arg1_, _template-arg2_, ..., _template-argm_) **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch 2.6.0.
|
||||
|
||||
_template-type1_ through _template-typen_ is list of template template
|
||||
types which should be combined with each of _template-arg1_ through
|
||||
_template-argm_, resulting in _n * m_ test cases. Inside the test case,
|
||||
@@ -191,6 +199,77 @@ _While there is an upper limit on the number of types you can specify
|
||||
in single `TEMPLATE_TEST_CASE` or `TEMPLATE_PRODUCT_TEST_CASE`, the limit
|
||||
is very high and should not be encountered in practice._
|
||||
|
||||
* **TEMPLATE_LIST_TEST_CASE(** _test name_, _tags_, _type list_ **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1627) in Catch 2.9.0.
|
||||
|
||||
_type list_ is a generic list of types on which test case should be instantiated.
|
||||
List can be `std::tuple`, `boost::mpl::list`, `boost::mp11::mp_list` or anything with
|
||||
`template <typename...>` signature.
|
||||
|
||||
This allows you to reuse the _type list_ in multiple test cases.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
using MyTypes = std::tuple<int, char, float>;
|
||||
TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside std::tuple", "[template][list]", MyTypes)
|
||||
{
|
||||
REQUIRE(sizeof(TestType) > 0);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Signature based parametrised test cases
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
|
||||
|
||||
In addition to [type parametrised test cases](#type-parametrised-test-cases) Catch2 also supports
|
||||
signature base parametrised test cases, in form of `TEMPLATE_TEST_CASE_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_SIG`.
|
||||
These test cases have similar syntax like [type parametrised test cases](#type-parametrised-test-cases), with one
|
||||
additional positional argument which specifies the signature.
|
||||
|
||||
### Signature
|
||||
Signature has some strict rules for these tests cases to work properly:
|
||||
* signature with multiple template parameters e.g. `typename T, size_t S` must have this format in test case declaration
|
||||
`((typename T, size_t S), T, S)`
|
||||
* signature with variadic template arguments e.g. `typename T, size_t S, typename...Ts` must have this format in test case declaration
|
||||
`((typename T, size_t S, typename...Ts), T, S, Ts...)`
|
||||
* signature with single non type template parameter e.g. `int V` must have this format in test case declaration `((int V), V)`
|
||||
* signature with single type template parameter e.g. `typename T` should not be used as it is in fact `TEMPLATE_TEST_CASE`
|
||||
|
||||
Currently Catch2 support up to 11 template parameters in signature
|
||||
|
||||
### Examples
|
||||
|
||||
* **TEMPLATE_TEST_CASE_SIG(** _test name_ , _tags_, _signature_, _type1_, _type2_, ..., _typen_ **)**
|
||||
|
||||
Inside `TEMPLATE_TEST_CASE_SIG` test case you can use the names of template parameters as defined in _signature_.
|
||||
|
||||
```cpp
|
||||
TEMPLATE_TEST_CASE_SIG("TemplateTestSig: arrays can be created from NTTP arguments", "[vector][template][nttp]",
|
||||
((typename T, int V), T, V), (int,5), (float,4), (std::string,15), ((std::tuple<int, float>), 6)) {
|
||||
|
||||
std::array<T, V> v;
|
||||
REQUIRE(v.size() > 1);
|
||||
}
|
||||
```
|
||||
|
||||
* **TEMPLATE_PRODUCT_TEST_CASE_SIG(** _test name_ , _tags_, _signature_, (_template-type1_, _template-type2_, ..., _template-typen_), (_template-arg1_, _template-arg2_, ..., _template-argm_) **)**
|
||||
|
||||
```cpp
|
||||
|
||||
template<typename T, size_t S>
|
||||
struct Bar {
|
||||
size_t size() { return S; }
|
||||
};
|
||||
|
||||
TEMPLATE_PRODUCT_TEST_CASE_SIG("A Template product test case with array signature", "[template][product][nttp]", ((typename T, size_t S), T, S), (std::array, Bar), ((int, 9), (float, 42))) {
|
||||
TestType x;
|
||||
REQUIRE(x.size() > 0);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -1,6 +1,8 @@
|
||||
<a id="top"></a>
|
||||
# Test fixtures
|
||||
|
||||
## Defining test fixtures
|
||||
|
||||
Although Catch allows you to group tests together as sections within a test case, it can still be convenient, sometimes, to group them using a more traditional test fixture. Catch fully supports this too. You define the test fixture as a simple structure:
|
||||
|
||||
```c++
|
||||
@@ -84,6 +86,58 @@ _While there is an upper limit on the number of types you can specify
|
||||
in single `TEMPLATE_TEST_CASE_METHOD` or `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
|
||||
the limit is very high and should not be encountered in practice._
|
||||
|
||||
## Signature-based parametrised test fixtures
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
|
||||
|
||||
Catch2 also provides `TEMPLATE_TEST_CASE_METHOD_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG` to support
|
||||
fixtures using non-type template parameters. These test cases work similar to `TEMPLATE_TEST_CASE_METHOD` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
|
||||
with additional positional argument for [signature](test-cases-and-sections.md#signature-based-parametrised-test-cases).
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
template <int V>
|
||||
struct Nttp_Fixture{
|
||||
int value = V;
|
||||
};
|
||||
|
||||
TEMPLATE_TEST_CASE_METHOD_SIG(Nttp_Fixture, "A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][nttp]",((int V), V), 1, 3, 6) {
|
||||
REQUIRE(Nttp_Fixture<V>::value > 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct Template_Fixture_2 {
|
||||
Template_Fixture_2() {}
|
||||
|
||||
T m_a;
|
||||
};
|
||||
|
||||
template< typename T, size_t V>
|
||||
struct Template_Foo_2 {
|
||||
size_t size() { return V; }
|
||||
};
|
||||
|
||||
TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(Template_Fixture_2, "A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds", "[class][template][product][nttp]", ((typename T, size_t S), T, S),(std::array, Template_Foo_2), ((int,2), (float,6)))
|
||||
{
|
||||
REQUIRE(Template_Fixture_2<TestType>{}.m_a.size() >= 2);
|
||||
}
|
||||
```
|
||||
|
||||
## Template fixtures with types specified in template type lists
|
||||
|
||||
Catch2 also provides `TEMPLATE_LIST_TEST_CASE_METHOD` to support template fixtures with types specified in
|
||||
template type lists like `std::tuple`, `boost::mpl::list` or `boost::mp11::mp_list`. This test case works the same as `TEMPLATE_TEST_CASE_METHOD`,
|
||||
only difference is the source of types. This allows you to reuse the template type list in multiple test cases.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
using MyTypes = std::tuple<int, char, double>;
|
||||
TEMPLATE_LIST_TEST_CASE_METHOD(Template_Fixture, "Template test case method with test types specified inside std::tuple", "[class][template][list]", MyTypes)
|
||||
{
|
||||
REQUIRE( Template_Fixture<TestType>::m_a == 1 );
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -6,6 +6,9 @@
|
||||
[Catch::StringMaker specialisation](#catchstringmaker-specialisation)<br>
|
||||
[Catch::is_range specialisation](#catchis_range-specialisation)<br>
|
||||
[Exceptions](#exceptions)<br>
|
||||
[Enums](#enums)<br>
|
||||
[Floating point precision](#floating-point-precision)<br>
|
||||
|
||||
|
||||
Catch needs to be able to convert types you use in assertions and logging expressions into strings (for logging and reporting purposes).
|
||||
Most built-in or std types are supported out of the box but there are two ways that you can tell Catch how to convert your own types (or other, third-party types) into strings.
|
||||
@@ -14,7 +17,7 @@ Most built-in or std types are supported out of the box but there are two ways t
|
||||
|
||||
This is the standard way of providing string conversions in C++ - and the chances are you may already provide this for your own purposes. If you're not familiar with this idiom it involves writing a free function of the form:
|
||||
|
||||
```
|
||||
```cpp
|
||||
std::ostream& operator << ( std::ostream& os, T const& value ) {
|
||||
os << convertMyTypeToString( value );
|
||||
return os;
|
||||
@@ -28,7 +31,7 @@ You should put this function in the same namespace as your type, or the global n
|
||||
## Catch::StringMaker specialisation
|
||||
If you don't want to provide an ```operator <<``` overload, or you want to convert your type differently for testing purposes, you can provide a specialization for `Catch::StringMaker<T>`:
|
||||
|
||||
```
|
||||
```cpp
|
||||
namespace Catch {
|
||||
template<>
|
||||
struct StringMaker<T> {
|
||||
@@ -60,12 +63,70 @@ namespace Catch {
|
||||
|
||||
By default all exceptions deriving from `std::exception` will be translated to strings by calling the `what()` method. For exception types that do not derive from `std::exception` - or if `what()` does not return a suitable string - use `CATCH_TRANSLATE_EXCEPTION`. This defines a function that takes your exception type, by reference, and returns a string. It can appear anywhere in the code - it doesn't have to be in the same translation unit. For example:
|
||||
|
||||
```
|
||||
```cpp
|
||||
CATCH_TRANSLATE_EXCEPTION( MyType& ex ) {
|
||||
return ex.message();
|
||||
}
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
> Introduced in Catch 2.8.0.
|
||||
|
||||
Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected.
|
||||
If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type.
|
||||
However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialiation for you with minimal code.
|
||||
Simply provide it the (qualified) enum name, followed by all the enum values, and you're done!
|
||||
|
||||
E.g.
|
||||
|
||||
```cpp
|
||||
enum class Fruits { Banana, Apple, Mango };
|
||||
|
||||
CATCH_REGISTER_ENUM( Fruits, Fruits::Banana, Fruits::Apple, Fruits::Mango )
|
||||
|
||||
TEST_CASE() {
|
||||
REQUIRE( Fruits::Mango == Fruits::Apple );
|
||||
}
|
||||
```
|
||||
|
||||
... or if the enum is in a namespace:
|
||||
```cpp
|
||||
namespace Bikeshed {
|
||||
enum class Colours { Red, Green, Blue };
|
||||
}
|
||||
|
||||
// Important!: This macro must appear at top level scope - not inside a namespace
|
||||
// You can fully qualify the names, or use a using if you prefer
|
||||
CATCH_REGISTER_ENUM( Bikeshed::Colours,
|
||||
Bikeshed::Colours::Red,
|
||||
Bikeshed::Colours::Green,
|
||||
Bikeshed::Colours::Blue )
|
||||
|
||||
TEST_CASE() {
|
||||
REQUIRE( Bikeshed::Colours::Red == Bikeshed::Colours::Blue );
|
||||
}
|
||||
```
|
||||
|
||||
## Floating point precision
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1614) in Catch 2.8.0.
|
||||
|
||||
Catch provides a built-in `StringMaker` specialization for both `float`
|
||||
and `double`. By default, it uses what we think is a reasonable precision,
|
||||
but you can customize it by modifying the `precision` static variable
|
||||
inside the `StringMaker` specialization, like so:
|
||||
|
||||
```cpp
|
||||
Catch::StringMaker<float>::precision = 15;
|
||||
const float testFloat1 = 1.12345678901234567899f;
|
||||
const float testFloat2 = 1.12345678991234567899f;
|
||||
REQUIRE(testFloat1 == testFloat2);
|
||||
```
|
||||
|
||||
This assertion will fail and print out the `testFloat1` and `testFloat2`
|
||||
to 15 decimal places.
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -10,7 +10,7 @@
|
||||
#define TWOBLUECUBES_CATCH_HPP_INCLUDED
|
||||
|
||||
#define CATCH_VERSION_MAJOR 2
|
||||
#define CATCH_VERSION_MINOR 7
|
||||
#define CATCH_VERSION_MINOR 9
|
||||
#define CATCH_VERSION_PATCH 2
|
||||
|
||||
#ifdef __clang__
|
||||
@@ -53,7 +53,6 @@
|
||||
#include "internal/catch_test_registry.h"
|
||||
#include "internal/catch_capture.hpp"
|
||||
#include "internal/catch_section.h"
|
||||
#include "internal/catch_benchmark.h"
|
||||
#include "internal/catch_interfaces_exception.h"
|
||||
#include "internal/catch_approx.h"
|
||||
#include "internal/catch_compiler_capabilities.h"
|
||||
@@ -75,10 +74,15 @@
|
||||
#include "internal/catch_objc.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES
|
||||
// Benchmarking needs the externally-facing parts of reporters to work
|
||||
#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#include "internal/catch_external_interfaces.h"
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#include "internal/benchmark/catch_benchmark.hpp"
|
||||
#endif
|
||||
|
||||
#endif // ! CATCH_CONFIG_IMPL_ONLY
|
||||
|
||||
#ifdef CATCH_IMPL
|
||||
@@ -89,6 +93,7 @@
|
||||
#include "internal/catch_default_main.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(CATCH_CONFIG_IMPL_ONLY)
|
||||
|
||||
#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
|
||||
@@ -132,6 +137,7 @@
|
||||
#endif // CATCH_CONFIG_DISABLE_MATCHERS
|
||||
|
||||
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
|
||||
#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
|
||||
#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
|
||||
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
|
||||
|
||||
@@ -149,14 +155,22 @@
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#else
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
|
||||
@@ -179,6 +193,13 @@
|
||||
#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
|
||||
#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#define CATCH_BENCHMARK(...) \
|
||||
INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
|
||||
#define CATCH_BENCHMARK_ADVANCED(name) \
|
||||
INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
|
||||
#else
|
||||
|
||||
@@ -232,14 +253,26 @@
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
|
||||
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#else
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
|
||||
#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
|
||||
@@ -266,6 +299,13 @@
|
||||
#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc )
|
||||
#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc )
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#define BENCHMARK(...) \
|
||||
INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
|
||||
#define BENCHMARK_ADVANCED(name) \
|
||||
INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
using Catch::Detail::Approx;
|
||||
|
||||
#else // CATCH_CONFIG_DISABLE
|
||||
@@ -305,9 +345,10 @@ using Catch::Detail::Approx;
|
||||
#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
|
||||
#endif // CATCH_CONFIG_DISABLE_MATCHERS
|
||||
|
||||
#define CATCH_INFO( msg ) (void)(0)
|
||||
#define CATCH_WARN( msg ) (void)(0)
|
||||
#define CATCH_CAPTURE( msg ) (void)(0)
|
||||
#define CATCH_INFO( msg ) (void)(0)
|
||||
#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
|
||||
#define CATCH_WARN( msg ) (void)(0)
|
||||
#define CATCH_CAPTURE( msg ) (void)(0)
|
||||
|
||||
#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
|
||||
#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
|
||||
@@ -322,15 +363,23 @@ using Catch::Detail::Approx;
|
||||
#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#else
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#endif
|
||||
|
||||
// "BDD-style" convenience wrappers
|
||||
@@ -382,6 +431,7 @@ using Catch::Detail::Approx;
|
||||
#endif // CATCH_CONFIG_DISABLE_MATCHERS
|
||||
|
||||
#define INFO( msg ) (void)(0)
|
||||
#define UNSCOPED_INFO( msg ) (void)(0)
|
||||
#define WARN( msg ) (void)(0)
|
||||
#define CAPTURE( msg ) (void)(0)
|
||||
|
||||
@@ -397,15 +447,23 @@ using Catch::Detail::Approx;
|
||||
#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
|
||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
|
||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#else
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
|
||||
#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
|
||||
#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
|
||||
#endif
|
||||
|
||||
#define STATIC_REQUIRE( ... ) (void)(0)
|
||||
|
122
include/internal/benchmark/catch_benchmark.hpp
Normal file
122
include/internal/benchmark/catch_benchmark.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Benchmark
|
||||
#ifndef TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED
|
||||
|
||||
#include "../catch_config.hpp"
|
||||
#include "../catch_context.h"
|
||||
#include "../catch_interfaces_reporter.h"
|
||||
#include "../catch_test_registry.h"
|
||||
|
||||
#include "catch_chronometer.hpp"
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_environment.hpp"
|
||||
#include "catch_execution_plan.hpp"
|
||||
#include "detail/catch_estimate_clock.hpp"
|
||||
#include "detail/catch_complete_invoke.hpp"
|
||||
#include "detail/catch_analyse.hpp"
|
||||
#include "detail/catch_benchmark_function.hpp"
|
||||
#include "detail/catch_run_for_at_least.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
struct Benchmark {
|
||||
Benchmark(std::string &&name)
|
||||
: name(std::move(name)) {}
|
||||
|
||||
template <class FUN>
|
||||
Benchmark(std::string &&name, FUN &&func)
|
||||
: fun(std::move(func)), name(std::move(name)) {}
|
||||
|
||||
template <typename Clock>
|
||||
ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
|
||||
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(Detail::warmup_time));
|
||||
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(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(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(Detail::warmup_time), Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
template <typename Clock = default_clock>
|
||||
void run() {
|
||||
IConfigPtr cfg = getCurrentContext().getConfig();
|
||||
|
||||
auto env = Detail::measure_environment<Clock>();
|
||||
|
||||
getResultCapture().benchmarkPreparing(name);
|
||||
CATCH_TRY{
|
||||
auto plan = user_code([&] {
|
||||
return prepare<Clock>(*cfg, env);
|
||||
});
|
||||
|
||||
BenchmarkInfo info {
|
||||
name,
|
||||
plan.estimated_duration.count(),
|
||||
plan.iterations_per_sample,
|
||||
cfg->benchmarkSamples(),
|
||||
cfg->benchmarkResamples(),
|
||||
env.clock_resolution.mean.count(),
|
||||
env.clock_cost.mean.count()
|
||||
};
|
||||
|
||||
getResultCapture().benchmarkStarting(info);
|
||||
|
||||
auto samples = user_code([&] {
|
||||
return plan.template run<Clock>(*cfg, env);
|
||||
});
|
||||
|
||||
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
|
||||
BenchmarkStats<std::chrono::duration<double, std::nano>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
|
||||
getResultCapture().benchmarkEnded(stats);
|
||||
|
||||
} CATCH_CATCH_ALL{
|
||||
if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
|
||||
std::rethrow_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
// sets lambda to be used in fun *and* executes benchmark!
|
||||
template <typename Fun,
|
||||
typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0>
|
||||
Benchmark & operator=(Fun func) {
|
||||
fun = Detail::BenchmarkFunction(func);
|
||||
run();
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Detail::BenchmarkFunction fun;
|
||||
std::string name;
|
||||
};
|
||||
}
|
||||
} // namespace Catch
|
||||
|
||||
#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
|
||||
#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
|
||||
|
||||
#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
|
||||
if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
|
||||
BenchmarkName = [&](int benchmarkIndex)
|
||||
|
||||
#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
|
||||
if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
|
||||
BenchmarkName = [&]
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED
|
71
include/internal/benchmark/catch_chronometer.hpp
Normal file
71
include/internal/benchmark/catch_chronometer.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// User-facing chronometer
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_CHRONOMETER_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_CHRONOMETER_HPP_INCLUDED
|
||||
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_optimizer.hpp"
|
||||
#include "detail/catch_complete_invoke.hpp"
|
||||
#include "../catch_meta.hpp"
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
struct ChronometerConcept {
|
||||
virtual void start() = 0;
|
||||
virtual void finish() = 0;
|
||||
virtual ~ChronometerConcept() = default;
|
||||
};
|
||||
template <typename Clock>
|
||||
struct ChronometerModel final : public ChronometerConcept {
|
||||
void start() override { started = Clock::now(); }
|
||||
void finish() override { finished = Clock::now(); }
|
||||
|
||||
ClockDuration<Clock> elapsed() const { return finished - started; }
|
||||
|
||||
TimePoint<Clock> started;
|
||||
TimePoint<Clock> finished;
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
struct Chronometer {
|
||||
public:
|
||||
template <typename Fun>
|
||||
void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
|
||||
|
||||
int runs() const { return k; }
|
||||
|
||||
Chronometer(Detail::ChronometerConcept& meter, int k)
|
||||
: impl(&meter)
|
||||
, k(k) {}
|
||||
|
||||
private:
|
||||
template <typename Fun>
|
||||
void measure(Fun&& fun, std::false_type) {
|
||||
measure([&fun](int) { return fun(); }, std::true_type());
|
||||
}
|
||||
|
||||
template <typename Fun>
|
||||
void measure(Fun&& fun, std::true_type) {
|
||||
Detail::optimizer_barrier();
|
||||
impl->start();
|
||||
for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i);
|
||||
impl->finish();
|
||||
Detail::optimizer_barrier();
|
||||
}
|
||||
|
||||
Detail::ChronometerConcept* impl;
|
||||
int k;
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CHRONOMETER_HPP_INCLUDED
|
40
include/internal/benchmark/catch_clock.hpp
Normal file
40
include/internal/benchmark/catch_clock.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Clocks
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_CLOCK_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_CLOCK_HPP_INCLUDED
|
||||
|
||||
#include <chrono>
|
||||
#include <ratio>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
template <typename Clock>
|
||||
using ClockDuration = typename Clock::duration;
|
||||
template <typename Clock>
|
||||
using FloatDuration = std::chrono::duration<double, typename Clock::period>;
|
||||
|
||||
template <typename Clock>
|
||||
using TimePoint = typename Clock::time_point;
|
||||
|
||||
using default_clock = std::chrono::steady_clock;
|
||||
|
||||
template <typename Clock>
|
||||
struct now {
|
||||
TimePoint<Clock> operator()() const {
|
||||
return Clock::now();
|
||||
}
|
||||
};
|
||||
|
||||
using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CLOCK_HPP_INCLUDED
|
73
include/internal/benchmark/catch_constructor.hpp
Normal file
73
include/internal/benchmark/catch_constructor.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Constructor and destructor helpers
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
template <typename T, bool Destruct>
|
||||
struct ObjectStorage
|
||||
{
|
||||
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
|
||||
|
||||
ObjectStorage() : data() {}
|
||||
|
||||
ObjectStorage(const ObjectStorage& other)
|
||||
{
|
||||
new(&data) T(other.stored_object());
|
||||
}
|
||||
|
||||
ObjectStorage(ObjectStorage&& other)
|
||||
{
|
||||
new(&data) T(std::move(other.stored_object()));
|
||||
}
|
||||
|
||||
~ObjectStorage() { destruct_on_exit<T>(); }
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args)
|
||||
{
|
||||
new (&data) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <bool AllowManualDestruction = !Destruct>
|
||||
typename std::enable_if<AllowManualDestruction>::type destruct()
|
||||
{
|
||||
stored_object().~T();
|
||||
}
|
||||
|
||||
private:
|
||||
// If this is a constructor benchmark, destruct the underlying object
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
|
||||
// Otherwise, don't
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
|
||||
|
||||
T& stored_object()
|
||||
{
|
||||
return *static_cast<T*>(static_cast<void*>(&data));
|
||||
}
|
||||
|
||||
TStorage data;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using storage_for = Detail::ObjectStorage<T, true>;
|
||||
|
||||
template <typename T>
|
||||
using destructable_object = Detail::ObjectStorage<T, false>;
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED
|
38
include/internal/benchmark/catch_environment.hpp
Normal file
38
include/internal/benchmark/catch_environment.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Environment information
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_ENVIRONMENT_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_ENVIRONMENT_HPP_INCLUDED
|
||||
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_outlier_classification.hpp"
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
template <typename Duration>
|
||||
struct EnvironmentEstimate {
|
||||
Duration mean;
|
||||
OutlierClassification outliers;
|
||||
|
||||
template <typename Duration2>
|
||||
operator EnvironmentEstimate<Duration2>() const {
|
||||
return { mean, outliers };
|
||||
}
|
||||
};
|
||||
template <typename Clock>
|
||||
struct Environment {
|
||||
using clock_type = Clock;
|
||||
EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
|
||||
EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_ENVIRONMENT_HPP_INCLUDED
|
31
include/internal/benchmark/catch_estimate.hpp
Normal file
31
include/internal/benchmark/catch_estimate.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Statistics estimates
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_ESTIMATE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_ESTIMATE_HPP_INCLUDED
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
template <typename Duration>
|
||||
struct Estimate {
|
||||
Duration point;
|
||||
Duration lower_bound;
|
||||
Duration upper_bound;
|
||||
double confidence_interval;
|
||||
|
||||
template <typename Duration2>
|
||||
operator Estimate<Duration2>() const {
|
||||
return { point, lower_bound, upper_bound, confidence_interval };
|
||||
}
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_ESTIMATE_HPP_INCLUDED
|
58
include/internal/benchmark/catch_execution_plan.hpp
Normal file
58
include/internal/benchmark/catch_execution_plan.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Execution plan
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
||||
|
||||
#include "../catch_config.hpp"
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_environment.hpp"
|
||||
#include "detail/catch_benchmark_function.hpp"
|
||||
#include "detail/catch_repeat.hpp"
|
||||
#include "detail/catch_run_for_at_least.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
template <typename Duration>
|
||||
struct ExecutionPlan {
|
||||
int iterations_per_sample;
|
||||
Duration estimated_duration;
|
||||
Detail::BenchmarkFunction benchmark;
|
||||
Duration warmup_time;
|
||||
int warmup_iterations;
|
||||
|
||||
template <typename Duration2>
|
||||
operator ExecutionPlan<Duration2>() const {
|
||||
return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
|
||||
}
|
||||
|
||||
template <typename Clock>
|
||||
std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
|
||||
// warmup a bit
|
||||
Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
|
||||
|
||||
std::vector<FloatDuration<Clock>> times;
|
||||
times.reserve(cfg.benchmarkSamples());
|
||||
std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
|
||||
Detail::ChronometerModel<Clock> model;
|
||||
this->benchmark(Chronometer(model, iterations_per_sample));
|
||||
auto sample_time = model.elapsed() - env.clock_cost.mean;
|
||||
if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
|
||||
return sample_time / iterations_per_sample;
|
||||
});
|
||||
return times;
|
||||
}
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_EXECUTION_PLAN_HPP_INCLUDED
|
68
include/internal/benchmark/catch_optimizer.hpp
Normal file
68
include/internal/benchmark/catch_optimizer.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Hinting the optimizer
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_OPTIMIZER_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_OPTIMIZER_HPP_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <atomic> // atomic_thread_fence
|
||||
#endif
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
template <typename T>
|
||||
inline void keep_memory(T* p) {
|
||||
asm volatile("" : : "g"(p) : "memory");
|
||||
}
|
||||
inline void keep_memory() {
|
||||
asm volatile("" : : : "memory");
|
||||
}
|
||||
|
||||
namespace Detail {
|
||||
inline void optimizer_barrier() { keep_memory(); }
|
||||
} // namespace Detail
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#pragma optimize("", off)
|
||||
template <typename T>
|
||||
inline void keep_memory(T* p) {
|
||||
// thanks @milleniumbug
|
||||
*reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
|
||||
}
|
||||
// TODO equivalent keep_memory()
|
||||
#pragma optimize("", on)
|
||||
|
||||
namespace Detail {
|
||||
inline void optimizer_barrier() {
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
} // namespace Detail
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline void deoptimize_value(T&& x) {
|
||||
keep_memory(&x);
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Args>
|
||||
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
|
||||
deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Args>
|
||||
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type {
|
||||
std::forward<Fn>(fn) (std::forward<Args...>(args...));
|
||||
}
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_OPTIMIZER_HPP_INCLUDED
|
29
include/internal/benchmark/catch_outlier_classification.hpp
Normal file
29
include/internal/benchmark/catch_outlier_classification.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Outlier information
|
||||
#ifndef TWOBLUECUBES_CATCH_OUTLIERS_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_OUTLIERS_HPP_INCLUDED
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
struct OutlierClassification {
|
||||
int samples_seen = 0;
|
||||
int low_severe = 0; // more than 3 times IQR below Q1
|
||||
int low_mild = 0; // 1.5 to 3 times IQR below Q1
|
||||
int high_mild = 0; // 1.5 to 3 times IQR above Q3
|
||||
int high_severe = 0; // more than 3 times IQR above Q3
|
||||
|
||||
int total() const {
|
||||
return low_severe + low_mild + high_mild + high_severe;
|
||||
}
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_OUTLIERS_HPP_INCLUDED
|
50
include/internal/benchmark/catch_sample_analysis.hpp
Normal file
50
include/internal/benchmark/catch_sample_analysis.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Benchmark results
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_BENCHMARK_RESULTS_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_BENCHMARK_RESULTS_HPP_INCLUDED
|
||||
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_estimate.hpp"
|
||||
#include "catch_outlier_classification.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
template <typename Duration>
|
||||
struct SampleAnalysis {
|
||||
std::vector<Duration> samples;
|
||||
Estimate<Duration> mean;
|
||||
Estimate<Duration> standard_deviation;
|
||||
OutlierClassification outliers;
|
||||
double outlier_variance;
|
||||
|
||||
template <typename Duration2>
|
||||
operator SampleAnalysis<Duration2>() const {
|
||||
std::vector<Duration2> samples2;
|
||||
samples2.reserve(samples.size());
|
||||
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
|
||||
return {
|
||||
std::move(samples2),
|
||||
mean,
|
||||
standard_deviation,
|
||||
outliers,
|
||||
outlier_variance,
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_BENCHMARK_RESULTS_HPP_INCLUDED
|
78
include/internal/benchmark/detail/catch_analyse.hpp
Normal file
78
include/internal/benchmark/detail/catch_analyse.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Run and analyse one benchmark
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_ANALYSE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_ANALYSE_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_sample_analysis.hpp"
|
||||
#include "catch_stats.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename Duration, typename Iterator>
|
||||
SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
|
||||
if (!cfg.benchmarkNoAnalysis()) {
|
||||
std::vector<double> samples;
|
||||
samples.reserve(last - first);
|
||||
std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
|
||||
|
||||
auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
|
||||
auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
|
||||
|
||||
auto wrap_estimate = [](Estimate<double> e) {
|
||||
return Estimate<Duration> {
|
||||
Duration(e.point),
|
||||
Duration(e.lower_bound),
|
||||
Duration(e.upper_bound),
|
||||
e.confidence_interval,
|
||||
};
|
||||
};
|
||||
std::vector<Duration> samples2;
|
||||
samples2.reserve(samples.size());
|
||||
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
|
||||
return {
|
||||
std::move(samples2),
|
||||
wrap_estimate(analysis.mean),
|
||||
wrap_estimate(analysis.standard_deviation),
|
||||
outliers,
|
||||
analysis.outlier_variance,
|
||||
};
|
||||
} else {
|
||||
std::vector<Duration> samples;
|
||||
samples.reserve(last - first);
|
||||
|
||||
Duration mean = Duration(0);
|
||||
int i = 0;
|
||||
for (auto it = first; it < last; ++it, ++i) {
|
||||
samples.push_back(Duration(*it));
|
||||
mean += Duration(*it);
|
||||
}
|
||||
mean /= i;
|
||||
|
||||
return {
|
||||
std::move(samples),
|
||||
Estimate<Duration>{mean, mean, mean, 0.0},
|
||||
Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
|
||||
OutlierClassification{},
|
||||
0.0
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_ANALYSE_HPP_INCLUDED
|
105
include/internal/benchmark/detail/catch_benchmark_function.hpp
Normal file
105
include/internal/benchmark/detail/catch_benchmark_function.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Dumb std::function implementation for consistent call overhead
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
|
||||
|
||||
#include "../catch_chronometer.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
#include "../../catch_meta.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename T>
|
||||
using Decay = typename std::decay<T>::type;
|
||||
template <typename T, typename U>
|
||||
struct is_related
|
||||
: std::is_same<Decay<T>, Decay<U>> {};
|
||||
|
||||
/// We need to reinvent std::function because every piece of code that might add overhead
|
||||
/// in a measurement context needs to have consistent performance characteristics so that we
|
||||
/// can account for it in the measurement.
|
||||
/// Implementations of std::function with optimizations that aren't always applicable, like
|
||||
/// small buffer optimizations, are not uncommon.
|
||||
/// This is effectively an implementation of std::function without any such optimizations;
|
||||
/// it may be slow, but it is consistently slow.
|
||||
struct BenchmarkFunction {
|
||||
private:
|
||||
struct callable {
|
||||
virtual void call(Chronometer meter) const = 0;
|
||||
virtual callable* clone() const = 0;
|
||||
virtual ~callable() = default;
|
||||
};
|
||||
template <typename Fun>
|
||||
struct model : public callable {
|
||||
model(Fun&& fun) : fun(std::move(fun)) {}
|
||||
model(Fun const& fun) : fun(fun) {}
|
||||
|
||||
model<Fun>* clone() const override { return new model<Fun>(*this); }
|
||||
|
||||
void call(Chronometer meter) const override {
|
||||
call(meter, is_callable<Fun(Chronometer)>());
|
||||
}
|
||||
void call(Chronometer meter, std::true_type) const {
|
||||
fun(meter);
|
||||
}
|
||||
void call(Chronometer meter, std::false_type) const {
|
||||
meter.measure(fun);
|
||||
}
|
||||
|
||||
Fun fun;
|
||||
};
|
||||
|
||||
struct do_nothing { void operator()() const {} };
|
||||
|
||||
template <typename T>
|
||||
BenchmarkFunction(model<T>* c) : f(c) {}
|
||||
|
||||
public:
|
||||
BenchmarkFunction()
|
||||
: f(new model<do_nothing>{ {} }) {}
|
||||
|
||||
template <typename Fun,
|
||||
typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
|
||||
BenchmarkFunction(Fun&& fun)
|
||||
: f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
|
||||
|
||||
BenchmarkFunction(BenchmarkFunction&& that)
|
||||
: f(std::move(that.f)) {}
|
||||
|
||||
BenchmarkFunction(BenchmarkFunction const& that)
|
||||
: f(that.f->clone()) {}
|
||||
|
||||
BenchmarkFunction& operator=(BenchmarkFunction&& that) {
|
||||
f = std::move(that.f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BenchmarkFunction& operator=(BenchmarkFunction const& that) {
|
||||
f.reset(that.f->clone());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()(Chronometer meter) const { f->call(meter); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<callable> f;
|
||||
};
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
|
69
include/internal/benchmark/detail/catch_complete_invoke.hpp
Normal file
69
include/internal/benchmark/detail/catch_complete_invoke.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Invoke with a special case for void
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_COMPLETE_INVOKE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_COMPLETE_INVOKE_HPP_INCLUDED
|
||||
|
||||
#include "../../catch_enforce.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename T>
|
||||
struct CompleteType { using type = T; };
|
||||
template <>
|
||||
struct CompleteType<void> { struct type {}; };
|
||||
|
||||
template <typename T>
|
||||
using CompleteType_t = typename CompleteType<T>::type;
|
||||
|
||||
template <typename Result>
|
||||
struct CompleteInvoker {
|
||||
template <typename Fun, typename... Args>
|
||||
static Result invoke(Fun&& fun, Args&&... args) {
|
||||
return std::forward<Fun>(fun)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct CompleteInvoker<void> {
|
||||
template <typename Fun, typename... Args>
|
||||
static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
|
||||
std::forward<Fun>(fun)(std::forward<Args>(args)...);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
template <typename Sig>
|
||||
using ResultOf_t = typename std::result_of<Sig>::type;
|
||||
|
||||
// invoke and not return void :(
|
||||
template <typename Fun, typename... Args>
|
||||
CompleteType_t<ResultOf_t<Fun(Args...)>> complete_invoke(Fun&& fun, Args&&... args) {
|
||||
return CompleteInvoker<ResultOf_t<Fun(Args...)>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
|
||||
} // namespace Detail
|
||||
|
||||
template <typename Fun>
|
||||
Detail::CompleteType_t<Detail::ResultOf_t<Fun()>> user_code(Fun&& fun) {
|
||||
CATCH_TRY{
|
||||
return Detail::complete_invoke(std::forward<Fun>(fun));
|
||||
} CATCH_CATCH_ALL{
|
||||
getResultCapture().benchmarkFailed(translateActiveException());
|
||||
CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg);
|
||||
}
|
||||
}
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_COMPLETE_INVOKE_HPP_INCLUDED
|
113
include/internal/benchmark/detail/catch_estimate_clock.hpp
Normal file
113
include/internal/benchmark/detail/catch_estimate_clock.hpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Environment measurement
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_environment.hpp"
|
||||
#include "catch_stats.hpp"
|
||||
#include "catch_measure.hpp"
|
||||
#include "catch_run_for_at_least.hpp"
|
||||
#include "../catch_clock.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename Clock>
|
||||
std::vector<double> resolution(int k) {
|
||||
std::vector<TimePoint<Clock>> times;
|
||||
times.reserve(k + 1);
|
||||
std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
|
||||
|
||||
std::vector<double> deltas;
|
||||
deltas.reserve(k);
|
||||
std::transform(std::next(times.begin()), times.end(), times.begin(),
|
||||
std::back_inserter(deltas),
|
||||
[](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
const auto warmup_iterations = 10000;
|
||||
const auto warmup_time = std::chrono::milliseconds(100);
|
||||
const auto minimum_ticks = 1000;
|
||||
const auto warmup_seed = 10000;
|
||||
const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
|
||||
const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
|
||||
const auto clock_cost_estimation_tick_limit = 100000;
|
||||
const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
|
||||
const auto clock_cost_estimation_iterations = 10000;
|
||||
|
||||
template <typename Clock>
|
||||
int warmup() {
|
||||
return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
|
||||
.iterations;
|
||||
}
|
||||
template <typename Clock>
|
||||
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
|
||||
auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
|
||||
.result;
|
||||
return {
|
||||
FloatDuration<Clock>(mean(r.begin(), r.end())),
|
||||
classify_outliers(r.begin(), r.end()),
|
||||
};
|
||||
}
|
||||
template <typename Clock>
|
||||
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
|
||||
auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit));
|
||||
auto time_clock = [](int k) {
|
||||
return Detail::measure<Clock>([k] {
|
||||
for (int i = 0; i < k; ++i) {
|
||||
volatile auto ignored = Clock::now();
|
||||
(void)ignored;
|
||||
}
|
||||
}).elapsed;
|
||||
};
|
||||
time_clock(1);
|
||||
int iters = clock_cost_estimation_iterations;
|
||||
auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
|
||||
std::vector<double> times;
|
||||
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
|
||||
times.reserve(nsamples);
|
||||
std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
|
||||
return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
|
||||
});
|
||||
return {
|
||||
FloatDuration<Clock>(mean(times.begin(), times.end())),
|
||||
classify_outliers(times.begin(), times.end()),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Clock>
|
||||
Environment<FloatDuration<Clock>> measure_environment() {
|
||||
static Environment<FloatDuration<Clock>>* env = nullptr;
|
||||
if (env) {
|
||||
return *env;
|
||||
}
|
||||
|
||||
auto iters = Detail::warmup<Clock>();
|
||||
auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
|
||||
auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
|
||||
|
||||
env = new Environment<FloatDuration<Clock>>{ resolution, cost };
|
||||
return *env;
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED
|
35
include/internal/benchmark/detail/catch_measure.hpp
Normal file
35
include/internal/benchmark/detail/catch_measure.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Measure
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_MEASURE_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_MEASURE_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
#include "catch_timing.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename Clock, typename Fun, typename... Args>
|
||||
TimingOf<Clock, Fun(Args...)> measure(Fun&& fun, Args&&... args) {
|
||||
auto start = Clock::now();
|
||||
auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...);
|
||||
auto end = Clock::now();
|
||||
auto delta = end - start;
|
||||
return { delta, std::forward<decltype(r)>(r), 1 };
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_MEASURE_HPP_INCLUDED
|
37
include/internal/benchmark/detail/catch_repeat.hpp
Normal file
37
include/internal/benchmark/detail/catch_repeat.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// repeat algorithm
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_REPEAT_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_REPEAT_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename Fun>
|
||||
struct repeater {
|
||||
void operator()(int k) const {
|
||||
for (int i = 0; i < k; ++i) {
|
||||
fun();
|
||||
}
|
||||
}
|
||||
Fun fun;
|
||||
};
|
||||
template <typename Fun>
|
||||
repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
|
||||
return { std::forward<Fun>(fun) };
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_REPEAT_HPP_INCLUDED
|
65
include/internal/benchmark/detail/catch_run_for_at_least.hpp
Normal file
65
include/internal/benchmark/detail/catch_run_for_at_least.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Run a function for a minimum amount of time
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_chronometer.hpp"
|
||||
#include "catch_measure.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
#include "catch_timing.hpp"
|
||||
#include "../../catch_meta.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename Clock, typename Fun>
|
||||
TimingOf<Clock, Fun(int)> measure_one(Fun&& fun, int iters, std::false_type) {
|
||||
return Detail::measure<Clock>(fun, iters);
|
||||
}
|
||||
template <typename Clock, typename Fun>
|
||||
TimingOf<Clock, Fun(Chronometer)> measure_one(Fun&& fun, int iters, std::true_type) {
|
||||
Detail::ChronometerModel<Clock> meter;
|
||||
auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
|
||||
|
||||
return { meter.elapsed(), std::move(result), iters };
|
||||
}
|
||||
|
||||
template <typename Clock, typename Fun>
|
||||
using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
|
||||
|
||||
struct optimized_away_error : std::exception {
|
||||
const char* what() const noexcept override {
|
||||
return "could not measure benchmark, maybe it was optimized away";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Clock, typename Fun>
|
||||
TimingOf<Clock, Fun(run_for_at_least_argument_t<Clock, Fun>)> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) {
|
||||
auto iters = seed;
|
||||
while (iters < (1 << 30)) {
|
||||
auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
|
||||
|
||||
if (Timing.elapsed >= how_long) {
|
||||
return { Timing.elapsed, std::move(Timing.result), iters };
|
||||
}
|
||||
iters *= 2;
|
||||
}
|
||||
throw optimized_away_error{};
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED
|
223
include/internal/benchmark/detail/catch_stats.cpp
Normal file
223
include/internal/benchmark/detail/catch_stats.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Created by Martin on 15/06/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Statistical analysis tools
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
|
||||
#include "catch_stats.hpp"
|
||||
|
||||
#include "../../catch_compiler_capabilities.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
|
||||
|
||||
#if defined(CATCH_CONFIG_USE_ASYNC)
|
||||
#include <future>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
double erf_inv(double x) {
|
||||
// Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
|
||||
double w, p;
|
||||
|
||||
w = -log((1.0 - x) * (1.0 + x));
|
||||
|
||||
if (w < 6.250000) {
|
||||
w = w - 3.125000;
|
||||
p = -3.6444120640178196996e-21;
|
||||
p = -1.685059138182016589e-19 + p * w;
|
||||
p = 1.2858480715256400167e-18 + p * w;
|
||||
p = 1.115787767802518096e-17 + p * w;
|
||||
p = -1.333171662854620906e-16 + p * w;
|
||||
p = 2.0972767875968561637e-17 + p * w;
|
||||
p = 6.6376381343583238325e-15 + p * w;
|
||||
p = -4.0545662729752068639e-14 + p * w;
|
||||
p = -8.1519341976054721522e-14 + p * w;
|
||||
p = 2.6335093153082322977e-12 + p * w;
|
||||
p = -1.2975133253453532498e-11 + p * w;
|
||||
p = -5.4154120542946279317e-11 + p * w;
|
||||
p = 1.051212273321532285e-09 + p * w;
|
||||
p = -4.1126339803469836976e-09 + p * w;
|
||||
p = -2.9070369957882005086e-08 + p * w;
|
||||
p = 4.2347877827932403518e-07 + p * w;
|
||||
p = -1.3654692000834678645e-06 + p * w;
|
||||
p = -1.3882523362786468719e-05 + p * w;
|
||||
p = 0.0001867342080340571352 + p * w;
|
||||
p = -0.00074070253416626697512 + p * w;
|
||||
p = -0.0060336708714301490533 + p * w;
|
||||
p = 0.24015818242558961693 + p * w;
|
||||
p = 1.6536545626831027356 + p * w;
|
||||
} else if (w < 16.000000) {
|
||||
w = sqrt(w) - 3.250000;
|
||||
p = 2.2137376921775787049e-09;
|
||||
p = 9.0756561938885390979e-08 + p * w;
|
||||
p = -2.7517406297064545428e-07 + p * w;
|
||||
p = 1.8239629214389227755e-08 + p * w;
|
||||
p = 1.5027403968909827627e-06 + p * w;
|
||||
p = -4.013867526981545969e-06 + p * w;
|
||||
p = 2.9234449089955446044e-06 + p * w;
|
||||
p = 1.2475304481671778723e-05 + p * w;
|
||||
p = -4.7318229009055733981e-05 + p * w;
|
||||
p = 6.8284851459573175448e-05 + p * w;
|
||||
p = 2.4031110387097893999e-05 + p * w;
|
||||
p = -0.0003550375203628474796 + p * w;
|
||||
p = 0.00095328937973738049703 + p * w;
|
||||
p = -0.0016882755560235047313 + p * w;
|
||||
p = 0.0024914420961078508066 + p * w;
|
||||
p = -0.0037512085075692412107 + p * w;
|
||||
p = 0.005370914553590063617 + p * w;
|
||||
p = 1.0052589676941592334 + p * w;
|
||||
p = 3.0838856104922207635 + p * w;
|
||||
} else {
|
||||
w = sqrt(w) - 5.000000;
|
||||
p = -2.7109920616438573243e-11;
|
||||
p = -2.5556418169965252055e-10 + p * w;
|
||||
p = 1.5076572693500548083e-09 + p * w;
|
||||
p = -3.7894654401267369937e-09 + p * w;
|
||||
p = 7.6157012080783393804e-09 + p * w;
|
||||
p = -1.4960026627149240478e-08 + p * w;
|
||||
p = 2.9147953450901080826e-08 + p * w;
|
||||
p = -6.7711997758452339498e-08 + p * w;
|
||||
p = 2.2900482228026654717e-07 + p * w;
|
||||
p = -9.9298272942317002539e-07 + p * w;
|
||||
p = 4.5260625972231537039e-06 + p * w;
|
||||
p = -1.9681778105531670567e-05 + p * w;
|
||||
p = 7.5995277030017761139e-05 + p * w;
|
||||
p = -0.00021503011930044477347 + p * w;
|
||||
p = -0.00013871931833623122026 + p * w;
|
||||
p = 1.0103004648645343977 + p * w;
|
||||
p = 4.8499064014085844221 + p * w;
|
||||
}
|
||||
return p * x;
|
||||
}
|
||||
|
||||
double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
|
||||
auto m = Catch::Benchmark::Detail::mean(first, last);
|
||||
double variance = std::accumulate(first, last, 0., [m](double a, double b) {
|
||||
double diff = b - m;
|
||||
return a + diff * diff;
|
||||
}) / (last - first);
|
||||
return std::sqrt(variance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
|
||||
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
|
||||
auto count = last - first;
|
||||
double idx = (count - 1) * k / static_cast<double>(q);
|
||||
int j = static_cast<int>(idx);
|
||||
double g = idx - j;
|
||||
std::nth_element(first, first + j, last);
|
||||
auto xj = first[j];
|
||||
if (g == 0) return xj;
|
||||
|
||||
auto xj1 = *std::min_element(first + (j + 1), last);
|
||||
return xj + g * (xj1 - xj);
|
||||
}
|
||||
|
||||
|
||||
double erfc_inv(double x) {
|
||||
return erf_inv(1.0 - x);
|
||||
}
|
||||
|
||||
double normal_quantile(double p) {
|
||||
static const double ROOT_TWO = std::sqrt(2.0);
|
||||
|
||||
double result = 0.0;
|
||||
assert(p >= 0 && p <= 1);
|
||||
if (p < 0 || p > 1) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = -erfc_inv(2.0 * p);
|
||||
// result *= normal distribution standard deviation (1.0) * sqrt(2)
|
||||
result *= /*sd * */ ROOT_TWO;
|
||||
// result += normal disttribution mean (0)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
|
||||
double sb = stddev.point;
|
||||
double mn = mean.point / n;
|
||||
double mg_min = mn / 2.;
|
||||
double sg = std::min(mg_min / 4., sb / std::sqrt(n));
|
||||
double sg2 = sg * sg;
|
||||
double sb2 = sb * sb;
|
||||
|
||||
auto c_max = [n, mn, sb2, sg2](double x) -> double {
|
||||
double k = mn - x;
|
||||
double d = k * k;
|
||||
double nd = n * d;
|
||||
double k0 = -n * nd;
|
||||
double k1 = sb2 - n * sg2 + nd;
|
||||
double det = k1 * k1 - 4 * sg2 * k0;
|
||||
return (int)(-2. * k0 / (k1 + std::sqrt(det)));
|
||||
};
|
||||
|
||||
auto var_out = [n, sb2, sg2](double c) {
|
||||
double nc = n - c;
|
||||
return (nc / n) * (sb2 - nc * sg2);
|
||||
};
|
||||
|
||||
return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2;
|
||||
}
|
||||
|
||||
|
||||
bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||
static std::random_device entropy;
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
|
||||
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
|
||||
|
||||
auto mean = &Detail::mean<std::vector<double>::iterator>;
|
||||
auto stddev = &standard_deviation;
|
||||
|
||||
#if defined(CATCH_CONFIG_USE_ASYNC)
|
||||
auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
|
||||
auto seed = entropy();
|
||||
return std::async(std::launch::async, [=] {
|
||||
std::mt19937 rng(seed);
|
||||
auto resampled = resample(rng, n_resamples, first, last, f);
|
||||
return bootstrap(confidence_level, first, last, resampled, f);
|
||||
});
|
||||
};
|
||||
|
||||
auto mean_future = Estimate(mean);
|
||||
auto stddev_future = Estimate(stddev);
|
||||
|
||||
auto mean_estimate = mean_future.get();
|
||||
auto stddev_estimate = stddev_future.get();
|
||||
#else
|
||||
auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
|
||||
auto seed = entropy();
|
||||
std::mt19937 rng(seed);
|
||||
auto resampled = resample(rng, n_resamples, first, last, f);
|
||||
return bootstrap(confidence_level, first, last, resampled, f);
|
||||
};
|
||||
|
||||
auto mean_estimate = Estimate(mean);
|
||||
auto stddev_estimate = Estimate(stddev);
|
||||
#endif // CATCH_USE_ASYNC
|
||||
|
||||
double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
|
||||
|
||||
return { mean_estimate, stddev_estimate, outlier_variance };
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
158
include/internal/benchmark/detail/catch_stats.hpp
Normal file
158
include/internal/benchmark/detail/catch_stats.hpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Statistical analysis tools
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_ANALYSIS_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_ANALYSIS_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "../catch_estimate.hpp"
|
||||
#include "../catch_outlier_classification.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
using sample = std::vector<double>;
|
||||
|
||||
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
|
||||
|
||||
template <typename Iterator>
|
||||
OutlierClassification classify_outliers(Iterator first, Iterator last) {
|
||||
std::vector<double> copy(first, last);
|
||||
|
||||
auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
|
||||
auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
|
||||
auto iqr = q3 - q1;
|
||||
auto los = q1 - (iqr * 3.);
|
||||
auto lom = q1 - (iqr * 1.5);
|
||||
auto him = q3 + (iqr * 1.5);
|
||||
auto his = q3 + (iqr * 3.);
|
||||
|
||||
OutlierClassification o;
|
||||
for (; first != last; ++first) {
|
||||
auto&& t = *first;
|
||||
if (t < los) ++o.low_severe;
|
||||
else if (t < lom) ++o.low_mild;
|
||||
else if (t > his) ++o.high_severe;
|
||||
else if (t > him) ++o.high_mild;
|
||||
++o.samples_seen;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
double mean(Iterator first, Iterator last) {
|
||||
auto count = last - first;
|
||||
double sum = std::accumulate(first, last, 0.);
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
template <typename URng, typename Iterator, typename Estimator>
|
||||
sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) {
|
||||
auto n = last - first;
|
||||
std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
|
||||
|
||||
sample out;
|
||||
out.reserve(resamples);
|
||||
std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
|
||||
std::vector<double> resampled;
|
||||
resampled.reserve(n);
|
||||
std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
|
||||
return estimator(resampled.begin(), resampled.end());
|
||||
});
|
||||
std::sort(out.begin(), out.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Estimator, typename Iterator>
|
||||
sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
|
||||
auto n = last - first;
|
||||
auto second = std::next(first);
|
||||
sample results;
|
||||
results.reserve(n);
|
||||
|
||||
for (auto it = first; it != last; ++it) {
|
||||
std::iter_swap(it, first);
|
||||
results.push_back(estimator(second, last));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
inline double normal_cdf(double x) {
|
||||
return std::erfc(-x / std::sqrt(2.0)) / 2.0;
|
||||
}
|
||||
|
||||
double erfc_inv(double x);
|
||||
|
||||
double normal_quantile(double p);
|
||||
|
||||
template <typename Iterator, typename Estimator>
|
||||
Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
|
||||
auto n_samples = last - first;
|
||||
|
||||
double point = estimator(first, last);
|
||||
// Degenerate case with a single sample
|
||||
if (n_samples == 1) return { point, point, point, confidence_level };
|
||||
|
||||
sample jack = jackknife(estimator, first, last);
|
||||
double jack_mean = mean(jack.begin(), jack.end());
|
||||
double sum_squares, sum_cubes;
|
||||
std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
|
||||
auto d = jack_mean - x;
|
||||
auto d2 = d * d;
|
||||
auto d3 = d2 * d;
|
||||
return { sqcb.first + d2, sqcb.second + d3 };
|
||||
});
|
||||
|
||||
double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
|
||||
int n = static_cast<int>(resample.size());
|
||||
double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n;
|
||||
// degenerate case with uniform samples
|
||||
if (prob_n == 0) return { point, point, point, confidence_level };
|
||||
|
||||
double bias = normal_quantile(prob_n);
|
||||
double z1 = normal_quantile((1. - confidence_level) / 2.);
|
||||
|
||||
auto cumn = [n](double x) -> int {
|
||||
return std::lround(normal_cdf(x) * n); };
|
||||
auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
|
||||
double b1 = bias + z1;
|
||||
double b2 = bias - z1;
|
||||
double a1 = a(b1);
|
||||
double a2 = a(b2);
|
||||
auto lo = std::max(cumn(a1), 0);
|
||||
auto hi = std::min(cumn(a2), n - 1);
|
||||
|
||||
return { point, resample[lo], resample[hi], confidence_level };
|
||||
}
|
||||
|
||||
double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
|
||||
|
||||
struct bootstrap_analysis {
|
||||
Estimate<double> mean;
|
||||
Estimate<double> standard_deviation;
|
||||
double outlier_variance;
|
||||
};
|
||||
|
||||
bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_ANALYSIS_HPP_INCLUDED
|
33
include/internal/benchmark/detail/catch_timing.hpp
Normal file
33
include/internal/benchmark/detail/catch_timing.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Created by Joachim on 16/04/2019.
|
||||
* Adapted from donated nonius code.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Timing
|
||||
|
||||
#ifndef TWOBLUECUBES_CATCH_DETAIL_TIMING_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_DETAIL_TIMING_HPP_INCLUDED
|
||||
|
||||
#include "../catch_clock.hpp"
|
||||
#include "catch_complete_invoke.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
template <typename Duration, typename Result>
|
||||
struct Timing {
|
||||
Duration elapsed;
|
||||
Result result;
|
||||
int iterations;
|
||||
};
|
||||
template <typename Clock, typename Sig>
|
||||
using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<Detail::ResultOf_t<Sig>>>;
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DETAIL_TIMING_HPP_INCLUDED
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Created by Phil on 04/07/2017.
|
||||
* Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#include "catch_benchmark.h"
|
||||
#include "catch_capture.hpp"
|
||||
#include "catch_interfaces_reporter.h"
|
||||
#include "catch_context.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
auto BenchmarkLooper::getResolution() -> uint64_t {
|
||||
return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple();
|
||||
}
|
||||
|
||||
void BenchmarkLooper::reportStart() {
|
||||
getResultCapture().benchmarkStarting( { m_name } );
|
||||
}
|
||||
auto BenchmarkLooper::needsMoreIterations() -> bool {
|
||||
auto elapsed = m_timer.getElapsedNanoseconds();
|
||||
|
||||
// Exponentially increasing iterations until we're confident in our timer resolution
|
||||
if( elapsed < m_resolution ) {
|
||||
m_iterationsToRun *= 10;
|
||||
return true;
|
||||
}
|
||||
|
||||
getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } );
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Created by Phil on 04/07/2017.
|
||||
* Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED
|
||||
|
||||
#include "catch_stringref.h"
|
||||
#include "catch_timer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class BenchmarkLooper {
|
||||
|
||||
std::string m_name;
|
||||
std::size_t m_count = 0;
|
||||
std::size_t m_iterationsToRun = 1;
|
||||
uint64_t m_resolution;
|
||||
Timer m_timer;
|
||||
|
||||
static auto getResolution() -> uint64_t;
|
||||
public:
|
||||
// Keep most of this inline as it's on the code path that is being timed
|
||||
BenchmarkLooper( StringRef name )
|
||||
: m_name( name ),
|
||||
m_resolution( getResolution() )
|
||||
{
|
||||
reportStart();
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
explicit operator bool() {
|
||||
if( m_count < m_iterationsToRun )
|
||||
return true;
|
||||
return needsMoreIterations();
|
||||
}
|
||||
|
||||
void increment() {
|
||||
++m_count;
|
||||
}
|
||||
|
||||
void reportStart();
|
||||
auto needsMoreIterations() -> bool;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#define BENCHMARK( name ) \
|
||||
for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() )
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_BENCHMARK_H_INCLUDED
|
@@ -196,11 +196,19 @@ namespace Catch {
|
||||
| Opt( setWaitForKeypress, "start|exit|both" )
|
||||
["--wait-for-keypress"]
|
||||
( "waits for a keypress before exiting" )
|
||||
| Opt( config.benchmarkResolutionMultiple, "multiplier" )
|
||||
["--benchmark-resolution-multiple"]
|
||||
( "multiple of clock resolution to run benchmarks" )
|
||||
|
||||
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||
| Opt( config.benchmarkSamples, "samples" )
|
||||
["--benchmark-samples"]
|
||||
( "number of samples to collect (default: 100)" )
|
||||
| Opt( config.benchmarkResamples, "resamples" )
|
||||
["--benchmark-resamples"]
|
||||
( "number of resamples for the bootstrap (default: 100000)" )
|
||||
| Opt( config.benchmarkConfidenceInterval, "confidence interval" )
|
||||
["--benchmark-confidence-interval"]
|
||||
( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
|
||||
| Opt( config.benchmarkNoAnalysis )
|
||||
["--benchmark-no-analysis"]
|
||||
( "perform only measurements; do not perform any analysis" )
|
||||
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||
( "which test or tests to use" );
|
||||
|
||||
return cli;
|
||||
|
@@ -64,6 +64,12 @@
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
|
||||
_Pragma( "clang diagnostic pop" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
_Pragma( "clang diagnostic push" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
_Pragma( "clang diagnostic pop" )
|
||||
|
||||
#endif // __clang__
|
||||
|
||||
|
||||
@@ -112,9 +118,9 @@
|
||||
// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
|
||||
# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
|
||||
&& !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
|
||||
&& !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
|
||||
|
||||
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
||||
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
||||
|
||||
# endif
|
||||
#endif // __CYGWIN__
|
||||
@@ -142,7 +148,11 @@
|
||||
# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
|
||||
# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
# endif
|
||||
#endif // _MSC_VER
|
||||
|
||||
#if defined(_REENTRANT) || defined(_MSC_VER)
|
||||
// Enable async processing, as -pthread is specified or no additional linking is required
|
||||
# define CATCH_INTERNAL_CONFIG_USE_ASYNC
|
||||
#endif // _MSC_VER
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -174,6 +184,18 @@
|
||||
#define CATCH_INTERNAL_CONFIG_COUNTER
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// RTX is a special version of Windows that is real time.
|
||||
// This means that it is detected as Windows, but does not provide
|
||||
// the same set of capabilities as real Windows does.
|
||||
#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
|
||||
#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
|
||||
#define CATCH_INTERNAL_CONFIG_NO_ASYNC
|
||||
#define CATCH_CONFIG_COLOUR_NONE
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Check if string_view is available and usable
|
||||
// The check is split apart to work around v140 (VS2015) preprocessor issue...
|
||||
@@ -191,6 +213,14 @@
|
||||
# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
#endif // __has_include
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Check if byte is available and usable
|
||||
#if defined(__has_include)
|
||||
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
||||
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
#endif // __has_include
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Check if variant is available and usable
|
||||
#if defined(__has_include)
|
||||
@@ -246,6 +276,11 @@
|
||||
# define CATCH_CONFIG_CPP17_VARIANT
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
|
||||
# define CATCH_CONFIG_CPP17_BYTE
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
|
||||
# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
|
||||
#endif
|
||||
@@ -262,6 +297,10 @@
|
||||
# define CATCH_CONFIG_POLYFILL_ISNAN
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
|
||||
# define CATCH_CONFIG_USE_ASYNC
|
||||
#endif
|
||||
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
|
||||
@@ -274,6 +313,10 @@
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
#define CATCH_TRY if ((true))
|
||||
|
@@ -32,7 +32,7 @@ namespace Catch {
|
||||
bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; }
|
||||
bool Config::listTags() const { return m_data.listTags; }
|
||||
bool Config::listReporters() const { return m_data.listReporters; }
|
||||
|
||||
|
||||
std::string Config::getProcessName() const { return m_data.processName; }
|
||||
std::string const& Config::getReporterName() const { return m_data.reporterName; }
|
||||
|
||||
@@ -54,13 +54,17 @@ namespace Catch {
|
||||
ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
|
||||
RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; }
|
||||
unsigned int Config::rngSeed() const { return m_data.rngSeed; }
|
||||
int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; }
|
||||
UseColour::YesOrNo Config::useColour() const { return m_data.useColour; }
|
||||
bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; }
|
||||
int Config::abortAfter() const { return m_data.abortAfter; }
|
||||
bool Config::showInvisibles() const { return m_data.showInvisibles; }
|
||||
Verbosity Config::verbosity() const { return m_data.verbosity; }
|
||||
|
||||
bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
|
||||
int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
|
||||
double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
|
||||
unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
|
||||
|
||||
IStream const* Config::openStream() {
|
||||
return Catch::makeStream(m_data.outputFilename);
|
||||
}
|
||||
|
@@ -42,7 +42,11 @@ namespace Catch {
|
||||
|
||||
int abortAfter = -1;
|
||||
unsigned int rngSeed = 0;
|
||||
int benchmarkResolutionMultiple = 100;
|
||||
|
||||
bool benchmarkNoAnalysis = false;
|
||||
unsigned int benchmarkSamples = 100;
|
||||
double benchmarkConfidenceInterval = 0.95;
|
||||
unsigned int benchmarkResamples = 100000;
|
||||
|
||||
Verbosity verbosity = Verbosity::Normal;
|
||||
WarnAbout::What warnings = WarnAbout::Nothing;
|
||||
@@ -100,12 +104,15 @@ namespace Catch {
|
||||
ShowDurations::OrNot showDurations() const override;
|
||||
RunTests::InWhatOrder runOrder() const override;
|
||||
unsigned int rngSeed() const override;
|
||||
int benchmarkResolutionMultiple() const override;
|
||||
UseColour::YesOrNo useColour() const override;
|
||||
bool shouldDebugBreak() const override;
|
||||
int abortAfter() const override;
|
||||
bool showInvisibles() const override;
|
||||
Verbosity verbosity() const override;
|
||||
bool benchmarkNoAnalysis() const override;
|
||||
int benchmarkSamples() const override;
|
||||
double benchmarkConfidenceInterval() const override;
|
||||
unsigned int benchmarkResamples() const override;
|
||||
|
||||
private:
|
||||
|
||||
|
@@ -222,7 +222,13 @@ namespace Catch {
|
||||
|
||||
void Colour::use( Code _colourCode ) {
|
||||
static IColourImpl* impl = platformColourInstance();
|
||||
impl->use( _colourCode );
|
||||
// Strictly speaking, this cannot possibly happen.
|
||||
// However, under some conditions it does happen (see #1626),
|
||||
// and this change is small enough that we can let practicality
|
||||
// triumph over purity in this case.
|
||||
if (impl != NULL) {
|
||||
impl->use( _colourCode );
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator << ( std::ostream& os, Colour const& ) {
|
||||
|
@@ -11,7 +11,16 @@
|
||||
#include "catch_platform.h"
|
||||
#include "catch_windows_h_proxy.h"
|
||||
|
||||
#ifdef CATCH_PLATFORM_WINDOWS
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
|
||||
namespace Catch {
|
||||
void writeToDebugConsole( std::string const& text ) {
|
||||
__android_log_print( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(CATCH_PLATFORM_WINDOWS)
|
||||
|
||||
namespace Catch {
|
||||
void writeToDebugConsole( std::string const& text ) {
|
||||
|
@@ -7,6 +7,9 @@
|
||||
|
||||
#include "catch_enforce.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace Catch {
|
||||
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
|
||||
[[noreturn]]
|
||||
@@ -16,4 +19,22 @@ namespace Catch {
|
||||
std::terminate();
|
||||
}
|
||||
#endif
|
||||
|
||||
[[noreturn]]
|
||||
void throw_logic_error(std::string const& msg) {
|
||||
throw_exception(std::logic_error(msg));
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void throw_domain_error(std::string const& msg) {
|
||||
throw_exception(std::domain_error(msg));
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void throw_runtime_error(std::string const& msg) {
|
||||
throw_exception(std::runtime_error(msg));
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Catch;
|
||||
|
@@ -11,8 +11,7 @@
|
||||
#include "catch_compiler_capabilities.h"
|
||||
#include "catch_stream.h"
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <exception>
|
||||
|
||||
namespace Catch {
|
||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
@@ -25,18 +24,30 @@ namespace Catch {
|
||||
[[noreturn]]
|
||||
void throw_exception(std::exception const& e);
|
||||
#endif
|
||||
|
||||
[[noreturn]]
|
||||
void throw_logic_error(std::string const& msg);
|
||||
[[noreturn]]
|
||||
void throw_domain_error(std::string const& msg);
|
||||
[[noreturn]]
|
||||
void throw_runtime_error(std::string const& msg);
|
||||
|
||||
} // namespace Catch;
|
||||
|
||||
#define CATCH_PREPARE_EXCEPTION( type, msg ) \
|
||||
type( ( Catch::ReusableStringStream() << msg ).str() )
|
||||
#define CATCH_INTERNAL_ERROR( msg ) \
|
||||
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg))
|
||||
#define CATCH_ERROR( msg ) \
|
||||
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg ))
|
||||
#define CATCH_RUNTIME_ERROR( msg ) \
|
||||
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg ))
|
||||
#define CATCH_ENFORCE( condition, msg ) \
|
||||
do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
|
||||
#define CATCH_MAKE_MSG(...) \
|
||||
(Catch::ReusableStringStream() << __VA_ARGS__).str()
|
||||
|
||||
#define CATCH_INTERNAL_ERROR(...) \
|
||||
Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__));
|
||||
|
||||
#define CATCH_ERROR(...) \
|
||||
Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ));
|
||||
|
||||
#define CATCH_RUNTIME_ERROR(...) \
|
||||
Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ));
|
||||
|
||||
#define CATCH_ENFORCE( condition, ... ) \
|
||||
do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
|
||||
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED
|
||||
|
65
include/internal/catch_enum_values_registry.cpp
Normal file
65
include/internal/catch_enum_values_registry.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Created by Phil on 4/4/2019.
|
||||
* Copyright 2019 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#include "catch_enum_values_registry.h"
|
||||
#include "catch_string_manip.h"
|
||||
#include "catch_stream.h"
|
||||
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
|
||||
|
||||
namespace Detail {
|
||||
|
||||
std::vector<std::string> parseEnums( StringRef enums ) {
|
||||
auto enumValues = splitStringRef( enums, ',' );
|
||||
std::vector<std::string> parsed;
|
||||
parsed.reserve( enumValues.size() );
|
||||
for( auto const& enumValue : enumValues ) {
|
||||
auto identifiers = splitStringRef( enumValue, ':' );
|
||||
parsed.push_back( Catch::trim( identifiers.back() ) );
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
EnumInfo::~EnumInfo() {}
|
||||
|
||||
StringRef EnumInfo::lookup( int value ) const {
|
||||
for( auto const& valueToName : m_values ) {
|
||||
if( valueToName.first == value )
|
||||
return valueToName.second;
|
||||
}
|
||||
return "{** unexpected enum value **}";
|
||||
}
|
||||
|
||||
std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
|
||||
std::unique_ptr<EnumInfo> enumInfo( new EnumInfo );
|
||||
enumInfo->m_name = enumName;
|
||||
enumInfo->m_values.reserve( values.size() );
|
||||
|
||||
const auto valueNames = Catch::Detail::parseEnums( allValueNames );
|
||||
assert( valueNames.size() == values.size() );
|
||||
std::size_t i = 0;
|
||||
for( auto value : values )
|
||||
enumInfo->m_values.push_back({ value, valueNames[i++] });
|
||||
|
||||
return enumInfo;
|
||||
}
|
||||
|
||||
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
|
||||
auto enumInfo = makeEnumInfo( enumName, allValueNames, values );
|
||||
EnumInfo* raw = enumInfo.get();
|
||||
m_enumInfos.push_back( std::move( enumInfo ) );
|
||||
return *raw;
|
||||
}
|
||||
|
||||
} // Detail
|
||||
} // Catch
|
||||
|
35
include/internal/catch_enum_values_registry.h
Normal file
35
include/internal/catch_enum_values_registry.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Created by Phil on 4/4/2019.
|
||||
* Copyright 2019 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED
|
||||
|
||||
#include "catch_interfaces_enum_values_registry.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
|
||||
|
||||
class EnumValuesRegistry : public IMutableEnumValuesRegistry {
|
||||
|
||||
std::vector<std::unique_ptr<EnumInfo>> m_enumInfos;
|
||||
|
||||
EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
|
||||
};
|
||||
|
||||
std::vector<std::string> parseEnums( StringRef enums );
|
||||
|
||||
} // Detail
|
||||
|
||||
} // Catch
|
||||
|
||||
#endif //TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED
|
@@ -97,7 +97,7 @@ namespace Catch {
|
||||
|
||||
// 32kb for the alternate stack seems to be sufficient. However, this value
|
||||
// is experimentally determined, so that's not guaranteed.
|
||||
constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
|
||||
static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
|
||||
|
||||
static SignalDefs signalDefs[] = {
|
||||
{ SIGINT, "SIGINT - Terminal interrupt signal" },
|
||||
|
@@ -70,6 +70,9 @@ namespace Generators {
|
||||
|
||||
template<typename T>
|
||||
class FixedValuesGenerator final : public IGenerator<T> {
|
||||
static_assert(!std::is_same<T, bool>::value,
|
||||
"ValuesGenerator does not support bools because of std::vector<bool>"
|
||||
"specialization, use SingleValue Generator instead.");
|
||||
std::vector<T> m_values;
|
||||
size_t m_idx = 0;
|
||||
public:
|
||||
|
@@ -91,6 +91,9 @@ namespace Generators {
|
||||
|
||||
template <typename T>
|
||||
class RepeatGenerator : public IGenerator<T> {
|
||||
static_assert(!std::is_same<T, bool>::value,
|
||||
"RepeatGenerator currently does not support bools"
|
||||
"because of std::vector<bool> specialization");
|
||||
GeneratorWrapper<T> m_generator;
|
||||
mutable std::vector<T> m_returned;
|
||||
size_t m_target_repeats;
|
||||
@@ -205,12 +208,14 @@ namespace Generators {
|
||||
m_chunk_size(size), m_generator(std::move(generator))
|
||||
{
|
||||
m_chunk.reserve(m_chunk_size);
|
||||
m_chunk.push_back(m_generator.get());
|
||||
for (size_t i = 1; i < m_chunk_size; ++i) {
|
||||
if (!m_generator.next()) {
|
||||
Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
|
||||
}
|
||||
if (m_chunk_size != 0) {
|
||||
m_chunk.push_back(m_generator.get());
|
||||
for (size_t i = 1; i < m_chunk_size; ++i) {
|
||||
if (!m_generator.next()) {
|
||||
Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
|
||||
}
|
||||
m_chunk.push_back(m_generator.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<T> const& get() const override {
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include "catch_stringref.h"
|
||||
#include "catch_result_type.h"
|
||||
@@ -22,14 +23,18 @@ namespace Catch {
|
||||
struct MessageInfo;
|
||||
struct MessageBuilder;
|
||||
struct Counts;
|
||||
struct BenchmarkInfo;
|
||||
struct BenchmarkStats;
|
||||
struct AssertionReaction;
|
||||
struct SourceLineInfo;
|
||||
|
||||
struct ITransientExpression;
|
||||
struct IGeneratorTracker;
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
struct BenchmarkInfo;
|
||||
template <typename Duration = std::chrono::duration<double, std::nano>>
|
||||
struct BenchmarkStats;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
struct IResultCapture {
|
||||
|
||||
virtual ~IResultCapture();
|
||||
@@ -41,8 +46,12 @@ namespace Catch {
|
||||
|
||||
virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
virtual void benchmarkPreparing( std::string const& name ) = 0;
|
||||
virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
|
||||
virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0;
|
||||
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
|
||||
virtual void benchmarkFailed( std::string const& error ) = 0;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
|
||||
virtual void popScopedMessage( MessageInfo const& message ) = 0;
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
|
||||
|
||||
#include "catch_common.h"
|
||||
#include "catch_option.hpp"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
@@ -50,7 +51,7 @@ namespace Catch {
|
||||
BeforeExit = 2,
|
||||
BeforeStartAndExit = BeforeStart | BeforeExit
|
||||
}; };
|
||||
|
||||
|
||||
class TestSpec;
|
||||
|
||||
struct IConfig : NonCopyable {
|
||||
@@ -72,10 +73,14 @@ namespace Catch {
|
||||
virtual std::vector<std::string> const& getTestsOrTags() const = 0;
|
||||
virtual RunTests::InWhatOrder runOrder() const = 0;
|
||||
virtual unsigned int rngSeed() const = 0;
|
||||
virtual int benchmarkResolutionMultiple() const = 0;
|
||||
virtual UseColour::YesOrNo useColour() const = 0;
|
||||
virtual std::vector<std::string> const& getSectionsToRun() const = 0;
|
||||
virtual Verbosity verbosity() const = 0;
|
||||
|
||||
virtual bool benchmarkNoAnalysis() const = 0;
|
||||
virtual int benchmarkSamples() const = 0;
|
||||
virtual double benchmarkConfidenceInterval() const = 0;
|
||||
virtual unsigned int benchmarkResamples() const = 0;
|
||||
};
|
||||
|
||||
using IConfigPtr = std::shared_ptr<IConfig const>;
|
||||
|
45
include/internal/catch_interfaces_enum_values_registry.h
Normal file
45
include/internal/catch_interfaces_enum_values_registry.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Created by Phil on 4/4/2019.
|
||||
* Copyright 2019 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED
|
||||
|
||||
#include "catch_stringref.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace Detail {
|
||||
struct EnumInfo {
|
||||
StringRef m_name;
|
||||
std::vector<std::pair<int, std::string>> m_values;
|
||||
|
||||
~EnumInfo();
|
||||
|
||||
StringRef lookup( int value ) const;
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
struct IMutableEnumValuesRegistry {
|
||||
virtual ~IMutableEnumValuesRegistry();
|
||||
|
||||
virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
|
||||
|
||||
template<typename E>
|
||||
Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
|
||||
std::vector<int> intValues;
|
||||
intValues.reserve( values.size() );
|
||||
for( auto enumValue : values )
|
||||
intValues.push_back( static_cast<int>( enumValue ) );
|
||||
return registerEnum( enumName, allEnums, intValues );
|
||||
}
|
||||
};
|
||||
|
||||
} // Catch
|
||||
|
||||
#endif //TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED
|
@@ -22,6 +22,8 @@ namespace Catch {
|
||||
struct IReporterRegistry;
|
||||
struct IReporterFactory;
|
||||
struct ITagAliasRegistry;
|
||||
struct IMutableEnumValuesRegistry;
|
||||
|
||||
class StartupExceptionRegistry;
|
||||
|
||||
using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
|
||||
@@ -32,7 +34,6 @@ namespace Catch {
|
||||
virtual IReporterRegistry const& getReporterRegistry() const = 0;
|
||||
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
|
||||
virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
|
||||
|
||||
virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
|
||||
|
||||
|
||||
@@ -47,6 +48,7 @@ namespace Catch {
|
||||
virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
|
||||
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
|
||||
virtual void registerStartupException() noexcept = 0;
|
||||
virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
|
||||
};
|
||||
|
||||
IRegistryHub const& getRegistryHub();
|
||||
|
@@ -18,12 +18,18 @@
|
||||
#include "catch_option.hpp"
|
||||
#include "catch_stringref.h"
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#include "benchmark/catch_estimate.hpp"
|
||||
#include "benchmark/catch_outlier_classification.hpp"
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -159,14 +165,43 @@ namespace Catch {
|
||||
bool aborting;
|
||||
};
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
struct BenchmarkInfo {
|
||||
std::string name;
|
||||
double estimatedDuration;
|
||||
int iterations;
|
||||
int samples;
|
||||
unsigned int resamples;
|
||||
double clockResolution;
|
||||
double clockCost;
|
||||
};
|
||||
|
||||
template <class Duration>
|
||||
struct BenchmarkStats {
|
||||
BenchmarkInfo info;
|
||||
std::size_t iterations;
|
||||
uint64_t elapsedTimeInNanoseconds;
|
||||
|
||||
std::vector<Duration> samples;
|
||||
Benchmark::Estimate<Duration> mean;
|
||||
Benchmark::Estimate<Duration> standardDeviation;
|
||||
Benchmark::OutlierClassification outliers;
|
||||
double outlierVariance;
|
||||
|
||||
template <typename Duration2>
|
||||
operator BenchmarkStats<Duration2>() const {
|
||||
std::vector<Duration2> samples2;
|
||||
samples2.reserve(samples.size());
|
||||
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
|
||||
return {
|
||||
info,
|
||||
std::move(samples2),
|
||||
mean,
|
||||
standardDeviation,
|
||||
outliers,
|
||||
outlierVariance,
|
||||
};
|
||||
}
|
||||
};
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
struct IStreamingReporter {
|
||||
virtual ~IStreamingReporter() = default;
|
||||
@@ -185,17 +220,18 @@ namespace Catch {
|
||||
virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
|
||||
virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
|
||||
|
||||
// *** experimental ***
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
virtual void benchmarkPreparing( std::string const& ) {}
|
||||
virtual void benchmarkStarting( BenchmarkInfo const& ) {}
|
||||
virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
|
||||
virtual void benchmarkFailed( std::string const& ) {}
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
|
||||
|
||||
// The return value indicates if the messages buffer should be cleared:
|
||||
virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
|
||||
|
||||
// *** experimental ***
|
||||
virtual void benchmarkEnded( BenchmarkStats const& ) {}
|
||||
|
||||
virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
|
||||
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
|
||||
virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
|
||||
|
@@ -28,6 +28,7 @@ namespace Catch {
|
||||
virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
|
||||
};
|
||||
|
||||
bool isThrowSafe( TestCase const& testCase, IConfig const& config );
|
||||
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
|
||||
std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
|
||||
std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
|
||||
|
@@ -84,9 +84,18 @@ namespace Catch {
|
||||
}
|
||||
|
||||
std::string TagInfo::all() const {
|
||||
std::string out;
|
||||
for( auto const& spelling : spellings )
|
||||
out += "[" + spelling + "]";
|
||||
size_t size = 0;
|
||||
for (auto const& spelling : spellings) {
|
||||
// Add 2 for the brackes
|
||||
size += spelling.size() + 2;
|
||||
}
|
||||
|
||||
std::string out; out.reserve(size);
|
||||
for (auto const& spelling : spellings) {
|
||||
out += '[';
|
||||
out += spelling;
|
||||
out += ']';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,15 @@ namespace Matchers {
|
||||
virtual bool match( ObjectT const& arg ) const = 0;
|
||||
};
|
||||
|
||||
#if defined(__OBJC__)
|
||||
// Hack to fix Catch GH issue #1661. Could use id for generic Object support.
|
||||
// use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
|
||||
template<>
|
||||
struct MatcherMethod<NSString*> {
|
||||
virtual bool match( NSString* arg ) const = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
@@ -14,6 +14,9 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
@@ -74,8 +77,16 @@ bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
|
||||
return ulpDiff <= maxUlpDiff;
|
||||
}
|
||||
|
||||
template <typename FP>
|
||||
FP step(FP start, FP direction, int steps) {
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
start = std::nextafter(start, direction);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
@@ -125,7 +136,29 @@ namespace Floating {
|
||||
#endif
|
||||
|
||||
std::string WithinUlpsMatcher::describe() const {
|
||||
return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
|
||||
std::stringstream ret;
|
||||
|
||||
ret << "is within " << m_ulps << " ULPs of " << ::Catch::Detail::stringify(m_target);
|
||||
|
||||
if (m_type == FloatingPointKind::Float) {
|
||||
ret << 'f';
|
||||
}
|
||||
|
||||
ret << " ([";
|
||||
ret << std::fixed << std::setprecision(std::numeric_limits<double>::max_digits10);
|
||||
if (m_type == FloatingPointKind::Double) {
|
||||
ret << step(m_target, static_cast<double>(-INFINITY), m_ulps)
|
||||
<< ", "
|
||||
<< step(m_target, static_cast<double>(INFINITY), m_ulps);
|
||||
} else {
|
||||
ret << step<float>(static_cast<float>(m_target), -INFINITY, m_ulps)
|
||||
<< ", "
|
||||
<< step<float>(static_cast<float>(m_target), INFINITY, m_ulps);
|
||||
}
|
||||
ret << "])";
|
||||
|
||||
return ret.str();
|
||||
//return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
|
||||
}
|
||||
|
||||
}// namespace Floating
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "catch_message.h"
|
||||
#include "catch_interfaces_capture.h"
|
||||
#include "catch_uncaught_exceptions.h"
|
||||
#include "catch_enforce.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <stack>
|
||||
@@ -76,6 +77,15 @@ namespace Catch {
|
||||
}
|
||||
return names.substr(start, end - start + 1);
|
||||
};
|
||||
auto skipq = [&] (size_t start, char quote) {
|
||||
for (auto i = start + 1; i < names.size() ; ++i) {
|
||||
if (names[i] == quote)
|
||||
return i;
|
||||
if (names[i] == '\\')
|
||||
++i;
|
||||
}
|
||||
CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
|
||||
};
|
||||
|
||||
size_t start = 0;
|
||||
std::stack<char> openings;
|
||||
@@ -96,6 +106,10 @@ namespace Catch {
|
||||
// case '>':
|
||||
openings.pop();
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
pos = skipq(pos, c);
|
||||
break;
|
||||
case ',':
|
||||
if (start != pos && openings.size() == 0) {
|
||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
||||
|
@@ -12,66 +12,27 @@
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
template< typename... >
|
||||
struct TypeList {};
|
||||
|
||||
template< typename... >
|
||||
struct append;
|
||||
|
||||
template< template<typename...> class L1
|
||||
, typename...E1
|
||||
, template<typename...> class L2
|
||||
, typename...E2
|
||||
>
|
||||
struct append< L1<E1...>, L2<E2...> > {
|
||||
using type = L1<E1..., E2...>;
|
||||
};
|
||||
|
||||
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
|
||||
, typename...
|
||||
>
|
||||
struct rewrap;
|
||||
|
||||
template< template<typename...> class Container
|
||||
, template<typename...> class List
|
||||
, typename...elems
|
||||
>
|
||||
struct rewrap<Container, List<elems...>> {
|
||||
using type = TypeList< Container< elems... > >;
|
||||
};
|
||||
|
||||
template< template<typename...> class Container
|
||||
, template<typename...> class List
|
||||
, class...Elems
|
||||
, typename...Elements>
|
||||
struct rewrap<Container, List<Elems...>, Elements...> {
|
||||
using type = typename append<TypeList<Container<Elems...>>, typename rewrap<Container, Elements...>::type>::type;
|
||||
};
|
||||
|
||||
template< template<typename...> class...Containers >
|
||||
struct combine {
|
||||
template< typename...Types >
|
||||
struct with_types {
|
||||
template< template <typename...> class Final >
|
||||
struct into {
|
||||
using type = typename append<Final<>, typename rewrap<Containers, Types...>::type...>::type;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct always_false : std::false_type {};
|
||||
|
||||
template <typename> struct true_given : std::true_type {};
|
||||
struct is_callable_tester {
|
||||
template <typename Fun, typename... Args>
|
||||
true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
|
||||
template <typename...>
|
||||
std::false_type static test(...);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_callable;
|
||||
|
||||
template <typename Fun, typename... Args>
|
||||
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
namespace mpl_{
|
||||
struct na;
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_META_HPP_INCLUDED
|
||||
|
@@ -116,7 +116,7 @@ namespace Catch {
|
||||
arcSafeRelease( m_substr );
|
||||
}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Catch {
|
||||
struct Equals : StringHolder {
|
||||
Equals( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str isEqualToString:m_substr];
|
||||
}
|
||||
@@ -139,7 +139,7 @@ namespace Catch {
|
||||
struct Contains : StringHolder {
|
||||
Contains( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location != NSNotFound;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ namespace Catch {
|
||||
struct StartsWith : StringHolder {
|
||||
StartsWith( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location == 0;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ namespace Catch {
|
||||
struct EndsWith : StringHolder {
|
||||
EndsWith( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location == [str length] - [m_substr length];
|
||||
}
|
||||
|
@@ -68,22 +68,155 @@
|
||||
#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
|
||||
#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
|
||||
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__)
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
|
||||
#else
|
||||
// MSVC is adding extra space and needs more calls to properly remove ()
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) Catch::TypeList<INTERNAL_CATCH_REMOVE_PARENS(types)>
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
|
||||
CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
|
||||
|
||||
#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\
|
||||
CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types))
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
|
||||
|
||||
#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() noexcept -> TypeList<Ts...> { return {}; }\
|
||||
\
|
||||
template<template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2> \
|
||||
constexpr auto append(L1<E1...>, L2<E2...>) noexcept -> L1<E1...,E2...> { return {}; }\
|
||||
template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
|
||||
constexpr auto append(L1<E1...>, L2<E2...>, Rest...) noexcept -> decltype(append(L1<E1...,E2...>{}, Rest{}...)) { return {}; }\
|
||||
template< template<typename...> class L1, typename...E1, typename...Rest>\
|
||||
constexpr auto append(L1<E1...>, TypeList<mpl_::na>, Rest...) noexcept -> L1<E1...> { return {}; }\
|
||||
\
|
||||
template< template<typename...> class Container, template<typename...> class List, typename...elems>\
|
||||
constexpr auto rewrap(List<elems...>) noexcept -> TypeList<Container<elems...>> { return {}; }\
|
||||
template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
|
||||
constexpr auto rewrap(List<Elems...>,Elements...) noexcept -> decltype(append(TypeList<Container<Elems...>>{}, rewrap<Container>(Elements{}...))) { return {}; }\
|
||||
\
|
||||
template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
|
||||
constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }\
|
||||
template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
|
||||
constexpr auto convert(List<Ts...>) noexcept -> decltype(append(Final<>{},TypeList<Ts>{}...)) { return {}; }
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_1(signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
||||
\
|
||||
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto rewrap(List<__VA_ARGS__>) noexcept -> TypeList<Container<__VA_ARGS__>> { return {}; }\
|
||||
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
|
||||
constexpr auto rewrap(List<__VA_ARGS__>,Elements...elems) noexcept -> decltype(append(TypeList<Container<__VA_ARGS__>>{}, rewrap<Container>(elems...))) { return {}; }\
|
||||
template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
|
||||
constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }
|
||||
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
static void TestName()
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
static void TestName()
|
||||
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
static void TestName()
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
static void TestName()
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
|
||||
template<typename Type>\
|
||||
void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
|
||||
{\
|
||||
Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
|
||||
}
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
|
||||
{\
|
||||
Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
|
||||
}
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
|
||||
template<typename Type>\
|
||||
void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
|
||||
{\
|
||||
Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
|
||||
}
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
|
||||
{\
|
||||
Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
|
||||
}
|
||||
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
|
||||
template<typename TestType> \
|
||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
|
||||
void test();\
|
||||
}
|
||||
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
|
||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
|
||||
void test();\
|
||||
}
|
||||
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
|
||||
template<typename TestType> \
|
||||
void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
|
||||
void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_NTTP_0
|
||||
#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_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__)
|
||||
#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
|
||||
#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_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_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__))
|
||||
#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
|
||||
#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(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__))
|
||||
#endif
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_PREPROCESSOR_HPP_INCLUDED
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "catch_tag_alias_registry.h"
|
||||
#include "catch_startup_exception_registry.h"
|
||||
#include "catch_singletons.hpp"
|
||||
#include "catch_enum_values_registry.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -60,6 +61,9 @@ namespace Catch {
|
||||
void registerStartupException() noexcept override {
|
||||
m_exceptionRegistry.add(std::current_exception());
|
||||
}
|
||||
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
|
||||
return m_enumValuesRegistry;
|
||||
}
|
||||
|
||||
private:
|
||||
TestRegistry m_testCaseRegistry;
|
||||
@@ -67,6 +71,7 @@ namespace Catch {
|
||||
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
|
||||
TagAliasRegistry m_tagAliasRegistry;
|
||||
StartupExceptionRegistry m_exceptionRegistry;
|
||||
Detail::EnumValuesRegistry m_enumValuesRegistry;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -230,12 +230,21 @@ namespace Catch {
|
||||
|
||||
m_unfinishedSections.push_back(endInfo);
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void RunContext::benchmarkPreparing(std::string const& name) {
|
||||
m_reporter->benchmarkPreparing(name);
|
||||
}
|
||||
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
|
||||
m_reporter->benchmarkStarting( info );
|
||||
}
|
||||
void RunContext::benchmarkEnded( BenchmarkStats const& stats ) {
|
||||
void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
|
||||
m_reporter->benchmarkEnded( stats );
|
||||
}
|
||||
void RunContext::benchmarkFailed(std::string const & error) {
|
||||
m_reporter->benchmarkFailed(error);
|
||||
}
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
void RunContext::pushScopedMessage(MessageInfo const & message) {
|
||||
m_messages.push_back(message);
|
||||
|
@@ -82,8 +82,12 @@ namespace Catch {
|
||||
|
||||
auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void benchmarkPreparing( std::string const& name ) override;
|
||||
void benchmarkStarting( BenchmarkInfo const& info ) override;
|
||||
void benchmarkEnded( BenchmarkStats const& stats ) override;
|
||||
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
|
||||
void benchmarkFailed( std::string const& error ) override;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
void pushScopedMessage( MessageInfo const& message ) override;
|
||||
void popScopedMessage( MessageInfo const& message ) override;
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -58,46 +60,53 @@ namespace Catch {
|
||||
return ret;
|
||||
}
|
||||
|
||||
class TestGroup {
|
||||
public:
|
||||
explicit TestGroup(std::shared_ptr<Config> const& config)
|
||||
: m_config{config}
|
||||
, m_context{config, makeReporter(config)}
|
||||
{
|
||||
auto const& allTestCases = getAllTestCasesSorted(*m_config);
|
||||
m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
|
||||
|
||||
Catch::Totals runTests(std::shared_ptr<Config> const& config) {
|
||||
auto reporter = makeReporter(config);
|
||||
|
||||
RunContext context(config, std::move(reporter));
|
||||
|
||||
Totals totals;
|
||||
|
||||
context.testGroupStarting(config->name(), 1, 1);
|
||||
|
||||
TestSpec testSpec = config->testSpec();
|
||||
|
||||
auto const& allTestCases = getAllTestCasesSorted(*config);
|
||||
for (auto const& testCase : allTestCases) {
|
||||
bool matching = (!testSpec.hasFilters() && !testCase.isHidden()) ||
|
||||
(testSpec.hasFilters() && matchTest(testCase, testSpec, *config));
|
||||
|
||||
if (!context.aborting() && matching)
|
||||
totals += context.runTest(testCase);
|
||||
else
|
||||
context.reporter().skipTest(testCase);
|
||||
if (m_matches.empty()) {
|
||||
for (auto const& test : allTestCases)
|
||||
if (!test.isHidden())
|
||||
m_tests.emplace(&test);
|
||||
} else {
|
||||
for (auto const& match : m_matches)
|
||||
m_tests.insert(match.tests.begin(), match.tests.end());
|
||||
}
|
||||
}
|
||||
|
||||
if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
|
||||
ReusableStringStream testConfig;
|
||||
|
||||
bool first = true;
|
||||
for (const auto& input : config->getTestsOrTags()) {
|
||||
if (!first) { testConfig << ' '; }
|
||||
first = false;
|
||||
testConfig << input;
|
||||
Totals execute() {
|
||||
Totals totals;
|
||||
m_context.testGroupStarting(m_config->name(), 1, 1);
|
||||
for (auto const& testCase : m_tests) {
|
||||
if (!m_context.aborting())
|
||||
totals += m_context.runTest(*testCase);
|
||||
else
|
||||
m_context.reporter().skipTest(*testCase);
|
||||
}
|
||||
|
||||
context.reporter().noMatchingTestCases(testConfig.str());
|
||||
totals.error = -1;
|
||||
for (auto const& match : m_matches) {
|
||||
if (match.tests.empty()) {
|
||||
m_context.reporter().noMatchingTestCases(match.name);
|
||||
totals.error = -1;
|
||||
}
|
||||
}
|
||||
m_context.testGroupEnded(m_config->name(), totals, 1, 1);
|
||||
return totals;
|
||||
}
|
||||
|
||||
context.testGroupEnded(config->name(), totals, 1, 1);
|
||||
return totals;
|
||||
}
|
||||
private:
|
||||
using Tests = std::set<TestCase const*>;
|
||||
|
||||
std::shared_ptr<Config> m_config;
|
||||
RunContext m_context;
|
||||
Tests m_tests;
|
||||
TestSpec::Matches m_matches;
|
||||
};
|
||||
|
||||
void applyFilenamesAsTags(Catch::IConfig const& config) {
|
||||
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
|
||||
@@ -134,6 +143,9 @@ namespace Catch {
|
||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
|
||||
if ( !exceptions.empty() ) {
|
||||
config();
|
||||
getCurrentMutableContext().setConfig(m_config);
|
||||
|
||||
m_startupExceptions = true;
|
||||
Colour colourGuard( Colour::Red );
|
||||
Catch::cerr() << "Errors occurred during startup!" << '\n';
|
||||
@@ -194,7 +206,7 @@ namespace Catch {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
|
||||
int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
|
||||
|
||||
char **utf8Argv = new char *[ argc ];
|
||||
@@ -271,7 +283,12 @@ namespace Catch {
|
||||
if( Option<std::size_t> listed = list( m_config ) )
|
||||
return static_cast<int>( *listed );
|
||||
|
||||
auto totals = runTests( m_config );
|
||||
TestGroup tests { m_config };
|
||||
auto const totals = tests.execute();
|
||||
|
||||
if( m_config->warnAboutNoTests() && totals.error == -1 )
|
||||
return 2;
|
||||
|
||||
// Note that on unices only the lower 8 bits are usually used, clamping
|
||||
// the return value to 255 prevents false negative when some multiple
|
||||
// of 256 tests has failed
|
||||
|
@@ -26,7 +26,7 @@ namespace Catch {
|
||||
void libIdentify();
|
||||
|
||||
int applyCommandLine( int argc, char const * const * argv );
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
|
||||
int applyCommandLine( int argc, wchar_t const * const * argv );
|
||||
#endif
|
||||
|
||||
|
@@ -25,7 +25,7 @@ namespace Catch {
|
||||
|
||||
Catch::IStream::~IStream() = default;
|
||||
|
||||
namespace detail { namespace {
|
||||
namespace Detail { namespace {
|
||||
template<typename WriterF, std::size_t bufferSize=256>
|
||||
class StreamBufImpl : public std::streambuf {
|
||||
char data[bufferSize];
|
||||
@@ -124,15 +124,15 @@ namespace Catch {
|
||||
|
||||
auto makeStream( StringRef const &filename ) -> IStream const* {
|
||||
if( filename.empty() )
|
||||
return new detail::CoutStream();
|
||||
return new Detail::CoutStream();
|
||||
else if( filename[0] == '%' ) {
|
||||
if( filename == "%debug" )
|
||||
return new detail::DebugOutStream();
|
||||
return new Detail::DebugOutStream();
|
||||
else
|
||||
CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
|
||||
}
|
||||
else
|
||||
return new detail::FileStream( filename );
|
||||
return new Detail::FileStream( filename );
|
||||
}
|
||||
|
||||
|
||||
|
@@ -6,11 +6,13 @@
|
||||
*/
|
||||
|
||||
#include "catch_string_manip.h"
|
||||
#include "catch_stringref.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -65,6 +67,21 @@ namespace Catch {
|
||||
return replaced;
|
||||
}
|
||||
|
||||
std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) {
|
||||
std::vector<StringRef> subStrings;
|
||||
std::size_t start = 0;
|
||||
for(std::size_t pos = 0; pos < str.size(); ++pos ) {
|
||||
if( str[pos] == delimiter ) {
|
||||
if( pos - start > 1 )
|
||||
subStrings.push_back( str.substr( start, pos-start ) );
|
||||
start = pos+1;
|
||||
}
|
||||
}
|
||||
if( start < str.size() )
|
||||
subStrings.push_back( str.substr( start, str.size()-start ) );
|
||||
return subStrings;
|
||||
}
|
||||
|
||||
pluralise::pluralise( std::size_t count, std::string const& label )
|
||||
: m_count( count ),
|
||||
m_label( label )
|
||||
|
@@ -7,8 +7,11 @@
|
||||
#ifndef TWOBLUECUBES_CATCH_STRING_MANIP_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_STRING_MANIP_H_INCLUDED
|
||||
|
||||
#include "catch_stringref.h"
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -20,6 +23,9 @@ namespace Catch {
|
||||
void toLowerInPlace( std::string& s );
|
||||
std::string toLower( std::string const& s );
|
||||
std::string trim( std::string const& str );
|
||||
|
||||
// !!! Be aware, returns refs into original string - make sure original string outlives them
|
||||
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
|
||||
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
|
||||
|
||||
struct pluralise {
|
||||
|
@@ -39,9 +39,11 @@ namespace Catch {
|
||||
}
|
||||
|
||||
auto StringRef::c_str() const -> char const* {
|
||||
if( isSubstring() )
|
||||
const_cast<StringRef*>( this )->takeOwnership();
|
||||
return m_start;
|
||||
if( !isSubstring() )
|
||||
return m_start;
|
||||
|
||||
const_cast<StringRef *>( this )->takeOwnership();
|
||||
return m_data;
|
||||
}
|
||||
auto StringRef::currentData() const noexcept -> char const* {
|
||||
return m_start;
|
||||
@@ -59,7 +61,6 @@ namespace Catch {
|
||||
m_data = new char[m_size+1];
|
||||
memcpy( m_data, m_start, m_size );
|
||||
m_data[m_size] = '\0';
|
||||
m_start = m_data;
|
||||
}
|
||||
}
|
||||
auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
|
||||
|
@@ -36,8 +36,13 @@ namespace Catch {
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
|
||||
return !testCase.throws() || config.allowThrows();
|
||||
}
|
||||
|
||||
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
|
||||
return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
|
||||
return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
|
||||
}
|
||||
|
||||
void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
|
||||
|
@@ -23,6 +23,8 @@ namespace Catch {
|
||||
struct IConfig;
|
||||
|
||||
std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
|
||||
|
||||
bool isThrowSafe( TestCase const& testCase, IConfig const& config );
|
||||
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
|
||||
|
||||
void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
|
||||
|
@@ -139,7 +139,7 @@ namespace TestCaseTracking {
|
||||
m_runState = CompletedSuccessfully;
|
||||
break;
|
||||
case ExecutingChildren:
|
||||
if( m_children.empty() || m_children.back()->isComplete() )
|
||||
if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
|
||||
m_runState = CompletedSuccessfully;
|
||||
break;
|
||||
|
||||
|
@@ -60,18 +60,47 @@ struct AutoReg : NonCopyable {
|
||||
}; \
|
||||
} \
|
||||
void TestName::test()
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \
|
||||
template<typename TestType> \
|
||||
static void TestName()
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
|
||||
INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
|
||||
namespace{ \
|
||||
template<typename TestType> \
|
||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
||||
void test(); \
|
||||
}; \
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
|
||||
INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
|
||||
} \
|
||||
template<typename TestType> \
|
||||
void TestName::test()
|
||||
} \
|
||||
INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -111,54 +140,61 @@ struct AutoReg : NonCopyable {
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
template<typename TestType> \
|
||||
static void TestFunc();\
|
||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
|
||||
namespace {\
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
|
||||
INTERNAL_CATCH_TYPE_GEN\
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
template<typename...Types> \
|
||||
struct TestName{\
|
||||
template<typename...Ts> \
|
||||
TestName(Ts...names){\
|
||||
CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
|
||||
TestName(){\
|
||||
int index = 0; \
|
||||
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
|
||||
using expander = int[];\
|
||||
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
|
||||
(void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \
|
||||
}\
|
||||
};\
|
||||
INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \
|
||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||
TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
|
||||
return 0;\
|
||||
}();\
|
||||
}\
|
||||
}\
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
template<typename TestType> \
|
||||
static void TestFunc()
|
||||
|
||||
#if defined(CATCH_CPP17_OR_GREATER)
|
||||
#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case");
|
||||
#else
|
||||
#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case");
|
||||
#endif
|
||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ )
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) )
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\
|
||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||
TestName<CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)>(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\
|
||||
return 0;\
|
||||
}();
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
template<typename TestType> static void TestFuncName(); \
|
||||
namespace { \
|
||||
namespace {\
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
|
||||
INTERNAL_CATCH_TYPE_GEN \
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
|
||||
template<typename... Types> \
|
||||
struct TestName { \
|
||||
TestName() { \
|
||||
CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \
|
||||
void reg_tests() { \
|
||||
int index = 0; \
|
||||
using expander = int[]; \
|
||||
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
|
||||
@@ -168,65 +204,121 @@ struct AutoReg : NonCopyable {
|
||||
} \
|
||||
}; \
|
||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
||||
using TestInit = Catch::combine<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)> \
|
||||
::with_types<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(TypesList)>::into<TestName>::type; \
|
||||
TestInit(); \
|
||||
using TestInit = decltype(create<TestName, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{})); \
|
||||
TestInit t; \
|
||||
t.reg_tests(); \
|
||||
return 0; \
|
||||
}(); \
|
||||
} \
|
||||
} \
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
template<typename TestType> \
|
||||
static void TestFuncName()
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
|
||||
INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ),Name,Tags,__VA_ARGS__)
|
||||
INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__)
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) )
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
|
||||
INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__)
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ \
|
||||
template<typename TestType> \
|
||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
||||
void test();\
|
||||
};\
|
||||
template<typename TestType> static void TestFunc(); \
|
||||
namespace {\
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
|
||||
INTERNAL_CATCH_TYPE_GEN\
|
||||
template<typename... Types> \
|
||||
struct TestName { \
|
||||
void reg_tests() { \
|
||||
int index = 0; \
|
||||
using expander = int[]; \
|
||||
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */\
|
||||
} \
|
||||
};\
|
||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
||||
using TestInit = decltype(convert<TestName>(std::declval<TmplList>())); \
|
||||
TestInit t; \
|
||||
t.reg_tests(); \
|
||||
return 0; \
|
||||
}(); \
|
||||
}}\
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
template<typename TestType> \
|
||||
static void TestFunc()
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
|
||||
INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList )
|
||||
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
namespace {\
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
|
||||
INTERNAL_CATCH_TYPE_GEN\
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
|
||||
INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
template<typename...Types> \
|
||||
struct TestNameClass{\
|
||||
template<typename...Ts> \
|
||||
TestNameClass(Ts...names){\
|
||||
CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
|
||||
TestNameClass(){\
|
||||
int index = 0; \
|
||||
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
|
||||
using expander = int[];\
|
||||
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
|
||||
(void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \
|
||||
}\
|
||||
};\
|
||||
INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\
|
||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||
TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
|
||||
return 0;\
|
||||
}();\
|
||||
}\
|
||||
}\
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
|
||||
template<typename TestType> \
|
||||
void TestName<TestType>::test()
|
||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS\
|
||||
INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ )
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) )
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
|
||||
INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
template<typename TestType> \
|
||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
||||
void test();\
|
||||
};\
|
||||
namespace {\
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
|
||||
INTERNAL_CATCH_TYPE_GEN \
|
||||
INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
|
||||
template<typename...Types>\
|
||||
struct TestNameClass{\
|
||||
TestNameClass(){\
|
||||
CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\
|
||||
void reg_tests(){\
|
||||
int index = 0;\
|
||||
using expander = int[];\
|
||||
constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
|
||||
@@ -236,22 +328,64 @@ struct AutoReg : NonCopyable {
|
||||
}\
|
||||
};\
|
||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||
using TestInit = Catch::combine<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>\
|
||||
::with_types<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(TypesList)>::into<TestNameClass>::type;\
|
||||
TestInit();\
|
||||
using TestInit = decltype(create<TestNameClass, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{}));\
|
||||
TestInit t;\
|
||||
t.reg_tests();\
|
||||
return 0;\
|
||||
}(); \
|
||||
}\
|
||||
}\
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
template<typename TestType> \
|
||||
void TestName<TestType>::test()
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
|
||||
INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ )
|
||||
INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, __VA_ARGS__ ) )
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
|
||||
INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
|
||||
#else
|
||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
|
||||
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
template<typename TestType> \
|
||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
||||
void test();\
|
||||
};\
|
||||
namespace {\
|
||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
|
||||
INTERNAL_CATCH_TYPE_GEN\
|
||||
template<typename...Types>\
|
||||
struct TestNameClass{\
|
||||
void reg_tests(){\
|
||||
int index = 0;\
|
||||
using expander = int[];\
|
||||
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */ \
|
||||
}\
|
||||
};\
|
||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||
using TestInit = decltype(convert<TestNameClass>(std::declval<TmplList>()));\
|
||||
TestInit t;\
|
||||
t.reg_tests();\
|
||||
return 0;\
|
||||
}(); \
|
||||
}}\
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
template<typename TestType> \
|
||||
void TestName<TestType>::test()
|
||||
|
||||
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
|
||||
INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList )
|
||||
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
|
||||
|
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "catch_test_spec.h"
|
||||
#include "catch_string_manip.h"
|
||||
#include "catch_interfaces_config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
@@ -15,45 +16,80 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
TestSpec::Pattern::~Pattern() = default;
|
||||
TestSpec::NamePattern::~NamePattern() = default;
|
||||
TestSpec::TagPattern::~TagPattern() = default;
|
||||
TestSpec::ExcludedPattern::~ExcludedPattern() = default;
|
||||
|
||||
TestSpec::NamePattern::NamePattern( std::string const& name )
|
||||
: m_wildcardPattern( toLower( name ), CaseSensitive::No )
|
||||
TestSpec::Pattern::Pattern( std::string const& name )
|
||||
: m_name( name )
|
||||
{}
|
||||
|
||||
TestSpec::Pattern::~Pattern() = default;
|
||||
|
||||
std::string const& TestSpec::Pattern::name() const {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
|
||||
TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
|
||||
: Pattern( filterString )
|
||||
, m_wildcardPattern( toLower( name ), CaseSensitive::No )
|
||||
{}
|
||||
|
||||
bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
|
||||
return m_wildcardPattern.matches( toLower( testCase.name ) );
|
||||
}
|
||||
|
||||
TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
|
||||
|
||||
TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
|
||||
: Pattern( filterString )
|
||||
, m_tag( toLower( tag ) )
|
||||
{}
|
||||
|
||||
bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
|
||||
return std::find(begin(testCase.lcaseTags),
|
||||
end(testCase.lcaseTags),
|
||||
m_tag) != end(testCase.lcaseTags);
|
||||
}
|
||||
|
||||
TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
|
||||
bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
|
||||
|
||||
TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
|
||||
: Pattern( underlyingPattern->name() )
|
||||
, m_underlyingPattern( underlyingPattern )
|
||||
{}
|
||||
|
||||
bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
|
||||
return !m_underlyingPattern->matches( testCase );
|
||||
}
|
||||
|
||||
|
||||
bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
|
||||
// All patterns in a filter must match for the filter to be a match
|
||||
for( auto const& pattern : m_patterns ) {
|
||||
if( !pattern->matches( testCase ) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
|
||||
}
|
||||
|
||||
std::string TestSpec::Filter::name() const {
|
||||
std::string name;
|
||||
for( auto const& p : m_patterns )
|
||||
name += p->name();
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
bool TestSpec::hasFilters() const {
|
||||
return !m_filters.empty();
|
||||
}
|
||||
|
||||
bool TestSpec::matches( TestCaseInfo const& testCase ) const {
|
||||
// A TestSpec matches if any filter matches
|
||||
for( auto const& filter : m_filters )
|
||||
if( filter.matches( testCase ) )
|
||||
return true;
|
||||
return false;
|
||||
return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
|
||||
}
|
||||
|
||||
TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const
|
||||
{
|
||||
Matches matches( m_filters.size() );
|
||||
std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
|
||||
std::vector<TestCase const*> currentMatches;
|
||||
for( auto const& test : testCases )
|
||||
if( isThrowSafe( test, config ) && filter.matches( test ) )
|
||||
currentMatches.emplace_back( &test );
|
||||
return FilterMatch{ filter.name(), currentMatches };
|
||||
} );
|
||||
return matches;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -22,17 +22,23 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct IConfig;
|
||||
|
||||
class TestSpec {
|
||||
struct Pattern {
|
||||
class Pattern {
|
||||
public:
|
||||
explicit Pattern( std::string const& name );
|
||||
virtual ~Pattern();
|
||||
virtual bool matches( TestCaseInfo const& testCase ) const = 0;
|
||||
std::string const& name() const;
|
||||
private:
|
||||
std::string const m_name;
|
||||
};
|
||||
using PatternPtr = std::shared_ptr<Pattern>;
|
||||
|
||||
class NamePattern : public Pattern {
|
||||
public:
|
||||
NamePattern( std::string const& name );
|
||||
virtual ~NamePattern();
|
||||
explicit NamePattern( std::string const& name, std::string const& filterString );
|
||||
bool matches( TestCaseInfo const& testCase ) const override;
|
||||
private:
|
||||
WildcardPattern m_wildcardPattern;
|
||||
@@ -40,8 +46,7 @@ namespace Catch {
|
||||
|
||||
class TagPattern : public Pattern {
|
||||
public:
|
||||
TagPattern( std::string const& tag );
|
||||
virtual ~TagPattern();
|
||||
explicit TagPattern( std::string const& tag, std::string const& filterString );
|
||||
bool matches( TestCaseInfo const& testCase ) const override;
|
||||
private:
|
||||
std::string m_tag;
|
||||
@@ -49,8 +54,7 @@ namespace Catch {
|
||||
|
||||
class ExcludedPattern : public Pattern {
|
||||
public:
|
||||
ExcludedPattern( PatternPtr const& underlyingPattern );
|
||||
virtual ~ExcludedPattern();
|
||||
explicit ExcludedPattern( PatternPtr const& underlyingPattern );
|
||||
bool matches( TestCaseInfo const& testCase ) const override;
|
||||
private:
|
||||
PatternPtr m_underlyingPattern;
|
||||
@@ -60,11 +64,19 @@ namespace Catch {
|
||||
std::vector<PatternPtr> m_patterns;
|
||||
|
||||
bool matches( TestCaseInfo const& testCase ) const;
|
||||
std::string name() const;
|
||||
};
|
||||
|
||||
public:
|
||||
struct FilterMatch {
|
||||
std::string name;
|
||||
std::vector<TestCase const*> tests;
|
||||
};
|
||||
using Matches = std::vector<FilterMatch>;
|
||||
|
||||
bool hasFilters() const;
|
||||
bool matches( TestCaseInfo const& testCase ) const;
|
||||
Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
|
||||
|
||||
private:
|
||||
std::vector<Filter> m_filters;
|
||||
|
@@ -14,64 +14,125 @@ namespace Catch {
|
||||
TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
|
||||
m_mode = None;
|
||||
m_exclusion = false;
|
||||
m_start = std::string::npos;
|
||||
m_arg = m_tagAliases->expandAliases( arg );
|
||||
m_escapeChars.clear();
|
||||
m_substring.reserve(m_arg.size());
|
||||
m_patternName.reserve(m_arg.size());
|
||||
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
|
||||
visitChar( m_arg[m_pos] );
|
||||
if( m_mode == Name )
|
||||
addPattern<TestSpec::NamePattern>();
|
||||
endMode();
|
||||
return *this;
|
||||
}
|
||||
TestSpec TestSpecParser::testSpec() {
|
||||
addFilter();
|
||||
return m_testSpec;
|
||||
}
|
||||
|
||||
void TestSpecParser::visitChar( char c ) {
|
||||
if( m_mode == None ) {
|
||||
switch( c ) {
|
||||
case ' ': return;
|
||||
case '~': m_exclusion = true; return;
|
||||
case '[': return startNewMode( Tag, ++m_pos );
|
||||
case '"': return startNewMode( QuotedName, ++m_pos );
|
||||
case '\\': return escape();
|
||||
default: startNewMode( Name, m_pos ); break;
|
||||
}
|
||||
if( c == ',' ) {
|
||||
endMode();
|
||||
addFilter();
|
||||
return;
|
||||
}
|
||||
if( m_mode == Name ) {
|
||||
if( c == ',' ) {
|
||||
addPattern<TestSpec::NamePattern>();
|
||||
addFilter();
|
||||
}
|
||||
else if( c == '[' ) {
|
||||
if( subString() == "exclude:" )
|
||||
m_exclusion = true;
|
||||
else
|
||||
addPattern<TestSpec::NamePattern>();
|
||||
startNewMode( Tag, ++m_pos );
|
||||
}
|
||||
else if( c == '\\' )
|
||||
escape();
|
||||
|
||||
switch( m_mode ) {
|
||||
case None:
|
||||
if( processNoneChar( c ) )
|
||||
return;
|
||||
break;
|
||||
case Name:
|
||||
processNameChar( c );
|
||||
break;
|
||||
case EscapedName:
|
||||
endMode();
|
||||
break;
|
||||
default:
|
||||
case Tag:
|
||||
case QuotedName:
|
||||
if( processOtherChar( c ) )
|
||||
return;
|
||||
break;
|
||||
}
|
||||
else if( m_mode == EscapedName )
|
||||
m_mode = Name;
|
||||
else if( m_mode == QuotedName && c == '"' )
|
||||
addPattern<TestSpec::NamePattern>();
|
||||
else if( m_mode == Tag && c == ']' )
|
||||
addPattern<TestSpec::TagPattern>();
|
||||
|
||||
m_substring += c;
|
||||
if( !isControlChar( c ) )
|
||||
m_patternName += c;
|
||||
}
|
||||
void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
|
||||
// Two of the processing methods return true to signal the caller to return
|
||||
// without adding the given character to the current pattern strings
|
||||
bool TestSpecParser::processNoneChar( char c ) {
|
||||
switch( c ) {
|
||||
case ' ':
|
||||
return true;
|
||||
case '~':
|
||||
m_exclusion = true;
|
||||
return false;
|
||||
case '[':
|
||||
startNewMode( Tag );
|
||||
return false;
|
||||
case '"':
|
||||
startNewMode( QuotedName );
|
||||
return false;
|
||||
case '\\':
|
||||
escape();
|
||||
return true;
|
||||
default:
|
||||
startNewMode( Name );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void TestSpecParser::processNameChar( char c ) {
|
||||
if( c == '[' ) {
|
||||
if( m_substring == "exclude:" )
|
||||
m_exclusion = true;
|
||||
else
|
||||
endMode();
|
||||
startNewMode( Tag );
|
||||
}
|
||||
}
|
||||
bool TestSpecParser::processOtherChar( char c ) {
|
||||
if( !isControlChar( c ) )
|
||||
return false;
|
||||
m_substring += c;
|
||||
endMode();
|
||||
return true;
|
||||
}
|
||||
void TestSpecParser::startNewMode( Mode mode ) {
|
||||
m_mode = mode;
|
||||
m_start = start;
|
||||
}
|
||||
void TestSpecParser::endMode() {
|
||||
switch( m_mode ) {
|
||||
case Name:
|
||||
case QuotedName:
|
||||
return addPattern<TestSpec::NamePattern>();
|
||||
case Tag:
|
||||
return addPattern<TestSpec::TagPattern>();
|
||||
case EscapedName:
|
||||
return startNewMode( Name );
|
||||
case None:
|
||||
default:
|
||||
return startNewMode( None );
|
||||
}
|
||||
}
|
||||
void TestSpecParser::escape() {
|
||||
if( m_mode == None )
|
||||
m_start = m_pos;
|
||||
m_mode = EscapedName;
|
||||
m_escapeChars.push_back( m_pos );
|
||||
}
|
||||
std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
|
||||
bool TestSpecParser::isControlChar( char c ) const {
|
||||
switch( m_mode ) {
|
||||
default:
|
||||
return false;
|
||||
case None:
|
||||
return c == '~';
|
||||
case Name:
|
||||
return c == '[';
|
||||
case EscapedName:
|
||||
return true;
|
||||
case QuotedName:
|
||||
return c == '"';
|
||||
case Tag:
|
||||
return c == '[' || c == ']';
|
||||
}
|
||||
}
|
||||
|
||||
void TestSpecParser::addFilter() {
|
||||
if( !m_currentFilter.m_patterns.empty() ) {
|
||||
|
@@ -23,8 +23,10 @@ namespace Catch {
|
||||
enum Mode{ None, Name, QuotedName, Tag, EscapedName };
|
||||
Mode m_mode = None;
|
||||
bool m_exclusion = false;
|
||||
std::size_t m_start = std::string::npos, m_pos = 0;
|
||||
std::size_t m_pos = 0;
|
||||
std::string m_arg;
|
||||
std::string m_substring;
|
||||
std::string m_patternName;
|
||||
std::vector<std::size_t> m_escapeChars;
|
||||
TestSpec::Filter m_currentFilter;
|
||||
TestSpec m_testSpec;
|
||||
@@ -38,26 +40,32 @@ namespace Catch {
|
||||
|
||||
private:
|
||||
void visitChar( char c );
|
||||
void startNewMode( Mode mode, std::size_t start );
|
||||
void startNewMode( Mode mode );
|
||||
bool processNoneChar( char c );
|
||||
void processNameChar( char c );
|
||||
bool processOtherChar( char c );
|
||||
void endMode();
|
||||
void escape();
|
||||
std::string subString() const;
|
||||
bool isControlChar( char c ) const;
|
||||
|
||||
template<typename T>
|
||||
void addPattern() {
|
||||
std::string token = subString();
|
||||
std::string token = m_patternName;
|
||||
for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
|
||||
token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
|
||||
token = token.substr( 0, m_escapeChars[i] - i ) + token.substr( m_escapeChars[i] -i +1 );
|
||||
m_escapeChars.clear();
|
||||
if( startsWith( token, "exclude:" ) ) {
|
||||
m_exclusion = true;
|
||||
token = token.substr( 8 );
|
||||
}
|
||||
if( !token.empty() ) {
|
||||
TestSpec::PatternPtr pattern = std::make_shared<T>( token );
|
||||
TestSpec::PatternPtr pattern = std::make_shared<T>( token, m_substring );
|
||||
if( m_exclusion )
|
||||
pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
|
||||
m_currentFilter.m_patterns.push_back( pattern );
|
||||
}
|
||||
m_substring.clear();
|
||||
m_patternName.clear();
|
||||
m_exclusion = false;
|
||||
m_mode = None;
|
||||
}
|
||||
|
@@ -170,6 +170,12 @@ std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_CPP17_BYTE)
|
||||
#include <cstddef>
|
||||
std::string StringMaker<std::byte>::convert(std::byte value) {
|
||||
return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
|
||||
}
|
||||
#endif // defined(CATCH_CONFIG_CPP17_BYTE)
|
||||
|
||||
std::string StringMaker<int>::convert(int value) {
|
||||
return ::Catch::Detail::stringify(static_cast<long long>(value));
|
||||
@@ -234,11 +240,16 @@ std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {
|
||||
return "nullptr";
|
||||
}
|
||||
|
||||
int StringMaker<float>::precision = 5;
|
||||
|
||||
std::string StringMaker<float>::convert(float value) {
|
||||
return fpToString(value, 5) + 'f';
|
||||
return fpToString(value, precision) + 'f';
|
||||
}
|
||||
|
||||
int StringMaker<double>::precision = 10;
|
||||
|
||||
std::string StringMaker<double>::convert(double value) {
|
||||
return fpToString(value, 10);
|
||||
return fpToString(value, precision);
|
||||
}
|
||||
|
||||
std::string ratio_string<std::atto>::symbol() { return "a"; }
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include <string>
|
||||
#include "catch_compiler_capabilities.h"
|
||||
#include "catch_stream.h"
|
||||
#include "catch_interfaces_enum_values_registry.h"
|
||||
|
||||
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
|
||||
#include <string_view>
|
||||
@@ -209,6 +210,12 @@ namespace Catch {
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(CATCH_CONFIG_CPP17_BYTE)
|
||||
template<>
|
||||
struct StringMaker<std::byte> {
|
||||
static std::string convert(std::byte value);
|
||||
};
|
||||
#endif // defined(CATCH_CONFIG_CPP17_BYTE)
|
||||
template<>
|
||||
struct StringMaker<int> {
|
||||
static std::string convert(int value);
|
||||
@@ -260,10 +267,13 @@ namespace Catch {
|
||||
template<>
|
||||
struct StringMaker<float> {
|
||||
static std::string convert(float value);
|
||||
static int precision;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct StringMaker<double> {
|
||||
static std::string convert(double value);
|
||||
static int precision;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -639,6 +649,17 @@ struct ratio_string<std::milli> {
|
||||
}
|
||||
#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
|
||||
|
||||
#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
|
||||
namespace Catch { \
|
||||
template<> struct StringMaker<enumName> { \
|
||||
static std::string convert( enumName value ) { \
|
||||
static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
|
||||
return enumInfo.lookup( static_cast<int>( value ) ); \
|
||||
} \
|
||||
}; \
|
||||
}
|
||||
|
||||
#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
|
@@ -37,7 +37,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
Version const& libraryVersion() {
|
||||
static Version version( 2, 7, 2, "", 0 );
|
||||
static Version version( 2, 9, 2, "", 0 );
|
||||
return version;
|
||||
}
|
||||
|
||||
|
@@ -209,24 +209,25 @@ private:
|
||||
if (itMessage == messages.end())
|
||||
return;
|
||||
|
||||
// using messages.end() directly yields (or auto) compilation error:
|
||||
std::vector<MessageInfo>::const_iterator itEnd = messages.end();
|
||||
const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
|
||||
const auto itEnd = messages.cend();
|
||||
const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
|
||||
|
||||
{
|
||||
Colour colourGuard(colour);
|
||||
stream << " with " << pluralise(N, "message") << ':';
|
||||
}
|
||||
|
||||
for (; itMessage != itEnd; ) {
|
||||
while (itMessage != itEnd) {
|
||||
// If this assertion is a warning ignore any INFO messages
|
||||
if (printInfoMessages || itMessage->type != ResultWas::Info) {
|
||||
stream << " '" << itMessage->message << '\'';
|
||||
if (++itMessage != itEnd) {
|
||||
printMessage();
|
||||
if (itMessage != itEnd) {
|
||||
Colour colourGuard(dimColour());
|
||||
stream << " and";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
++itMessage;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,10 +20,16 @@
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
|
||||
// Note that 4062 (not all labels are handled
|
||||
// and default is missing) is enabled
|
||||
// Note that 4062 (not all labels are handled and default is missing) is enabled
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
// For simplicity, benchmarking-only helpers are always enabled
|
||||
# pragma clang diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -208,6 +214,10 @@ class Duration {
|
||||
Unit m_units;
|
||||
|
||||
public:
|
||||
explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
|
||||
: Duration(static_cast<uint64_t>(inNanoseconds), units) {
|
||||
}
|
||||
|
||||
explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
|
||||
: m_inNanoseconds(inNanoseconds),
|
||||
m_units(units) {
|
||||
@@ -283,9 +293,15 @@ public:
|
||||
if (!m_isOpen) {
|
||||
m_isOpen = true;
|
||||
*this << RowBreak();
|
||||
for (auto const& info : m_columnInfos)
|
||||
*this << info.name << ColumnBreak();
|
||||
*this << RowBreak();
|
||||
|
||||
Columns headerCols;
|
||||
Spacer spacer(2);
|
||||
for (auto const& info : m_columnInfos) {
|
||||
headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
|
||||
headerCols += spacer;
|
||||
}
|
||||
m_os << headerCols << "\n";
|
||||
|
||||
m_os << Catch::getLineOfChars<'-'>() << "\n";
|
||||
}
|
||||
}
|
||||
@@ -340,9 +356,9 @@ ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
|
||||
m_tablePrinter(new TablePrinter(config.stream(),
|
||||
{
|
||||
{ "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
|
||||
{ "iters", 8, ColumnInfo::Right },
|
||||
{ "elapsed ns", 14, ColumnInfo::Right },
|
||||
{ "average", 14, ColumnInfo::Right }
|
||||
{ "samples mean std dev", 14, ColumnInfo::Right },
|
||||
{ "iterations low mean low std dev", 14, ColumnInfo::Right },
|
||||
{ "estimated high mean high std dev", 14, ColumnInfo::Right }
|
||||
})) {}
|
||||
ConsoleReporter::~ConsoleReporter() = default;
|
||||
|
||||
@@ -374,6 +390,7 @@ bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
|
||||
}
|
||||
|
||||
void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
|
||||
m_tablePrinter->close();
|
||||
m_headerPrinted = false;
|
||||
StreamingReporterBase::sectionStarting(_sectionInfo);
|
||||
}
|
||||
@@ -397,29 +414,45 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
|
||||
StreamingReporterBase::sectionEnded(_sectionStats);
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void ConsoleReporter::benchmarkPreparing(std::string const& name) {
|
||||
lazyPrintWithoutClosingBenchmarkTable();
|
||||
|
||||
auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
|
||||
|
||||
bool firstLine = true;
|
||||
for (auto line : nameCol) {
|
||||
if (!firstLine)
|
||||
(*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
|
||||
else
|
||||
firstLine = false;
|
||||
|
||||
(*m_tablePrinter) << line << ColumnBreak();
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
|
||||
lazyPrintWithoutClosingBenchmarkTable();
|
||||
|
||||
auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) );
|
||||
|
||||
bool firstLine = true;
|
||||
for (auto line : nameCol) {
|
||||
if (!firstLine)
|
||||
(*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
|
||||
else
|
||||
firstLine = false;
|
||||
|
||||
(*m_tablePrinter) << line << ColumnBreak();
|
||||
}
|
||||
(*m_tablePrinter) << info.samples << ColumnBreak()
|
||||
<< info.iterations << ColumnBreak()
|
||||
<< Duration(info.estimatedDuration) << ColumnBreak();
|
||||
}
|
||||
void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) {
|
||||
Duration average(stats.elapsedTimeInNanoseconds / stats.iterations);
|
||||
void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
|
||||
(*m_tablePrinter) << ColumnBreak()
|
||||
<< Duration(stats.mean.point.count()) << ColumnBreak()
|
||||
<< Duration(stats.mean.lower_bound.count()) << ColumnBreak()
|
||||
<< Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
|
||||
<< Duration(stats.standardDeviation.point.count()) << ColumnBreak()
|
||||
<< Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
|
||||
<< Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
|
||||
}
|
||||
|
||||
void ConsoleReporter::benchmarkFailed(std::string const& error) {
|
||||
Colour colour(Colour::Red);
|
||||
(*m_tablePrinter)
|
||||
<< stats.iterations << ColumnBreak()
|
||||
<< stats.elapsedTimeInNanoseconds << ColumnBreak()
|
||||
<< average << ColumnBreak();
|
||||
<< "Benchmark failed (" << error << ")"
|
||||
<< ColumnBreak() << RowBreak();
|
||||
}
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
|
||||
m_tablePrinter->close();
|
||||
@@ -638,3 +671,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
@@ -39,9 +39,12 @@ namespace Catch {
|
||||
void sectionStarting(SectionInfo const& _sectionInfo) override;
|
||||
void sectionEnded(SectionStats const& _sectionStats) override;
|
||||
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void benchmarkPreparing(std::string const& name) override;
|
||||
void benchmarkStarting(BenchmarkInfo const& info) override;
|
||||
void benchmarkEnded(BenchmarkStats const& stats) override;
|
||||
void benchmarkEnded(BenchmarkStats<> const& stats) override;
|
||||
void benchmarkFailed(std::string const& error) override;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
void testCaseEnded(TestCaseStats const& _testCaseStats) override;
|
||||
void testGroupEnded(TestGroupStats const& _testGroupStats) override;
|
||||
|
@@ -42,19 +42,34 @@ namespace Catch {
|
||||
m_reporter->noMatchingTestCases( spec );
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void ListeningReporter::benchmarkPreparing( std::string const& name ) {
|
||||
for (auto const& listener : m_listeners) {
|
||||
listener->benchmarkPreparing(name);
|
||||
}
|
||||
m_reporter->benchmarkPreparing(name);
|
||||
}
|
||||
void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
|
||||
for ( auto const& listener : m_listeners ) {
|
||||
listener->benchmarkStarting( benchmarkInfo );
|
||||
}
|
||||
m_reporter->benchmarkStarting( benchmarkInfo );
|
||||
}
|
||||
void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
|
||||
void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
|
||||
for ( auto const& listener : m_listeners ) {
|
||||
listener->benchmarkEnded( benchmarkStats );
|
||||
}
|
||||
m_reporter->benchmarkEnded( benchmarkStats );
|
||||
}
|
||||
|
||||
void ListeningReporter::benchmarkFailed( std::string const& error ) {
|
||||
for (auto const& listener : m_listeners) {
|
||||
listener->benchmarkFailed(error);
|
||||
}
|
||||
m_reporter->benchmarkFailed(error);
|
||||
}
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
|
||||
for ( auto const& listener : m_listeners ) {
|
||||
listener->testRunStarting( testRunInfo );
|
||||
|
@@ -31,8 +31,12 @@ namespace Catch {
|
||||
|
||||
static std::set<Verbosity> getSupportedVerbosities();
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void benchmarkPreparing(std::string const& name) override;
|
||||
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
|
||||
void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override;
|
||||
void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
|
||||
void benchmarkFailed(std::string const&) override;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
||||
void testGroupStarting( GroupInfo const& groupInfo ) override;
|
||||
|
@@ -219,6 +219,51 @@ namespace Catch {
|
||||
m_xml.endElement();
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void XmlReporter::benchmarkPreparing(std::string const& name) {
|
||||
m_xml.startElement("BenchmarkResults")
|
||||
.writeAttribute("name", name);
|
||||
}
|
||||
|
||||
void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
|
||||
m_xml.writeAttribute("samples", info.samples)
|
||||
.writeAttribute("resamples", info.resamples)
|
||||
.writeAttribute("iterations", info.iterations)
|
||||
.writeAttribute("clockResolution", static_cast<uint64_t>(info.clockResolution))
|
||||
.writeAttribute("estimatedDuration", static_cast<uint64_t>(info.estimatedDuration))
|
||||
.writeComment("All values in nano seconds");
|
||||
}
|
||||
|
||||
void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
|
||||
m_xml.startElement("mean")
|
||||
.writeAttribute("value", static_cast<uint64_t>(benchmarkStats.mean.point.count()))
|
||||
.writeAttribute("lowerBound", static_cast<uint64_t>(benchmarkStats.mean.lower_bound.count()))
|
||||
.writeAttribute("upperBound", static_cast<uint64_t>(benchmarkStats.mean.upper_bound.count()))
|
||||
.writeAttribute("ci", benchmarkStats.mean.confidence_interval);
|
||||
m_xml.endElement();
|
||||
m_xml.startElement("standardDeviation")
|
||||
.writeAttribute("value", benchmarkStats.standardDeviation.point.count())
|
||||
.writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count())
|
||||
.writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count())
|
||||
.writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval);
|
||||
m_xml.endElement();
|
||||
m_xml.startElement("outliers")
|
||||
.writeAttribute("variance", benchmarkStats.outlierVariance)
|
||||
.writeAttribute("lowMild", benchmarkStats.outliers.low_mild)
|
||||
.writeAttribute("lowSevere", benchmarkStats.outliers.low_severe)
|
||||
.writeAttribute("highMild", benchmarkStats.outliers.high_mild)
|
||||
.writeAttribute("highSevere", benchmarkStats.outliers.high_severe);
|
||||
m_xml.endElement();
|
||||
m_xml.endElement();
|
||||
}
|
||||
|
||||
void XmlReporter::benchmarkFailed(std::string const &error) {
|
||||
m_xml.scopedElement("failed").
|
||||
writeAttribute("message", error);
|
||||
m_xml.endElement();
|
||||
}
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
CATCH_REGISTER_REPORTER( "xml", XmlReporter )
|
||||
|
||||
} // end namespace Catch
|
||||
|
@@ -50,6 +50,13 @@ namespace Catch {
|
||||
|
||||
void testRunEnded(TestRunStats const& testRunStats) override;
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void benchmarkPreparing(std::string const& name) override;
|
||||
void benchmarkStarting(BenchmarkInfo const&) override;
|
||||
void benchmarkEnded(BenchmarkStats<> const&) override;
|
||||
void benchmarkFailed(std::string const&) override;
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
private:
|
||||
Timer m_testCaseTimer;
|
||||
XmlWriter m_xml;
|
||||
|
@@ -4,6 +4,6 @@ import glob
|
||||
import subprocess
|
||||
|
||||
if __name__ == '__main__':
|
||||
cov_files = list(glob.glob('cov-report*.bin'))
|
||||
cov_files = list(glob.glob('projects/cov-report*.bin'))
|
||||
base_cmd = ['OpenCppCoverage', '--quiet', '--export_type=cobertura:cobertura.xml'] + ['--input_coverage={}'.format(f) for f in cov_files]
|
||||
subprocess.call(base_cmd)
|
||||
subprocess.check_call(base_cmd)
|
||||
|
@@ -89,27 +89,30 @@ std::string windowsify_path(std::string path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
void exec_cmd(std::string const& cmd, int log_num, std::string const& path) {
|
||||
int exec_cmd(std::string const& cmd, int log_num, std::string const& path) {
|
||||
std::array<char, 128> buffer;
|
||||
#if defined(_WIN32)
|
||||
|
||||
// cmd has already been escaped outside this function.
|
||||
auto real_cmd = "OpenCppCoverage --export_type binary:cov-report" + std::to_string(log_num)
|
||||
+ ".bin --quiet " + "--sources " + escape_arg(path) + " --cover_children -- " + cmd;
|
||||
std::cout << "=== Marker ===: Cmd: " << real_cmd << '\n';
|
||||
std::shared_ptr<FILE> pipe(_popen(real_cmd.c_str(), "r"), _pclose);
|
||||
#else // Just for testing, in the real world we will always work under WIN32
|
||||
(void)log_num; (void)path;
|
||||
std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
|
||||
#endif
|
||||
|
||||
auto pipe = _popen(real_cmd.c_str(), "r");
|
||||
|
||||
if (!pipe) {
|
||||
throw std::runtime_error("popen() failed!");
|
||||
}
|
||||
while (!feof(pipe.get())) {
|
||||
if (fgets(buffer.data(), 128, pipe.get()) != nullptr) {
|
||||
while (!feof(pipe)) {
|
||||
if (fgets(buffer.data(), 128, pipe) != nullptr) {
|
||||
std::cout << buffer.data();
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = _pclose(pipe);
|
||||
if (ret == -1) {
|
||||
throw std::runtime_error("underlying error in pclose()");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// argv should be:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user