forked from catchorg/Catch2
Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
e4b16053a6 | |||
42ee66b5e6 | |||
a0c6a28460 | |||
c8363143e7 | |||
7a52dfa77b | |||
9131736630 | |||
0631b607ee | |||
dff7513b28 | |||
bf5aa7b383 | |||
dba9197ec7 | |||
f60c15364b | |||
b3cf1bfb5d | |||
73b93ce6bc | |||
8008625d7e | |||
ce7b153021 | |||
535205e2ac | |||
689fdcd7dc | |||
a153fce724 | |||
06c0e1cfab | |||
05d7eb5a00 | |||
f53bb3ae7b | |||
ce8a7b3390 | |||
6dce539fad | |||
5a40b2275c | |||
598895d048 | |||
0dc82e08df | |||
8ca504cbc9 | |||
c57b5cdf43 | |||
d84777c9cb | |||
51fdbedd13 | |||
10f0a58643 | |||
fe64c28925 | |||
7d07efc92b | |||
f3c678c0ab | |||
46539b6d9b | |||
10596b2278 | |||
897fe2a01b | |||
aad926baf8 | |||
4e8399d835 | |||
9a2a4eadc0 | |||
fb806da76f | |||
50bf00e266 | |||
9f08097f55 | |||
1f881ab464 | |||
c487b27d9d | |||
3230760db2 | |||
b3ebce715e | |||
d0f70fdfd6 | |||
4f4ad8ada9 | |||
5b665be643 | |||
2598116aa6 | |||
173aa3f1f4 | |||
28437e1214 | |||
3c8fb6bbb2 | |||
72f3ce4db5 | |||
62167d756e | |||
6783411349 | |||
7b4dd326c0 | |||
1dfaa8abe7 | |||
ba94278bdd | |||
8e5a4b6f70 | |||
9b884d8107 | |||
8a1b3b81cb | |||
e5aabb6714 | |||
3a1ef14097 | |||
13fae1e2ff | |||
3220ae6d4a | |||
0a0ebf5003 | |||
69f35a5ac8 |
8
.github/workflows/linux-other-builds.yml
vendored
8
.github/workflows/linux-other-builds.yml
vendored
@ -29,13 +29,13 @@ jobs:
|
||||
build_type: Debug
|
||||
std: 14
|
||||
other_pkgs: g++-7
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
|
||||
- cxx: g++-7
|
||||
build_description: Extras + Examples
|
||||
build_type: Release
|
||||
std: 14
|
||||
other_pkgs: g++-7
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
|
||||
|
||||
# Extras and examples with Clang-10
|
||||
- cxx: clang++-10
|
||||
@ -43,13 +43,13 @@ jobs:
|
||||
build_type: Debug
|
||||
std: 17
|
||||
other_pkgs: clang-10
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
|
||||
- cxx: clang++-10
|
||||
build_description: Extras + Examples
|
||||
build_type: Release
|
||||
std: 17
|
||||
other_pkgs: clang-10
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
|
||||
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
|
||||
|
||||
# Configure tests with Clang-10
|
||||
- cxx: clang++-10
|
||||
|
@ -43,12 +43,14 @@ expand_template(
|
||||
"#cmakedefine CATCH_CONFIG_NO_GLOBAL_NEXTAFTER": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_POSIX_SIGNALS": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_USE_ASYNC": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_WCHAR": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_WINDOWS_SEH": "",
|
||||
"#cmakedefine CATCH_CONFIG_NOSTDOUT": "",
|
||||
"#cmakedefine CATCH_CONFIG_POSIX_SIGNALS": "",
|
||||
"#cmakedefine CATCH_CONFIG_PREFIX_ALL": "",
|
||||
"#cmakedefine CATCH_CONFIG_SHARED_LIBRARY": "",
|
||||
"#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "",
|
||||
"#cmakedefine CATCH_CONFIG_USE_ASYNC": "",
|
||||
"#cmakedefine CATCH_CONFIG_WCHAR": "",
|
||||
"#cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG": "",
|
||||
|
@ -41,6 +41,7 @@ set(_OverridableOptions
|
||||
"WCHAR"
|
||||
"WINDOWS_SEH"
|
||||
"GETENV"
|
||||
"EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT"
|
||||
)
|
||||
|
||||
foreach(OptionName ${_OverridableOptions})
|
||||
@ -68,7 +69,11 @@ set(_OtherConfigOptions
|
||||
foreach(OptionName ${_OtherConfigOptions})
|
||||
AddConfigOption(${OptionName})
|
||||
endforeach()
|
||||
set(CATCH_CONFIG_SHARED_LIBRARY ${BUILD_SHARED_LIBS})
|
||||
if(DEFINED BUILD_SHARED_LIBS)
|
||||
set(CATCH_CONFIG_SHARED_LIBRARY ${BUILD_SHARED_LIBS})
|
||||
else()
|
||||
set(CATCH_CONFIG_SHARED_LIBRARY "")
|
||||
endif()
|
||||
|
||||
set(CATCH_CONFIG_DEFAULT_REPORTER "console" CACHE STRING "Read docs/configuration.md for details. The name of the reporter should be without quotes.")
|
||||
set(CATCH_CONFIG_CONSOLE_WIDTH "80" CACHE STRING "Read docs/configuration.md for details. Must form a valid integer literal.")
|
||||
|
@ -74,6 +74,7 @@ function(add_warnings_to_targets targets)
|
||||
"-Woverloaded-virtual"
|
||||
"-Wparentheses"
|
||||
"-Wpedantic"
|
||||
"-Wredundant-decls"
|
||||
"-Wreorder"
|
||||
"-Wreturn-std-move"
|
||||
"-Wshadow"
|
||||
@ -83,7 +84,7 @@ function(add_warnings_to_targets targets)
|
||||
"-Wundef"
|
||||
"-Wuninitialized"
|
||||
"-Wunneeded-internal-declaration"
|
||||
"-Wunreachable-code"
|
||||
"-Wunreachable-code-aggressive"
|
||||
"-Wunused"
|
||||
"-Wunused-function"
|
||||
"-Wunused-parameter"
|
||||
|
@ -21,6 +21,7 @@ cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io"
|
||||
cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF)
|
||||
cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
|
||||
cmake_dependent_option(CATCH_ENABLE_CONFIGURE_TESTS "Enable CMake configuration tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
|
||||
cmake_dependent_option(CATCH_ENABLE_CMAKE_HELPER_TESTS "Enable CMake helper tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
|
||||
|
||||
|
||||
# Catch2's build breaks if done in-tree. You probably should not build
|
||||
@ -148,6 +149,8 @@ if (NOT_SUBPROJECT)
|
||||
"extras/ParseAndAddCatchTests.cmake"
|
||||
"extras/Catch.cmake"
|
||||
"extras/CatchAddTests.cmake"
|
||||
"extras/CatchShardTests.cmake"
|
||||
"extras/CatchShardTestsImpl.cmake"
|
||||
DESTINATION
|
||||
${CATCH_CMAKE_CONFIG_DESTINATION}
|
||||
)
|
||||
|
@ -18,7 +18,8 @@
|
||||
"CATCH_BUILD_EXAMPLES": "ON",
|
||||
"CATCH_BUILD_EXTRA_TESTS": "ON",
|
||||
"CATCH_BUILD_SURROGATES": "ON",
|
||||
"CATCH_ENABLE_CONFIGURE_TESTS": "ON"
|
||||
"CATCH_ENABLE_CONFIGURE_TESTS": "ON",
|
||||
"CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -4,12 +4,13 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
|
||||
],
|
||||
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
|
||||
)
|
||||
|
||||
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
|
||||
|
||||
bazel_skylib_workspace()
|
||||
|
@ -126,6 +126,7 @@ catch_discover_tests(target
|
||||
[OUTPUT_DIR dir]
|
||||
[OUTPUT_PREFIX prefix]
|
||||
[OUTPUT_SUFFIX suffix]
|
||||
[DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
|
||||
)
|
||||
```
|
||||
|
||||
@ -198,6 +199,16 @@ If specified, `suffix` is added to each output file name, like so
|
||||
`--out dir/<test_name>suffix`. This can be used to add a file extension to
|
||||
the output file name e.g. ".xml".
|
||||
|
||||
* `DISCOVERY_MODE mode`
|
||||
|
||||
If specified allows control over when test discovery is performed.
|
||||
For a value of `POST_BUILD` (default) test discovery is performed at build time.
|
||||
For a a value of `PRE_TEST` test discovery is delayed until just prior to test
|
||||
execution (useful e.g. in cross-compilation environments).
|
||||
``DISCOVERY_MODE`` defaults to the value of the
|
||||
``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when
|
||||
calling ``catch_discover_tests``. This provides a mechanism for globally
|
||||
selecting a preferred test discovery behavior.
|
||||
|
||||
### `ParseAndAddCatchTests.cmake`
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
[Enabling stringification](#enabling-stringification)<br>
|
||||
[Disabling exceptions](#disabling-exceptions)<br>
|
||||
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
|
||||
[Static analysis support](#static-analysis-support)<br>
|
||||
|
||||
Catch2 is designed to "just work" as much as possible, and most of the
|
||||
configuration options below are changed automatically during compilation,
|
||||
@ -264,6 +265,31 @@ The macro will be used as is, that is, `CATCH_BREAK_INTO_DEBUGGER();`
|
||||
must compile and must break into debugger.
|
||||
|
||||
|
||||
## Static analysis support
|
||||
|
||||
> Introduced in Catch2 X.Y.Z.
|
||||
|
||||
Some parts of Catch2, e.g. `SECTION`s, can be hard for static analysis
|
||||
tools to reason about. Catch2 can change its internals to help static
|
||||
analysis tools reason about the tests.
|
||||
|
||||
Catch2 automatically detects some static analysis tools (initial
|
||||
implementation checks for clang-tidy and Coverity), but you can override
|
||||
its detection (in either direction) via
|
||||
|
||||
```
|
||||
CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT // force enables static analysis help
|
||||
CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT // force disables static analysis help
|
||||
```
|
||||
|
||||
_As the name suggests, this is currently experimental, and thus we provide
|
||||
no backwards compatibility guarantees._
|
||||
|
||||
**DO NOT ENABLE THIS FOR BUILDS YOU INTEND TO RUN.** The changed internals
|
||||
are not meant to be runnable, only "scannable".
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@ -28,7 +28,7 @@ depending on how often the cleanup needs to happen.
|
||||
## Why cannot I derive from the built-in reporters?
|
||||
|
||||
They are not made to be overridden, in that we do not attempt to maintain
|
||||
a consistent internal state if a member function is overriden, and by
|
||||
a consistent internal state if a member function is overridden, and by
|
||||
forbidding users from using them as a base class, we can refactor them
|
||||
as needed later.
|
||||
|
||||
|
@ -134,7 +134,7 @@ type, making their usage much nicer. These are
|
||||
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
|
||||
* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
|
||||
* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
|
||||
* `range(Arithemtic start, Arithmetic end)` for `RangeGenerator<Arithmetic>` with a step size of `1`
|
||||
* `range(Arithmetic start, Arithmetic end)` for `RangeGenerator<Arithmetic>` with a step size of `1`
|
||||
* `range(Arithmetic start, Arithmetic end, Arithmetic step)` for `RangeGenerator<Arithmetic>` with a custom step size
|
||||
* `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator<T>`
|
||||
* `from_range(Container const&)` for `IteratorGenerator<T>`
|
||||
@ -221,3 +221,21 @@ For full example of implementing your own generator, look into Catch2's
|
||||
examples, specifically
|
||||
[Generators: Create your own generator](../examples/300-Gen-OwnGenerator.cpp).
|
||||
|
||||
|
||||
### Handling empty generators
|
||||
|
||||
The generator interface assumes that a generator always has at least one
|
||||
element. This is not always true, e.g. if the generator depends on an external
|
||||
datafile, the file might be missing.
|
||||
|
||||
There are two ways to handle this, depending on whether you want this
|
||||
to be an error or not.
|
||||
|
||||
* If empty generator **is** an error, throw an exception in constructor.
|
||||
* If empty generator **is not** an error, use the [`SKIP`](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@ -95,6 +95,9 @@ A C++ client library for Consul. Consul is a distributed tool for discovering an
|
||||
### [Reactive-Extensions/ RxCpp](https://github.com/Reactive-Extensions/RxCpp)
|
||||
A library of algorithms for values-distributed-in-time.
|
||||
|
||||
### [SFML](https://github.com/SFML/SFML)
|
||||
Simple and Fast Multimedia Library.
|
||||
|
||||
### [SOCI](https://github.com/SOCI/soci)
|
||||
The C++ Database Access Library.
|
||||
|
||||
@ -110,6 +113,12 @@ A header-only TOML parser and serializer for modern C++.
|
||||
### [Trompeloeil](https://github.com/rollbear/trompeloeil)
|
||||
A thread-safe header-only mocking framework for C++14.
|
||||
|
||||
### [wxWidgets](https://www.wxwidgets.org/)
|
||||
Cross-Platform C++ GUI Library.
|
||||
|
||||
### [xmlwrapp](https://github.com/vslavik/xmlwrapp)
|
||||
C++ XML parsing library using libxml2.
|
||||
|
||||
## Applications & Tools
|
||||
|
||||
### [App Mesh](https://github.com/laoshanxi/app-mesh)
|
||||
@ -137,7 +146,7 @@ Newsbeuter is an open-source RSS/Atom feed reader for text terminals.
|
||||
A 2D, Zombie, RPG game which is being made on our own engine.
|
||||
|
||||
### [raspigcd](https://github.com/pantadeusz/raspigcd)
|
||||
Low level CLI app and library for execution of GCODE on Raspberry Pi without any additional microcontrolers (just RPi + Stepsticks).
|
||||
Low level CLI app and library for execution of GCODE on Raspberry Pi without any additional microcontrollers (just RPi + Stepsticks).
|
||||
|
||||
### [SpECTRE](https://github.com/sxs-collaboration/spectre)
|
||||
SpECTRE is a code for multi-scale, multi-physics problems in astrophysics and gravitational physics.
|
||||
|
@ -149,7 +149,7 @@
|
||||
|
||||
### Fixes
|
||||
* Cleaned out some warnings and static analysis issues
|
||||
* Suppressed `-Wcomma` warning rarely occuring in templated test cases (#2543)
|
||||
* Suppressed `-Wcomma` warning rarely occurring in templated test cases (#2543)
|
||||
* Constified implementation details in `INFO` (#2564)
|
||||
* Made `MatcherGenericBase` copy constructor const (#2566)
|
||||
* Fixed serialization of test filters so the output roundtrips
|
||||
@ -517,7 +517,7 @@ v3 releases.
|
||||
* The `SECTION`(s) before the `GENERATE` will not be run multiple times, the following ones will.
|
||||
* Added `-D`/`--min-duration` command line flag (#1910)
|
||||
* If a test takes longer to finish than the provided value, its name and duration will be printed.
|
||||
* This flag is overriden by setting `-d`/`--duration`.
|
||||
* This flag is overridden by setting `-d`/`--duration`.
|
||||
|
||||
### Fixes
|
||||
* `TAPReporter` no longer skips successful assertions (#1983)
|
||||
@ -585,7 +585,7 @@ v3 releases.
|
||||
### Fixes
|
||||
* Fixed computation of benchmarking column widths in ConsoleReporter (#1885, #1886)
|
||||
* Suppressed clang-tidy's `cppcoreguidelines-pro-type-vararg` in assertions (#1901)
|
||||
* It was a false positive trigered by the new warning support workaround
|
||||
* It was a false positive triggered by the new warning support workaround
|
||||
* Fixed bug in test specification parser handling of OR'd patterns using escaping (#1905)
|
||||
|
||||
### Miscellaneous
|
||||
@ -922,7 +922,7 @@ v3 releases.
|
||||
|
||||
### Contrib
|
||||
* `ParseAndAddCatchTests` has learned how to use `DISABLED` CTest property (#1452)
|
||||
* `ParseAndAddCatchTests` now works when there is a whitspace before the test name (#1493)
|
||||
* `ParseAndAddCatchTests` now works when there is a whitespace before the test name (#1493)
|
||||
|
||||
|
||||
### Miscellaneous
|
||||
|
@ -96,12 +96,12 @@ void assertionStarting( AssertionInfo const& assertionInfo );
|
||||
void assertionEnded( AssertionStats const& assertionStats );
|
||||
```
|
||||
|
||||
`assertionStarting` is called after the expression is captured, but before
|
||||
the assertion expression is evaluated. This might seem like a minor
|
||||
distinction, but what it means is that if you have assertion like
|
||||
`REQUIRE( a + b == c + d )`, then what happens is that `a + b` and `c + d`
|
||||
are evaluated before `assertionStarting` is emitted, while the `==` is
|
||||
evaluated after the event.
|
||||
The `assertionStarting` event is emitted before the expression in the
|
||||
assertion is captured or evaluated and `assertionEnded` is emitted
|
||||
afterwards. This means that given assertion like `REQUIRE(a + b == c + d)`,
|
||||
Catch2 first emits `assertionStarting` event, then `a + b` and `c + d`
|
||||
are evaluated, then their results are captured, the comparison is evaluated,
|
||||
and then `assertionEnded` event is emitted.
|
||||
|
||||
|
||||
## Benchmarking events
|
||||
|
@ -52,7 +52,7 @@ its machine-readable XML output to file `result-junit.xml`, and the
|
||||
uses ANSI colour codes for colouring the output.
|
||||
|
||||
Using multiple reporters (or one reporter and one-or-more [event
|
||||
listeners](event-listener.md#top)) can have surprisingly complex semantics
|
||||
listeners](event-listeners.md#top)) can have surprisingly complex semantics
|
||||
when using customization points provided to reporters by Catch2, namely
|
||||
capturing stdout/stderr from test cases.
|
||||
|
||||
|
@ -84,6 +84,12 @@ exit code, same as it does if no test cases have run. This behaviour can
|
||||
be overridden using the [--allow-running-no-tests](command-line.md#no-tests-override)
|
||||
flag.
|
||||
|
||||
### `SKIP` inside generators
|
||||
|
||||
You can also use the `SKIP` macro inside generator's constructor to handle
|
||||
cases where the generator is empty, but you do not want to fail the test
|
||||
case.
|
||||
|
||||
|
||||
## Passing and failing test cases
|
||||
|
||||
|
@ -44,11 +44,11 @@ TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][genera
|
||||
|
||||
/* Possible simplifications where less legacy toolchain support is needed:
|
||||
*
|
||||
* - With libstdc++6 or newer, the make_tuple() calls can be ommitted
|
||||
* - With libstdc++6 or newer, the make_tuple() calls can be omitted
|
||||
* (technically C++17 but does not require -std in GCC/Clang). See
|
||||
* https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
|
||||
*
|
||||
* - In C++17 mode std::tie() and the preceding variable delcarations can be
|
||||
* - In C++17 mode std::tie() and the preceding variable declarations can be
|
||||
* replaced by structured bindings: auto [test_input, expected] = GENERATE(
|
||||
* table<std::string, size_t>({ ...
|
||||
*/
|
||||
|
@ -35,8 +35,9 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
[TEST_LIST var]
|
||||
[REPORTER reporter]
|
||||
[OUTPUT_DIR dir]
|
||||
[OUTPUT_PREFIX prefix}
|
||||
[OUTPUT_PREFIX prefix]
|
||||
[OUTPUT_SUFFIX suffix]
|
||||
[DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
|
||||
)
|
||||
|
||||
``catch_discover_tests`` sets up a post-build command on the test executable
|
||||
@ -123,14 +124,28 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
test executable and when the tests are executed themselves. This requires
|
||||
cmake/ctest >= 3.22.
|
||||
|
||||
`DISCOVERY_MODE mode``
|
||||
Provides control over when ``catch_discover_tests`` performs test discovery.
|
||||
By default, ``POST_BUILD`` sets up a post-build command to perform test discovery
|
||||
at build time. In certain scenarios, like cross-compiling, this ``POST_BUILD``
|
||||
behavior is not desirable. By contrast, ``PRE_TEST`` delays test discovery until
|
||||
just prior to test execution. This way test discovery occurs in the target environment
|
||||
where the test has a better chance at finding appropriate runtime dependencies.
|
||||
|
||||
``DISCOVERY_MODE`` defaults to the value of the
|
||||
``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when
|
||||
calling ``catch_discover_tests``. This provides a mechanism for globally selecting
|
||||
a preferred test discovery behavior without having to modify each call site.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
function(catch_discover_tests TARGET)
|
||||
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX"
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX;DISCOVERY_MODE"
|
||||
"TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS"
|
||||
${ARGN}
|
||||
)
|
||||
@ -141,12 +156,20 @@ function(catch_discover_tests TARGET)
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
|
||||
if (_DL_PATHS)
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.22.0")
|
||||
message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT _DISCOVERY_MODE)
|
||||
if(NOT CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE)
|
||||
set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
|
||||
endif()
|
||||
set(_DISCOVERY_MODE ${CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE})
|
||||
endif()
|
||||
if (NOT _DISCOVERY_MODE MATCHES "^(POST_BUILD|PRE_TEST)$")
|
||||
message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
|
||||
endif()
|
||||
|
||||
## Generate a unique name based on the extra arguments
|
||||
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}")
|
||||
@ -159,39 +182,77 @@ function(catch_discover_tests TARGET)
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CROSSCOMPILING_EMULATOR
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-D "TEST_TARGET=${TARGET}"
|
||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||
-D "TEST_SPEC=${_TEST_SPEC}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "TEST_REPORTER=${_REPORTER}"
|
||||
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
|
||||
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
|
||||
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
|
||||
-D "TEST_DL_PATHS=${_DL_PATHS}"
|
||||
-D "CTEST_FILE=${ctest_tests_file}"
|
||||
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
file(WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||
" include(\"${ctest_tests_file}\")\n"
|
||||
"else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
|
||||
"endif()\n"
|
||||
)
|
||||
if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-D "TEST_TARGET=${TARGET}"
|
||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||
-D "TEST_SPEC=${_TEST_SPEC}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "TEST_REPORTER=${_REPORTER}"
|
||||
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
|
||||
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
|
||||
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
|
||||
-D "TEST_DL_PATHS=${_DL_PATHS}"
|
||||
-D "CTEST_FILE=${ctest_tests_file}"
|
||||
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
|
||||
file(WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||
" include(\"${ctest_tests_file}\")\n"
|
||||
"else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
|
||||
"endif()\n"
|
||||
)
|
||||
|
||||
elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
|
||||
|
||||
string(CONCAT ctest_include_content
|
||||
"if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n"
|
||||
" if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n"
|
||||
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\" OR\n"
|
||||
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n"
|
||||
" include(\"${_CATCH_DISCOVER_TESTS_SCRIPT}\")" "\n"
|
||||
" catch_discover_tests_impl(" "\n"
|
||||
" TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n"
|
||||
" TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n"
|
||||
" TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n"
|
||||
" TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n"
|
||||
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
|
||||
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
|
||||
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
|
||||
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
|
||||
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
|
||||
" TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n"
|
||||
" TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n"
|
||||
" TEST_OUTPUT_PREFIX" " [==[" "${_OUTPUT_PREFIX}" "]==]" "\n"
|
||||
" TEST_OUTPUT_SUFFIX" " [==[" "${_OUTPUT_SUFFIX}" "]==]" "\n"
|
||||
" CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n"
|
||||
" TEST_DL_PATHS" " [==[" "${_DL_PATHS}" "]==]" "\n"
|
||||
" CTEST_FILE" " [==[" "${CTEST_FILE}" "]==]" "\n"
|
||||
" )" "\n"
|
||||
" endif()" "\n"
|
||||
" include(\"${ctest_tests_file}\")" "\n"
|
||||
"else()" "\n"
|
||||
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n"
|
||||
"endif()" "\n"
|
||||
)
|
||||
|
||||
file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_content}")
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(DIRECTORY
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||
@ -204,9 +265,7 @@ function(catch_discover_tests TARGET)
|
||||
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Cannot set more than one TEST_INCLUDE_FILE"
|
||||
)
|
||||
message(FATAL_ERROR "Cannot set more than one TEST_INCLUDE_FILE")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -1,28 +1,6 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
set(prefix "${TEST_PREFIX}")
|
||||
set(suffix "${TEST_SUFFIX}")
|
||||
set(spec ${TEST_SPEC})
|
||||
set(extra_args ${TEST_EXTRA_ARGS})
|
||||
set(properties ${TEST_PROPERTIES})
|
||||
set(reporter ${TEST_REPORTER})
|
||||
set(output_dir ${TEST_OUTPUT_DIR})
|
||||
set(output_prefix ${TEST_OUTPUT_PREFIX})
|
||||
set(output_suffix ${TEST_OUTPUT_SUFFIX})
|
||||
set(dl_paths ${TEST_DL_PATHS})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
|
||||
if(WIN32)
|
||||
set(dl_paths_variable_name PATH)
|
||||
elseif(APPLE)
|
||||
set(dl_paths_variable_name DYLD_LIBRARY_PATH)
|
||||
else()
|
||||
set(dl_paths_variable_name LD_LIBRARY_PATH)
|
||||
endif()
|
||||
|
||||
function(add_command NAME)
|
||||
set(_args "")
|
||||
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
|
||||
@ -38,119 +16,177 @@ function(add_command NAME)
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
|
||||
function(catch_discover_tests_impl)
|
||||
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_DL_PATHS;TEST_OUTPUT_DIR;TEST_OUTPUT_PREFIX;TEST_OUTPUT_SUFFIX;TEST_PREFIX;TEST_REPORTER;TEST_SPEC;TEST_SUFFIX;TEST_LIST;CTEST_FILE"
|
||||
"TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR"
|
||||
${ARGN}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(dl_paths)
|
||||
cmake_path(CONVERT "${dl_paths}" TO_NATIVE_PATH_LIST paths)
|
||||
set(ENV{${dl_paths_variable_name}} "${paths}")
|
||||
endif()
|
||||
set(prefix "${_TEST_PREFIX}")
|
||||
set(suffix "${_TEST_SUFFIX}")
|
||||
set(spec ${_TEST_SPEC})
|
||||
set(extra_args ${_TEST_EXTRA_ARGS})
|
||||
set(properties ${_TEST_PROPERTIES})
|
||||
set(reporter ${_TEST_REPORTER})
|
||||
set(output_dir ${_TEST_OUTPUT_DIR})
|
||||
set(output_prefix ${_TEST_OUTPUT_PREFIX})
|
||||
set(output_suffix ${_TEST_OUTPUT_SUFFIX})
|
||||
set(dl_paths ${_TEST_DL_PATHS})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${result}\n"
|
||||
" Output: ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Run test executable to get list of available reporters
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-reporters
|
||||
OUTPUT_VARIABLE reporters_output
|
||||
RESULT_VARIABLE reporters_result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
if(NOT ${reporters_result} EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${reporters_result}\n"
|
||||
" Output: ${reporters_output}\n"
|
||||
)
|
||||
endif()
|
||||
string(FIND "${reporters_output}" "${reporter}" reporter_is_valid)
|
||||
if(reporter AND ${reporter_is_valid} EQUAL -1)
|
||||
message(FATAL_ERROR
|
||||
"\"${reporter}\" is not a valid reporter!\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Prepare reporter
|
||||
if(reporter)
|
||||
set(reporter_arg "--reporter ${reporter}")
|
||||
endif()
|
||||
|
||||
# Prepare output dir
|
||||
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
|
||||
set(output_dir "${TEST_WORKING_DIR}/${output_dir}")
|
||||
if(NOT EXISTS ${output_dir})
|
||||
file(MAKE_DIRECTORY ${output_dir})
|
||||
if(WIN32)
|
||||
set(dl_paths_variable_name PATH)
|
||||
elseif(APPLE)
|
||||
set(dl_paths_variable_name DYLD_LIBRARY_PATH)
|
||||
else()
|
||||
set(dl_paths_variable_name LD_LIBRARY_PATH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(dl_paths)
|
||||
foreach(path ${dl_paths})
|
||||
cmake_path(NATIVE_PATH path native_path)
|
||||
list(APPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}")
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${_TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable '${_TEST_EXECUTABLE}' does not exist"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(dl_paths)
|
||||
cmake_path(CONVERT "${dl_paths}" TO_NATIVE_PATH_LIST paths)
|
||||
set(ENV{${dl_paths_variable_name}} "${paths}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${_TEST_EXECUTABLE}':\n"
|
||||
" Result: ${result}\n"
|
||||
" Output: ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Make sure to escape ; (semicolons) in test names first, because
|
||||
# that'd break the foreach loop for "Parse output" later and create
|
||||
# wrongly splitted and thus failing test cases (false positives)
|
||||
string(REPLACE ";" "\;" output "${output}")
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Prepare reporter
|
||||
if(reporter)
|
||||
set(reporter_arg "--reporter ${reporter}")
|
||||
|
||||
# Run test executable to check whether reporter is available
|
||||
# note that the use of --list-reporters is not the important part,
|
||||
# we only want to check whether the execution succeeds with ${reporter_arg}
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters
|
||||
OUTPUT_VARIABLE reporter_check_output
|
||||
RESULT_VARIABLE reporter_check_result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
if(${reporter_check_result} EQUAL 255)
|
||||
message(FATAL_ERROR
|
||||
"\"${reporter}\" is not a valid reporter!\n"
|
||||
)
|
||||
elseif(NOT ${reporter_check_result} EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${reporter_check_result}\n"
|
||||
" Output: ${reporter_check_output}\n"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Prepare output dir
|
||||
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
|
||||
set(output_dir "${_TEST_WORKING_DIR}/${output_dir}")
|
||||
if(NOT EXISTS ${output_dir})
|
||||
file(MAKE_DIRECTORY ${output_dir})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(dl_paths)
|
||||
foreach(path ${dl_paths})
|
||||
cmake_path(NATIVE_PATH path native_path)
|
||||
list(APPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
set(test "${line}")
|
||||
# Escape characters in test case names that would be parsed by Catch2
|
||||
# Note that the \ escaping must happen FIRST! Do not change the order.
|
||||
set(test_name "${test}")
|
||||
foreach(char \\ , [ ])
|
||||
string(REPLACE ${char} "\\${char}" test_name "${test_name}")
|
||||
endforeach(char)
|
||||
# ...add output dir
|
||||
if(output_dir)
|
||||
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean "${test_name}")
|
||||
set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}")
|
||||
endif()
|
||||
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${_TEST_EXECUTOR}
|
||||
"${_TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
"${reporter_arg}"
|
||||
"${output_dir_arg}"
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
|
||||
if(environment_modifications)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
ENVIRONMENT_MODIFICATION "${environment_modifications}")
|
||||
endif()
|
||||
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
add_command(set ${_TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${_CTEST_FILE}" "${script}")
|
||||
endfunction()
|
||||
|
||||
if(CMAKE_SCRIPT_MODE_FILE)
|
||||
catch_discover_tests_impl(
|
||||
TEST_EXECUTABLE ${TEST_EXECUTABLE}
|
||||
TEST_EXECUTOR ${TEST_EXECUTOR}
|
||||
TEST_WORKING_DIR ${TEST_WORKING_DIR}
|
||||
TEST_SPEC ${TEST_SPEC}
|
||||
TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
|
||||
TEST_PROPERTIES ${TEST_PROPERTIES}
|
||||
TEST_PREFIX ${TEST_PREFIX}
|
||||
TEST_SUFFIX ${TEST_SUFFIX}
|
||||
TEST_LIST ${TEST_LIST}
|
||||
TEST_REPORTER ${TEST_REPORTER}
|
||||
TEST_OUTPUT_DIR ${TEST_OUTPUT_DIR}
|
||||
TEST_OUTPUT_PREFIX ${TEST_OUTPUT_PREFIX}
|
||||
TEST_OUTPUT_SUFFIX ${TEST_OUTPUT_SUFFIX}
|
||||
TEST_DL_PATHS ${TEST_DL_PATHS}
|
||||
CTEST_FILE ${CTEST_FILE}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
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)
|
||||
# ...add output dir
|
||||
if(output_dir)
|
||||
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean ${test_name})
|
||||
set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}")
|
||||
endif()
|
||||
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
"${reporter_arg}"
|
||||
"${output_dir_arg}"
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
|
||||
if(environment_modifications)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
ENVIRONMENT_MODIFICATION "${environment_modifications}")
|
||||
endif()
|
||||
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
add_command(set ${TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${CTEST_FILE}" "${script}")
|
||||
|
@ -46,7 +46,7 @@ function(catch_add_sharded_tests TARGET)
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||
)
|
||||
|
||||
set(shard_impl_script_file "${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake")
|
||||
set(shard_impl_script_file "${_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT}")
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
@ -64,3 +64,11 @@ function(catch_add_sharded_tests TARGET)
|
||||
|
||||
|
||||
endfunction()
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
set(_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT
|
||||
${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake
|
||||
CACHE INTERNAL "Catch2 full path to CatchShardTestsImpl.cmake helper file"
|
||||
)
|
||||
|
@ -10,8 +10,10 @@ project(
|
||||
'cpp',
|
||||
version: '3.3.2', # CML version placeholder, don't delete
|
||||
license: 'BSL-1.0',
|
||||
meson_version: '>=0.50.0',
|
||||
meson_version: '>=0.54.1',
|
||||
)
|
||||
|
||||
subdir('src/catch2')
|
||||
subdir('tests')
|
||||
if get_option('tests')
|
||||
subdir('tests')
|
||||
endif
|
||||
|
1
meson_options.txt
Normal file
1
meson_options.txt
Normal file
@ -0,0 +1 @@
|
||||
option('tests', type: 'boolean', value: true, description: 'Build the unit tests')
|
@ -21,6 +21,8 @@ set(BENCHMARK_HEADERS
|
||||
${SOURCES_DIR}/benchmark/catch_sample_analysis.hpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_analyse.hpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.hpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats.hpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats_fwd.hpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_complete_invoke.hpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_estimate_clock.hpp
|
||||
${SOURCES_DIR}/benchmark/detail/catch_measure.hpp
|
||||
@ -71,6 +73,7 @@ set(IMPL_HEADERS
|
||||
${SOURCES_DIR}/internal/catch_compiler_capabilities.hpp
|
||||
${SOURCES_DIR}/internal/catch_config_android_logwrite.hpp
|
||||
${SOURCES_DIR}/internal/catch_config_counter.hpp
|
||||
${SOURCES_DIR}/internal/catch_config_static_analysis_support.hpp
|
||||
${SOURCES_DIR}/internal/catch_config_uncaught_exceptions.hpp
|
||||
${SOURCES_DIR}/internal/catch_config_wchar.hpp
|
||||
${SOURCES_DIR}/internal/catch_console_colour.hpp
|
||||
@ -128,6 +131,7 @@ set(IMPL_HEADERS
|
||||
${SOURCES_DIR}/internal/catch_test_failure_exception.hpp
|
||||
${SOURCES_DIR}/internal/catch_test_macro_impl.hpp
|
||||
${SOURCES_DIR}/internal/catch_test_registry.hpp
|
||||
${SOURCES_DIR}/internal/catch_test_run_info.hpp
|
||||
${SOURCES_DIR}/internal/catch_test_spec_parser.hpp
|
||||
${SOURCES_DIR}/internal/catch_textflow.hpp
|
||||
${SOURCES_DIR}/internal/catch_to_string.hpp
|
||||
@ -153,6 +157,7 @@ set(IMPL_SOURCES
|
||||
${SOURCES_DIR}/catch_timer.cpp
|
||||
${SOURCES_DIR}/catch_tostring.cpp
|
||||
${SOURCES_DIR}/catch_totals.cpp
|
||||
${SOURCES_DIR}/catch_translate_exception.cpp
|
||||
${SOURCES_DIR}/catch_version.cpp
|
||||
${SOURCES_DIR}/internal/catch_assertion_handler.cpp
|
||||
${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.cpp
|
||||
@ -216,8 +221,8 @@ set(INTERFACE_HEADERS
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.hpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.hpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.hpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_tag_alias_registry.hpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_test_invoker.hpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_testcase.hpp
|
||||
)
|
||||
set(INTERFACE_SOURCES
|
||||
@ -228,7 +233,6 @@ set(INTERFACE_SOURCES
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.cpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.cpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.cpp
|
||||
${SOURCES_DIR}/interfaces/catch_interfaces_testcase.cpp
|
||||
)
|
||||
set(INTERFACE_FILES ${INTERFACE_HEADERS} ${INTERFACE_SOURCES})
|
||||
|
@ -10,23 +10,27 @@
|
||||
#ifndef CATCH_BENCHMARK_HPP_INCLUDED
|
||||
#define CATCH_BENCHMARK_HPP_INCLUDED
|
||||
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||
#include <catch2/internal/catch_context.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/internal/catch_unique_name.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/benchmark/catch_chronometer.hpp>
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/internal/catch_unique_name.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
|
||||
#include <catch2/benchmark/catch_clock.hpp>
|
||||
#include <catch2/benchmark/catch_environment.hpp>
|
||||
#include <catch2/benchmark/catch_execution_plan.hpp>
|
||||
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
|
||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
||||
#include <catch2/benchmark/detail/catch_analyse.hpp>
|
||||
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
|
||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -82,7 +86,7 @@ namespace Catch {
|
||||
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
|
||||
BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
|
||||
getResultCapture().benchmarkEnded(stats);
|
||||
} CATCH_CATCH_ANON (TestFailureException) {
|
||||
} CATCH_CATCH_ANON (TestFailureException const&) {
|
||||
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
|
||||
} CATCH_CATCH_ALL{
|
||||
getResultCapture().benchmarkFailed(translateActiveException());
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <catch2/benchmark/catch_sample_analysis.hpp>
|
||||
#include <catch2/benchmark/detail/catch_analyse.hpp>
|
||||
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
|
||||
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
|
||||
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
|
||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
||||
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
|
||||
#include <catch2/benchmark/detail/catch_measure.hpp>
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include <catch2/benchmark/catch_clock.hpp>
|
||||
#include <catch2/benchmark/catch_optimizer.hpp>
|
||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
||||
#include <catch2/internal/catch_meta.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
|
@ -17,8 +17,7 @@
|
||||
#include <catch2/benchmark/detail/catch_repeat.hpp>
|
||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
@ -41,14 +40,17 @@ namespace Catch {
|
||||
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] {
|
||||
const auto num_samples = cfg.benchmarkSamples();
|
||||
times.reserve( num_samples );
|
||||
for ( size_t i = 0; i < num_samples; ++i ) {
|
||||
Detail::ChronometerModel<Clock> model;
|
||||
this->benchmark(Chronometer(model, iterations_per_sample));
|
||||
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;
|
||||
});
|
||||
if ( sample_time < FloatDuration<Clock>::zero() ) {
|
||||
sample_time = FloatDuration<Clock>::zero();
|
||||
}
|
||||
times.push_back(sample_time / iterations_per_sample);
|
||||
}
|
||||
return times;
|
||||
}
|
||||
};
|
||||
|
@ -10,7 +10,7 @@
|
||||
#ifndef CATCH_OPTIMIZER_HPP_INCLUDED
|
||||
#define CATCH_OPTIMIZER_HPP_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
|
||||
# include <atomic> // atomic_thread_fence
|
||||
#endif
|
||||
|
||||
@ -32,16 +32,23 @@ namespace Catch {
|
||||
namespace Detail {
|
||||
inline void optimizer_barrier() { keep_memory(); }
|
||||
} // namespace Detail
|
||||
#elif defined(_MSC_VER)
|
||||
#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
|
||||
|
||||
#if defined(_MSVC_VER)
|
||||
#pragma optimize("", off)
|
||||
#elif defined(__IAR_SYSTEMS_ICC__)
|
||||
// For IAR the pragma only affects the following function
|
||||
#pragma optimize=disable
|
||||
#endif
|
||||
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()
|
||||
#if defined(_MSVC_VER)
|
||||
#pragma optimize("", on)
|
||||
#endif
|
||||
|
||||
namespace Detail {
|
||||
inline void optimizer_barrier() {
|
||||
|
@ -10,14 +10,11 @@
|
||||
#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
||||
#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
|
||||
|
||||
#include <catch2/benchmark/catch_clock.hpp>
|
||||
#include <catch2/benchmark/catch_estimate.hpp>
|
||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
@ -33,7 +30,9 @@ namespace Catch {
|
||||
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); });
|
||||
for (auto const& d : samples) {
|
||||
samples2.push_back(Duration2(d));
|
||||
}
|
||||
return {
|
||||
CATCH_MOVE(samples2),
|
||||
mean,
|
||||
|
@ -10,15 +10,12 @@
|
||||
#ifndef CATCH_ANALYSE_HPP_INCLUDED
|
||||
#define CATCH_ANALYSE_HPP_INCLUDED
|
||||
|
||||
#include <catch2/benchmark/catch_clock.hpp>
|
||||
#include <catch2/benchmark/catch_environment.hpp>
|
||||
#include <catch2/benchmark/catch_sample_analysis.hpp>
|
||||
#include <catch2/benchmark/detail/catch_stats.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
@ -29,7 +26,9 @@ namespace Catch {
|
||||
if (!cfg.benchmarkNoAnalysis()) {
|
||||
std::vector<double> samples;
|
||||
samples.reserve(static_cast<size_t>(last - first));
|
||||
std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
|
||||
for (auto current = first; current != last; ++current) {
|
||||
samples.push_back( current->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());
|
||||
@ -44,7 +43,10 @@ namespace Catch {
|
||||
};
|
||||
std::vector<Duration> samples2;
|
||||
samples2.reserve(samples.size());
|
||||
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
|
||||
for (auto s : samples) {
|
||||
samples2.push_back( Duration( s ) );
|
||||
}
|
||||
|
||||
return {
|
||||
CATCH_MOVE(samples2),
|
||||
wrap_estimate(analysis.mean),
|
||||
|
@ -11,7 +11,6 @@
|
||||
#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
|
||||
|
||||
#include <catch2/benchmark/catch_chronometer.hpp>
|
||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
||||
#include <catch2/internal/catch_meta.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
64
src/catch2/benchmark/detail/catch_benchmark_stats.hpp
Normal file
64
src/catch2/benchmark/detail/catch_benchmark_stats.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
||||
#define CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/benchmark/catch_estimate.hpp>
|
||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
||||
// The fwd decl & default specialization needs to be seen by VS2017 before
|
||||
// BenchmarkStats itself, or VS2017 will report compilation error.
|
||||
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct BenchmarkInfo {
|
||||
std::string name;
|
||||
double estimatedDuration;
|
||||
int iterations;
|
||||
unsigned int samples;
|
||||
unsigned int resamples;
|
||||
double clockResolution;
|
||||
double clockCost;
|
||||
};
|
||||
|
||||
template <class Duration>
|
||||
struct BenchmarkStats {
|
||||
BenchmarkInfo info;
|
||||
|
||||
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());
|
||||
for (auto const& sample : samples) {
|
||||
samples2.push_back(Duration2(sample));
|
||||
}
|
||||
return {
|
||||
info,
|
||||
CATCH_MOVE(samples2),
|
||||
mean,
|
||||
standardDeviation,
|
||||
outliers,
|
||||
outlierVariance,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED
|
23
src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp
Normal file
23
src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
||||
#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
// We cannot forward declare the type with default template argument
|
||||
// multiple times, so it is split out into a separate header so that
|
||||
// we can prevent multiple declarations in dependees
|
||||
template <typename Duration = std::chrono::duration<double, std::nano>>
|
||||
struct BenchmarkStats;
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
|
@ -10,14 +10,9 @@
|
||||
#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
||||
#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/internal/catch_meta.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
@ -30,26 +29,29 @@ namespace Catch {
|
||||
std::vector<double> resolution(int k) {
|
||||
std::vector<TimePoint<Clock>> times;
|
||||
times.reserve(static_cast<size_t>(k + 1));
|
||||
std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
|
||||
for ( int i = 0; i < k + 1; ++i ) {
|
||||
times.push_back( Clock::now() );
|
||||
}
|
||||
|
||||
std::vector<double> deltas;
|
||||
deltas.reserve(static_cast<size_t>(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()); });
|
||||
for ( size_t idx = 1; idx < times.size(); ++idx ) {
|
||||
deltas.push_back( static_cast<double>(
|
||||
( times[idx] - times[idx - 1] ).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;
|
||||
constexpr auto warmup_iterations = 10000;
|
||||
constexpr auto warmup_time = std::chrono::milliseconds(100);
|
||||
constexpr auto minimum_ticks = 1000;
|
||||
constexpr auto warmup_seed = 10000;
|
||||
constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
|
||||
constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
|
||||
constexpr auto clock_cost_estimation_tick_limit = 100000;
|
||||
constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10);
|
||||
constexpr auto clock_cost_estimation_iterations = 10000;
|
||||
|
||||
template <typename Clock>
|
||||
int warmup() {
|
||||
@ -84,9 +86,11 @@ namespace Catch {
|
||||
std::vector<double> times;
|
||||
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
|
||||
times.reserve(static_cast<size_t>(nsamples));
|
||||
std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
|
||||
return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
|
||||
});
|
||||
for ( int s = 0; s < nsamples; ++s ) {
|
||||
times.push_back( static_cast<double>(
|
||||
( time_clock( r.iterations ) / r.iterations )
|
||||
.count() ) );
|
||||
}
|
||||
return {
|
||||
FloatDuration<Clock>(mean(times.begin(), times.end())),
|
||||
classify_outliers(times.begin(), times.end()),
|
||||
|
@ -10,7 +10,6 @@
|
||||
#ifndef CATCH_MEASURE_HPP_INCLUDED
|
||||
#define CATCH_MEASURE_HPP_INCLUDED
|
||||
|
||||
#include <catch2/benchmark/catch_clock.hpp>
|
||||
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
|
||||
#include <catch2/benchmark/detail/catch_timing.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
@ -7,9 +7,10 @@
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
|
||||
#include <exception>
|
||||
#include <catch2/internal/catch_enforce.hpp>
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
|
||||
|
||||
@ -21,117 +21,172 @@
|
||||
#include <future>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
namespace {
|
||||
|
||||
using Catch::Benchmark::Detail::sample;
|
||||
template <typename URng, typename Estimator>
|
||||
static sample
|
||||
resample( URng& rng,
|
||||
unsigned int resamples,
|
||||
std::vector<double>::const_iterator first,
|
||||
std::vector<double>::const_iterator last,
|
||||
Estimator& estimator ) {
|
||||
auto n = static_cast<size_t>( last - first );
|
||||
std::uniform_int_distribution<decltype( n )> dist( 0,
|
||||
n - 1 );
|
||||
|
||||
template <typename URng, typename Estimator>
|
||||
sample resample(URng& rng, unsigned int resamples, std::vector<double>::iterator first, std::vector<double>::iterator last, Estimator& estimator) {
|
||||
auto n = static_cast<size_t>(last - first);
|
||||
std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
|
||||
sample out;
|
||||
out.reserve( resamples );
|
||||
// We allocate the vector outside the loop to avoid realloc
|
||||
// per resample
|
||||
std::vector<double> resampled;
|
||||
resampled.reserve( n );
|
||||
for ( size_t i = 0; i < resamples; ++i ) {
|
||||
resampled.clear();
|
||||
for ( size_t s = 0; s < n; ++s ) {
|
||||
resampled.push_back(
|
||||
first[static_cast<std::ptrdiff_t>(
|
||||
dist( rng ) )] );
|
||||
}
|
||||
const auto estimate =
|
||||
estimator( resampled.begin(), resampled.end() );
|
||||
out.push_back( estimate );
|
||||
}
|
||||
std::sort( out.begin(), out.end() );
|
||||
return out;
|
||||
}
|
||||
|
||||
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[static_cast<std::ptrdiff_t>(dist(rng))]; });
|
||||
return estimator(resampled.begin(), resampled.end());
|
||||
});
|
||||
std::sort(out.begin(), out.end());
|
||||
return out;
|
||||
}
|
||||
static 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 static_cast<int>( -2. * k0 /
|
||||
( k1 + std::sqrt( det ) ) );
|
||||
};
|
||||
|
||||
double erf_inv(double x) {
|
||||
// Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
|
||||
double w, p;
|
||||
auto var_out = [n, sb2, sg2]( double c ) {
|
||||
double nc = n - c;
|
||||
return ( nc / n ) * ( sb2 - nc * sg2 );
|
||||
};
|
||||
|
||||
w = -log((1.0 - x) * (1.0 + x));
|
||||
return (std::min)( var_out( 1 ),
|
||||
var_out(
|
||||
(std::min)( c_max( 0. ),
|
||||
c_max( mg_min ) ) ) ) /
|
||||
sb2;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
static double erf_inv( double x ) {
|
||||
// Code accompanying the article "Approximating the erfinv
|
||||
// function" in GPU Computing Gems, Volume 2
|
||||
double w, p;
|
||||
|
||||
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 );
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static double
|
||||
standard_deviation( std::vector<double>::const_iterator first,
|
||||
std::vector<double>::const_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
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
@ -161,6 +216,47 @@ namespace Catch {
|
||||
return xj + g * (xj1 - xj);
|
||||
}
|
||||
|
||||
OutlierClassification
|
||||
classify_outliers( std::vector<double>::const_iterator first,
|
||||
std::vector<double>::const_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 ) {
|
||||
const double 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;
|
||||
}
|
||||
|
||||
double mean( std::vector<double>::const_iterator first,
|
||||
std::vector<double>::const_iterator last ) {
|
||||
auto count = last - first;
|
||||
double sum = 0.;
|
||||
while (first != last) {
|
||||
sum += *first;
|
||||
++first;
|
||||
}
|
||||
return sum / static_cast<double>(count);
|
||||
}
|
||||
|
||||
|
||||
double erfc_inv(double x) {
|
||||
return erf_inv(1.0 - x);
|
||||
@ -182,35 +278,10 @@ namespace Catch {
|
||||
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 static_cast<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, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
|
||||
bootstrap_analysis analyse_samples(double confidence_level,
|
||||
unsigned int n_resamples,
|
||||
std::vector<double>::iterator first,
|
||||
std::vector<double>::iterator last) {
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||
static std::random_device entropy;
|
||||
@ -218,11 +289,12 @@ namespace Catch {
|
||||
|
||||
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 mean = &Detail::mean;
|
||||
auto stddev = &standard_deviation;
|
||||
|
||||
#if defined(CATCH_CONFIG_USE_ASYNC)
|
||||
auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
|
||||
auto Estimate = [=](double(*f)(std::vector<double>::const_iterator,
|
||||
std::vector<double>::const_iterator)) {
|
||||
auto seed = entropy();
|
||||
return std::async(std::launch::async, [=] {
|
||||
std::mt19937 rng(seed);
|
||||
@ -237,7 +309,8 @@ namespace Catch {
|
||||
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 Estimate = [=](double(*f)(std::vector<double>::const_iterator,
|
||||
std::vector<double>::const_iterator)) {
|
||||
auto seed = entropy();
|
||||
std::mt19937 rng(seed);
|
||||
auto resampled = resample(rng, n_resamples, first, last, f);
|
||||
|
@ -15,8 +15,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
#include <cmath>
|
||||
|
||||
namespace Catch {
|
||||
@ -30,39 +28,17 @@ namespace Catch {
|
||||
|
||||
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);
|
||||
OutlierClassification
|
||||
classify_outliers( std::vector<double>::const_iterator first,
|
||||
std::vector<double>::const_iterator 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.);
|
||||
double mean( std::vector<double>::const_iterator first,
|
||||
std::vector<double>::const_iterator last );
|
||||
|
||||
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 / static_cast<double>(count);
|
||||
}
|
||||
|
||||
template <typename Estimator, typename Iterator>
|
||||
sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
|
||||
template <typename Estimator>
|
||||
sample jackknife(Estimator&& estimator,
|
||||
std::vector<double>::iterator first,
|
||||
std::vector<double>::iterator last) {
|
||||
auto n = static_cast<size_t>(last - first);
|
||||
auto second = first;
|
||||
++second;
|
||||
@ -85,8 +61,12 @@ namespace Catch {
|
||||
|
||||
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) {
|
||||
template <typename Estimator>
|
||||
Estimate<double> bootstrap( double confidence_level,
|
||||
std::vector<double>::iterator first,
|
||||
std::vector<double>::iterator last,
|
||||
sample const& resample,
|
||||
Estimator&& estimator ) {
|
||||
auto n_samples = last - first;
|
||||
|
||||
double point = estimator(first, last);
|
||||
@ -95,13 +75,13 @@ namespace Catch {
|
||||
|
||||
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 sum_squares = 0, sum_cubes = 0;
|
||||
for (double x : jack) {
|
||||
auto difference = jack_mean - x;
|
||||
auto square = difference * difference;
|
||||
auto cube = square * difference;
|
||||
sum_squares += square; sum_cubes += cube;
|
||||
}
|
||||
|
||||
double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
|
||||
long n = static_cast<long>(resample.size());
|
||||
@ -128,15 +108,16 @@ namespace Catch {
|
||||
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, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
|
||||
bootstrap_analysis analyse_samples(double confidence_level,
|
||||
unsigned int n_resamples,
|
||||
std::vector<double>::iterator first,
|
||||
std::vector<double>::iterator last);
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
} // namespace Catch
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||
#include <catch2/internal/catch_config_android_logwrite.hpp>
|
||||
#include <catch2/internal/catch_config_counter.hpp>
|
||||
#include <catch2/internal/catch_config_static_analysis_support.hpp>
|
||||
#include <catch2/internal/catch_config_uncaught_exceptions.hpp>
|
||||
#include <catch2/internal/catch_config_wchar.hpp>
|
||||
#include <catch2/internal/catch_console_colour.hpp>
|
||||
@ -86,6 +87,7 @@
|
||||
#include <catch2/internal/catch_platform.hpp>
|
||||
#include <catch2/internal/catch_polyfills.hpp>
|
||||
#include <catch2/internal/catch_preprocessor.hpp>
|
||||
#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
|
||||
#include <catch2/internal/catch_preprocessor_remove_parens.hpp>
|
||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
||||
#include <catch2/internal/catch_random_seed_generation.hpp>
|
||||
@ -111,6 +113,7 @@
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/internal/catch_test_macro_impl.hpp>
|
||||
#include <catch2/internal/catch_test_registry.hpp>
|
||||
#include <catch2/internal/catch_test_run_info.hpp>
|
||||
#include <catch2/internal/catch_test_spec_parser.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/internal/catch_to_string.hpp>
|
||||
|
@ -105,7 +105,7 @@ namespace Catch {
|
||||
elem = trim(elem);
|
||||
}
|
||||
|
||||
// Insert the default reporter if user hasn't asked for a specfic one
|
||||
// Insert the default reporter if user hasn't asked for a specific one
|
||||
if ( m_data.reporterSpecifications.empty() ) {
|
||||
m_data.reporterSpecifications.push_back( {
|
||||
#if defined( CATCH_CONFIG_DEFAULT_REPORTER )
|
||||
|
@ -37,7 +37,11 @@ namespace Catch {
|
||||
}
|
||||
|
||||
|
||||
Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
|
||||
Capturer::Capturer( StringRef macroName,
|
||||
SourceLineInfo const& lineInfo,
|
||||
ResultWas::OfType resultType,
|
||||
StringRef names ):
|
||||
m_resultCapture( getResultCapture() ) {
|
||||
auto trimmed = [&] (size_t start, size_t end) {
|
||||
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
|
||||
++start;
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||
#include <catch2/internal/catch_stream_end_stop.hpp>
|
||||
#include <catch2/internal/catch_message_info.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||
#include <catch2/catch_tostring.hpp>
|
||||
|
||||
#include <string>
|
||||
@ -21,6 +20,7 @@
|
||||
namespace Catch {
|
||||
|
||||
struct SourceLineInfo;
|
||||
class IResultCapture;
|
||||
|
||||
struct MessageStream {
|
||||
|
||||
@ -61,7 +61,7 @@ namespace Catch {
|
||||
|
||||
class Capturer {
|
||||
std::vector<MessageInfo> m_messages;
|
||||
IResultCapture& m_resultCapture = getResultCapture();
|
||||
IResultCapture& m_resultCapture;
|
||||
size_t m_captured = 0;
|
||||
public:
|
||||
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
|
||||
|
@ -20,6 +20,9 @@
|
||||
#include <catch2/internal/catch_noncopyable.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@ -31,7 +34,7 @@ namespace Catch {
|
||||
|
||||
public: // IRegistryHub
|
||||
RegistryHub() = default;
|
||||
IReporterRegistry const& getReporterRegistry() const override {
|
||||
ReporterRegistry const& getReporterRegistry() const override {
|
||||
return m_reporterRegistry;
|
||||
}
|
||||
ITestCaseRegistry const& getTestCaseRegistry() const override {
|
||||
|
@ -13,13 +13,13 @@
|
||||
#include <catch2/internal/catch_run_context.hpp>
|
||||
#include <catch2/catch_test_spec.hpp>
|
||||
#include <catch2/catch_version.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/internal/catch_startup_exception_registry.hpp>
|
||||
#include <catch2/internal/catch_sharding.hpp>
|
||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/internal/catch_windows_h_proxy.hpp>
|
||||
#include <catch2/reporters/catch_reporter_multi.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
|
||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_stdstreams.hpp>
|
||||
@ -27,6 +27,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
|
||||
|
@ -139,12 +139,20 @@ namespace Catch {
|
||||
for (size_t idx = 0; idx < originalTags.size(); ++idx) {
|
||||
auto c = originalTags[idx];
|
||||
if (c == '[') {
|
||||
assert(!inTag);
|
||||
CATCH_ENFORCE(
|
||||
!inTag,
|
||||
"Found '[' inside a tag while registering test case '"
|
||||
<< _nameAndTags.name << "' at " << _lineInfo );
|
||||
|
||||
inTag = true;
|
||||
tagStart = idx;
|
||||
}
|
||||
if (c == ']') {
|
||||
assert(inTag);
|
||||
CATCH_ENFORCE(
|
||||
inTag,
|
||||
"Found unmatched ']' while registering test case '"
|
||||
<< _nameAndTags.name << "' at " << _lineInfo );
|
||||
|
||||
inTag = false;
|
||||
tagEnd = idx;
|
||||
assert(tagStart < tagEnd);
|
||||
@ -153,7 +161,11 @@ namespace Catch {
|
||||
// it over to backing storage and actually reference the
|
||||
// backing storage in the saved tags
|
||||
StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
|
||||
CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed");
|
||||
CATCH_ENFORCE( !tagStr.empty(),
|
||||
"Found an empty tag while registering test case '"
|
||||
<< _nameAndTags.name << "' at "
|
||||
<< _lineInfo );
|
||||
|
||||
enforceNotReservedTag(tagStr, lineInfo);
|
||||
properties |= parseSpecialTag(tagStr);
|
||||
// When copying a tag to the backing storage, we need to
|
||||
@ -167,8 +179,12 @@ namespace Catch {
|
||||
// the tags.
|
||||
internalAppendTag(tagStr);
|
||||
}
|
||||
(void)inTag; // Silence "set-but-unused" warning in release mode.
|
||||
}
|
||||
CATCH_ENFORCE( !inTag,
|
||||
"Found an unclosed tag while registering test case '"
|
||||
<< _nameAndTags.name << "' at " << _lineInfo );
|
||||
|
||||
|
||||
// Add [.] if relevant
|
||||
if (isHidden()) {
|
||||
internalAppendTag("."_sr);
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#include <catch2/catch_test_spec.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
|
||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/catch_test_case_info.hpp>
|
||||
@ -106,16 +108,18 @@ namespace Catch {
|
||||
return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
|
||||
}
|
||||
|
||||
TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const
|
||||
{
|
||||
Matches matches( m_filters.size() );
|
||||
std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
|
||||
TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const {
|
||||
Matches matches;
|
||||
matches.reserve( m_filters.size() );
|
||||
for ( auto const& filter : m_filters ) {
|
||||
std::vector<TestCaseHandle const*> currentMatches;
|
||||
for( auto const& test : testCases )
|
||||
if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) )
|
||||
for ( auto const& test : testCases )
|
||||
if ( isThrowSafe( test, config ) &&
|
||||
filter.matches( test.getTestCaseInfo() ) )
|
||||
currentMatches.emplace_back( &test );
|
||||
return FilterMatch{ extractFilterName(filter), currentMatches };
|
||||
} );
|
||||
matches.push_back(
|
||||
FilterMatch{ extractFilterName( filter ), currentMatches } );
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,6 @@ namespace Catch {
|
||||
} // namespace Detail
|
||||
|
||||
|
||||
// If we decide for C++14, change these to enable_if_ts
|
||||
template <typename T, typename = void>
|
||||
struct StringMaker {
|
||||
template <typename Fake = T>
|
||||
|
20
src/catch2/catch_translate_exception.cpp
Normal file
20
src/catch2/catch_translate_exception.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <catch2/catch_translate_exception.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
void registerTranslatorImpl(
|
||||
Detail::unique_ptr<IExceptionTranslator>&& translator ) {
|
||||
getMutableRegistryHub().registerTranslator(
|
||||
CATCH_MOVE( translator ) );
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Catch
|
@ -15,6 +15,10 @@
|
||||
#include <exception>
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
void registerTranslatorImpl(
|
||||
Detail::unique_ptr<IExceptionTranslator>&& translator );
|
||||
}
|
||||
|
||||
class ExceptionTranslatorRegistrar {
|
||||
template<typename T>
|
||||
@ -48,9 +52,9 @@ namespace Catch {
|
||||
public:
|
||||
template<typename T>
|
||||
ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
|
||||
getMutableRegistryHub().registerTranslator(
|
||||
Detail::make_unique<ExceptionTranslator<T>>(translateFunction)
|
||||
);
|
||||
Detail::registerTranslatorImpl(
|
||||
Detail::make_unique<ExceptionTranslator<T>>(
|
||||
translateFunction ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -169,9 +169,18 @@
|
||||
#endif
|
||||
|
||||
|
||||
#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
||||
#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
||||
|
||||
#if defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \
|
||||
defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT )
|
||||
# error Cannot force STATIC_ANALYSIS_SUPPORT to both ON and OFF
|
||||
#endif
|
||||
|
||||
|
||||
// ------
|
||||
// Simple toggle defines
|
||||
// their value is never used and they cannot be overriden
|
||||
// their value is never used and they cannot be overridden
|
||||
// ------
|
||||
|
||||
|
||||
|
@ -30,8 +30,8 @@
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_test_invoker.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
|
||||
|
||||
#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
#include <catch2/internal/catch_result_type.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@ -31,8 +32,6 @@ namespace Catch {
|
||||
class IGeneratorTracker;
|
||||
|
||||
struct BenchmarkInfo;
|
||||
template <typename Duration = std::chrono::duration<double, std::nano>>
|
||||
struct BenchmarkStats;
|
||||
|
||||
namespace Generators {
|
||||
class GeneratorUntypedBase;
|
||||
@ -44,6 +43,7 @@ namespace Catch {
|
||||
public:
|
||||
virtual ~IResultCapture();
|
||||
|
||||
virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0;
|
||||
virtual bool sectionStarted( StringRef sectionName,
|
||||
SourceLineInfo const& sectionLineInfo,
|
||||
Counts& assertions ) = 0;
|
||||
@ -84,7 +84,7 @@ namespace Catch {
|
||||
AssertionReaction& reaction ) = 0;
|
||||
virtual void handleUnexpectedInflightException
|
||||
( AssertionInfo const& info,
|
||||
std::string const& message,
|
||||
std::string&& message,
|
||||
AssertionReaction& reaction ) = 0;
|
||||
virtual void handleIncomplete
|
||||
( AssertionInfo const& info ) = 0;
|
||||
|
@ -8,7 +8,6 @@
|
||||
#ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
|
||||
#define CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
|
||||
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
|
||||
#include <string>
|
||||
|
@ -19,7 +19,7 @@ namespace Catch {
|
||||
class ITestCaseRegistry;
|
||||
class IExceptionTranslatorRegistry;
|
||||
class IExceptionTranslator;
|
||||
class IReporterRegistry;
|
||||
class ReporterRegistry;
|
||||
class IReporterFactory;
|
||||
class ITagAliasRegistry;
|
||||
class ITestInvoker;
|
||||
@ -35,7 +35,7 @@ namespace Catch {
|
||||
public:
|
||||
virtual ~IRegistryHub(); // = default
|
||||
|
||||
virtual IReporterRegistry const& getReporterRegistry() const = 0;
|
||||
virtual ReporterRegistry const& getReporterRegistry() const = 0;
|
||||
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
|
||||
virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
|
||||
virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
|
||||
|
@ -54,8 +54,6 @@ namespace Catch {
|
||||
infoMessages( _infoMessages ),
|
||||
totals( _totals )
|
||||
{
|
||||
assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
|
||||
|
||||
if( assertionResult.hasMessage() ) {
|
||||
// Copy message into messages list.
|
||||
// !TBD This should have been done earlier, somewhere
|
||||
|
@ -13,11 +13,10 @@
|
||||
#include <catch2/catch_assertion_result.hpp>
|
||||
#include <catch2/internal/catch_message_info.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
#include <catch2/internal/catch_test_run_info.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/benchmark/catch_estimate.hpp>
|
||||
#include <catch2/benchmark/catch_outlier_classification.hpp>
|
||||
|
||||
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@ -57,11 +56,6 @@ namespace Catch {
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
};
|
||||
|
||||
struct TestRunInfo {
|
||||
constexpr TestRunInfo(StringRef _name) : name(_name) {}
|
||||
StringRef name;
|
||||
};
|
||||
|
||||
struct AssertionStats {
|
||||
AssertionStats( AssertionResult const& _assertionResult,
|
||||
std::vector<MessageInfo> const& _infoMessages,
|
||||
@ -113,45 +107,6 @@ namespace Catch {
|
||||
bool aborting;
|
||||
};
|
||||
|
||||
|
||||
struct BenchmarkInfo {
|
||||
std::string name;
|
||||
double estimatedDuration;
|
||||
int iterations;
|
||||
unsigned int samples;
|
||||
unsigned int resamples;
|
||||
double clockResolution;
|
||||
double clockCost;
|
||||
};
|
||||
|
||||
template <class Duration>
|
||||
struct BenchmarkStats {
|
||||
BenchmarkInfo info;
|
||||
|
||||
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());
|
||||
for (auto const& sample : samples) {
|
||||
samples2.push_back(Duration2(sample));
|
||||
}
|
||||
return {
|
||||
info,
|
||||
CATCH_MOVE(samples2),
|
||||
mean,
|
||||
standardDeviation,
|
||||
outliers,
|
||||
outlierVariance,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
//! By setting up its preferences, a reporter can modify Catch2's behaviour
|
||||
//! in some regards, e.g. it can request Catch2 to capture writes to
|
||||
//! stdout/stderr during test execution, and pass them to the reporter.
|
||||
@ -250,7 +205,7 @@ namespace Catch {
|
||||
*/
|
||||
virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
|
||||
|
||||
//! Called if a fatal error (signal/structured exception) occured
|
||||
//! Called if a fatal error (signal/structured exception) occurred
|
||||
virtual void fatalErrorEncountered( StringRef error ) = 0;
|
||||
|
||||
//! Writes out information about provided reporters using reporter-specific format
|
||||
|
@ -1,42 +0,0 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
|
||||
#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class IConfig;
|
||||
|
||||
class IEventListener;
|
||||
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
||||
class IReporterFactory;
|
||||
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
|
||||
struct ReporterConfig;
|
||||
class EventListenerFactory;
|
||||
|
||||
class IReporterRegistry {
|
||||
public:
|
||||
using FactoryMap = std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>;
|
||||
using Listeners = std::vector<Detail::unique_ptr<EventListenerFactory>>;
|
||||
|
||||
virtual ~IReporterRegistry(); // = default
|
||||
virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0;
|
||||
virtual FactoryMap const& getFactories() const = 0;
|
||||
virtual Listeners const& getListeners() const = 0;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
|
21
src/catch2/interfaces/catch_interfaces_test_invoker.hpp
Normal file
21
src/catch2/interfaces/catch_interfaces_test_invoker.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
|
||||
#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class ITestInvoker {
|
||||
public:
|
||||
virtual void invoke() const = 0;
|
||||
virtual ~ITestInvoker(); // = default
|
||||
};
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
|
@ -9,6 +9,5 @@
|
||||
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
|
||||
|
||||
namespace Catch {
|
||||
ITestInvoker::~ITestInvoker() = default;
|
||||
ITestCaseRegistry::~ITestCaseRegistry() = default;
|
||||
}
|
||||
|
@ -12,15 +12,7 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class TestSpec;
|
||||
struct TestCaseInfo;
|
||||
|
||||
class ITestInvoker {
|
||||
public:
|
||||
virtual void invoke () const = 0;
|
||||
virtual ~ITestInvoker(); // = default
|
||||
};
|
||||
|
||||
class TestCaseHandle;
|
||||
class IConfig;
|
||||
|
||||
@ -33,11 +25,6 @@ namespace Catch {
|
||||
virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
|
||||
};
|
||||
|
||||
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
|
||||
bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
|
||||
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
|
||||
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
|
||||
|
||||
}
|
||||
|
||||
#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <catch2/internal/catch_debugger.hpp>
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/internal/catch_run_context.hpp>
|
||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||
|
||||
namespace Catch {
|
||||
@ -24,7 +23,9 @@ namespace Catch {
|
||||
ResultDisposition::Flags resultDisposition )
|
||||
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
|
||||
m_resultCapture( getResultCapture() )
|
||||
{}
|
||||
{
|
||||
m_resultCapture.notifyAssertionStarted( m_assertionInfo );
|
||||
}
|
||||
|
||||
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
|
||||
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
|
||||
@ -38,7 +39,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
void AssertionHandler::complete() {
|
||||
setCompleted();
|
||||
m_completed = true;
|
||||
if( m_reaction.shouldDebugBreak ) {
|
||||
|
||||
// If you find your debugger stopping you here then go one level up on the
|
||||
@ -51,16 +52,9 @@ namespace Catch {
|
||||
throw_test_failure_exception();
|
||||
}
|
||||
if ( m_reaction.shouldSkip ) {
|
||||
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
|
||||
throw Catch::TestSkipException();
|
||||
#else
|
||||
CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
|
||||
#endif
|
||||
throw_test_skip_exception();
|
||||
}
|
||||
}
|
||||
void AssertionHandler::setCompleted() {
|
||||
m_completed = true;
|
||||
}
|
||||
|
||||
void AssertionHandler::handleUnexpectedInflightException() {
|
||||
m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
|
||||
|
@ -11,14 +11,11 @@
|
||||
#include <catch2/catch_assertion_info.hpp>
|
||||
#include <catch2/internal/catch_decomposer.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||
#include <catch2/internal/catch_lazy_expr.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class IResultCapture;
|
||||
|
||||
struct AssertionReaction {
|
||||
bool shouldDebugBreak = false;
|
||||
bool shouldThrow = false;
|
||||
@ -59,7 +56,6 @@ namespace Catch {
|
||||
void handleUnexpectedInflightException();
|
||||
|
||||
void complete();
|
||||
void setCompleted();
|
||||
|
||||
// query
|
||||
auto allowThrows() const -> bool;
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <catch2/catch_config.hpp>
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
|
||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
||||
#include <catch2/internal/catch_console_colour.hpp>
|
||||
#include <catch2/internal/catch_parse_numbers.hpp>
|
||||
#include <catch2/internal/catch_reporter_spec_parser.hpp>
|
||||
@ -144,7 +144,7 @@ namespace Catch {
|
||||
|
||||
auto const& reporterSpec = *parsed;
|
||||
|
||||
IReporterRegistry::FactoryMap const& factories =
|
||||
auto const& factories =
|
||||
getRegistryHub().getReporterRegistry().getFactories();
|
||||
auto result = factories.find( reporterSpec.name() );
|
||||
|
||||
|
@ -50,12 +50,18 @@
|
||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wunused-result\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wshadow\"" )
|
||||
|
||||
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
@ -128,6 +134,9 @@
|
||||
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
|
||||
_Pragma( "clang diagnostic ignored \"-Wcomma\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
||||
_Pragma( "clang diagnostic ignored \"-Wshadow\"" )
|
||||
|
||||
#endif // __clang__
|
||||
|
||||
|
||||
@ -365,6 +374,9 @@
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT)
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
|
||||
#endif
|
||||
@ -374,6 +386,16 @@
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
|
||||
#endif
|
||||
#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS )
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
|
||||
#endif
|
||||
#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS )
|
||||
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS
|
||||
#endif
|
||||
#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS )
|
||||
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS
|
||||
#endif
|
||||
|
||||
|
||||
// The goal of this macro is to avoid evaluation of the arguments, but
|
||||
// still have the compiler warn on problems inside...
|
||||
@ -387,13 +409,6 @@
|
||||
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
|
||||
#endif
|
||||
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
|
||||
#endif
|
||||
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
#define CATCH_TRY if ((true))
|
||||
|
@ -18,6 +18,8 @@
|
||||
#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED
|
||||
#define CATCH_CONFIG_COUNTER_HPP_INCLUDED
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
|
||||
#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
|
||||
#define CATCH_INTERNAL_CONFIG_COUNTER
|
||||
#endif
|
||||
|
34
src/catch2/internal/catch_config_static_analysis_support.hpp
Normal file
34
src/catch2/internal/catch_config_static_analysis_support.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
/** \file
|
||||
* Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option
|
||||
*
|
||||
* Some of Catch2's macros can be defined differently to work better with
|
||||
* static analysis tools, like clang-tidy or coverity.
|
||||
* Currently the main use case is to show that `SECTION`s are executed
|
||||
* exclusively, and not all in one run of a `TEST_CASE`.
|
||||
*/
|
||||
|
||||
#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
|
||||
#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
|
||||
#if defined(__clang_analyzer__) || defined(__COVERITY__)
|
||||
#define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT
|
||||
#endif
|
||||
|
||||
#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \
|
||||
!defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \
|
||||
!defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT )
|
||||
# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
||||
#endif
|
||||
|
||||
|
||||
#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
|
@ -17,6 +17,8 @@
|
||||
#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
|
||||
#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
||||
|
@ -17,6 +17,8 @@
|
||||
#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED
|
||||
#define CATCH_CONFIG_WCHAR_HPP_INCLUDED
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
|
||||
// We assume that WCHAR should be enabled by default, and only disabled
|
||||
// for a shortlist (so far only DJGPP) of compilers.
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace Catch {
|
||||
namespace {
|
||||
//! A do-nothing implementation of colour, used as fallback for unknown
|
||||
//! platforms, and when the user asks to deactivate all colours.
|
||||
class NoColourImpl : public ColourImpl {
|
||||
class NoColourImpl final : public ColourImpl {
|
||||
public:
|
||||
NoColourImpl( IStream* stream ): ColourImpl( stream ) {}
|
||||
|
||||
@ -103,7 +103,7 @@ namespace Catch {
|
||||
namespace Catch {
|
||||
namespace {
|
||||
|
||||
class Win32ColourImpl : public ColourImpl {
|
||||
class Win32ColourImpl final : public ColourImpl {
|
||||
public:
|
||||
Win32ColourImpl(IStream* stream):
|
||||
ColourImpl(stream) {
|
||||
@ -169,7 +169,7 @@ namespace {
|
||||
namespace Catch {
|
||||
namespace {
|
||||
|
||||
class ANSIColourImpl : public ColourImpl {
|
||||
class ANSIColourImpl final : public ColourImpl {
|
||||
public:
|
||||
ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {}
|
||||
|
||||
|
@ -11,49 +11,27 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class Context : public IMutableContext, private Detail::NonCopyable {
|
||||
Context* Context::currentContext = nullptr;
|
||||
|
||||
public: // IContext
|
||||
IResultCapture* getResultCapture() override {
|
||||
return m_resultCapture;
|
||||
}
|
||||
|
||||
IConfig const* getConfig() const override {
|
||||
return m_config;
|
||||
}
|
||||
|
||||
~Context() override;
|
||||
|
||||
public: // IMutableContext
|
||||
void setResultCapture( IResultCapture* resultCapture ) override {
|
||||
m_resultCapture = resultCapture;
|
||||
}
|
||||
void setConfig( IConfig const* config ) override {
|
||||
m_config = config;
|
||||
}
|
||||
|
||||
friend IMutableContext& getCurrentMutableContext();
|
||||
|
||||
private:
|
||||
IConfig const* m_config = nullptr;
|
||||
IResultCapture* m_resultCapture = nullptr;
|
||||
};
|
||||
|
||||
IMutableContext *IMutableContext::currentContext = nullptr;
|
||||
|
||||
void IMutableContext::createContext()
|
||||
{
|
||||
void cleanUpContext() {
|
||||
delete Context::currentContext;
|
||||
Context::currentContext = nullptr;
|
||||
}
|
||||
void Context::createContext() {
|
||||
currentContext = new Context();
|
||||
}
|
||||
|
||||
void cleanUpContext() {
|
||||
delete IMutableContext::currentContext;
|
||||
IMutableContext::currentContext = nullptr;
|
||||
Context& getCurrentMutableContext() {
|
||||
if ( !Context::currentContext ) { Context::createContext(); }
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
||||
return *Context::currentContext;
|
||||
}
|
||||
IContext::~IContext() = default;
|
||||
IMutableContext::~IMutableContext() = default;
|
||||
Context::~Context() = default;
|
||||
|
||||
void Context::setResultCapture( IResultCapture* resultCapture ) {
|
||||
m_resultCapture = resultCapture;
|
||||
}
|
||||
|
||||
void Context::setConfig( IConfig const* config ) { m_config = config; }
|
||||
|
||||
SimplePcg32& sharedRng() {
|
||||
static SimplePcg32 s_rng;
|
||||
|
@ -15,38 +15,31 @@ namespace Catch {
|
||||
class IResultCapture;
|
||||
class IConfig;
|
||||
|
||||
class IContext {
|
||||
public:
|
||||
virtual ~IContext(); // = default
|
||||
class Context {
|
||||
IConfig const* m_config = nullptr;
|
||||
IResultCapture* m_resultCapture = nullptr;
|
||||
|
||||
virtual IResultCapture* getResultCapture() = 0;
|
||||
virtual IConfig const* getConfig() const = 0;
|
||||
};
|
||||
|
||||
class IMutableContext : public IContext {
|
||||
public:
|
||||
~IMutableContext() override; // = default
|
||||
virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
|
||||
virtual void setConfig( IConfig const* config ) = 0;
|
||||
|
||||
private:
|
||||
CATCH_EXPORT static IMutableContext* currentContext;
|
||||
friend IMutableContext& getCurrentMutableContext();
|
||||
friend void cleanUpContext();
|
||||
CATCH_EXPORT static Context* currentContext;
|
||||
friend Context& getCurrentMutableContext();
|
||||
friend Context const& getCurrentContext();
|
||||
static void createContext();
|
||||
friend void cleanUpContext();
|
||||
|
||||
public:
|
||||
IResultCapture* getResultCapture() const { return m_resultCapture; }
|
||||
IConfig const* getConfig() const { return m_config; }
|
||||
void setResultCapture( IResultCapture* resultCapture );
|
||||
void setConfig( IConfig const* config );
|
||||
};
|
||||
|
||||
inline IMutableContext& getCurrentMutableContext()
|
||||
{
|
||||
if( !IMutableContext::currentContext )
|
||||
IMutableContext::createContext();
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
||||
return *IMutableContext::currentContext;
|
||||
}
|
||||
Context& getCurrentMutableContext();
|
||||
|
||||
inline IContext& getCurrentContext()
|
||||
{
|
||||
return getCurrentMutableContext();
|
||||
inline Context const& getCurrentContext() {
|
||||
// We duplicate the logic from `getCurrentMutableContext` here,
|
||||
// to avoid paying the call overhead in debug mode.
|
||||
if ( !Context::currentContext ) { Context::createContext(); }
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
||||
return *Context::currentContext;
|
||||
}
|
||||
|
||||
void cleanUpContext();
|
||||
|
@ -11,8 +11,24 @@
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
static std::string tryTranslators(
|
||||
std::vector<
|
||||
Detail::unique_ptr<IExceptionTranslator const>> const& translators ) {
|
||||
if ( translators.empty() ) {
|
||||
std::rethrow_exception( std::current_exception() );
|
||||
} else {
|
||||
return translators[0]->translate( translators.begin() + 1,
|
||||
translators.end() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
|
||||
}
|
||||
|
||||
@ -37,7 +53,7 @@ namespace Catch {
|
||||
// First we try user-registered translators. If none of them can
|
||||
// handle the exception, it will be rethrown handled by our defaults.
|
||||
try {
|
||||
return tryTranslators();
|
||||
return tryTranslators(m_translators);
|
||||
}
|
||||
// To avoid having to handle TFE explicitly everywhere, we just
|
||||
// rethrow it so that it goes back up the caller.
|
||||
@ -61,23 +77,10 @@ namespace Catch {
|
||||
}
|
||||
}
|
||||
|
||||
std::string ExceptionTranslatorRegistry::tryTranslators() const {
|
||||
if (m_translators.empty()) {
|
||||
std::rethrow_exception(std::current_exception());
|
||||
} else {
|
||||
return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
|
||||
}
|
||||
}
|
||||
|
||||
#else // ^^ Exceptions are enabled // Exceptions are disabled vv
|
||||
std::string ExceptionTranslatorRegistry::translateActiveException() const {
|
||||
CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
|
||||
}
|
||||
|
||||
std::string ExceptionTranslatorRegistry::tryTranslators() const {
|
||||
CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ namespace Catch {
|
||||
~ExceptionTranslatorRegistry() override;
|
||||
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
|
||||
std::string translateActiveException() const override;
|
||||
std::string tryTranslators() const;
|
||||
|
||||
private:
|
||||
ExceptionTranslators m_translators;
|
||||
|
@ -24,7 +24,7 @@ namespace Catch {
|
||||
namespace Detail {
|
||||
namespace {
|
||||
template<typename WriterF, std::size_t bufferSize=256>
|
||||
class StreamBufImpl : public std::streambuf {
|
||||
class StreamBufImpl final : public std::streambuf {
|
||||
char data[bufferSize];
|
||||
WriterF m_writer;
|
||||
|
||||
@ -72,7 +72,7 @@ namespace Detail {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FileStream : public IStream {
|
||||
class FileStream final : public IStream {
|
||||
std::ofstream m_ofs;
|
||||
public:
|
||||
FileStream( std::string const& filename ) {
|
||||
@ -89,7 +89,7 @@ namespace Detail {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CoutStream : public IStream {
|
||||
class CoutStream final : public IStream {
|
||||
std::ostream m_os;
|
||||
public:
|
||||
// Store the streambuf from cout up-front because
|
||||
@ -118,7 +118,7 @@ namespace Detail {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class DebugOutStream : public IStream {
|
||||
class DebugOutStream final : public IStream {
|
||||
Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
|
||||
std::ostream m_os;
|
||||
public:
|
||||
|
@ -9,9 +9,9 @@
|
||||
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
|
||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
||||
|
||||
@ -54,7 +54,7 @@ namespace Catch {
|
||||
void listReporters(IEventListener& reporter) {
|
||||
std::vector<ReporterDescription> descriptions;
|
||||
|
||||
IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
|
||||
auto const& factories = getRegistryHub().getReporterRegistry().getFactories();
|
||||
descriptions.reserve(factories.size());
|
||||
for (auto const& fac : factories) {
|
||||
descriptions.push_back({ fac.first, fac.second->getDescription() });
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include <catch2/internal/catch_result_type.hpp>
|
||||
#include <catch2/internal/catch_source_line_info.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
#ifndef CATCH_OPTIONAL_HPP_INCLUDED
|
||||
#define CATCH_OPTIONAL_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace Catch {
|
||||
@ -16,35 +18,50 @@ namespace Catch {
|
||||
template<typename T>
|
||||
class Optional {
|
||||
public:
|
||||
Optional() : nullableValue( nullptr ) {}
|
||||
Optional( T const& _value )
|
||||
: nullableValue( new( storage ) T( _value ) )
|
||||
{}
|
||||
Optional( Optional const& _other )
|
||||
: nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
|
||||
{}
|
||||
Optional(): nullableValue( nullptr ) {}
|
||||
~Optional() { reset(); }
|
||||
|
||||
~Optional() {
|
||||
Optional( T const& _value ):
|
||||
nullableValue( new ( storage ) T( _value ) ) {}
|
||||
Optional( T&& _value ):
|
||||
nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {}
|
||||
|
||||
Optional& operator=( T const& _value ) {
|
||||
reset();
|
||||
nullableValue = new ( storage ) T( _value );
|
||||
return *this;
|
||||
}
|
||||
Optional& operator=( T&& _value ) {
|
||||
reset();
|
||||
nullableValue = new ( storage ) T( CATCH_MOVE( _value ) );
|
||||
return *this;
|
||||
}
|
||||
|
||||
Optional& operator= ( Optional const& _other ) {
|
||||
if( &_other != this ) {
|
||||
Optional( Optional const& _other ):
|
||||
nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {}
|
||||
Optional( Optional&& _other ):
|
||||
nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) )
|
||||
: nullptr ) {}
|
||||
|
||||
Optional& operator=( Optional const& _other ) {
|
||||
if ( &_other != this ) {
|
||||
reset();
|
||||
if( _other )
|
||||
nullableValue = new( storage ) T( *_other );
|
||||
if ( _other ) { nullableValue = new ( storage ) T( *_other ); }
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Optional& operator = ( T const& _value ) {
|
||||
reset();
|
||||
nullableValue = new( storage ) T( _value );
|
||||
Optional& operator=( Optional&& _other ) {
|
||||
if ( &_other != this ) {
|
||||
reset();
|
||||
if ( _other ) {
|
||||
nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) );
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if( nullableValue )
|
||||
nullableValue->~T();
|
||||
if ( nullableValue ) { nullableValue->~T(); }
|
||||
nullableValue = nullptr;
|
||||
}
|
||||
|
||||
@ -91,7 +108,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
private:
|
||||
T *nullableValue;
|
||||
T* nullableValue;
|
||||
alignas(alignof(T)) char storage[sizeof(T)];
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
|
||||
#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
|
||||
#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
|
||||
#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr
|
||||
#else
|
||||
#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr
|
||||
#endif
|
||||
|
||||
#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
|
@ -5,61 +5,84 @@
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
||||
|
||||
#include <catch2/reporters/catch_reporter_registrars.hpp>
|
||||
#include <catch2/internal/catch_reporter_registry.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
|
||||
#include <catch2/internal/catch_enforce.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/reporters/catch_reporter_automake.hpp>
|
||||
#include <catch2/reporters/catch_reporter_compact.hpp>
|
||||
#include <catch2/reporters/catch_reporter_console.hpp>
|
||||
#include <catch2/reporters/catch_reporter_junit.hpp>
|
||||
#include <catch2/reporters/catch_reporter_registrars.hpp>
|
||||
#include <catch2/reporters/catch_reporter_sonarqube.hpp>
|
||||
#include <catch2/reporters/catch_reporter_tap.hpp>
|
||||
#include <catch2/reporters/catch_reporter_teamcity.hpp>
|
||||
#include <catch2/reporters/catch_reporter_xml.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_enforce.hpp>
|
||||
|
||||
namespace Catch {
|
||||
struct ReporterRegistry::ReporterRegistryImpl {
|
||||
std::vector<Detail::unique_ptr<EventListenerFactory>> listeners;
|
||||
std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>
|
||||
factories;
|
||||
};
|
||||
|
||||
ReporterRegistry::ReporterRegistry() {
|
||||
ReporterRegistry::ReporterRegistry():
|
||||
m_impl( Detail::make_unique<ReporterRegistryImpl>() ) {
|
||||
// Because it is impossible to move out of initializer list,
|
||||
// we have to add the elements manually
|
||||
m_factories["Automake"] = Detail::make_unique<ReporterFactory<AutomakeReporter>>();
|
||||
m_factories["compact"] = Detail::make_unique<ReporterFactory<CompactReporter>>();
|
||||
m_factories["console"] = Detail::make_unique<ReporterFactory<ConsoleReporter>>();
|
||||
m_factories["JUnit"] = Detail::make_unique<ReporterFactory<JunitReporter>>();
|
||||
m_factories["SonarQube"] = Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
|
||||
m_factories["TAP"] = Detail::make_unique<ReporterFactory<TAPReporter>>();
|
||||
m_factories["TeamCity"] = Detail::make_unique<ReporterFactory<TeamCityReporter>>();
|
||||
m_factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>();
|
||||
m_impl->factories["Automake"] =
|
||||
Detail::make_unique<ReporterFactory<AutomakeReporter>>();
|
||||
m_impl->factories["compact"] =
|
||||
Detail::make_unique<ReporterFactory<CompactReporter>>();
|
||||
m_impl->factories["console"] =
|
||||
Detail::make_unique<ReporterFactory<ConsoleReporter>>();
|
||||
m_impl->factories["JUnit"] =
|
||||
Detail::make_unique<ReporterFactory<JunitReporter>>();
|
||||
m_impl->factories["SonarQube"] =
|
||||
Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
|
||||
m_impl->factories["TAP"] =
|
||||
Detail::make_unique<ReporterFactory<TAPReporter>>();
|
||||
m_impl->factories["TeamCity"] =
|
||||
Detail::make_unique<ReporterFactory<TeamCityReporter>>();
|
||||
m_impl->factories["XML"] =
|
||||
Detail::make_unique<ReporterFactory<XmlReporter>>();
|
||||
}
|
||||
|
||||
ReporterRegistry::~ReporterRegistry() = default;
|
||||
|
||||
|
||||
IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const {
|
||||
auto it = m_factories.find( name );
|
||||
if( it == m_factories.end() )
|
||||
return nullptr;
|
||||
return it->second->create( CATCH_MOVE(config) );
|
||||
IEventListenerPtr
|
||||
ReporterRegistry::create( std::string const& name,
|
||||
ReporterConfig&& config ) const {
|
||||
auto it = m_impl->factories.find( name );
|
||||
if ( it == m_impl->factories.end() ) return nullptr;
|
||||
return it->second->create( CATCH_MOVE( config ) );
|
||||
}
|
||||
|
||||
void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) {
|
||||
void ReporterRegistry::registerReporter( std::string const& name,
|
||||
IReporterFactoryPtr factory ) {
|
||||
CATCH_ENFORCE( name.find( "::" ) == name.npos,
|
||||
"'::' is not allowed in reporter name: '" + name + '\'' );
|
||||
auto ret = m_factories.emplace(name, CATCH_MOVE(factory));
|
||||
CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" );
|
||||
"'::' is not allowed in reporter name: '" + name +
|
||||
'\'' );
|
||||
auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) );
|
||||
CATCH_ENFORCE( ret.second,
|
||||
"reporter using '" + name +
|
||||
"' as name was already registered" );
|
||||
}
|
||||
void ReporterRegistry::registerListener(
|
||||
Detail::unique_ptr<EventListenerFactory> factory ) {
|
||||
m_listeners.push_back( CATCH_MOVE(factory) );
|
||||
m_impl->listeners.push_back( CATCH_MOVE( factory ) );
|
||||
}
|
||||
|
||||
IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
|
||||
return m_factories;
|
||||
}
|
||||
IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
|
||||
return m_listeners;
|
||||
std::map<std::string,
|
||||
IReporterFactoryPtr,
|
||||
Detail::CaseInsensitiveLess> const&
|
||||
ReporterRegistry::getFactories() const {
|
||||
return m_impl->factories;
|
||||
}
|
||||
|
||||
}
|
||||
std::vector<Detail::unique_ptr<EventListenerFactory>> const&
|
||||
ReporterRegistry::getListeners() const {
|
||||
return m_impl->listeners;
|
||||
}
|
||||
} // namespace Catch
|
||||
|
@ -8,31 +8,48 @@
|
||||
#ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED
|
||||
#define CATCH_REPORTER_REGISTRY_HPP_INCLUDED
|
||||
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
|
||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class ReporterRegistry : public IReporterRegistry {
|
||||
class IEventListener;
|
||||
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
||||
class IReporterFactory;
|
||||
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
|
||||
struct ReporterConfig;
|
||||
class EventListenerFactory;
|
||||
|
||||
class ReporterRegistry {
|
||||
struct ReporterRegistryImpl;
|
||||
Detail::unique_ptr<ReporterRegistryImpl> m_impl;
|
||||
|
||||
public:
|
||||
|
||||
ReporterRegistry();
|
||||
~ReporterRegistry() override; // = default, out of line to allow fwd decl
|
||||
~ReporterRegistry(); // = default;
|
||||
|
||||
IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override;
|
||||
IEventListenerPtr create( std::string const& name,
|
||||
ReporterConfig&& config ) const;
|
||||
|
||||
void registerReporter( std::string const& name, IReporterFactoryPtr factory );
|
||||
void registerListener( Detail::unique_ptr<EventListenerFactory> factory );
|
||||
void registerReporter( std::string const& name,
|
||||
IReporterFactoryPtr factory );
|
||||
|
||||
FactoryMap const& getFactories() const override;
|
||||
Listeners const& getListeners() const override;
|
||||
void
|
||||
registerListener( Detail::unique_ptr<EventListenerFactory> factory );
|
||||
|
||||
private:
|
||||
FactoryMap m_factories;
|
||||
Listeners m_listeners;
|
||||
std::map<std::string,
|
||||
IReporterFactoryPtr,
|
||||
Detail::CaseInsensitiveLess> const&
|
||||
getFactories() const;
|
||||
|
||||
std::vector<Detail::unique_ptr<EventListenerFactory>> const&
|
||||
getListeners() const;
|
||||
};
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED
|
||||
|
@ -21,9 +21,9 @@ namespace Catch {
|
||||
};
|
||||
|
||||
kvPair splitKVPair(StringRef kvString) {
|
||||
auto splitPos = static_cast<size_t>( std::distance(
|
||||
kvString.begin(),
|
||||
std::find( kvString.begin(), kvString.end(), '=' ) ) );
|
||||
auto splitPos = static_cast<size_t>(
|
||||
std::find( kvString.begin(), kvString.end(), '=' ) -
|
||||
kvString.begin() );
|
||||
|
||||
return { kvString.substr( 0, splitPos ),
|
||||
kvString.substr( splitPos + 1, kvString.size() ) };
|
||||
|
@ -8,8 +8,9 @@
|
||||
#include <catch2/internal/catch_run_context.hpp>
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||
#include <catch2/internal/catch_context.hpp>
|
||||
#include <catch2/internal/catch_enforce.hpp>
|
||||
@ -26,146 +27,152 @@
|
||||
namespace Catch {
|
||||
|
||||
namespace Generators {
|
||||
struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
|
||||
GeneratorBasePtr m_generator;
|
||||
namespace {
|
||||
struct GeneratorTracker final : TestCaseTracking::TrackerBase,
|
||||
IGeneratorTracker {
|
||||
GeneratorBasePtr m_generator;
|
||||
|
||||
GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
|
||||
: TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent )
|
||||
{}
|
||||
~GeneratorTracker() override;
|
||||
GeneratorTracker(
|
||||
TestCaseTracking::NameAndLocation&& nameAndLocation,
|
||||
TrackerContext& ctx,
|
||||
ITracker* parent ):
|
||||
TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {}
|
||||
~GeneratorTracker() override = default;
|
||||
|
||||
static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef const& nameAndLocation ) {
|
||||
GeneratorTracker* tracker;
|
||||
static GeneratorTracker*
|
||||
acquire( TrackerContext& ctx,
|
||||
TestCaseTracking::NameAndLocationRef const&
|
||||
nameAndLocation ) {
|
||||
GeneratorTracker* tracker;
|
||||
|
||||
ITracker& currentTracker = ctx.currentTracker();
|
||||
// Under specific circumstances, the generator we want
|
||||
// to acquire is also the current tracker. If this is
|
||||
// the case, we have to avoid looking through current
|
||||
// tracker's children, and instead return the current
|
||||
// tracker.
|
||||
// A case where this check is important is e.g.
|
||||
// for (int i = 0; i < 5; ++i) {
|
||||
// int n = GENERATE(1, 2);
|
||||
// }
|
||||
//
|
||||
// without it, the code above creates 5 nested generators.
|
||||
if ( currentTracker.nameAndLocation() == nameAndLocation ) {
|
||||
auto thisTracker =
|
||||
currentTracker.parent()->findChild( nameAndLocation );
|
||||
assert( thisTracker );
|
||||
assert( thisTracker->isGeneratorTracker() );
|
||||
tracker = static_cast<GeneratorTracker*>( thisTracker );
|
||||
} else if ( ITracker* childTracker =
|
||||
currentTracker.findChild( nameAndLocation ) ) {
|
||||
assert( childTracker );
|
||||
assert( childTracker->isGeneratorTracker() );
|
||||
tracker = static_cast<GeneratorTracker*>( childTracker );
|
||||
} else {
|
||||
return nullptr;
|
||||
ITracker& currentTracker = ctx.currentTracker();
|
||||
// Under specific circumstances, the generator we want
|
||||
// to acquire is also the current tracker. If this is
|
||||
// the case, we have to avoid looking through current
|
||||
// tracker's children, and instead return the current
|
||||
// tracker.
|
||||
// A case where this check is important is e.g.
|
||||
// for (int i = 0; i < 5; ++i) {
|
||||
// int n = GENERATE(1, 2);
|
||||
// }
|
||||
//
|
||||
// without it, the code above creates 5 nested generators.
|
||||
if ( currentTracker.nameAndLocation() == nameAndLocation ) {
|
||||
auto thisTracker = currentTracker.parent()->findChild(
|
||||
nameAndLocation );
|
||||
assert( thisTracker );
|
||||
assert( thisTracker->isGeneratorTracker() );
|
||||
tracker = static_cast<GeneratorTracker*>( thisTracker );
|
||||
} else if ( ITracker* childTracker =
|
||||
currentTracker.findChild(
|
||||
nameAndLocation ) ) {
|
||||
assert( childTracker );
|
||||
assert( childTracker->isGeneratorTracker() );
|
||||
tracker =
|
||||
static_cast<GeneratorTracker*>( childTracker );
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( !tracker->isComplete() ) { tracker->open(); }
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
if( !tracker->isComplete() ) {
|
||||
tracker->open();
|
||||
// TrackerBase interface
|
||||
bool isGeneratorTracker() const override { return true; }
|
||||
auto hasGenerator() const -> bool override {
|
||||
return !!m_generator;
|
||||
}
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
// TrackerBase interface
|
||||
bool isGeneratorTracker() const override { return true; }
|
||||
auto hasGenerator() const -> bool override {
|
||||
return !!m_generator;
|
||||
}
|
||||
void close() override {
|
||||
TrackerBase::close();
|
||||
// If a generator has a child (it is followed by a section)
|
||||
// and none of its children have started, then we must wait
|
||||
// until later to start consuming its values.
|
||||
// This catches cases where `GENERATE` is placed between two
|
||||
// `SECTION`s.
|
||||
// **The check for m_children.empty cannot be removed**.
|
||||
// doing so would break `GENERATE` _not_ followed by `SECTION`s.
|
||||
const bool should_wait_for_child = [&]() {
|
||||
// No children -> nobody to wait for
|
||||
if ( m_children.empty() ) {
|
||||
return false;
|
||||
}
|
||||
// If at least one child started executing, don't wait
|
||||
if ( std::find_if(
|
||||
m_children.begin(),
|
||||
m_children.end(),
|
||||
[]( TestCaseTracking::ITrackerPtr const& tracker ) {
|
||||
return tracker->hasStarted();
|
||||
} ) != m_children.end() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No children have started. We need to check if they _can_
|
||||
// start, and thus we should wait for them, or they cannot
|
||||
// start (due to filters), and we shouldn't wait for them
|
||||
ITracker* parent = m_parent;
|
||||
// This is safe: there is always at least one section
|
||||
// tracker in a test case tracking tree
|
||||
while ( !parent->isSectionTracker() ) {
|
||||
parent = parent->parent();
|
||||
}
|
||||
assert( parent &&
|
||||
"Missing root (test case) level section" );
|
||||
|
||||
auto const& parentSection =
|
||||
static_cast<SectionTracker const&>( *parent );
|
||||
auto const& filters = parentSection.getFilters();
|
||||
// No filters -> no restrictions on running sections
|
||||
if ( filters.empty() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for ( auto const& child : m_children ) {
|
||||
if ( child->isSectionTracker() &&
|
||||
std::find(
|
||||
filters.begin(),
|
||||
filters.end(),
|
||||
static_cast<SectionTracker const&>( *child )
|
||||
.trimmedName() ) != filters.end() ) {
|
||||
return true;
|
||||
void close() override {
|
||||
TrackerBase::close();
|
||||
// If a generator has a child (it is followed by a section)
|
||||
// and none of its children have started, then we must wait
|
||||
// until later to start consuming its values.
|
||||
// This catches cases where `GENERATE` is placed between two
|
||||
// `SECTION`s.
|
||||
// **The check for m_children.empty cannot be removed**.
|
||||
// doing so would break `GENERATE` _not_ followed by
|
||||
// `SECTION`s.
|
||||
const bool should_wait_for_child = [&]() {
|
||||
// No children -> nobody to wait for
|
||||
if ( m_children.empty() ) { return false; }
|
||||
// If at least one child started executing, don't wait
|
||||
if ( std::find_if(
|
||||
m_children.begin(),
|
||||
m_children.end(),
|
||||
[]( TestCaseTracking::ITrackerPtr const&
|
||||
tracker ) {
|
||||
return tracker->hasStarted();
|
||||
} ) != m_children.end() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No children have started. We need to check if they
|
||||
// _can_ start, and thus we should wait for them, or
|
||||
// they cannot start (due to filters), and we shouldn't
|
||||
// wait for them
|
||||
ITracker* parent = m_parent;
|
||||
// This is safe: there is always at least one section
|
||||
// tracker in a test case tracking tree
|
||||
while ( !parent->isSectionTracker() ) {
|
||||
parent = parent->parent();
|
||||
}
|
||||
assert( parent &&
|
||||
"Missing root (test case) level section" );
|
||||
|
||||
auto const& parentSection =
|
||||
static_cast<SectionTracker const&>( *parent );
|
||||
auto const& filters = parentSection.getFilters();
|
||||
// No filters -> no restrictions on running sections
|
||||
if ( filters.empty() ) { return true; }
|
||||
|
||||
for ( auto const& child : m_children ) {
|
||||
if ( child->isSectionTracker() &&
|
||||
std::find( filters.begin(),
|
||||
filters.end(),
|
||||
static_cast<SectionTracker const&>(
|
||||
*child )
|
||||
.trimmedName() ) !=
|
||||
filters.end() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
// This check is a bit tricky, because m_generator->next()
|
||||
// has a side-effect, where it consumes generator's current
|
||||
// value, but we do not want to invoke the side-effect if
|
||||
// this generator is still waiting for any child to start.
|
||||
assert( m_generator && "Tracker without generator" );
|
||||
if ( should_wait_for_child ||
|
||||
( m_runState == CompletedSuccessfully &&
|
||||
m_generator->countedNext() ) ) {
|
||||
m_children.clear();
|
||||
m_runState = Executing;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
// This check is a bit tricky, because m_generator->next()
|
||||
// has a side-effect, where it consumes generator's current
|
||||
// value, but we do not want to invoke the side-effect if
|
||||
// this generator is still waiting for any child to start.
|
||||
assert( m_generator && "Tracker without generator" );
|
||||
if ( should_wait_for_child ||
|
||||
( m_runState == CompletedSuccessfully &&
|
||||
m_generator->countedNext() ) ) {
|
||||
m_children.clear();
|
||||
m_runState = Executing;
|
||||
}
|
||||
}
|
||||
|
||||
// IGeneratorTracker interface
|
||||
auto getGenerator() const -> GeneratorBasePtr const& override {
|
||||
return m_generator;
|
||||
}
|
||||
void setGenerator( GeneratorBasePtr&& generator ) override {
|
||||
m_generator = CATCH_MOVE( generator );
|
||||
}
|
||||
};
|
||||
GeneratorTracker::~GeneratorTracker() = default;
|
||||
// IGeneratorTracker interface
|
||||
auto getGenerator() const -> GeneratorBasePtr const& override {
|
||||
return m_generator;
|
||||
}
|
||||
void setGenerator( GeneratorBasePtr&& generator ) override {
|
||||
m_generator = CATCH_MOVE( generator );
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
}
|
||||
|
||||
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
|
||||
: m_runInfo(_config->name()),
|
||||
m_context(getCurrentMutableContext()),
|
||||
m_config(_config),
|
||||
m_reporter(CATCH_MOVE(reporter)),
|
||||
m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
|
||||
m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
|
||||
{
|
||||
m_context.setResultCapture(this);
|
||||
getCurrentMutableContext().setResultCapture( this );
|
||||
m_reporter->testRunStarting(m_runInfo);
|
||||
}
|
||||
|
||||
@ -260,7 +267,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
|
||||
void RunContext::assertionEnded(AssertionResult const & result) {
|
||||
void RunContext::assertionEnded(AssertionResult&& result) {
|
||||
if (result.getResultType() == ResultWas::Ok) {
|
||||
m_totals.assertions.passed++;
|
||||
m_lastAssertionPassed = true;
|
||||
@ -282,19 +289,26 @@ namespace Catch {
|
||||
|
||||
m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
|
||||
|
||||
if (result.getResultType() != ResultWas::Warning)
|
||||
if ( result.getResultType() != ResultWas::Warning ) {
|
||||
m_messageScopes.clear();
|
||||
}
|
||||
|
||||
// Reset working state
|
||||
resetAssertionInfo();
|
||||
m_lastResult = result;
|
||||
m_lastResult = CATCH_MOVE( result );
|
||||
}
|
||||
void RunContext::resetAssertionInfo() {
|
||||
m_lastAssertionInfo.macroName = StringRef();
|
||||
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
|
||||
}
|
||||
|
||||
bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) {
|
||||
void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
|
||||
m_reporter->assertionStarting( info );
|
||||
}
|
||||
|
||||
bool RunContext::sectionStarted( StringRef sectionName,
|
||||
SourceLineInfo const& sectionLineInfo,
|
||||
Counts& assertions ) {
|
||||
ITracker& sectionTracker =
|
||||
SectionTracker::acquire( m_trackerContext,
|
||||
TestCaseTracking::NameAndLocationRef(
|
||||
@ -432,7 +446,7 @@ namespace Catch {
|
||||
tempResult.message = static_cast<std::string>(message);
|
||||
AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
|
||||
|
||||
assertionEnded(result);
|
||||
assertionEnded(CATCH_MOVE(result) );
|
||||
|
||||
handleUnfinishedSections();
|
||||
|
||||
@ -554,8 +568,6 @@ namespace Catch {
|
||||
ITransientExpression const& expr,
|
||||
AssertionReaction& reaction
|
||||
) {
|
||||
m_reporter->assertionStarting( info );
|
||||
|
||||
bool negated = isFalseTest( info.resultDisposition );
|
||||
bool result = expr.getResult() != negated;
|
||||
|
||||
@ -584,7 +596,7 @@ namespace Catch {
|
||||
AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
|
||||
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
|
||||
|
||||
assertionEnded( assertionResult );
|
||||
assertionEnded( CATCH_MOVE(assertionResult) );
|
||||
}
|
||||
|
||||
void RunContext::handleMessage(
|
||||
@ -593,16 +605,16 @@ namespace Catch {
|
||||
StringRef message,
|
||||
AssertionReaction& reaction
|
||||
) {
|
||||
m_reporter->assertionStarting( info );
|
||||
|
||||
m_lastAssertionInfo = info;
|
||||
|
||||
AssertionResultData data( resultType, LazyExpression( false ) );
|
||||
data.message = static_cast<std::string>(message);
|
||||
AssertionResult assertionResult{ m_lastAssertionInfo,
|
||||
CATCH_MOVE( data ) };
|
||||
assertionEnded( assertionResult );
|
||||
if ( !assertionResult.isOk() ) {
|
||||
|
||||
const auto isOk = assertionResult.isOk();
|
||||
assertionEnded( CATCH_MOVE(assertionResult) );
|
||||
if ( !isOk ) {
|
||||
populateReaction( reaction );
|
||||
} else if ( resultType == ResultWas::ExplicitSkip ) {
|
||||
// TODO: Need to handle this explicitly, as ExplicitSkip is
|
||||
@ -619,15 +631,15 @@ namespace Catch {
|
||||
|
||||
void RunContext::handleUnexpectedInflightException(
|
||||
AssertionInfo const& info,
|
||||
std::string const& message,
|
||||
std::string&& message,
|
||||
AssertionReaction& reaction
|
||||
) {
|
||||
m_lastAssertionInfo = info;
|
||||
|
||||
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
|
||||
data.message = message;
|
||||
data.message = CATCH_MOVE(message);
|
||||
AssertionResult assertionResult{ info, CATCH_MOVE(data) };
|
||||
assertionEnded( assertionResult );
|
||||
assertionEnded( CATCH_MOVE(assertionResult) );
|
||||
populateReaction( reaction );
|
||||
}
|
||||
|
||||
@ -645,7 +657,7 @@ namespace Catch {
|
||||
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
|
||||
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
|
||||
AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
|
||||
assertionEnded( assertionResult );
|
||||
assertionEnded( CATCH_MOVE(assertionResult) );
|
||||
}
|
||||
void RunContext::handleNonExpr(
|
||||
AssertionInfo const &info,
|
||||
@ -656,10 +668,10 @@ namespace Catch {
|
||||
|
||||
AssertionResultData data( resultType, LazyExpression( false ) );
|
||||
AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
|
||||
assertionEnded( assertionResult );
|
||||
|
||||
if( !assertionResult.isOk() )
|
||||
populateReaction( reaction );
|
||||
const auto isOk = assertionResult.isOk();
|
||||
assertionEnded( CATCH_MOVE(assertionResult) );
|
||||
if ( !isOk ) { populateReaction( reaction ); }
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,8 +8,9 @@
|
||||
#ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED
|
||||
#define CATCH_RUN_CONTEXT_HPP_INCLUDED
|
||||
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||
#include <catch2/internal/catch_test_registry.hpp>
|
||||
#include <catch2/internal/catch_test_run_info.hpp>
|
||||
#include <catch2/internal/catch_fatal_condition_handler.hpp>
|
||||
#include <catch2/catch_test_case_info.hpp>
|
||||
#include <catch2/catch_message.hpp>
|
||||
@ -24,13 +25,14 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class IMutableContext;
|
||||
class IGeneratorTracker;
|
||||
class IConfig;
|
||||
class IEventListener;
|
||||
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RunContext : public IResultCapture {
|
||||
class RunContext final : public IResultCapture {
|
||||
|
||||
public:
|
||||
RunContext( RunContext const& ) = delete;
|
||||
@ -59,7 +61,7 @@ namespace Catch {
|
||||
AssertionReaction& reaction ) override;
|
||||
void handleUnexpectedInflightException
|
||||
( AssertionInfo const& info,
|
||||
std::string const& message,
|
||||
std::string&& message,
|
||||
AssertionReaction& reaction ) override;
|
||||
void handleIncomplete
|
||||
( AssertionInfo const& info ) override;
|
||||
@ -68,6 +70,7 @@ namespace Catch {
|
||||
ResultWas::OfType resultType,
|
||||
AssertionReaction &reaction ) override;
|
||||
|
||||
void notifyAssertionStarted( AssertionInfo const& info ) override;
|
||||
bool sectionStarted( StringRef sectionName,
|
||||
SourceLineInfo const& sectionLineInfo,
|
||||
Counts& assertions ) override;
|
||||
@ -118,7 +121,7 @@ namespace Catch {
|
||||
void resetAssertionInfo();
|
||||
bool testForMissingAssertions( Counts& assertions );
|
||||
|
||||
void assertionEnded( AssertionResult const& result );
|
||||
void assertionEnded( AssertionResult&& result );
|
||||
void reportExpr
|
||||
( AssertionInfo const &info,
|
||||
ResultWas::OfType resultType,
|
||||
@ -132,7 +135,6 @@ namespace Catch {
|
||||
void handleUnfinishedSections();
|
||||
|
||||
TestRunInfo m_runInfo;
|
||||
IMutableContext& m_context;
|
||||
TestCaseHandle const* m_activeTestCase = nullptr;
|
||||
ITracker* m_testCaseTracker = nullptr;
|
||||
Optional<AssertionResult> m_lastResult;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define CATCH_SECTION_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||
#include <catch2/internal/catch_config_static_analysis_support.hpp>
|
||||
#include <catch2/internal/catch_noncopyable.hpp>
|
||||
#include <catch2/catch_section_info.hpp>
|
||||
#include <catch2/catch_timer.hpp>
|
||||
@ -38,16 +39,62 @@ namespace Catch {
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#define INTERNAL_CATCH_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
|
||||
# define INTERNAL_CATCH_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
|
||||
catch_internal_Section ) = \
|
||||
Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
|
||||
catch_internal_Section ) = \
|
||||
Catch::SectionInfo( \
|
||||
CATCH_INTERNAL_LINEINFO, \
|
||||
( Catch::ReusableStringStream() << __VA_ARGS__ ) \
|
||||
.str() ) ) \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
#else
|
||||
|
||||
// These section definitions imply that at most one section at one level
|
||||
// will be intered (because only one section's __LINE__ can be equal to
|
||||
// the dummy `catchInternalSectionHint` variable from `TEST_CASE`).
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
// Intentionally without linkage, as it should only be used as a dummy
|
||||
// symbol for static analysis.
|
||||
int GetNewSectionHint();
|
||||
} // namespace Detail
|
||||
} // namespace Catch
|
||||
|
||||
|
||||
# define INTERNAL_CATCH_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
||||
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \
|
||||
catchInternalSectionHint, \
|
||||
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
|
||||
catchInternalPreviousSectionHint == __LINE__ ) \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
||||
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \
|
||||
catchInternalSectionHint, \
|
||||
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
|
||||
catchInternalPreviousSectionHint == __LINE__ ) \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
#endif
|
||||
|
||||
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
#endif // CATCH_SECTION_HPP_INCLUDED
|
||||
|
@ -17,10 +17,10 @@ namespace Catch {
|
||||
// as well as
|
||||
// << stuff +StreamEndStop
|
||||
struct StreamEndStop {
|
||||
StringRef operator+() const { return StringRef(); }
|
||||
constexpr StringRef operator+() const { return StringRef(); }
|
||||
|
||||
template <typename T>
|
||||
friend T const& operator+( T const& value, StreamEndStop ) {
|
||||
constexpr friend T const& operator+( T const& value, StreamEndStop ) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
@ -32,9 +31,9 @@ namespace Catch {
|
||||
return s.find( infix ) != std::string::npos;
|
||||
}
|
||||
void toLowerInPlace( std::string& s ) {
|
||||
std::transform( s.begin(), s.end(), s.begin(), []( char c ) {
|
||||
return toLower( c );
|
||||
} );
|
||||
for ( char& c : s ) {
|
||||
c = toLower( c );
|
||||
}
|
||||
}
|
||||
std::string toLower( std::string const& s ) {
|
||||
std::string lc = s;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <catch2/internal/catch_context.hpp>
|
||||
#include <catch2/internal/catch_enforce.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
||||
#include <catch2/internal/catch_run_context.hpp>
|
||||
@ -23,6 +24,38 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
static void enforceNoDuplicateTestCases(
|
||||
std::vector<TestCaseHandle> const& tests ) {
|
||||
auto testInfoCmp = []( TestCaseInfo const* lhs,
|
||||
TestCaseInfo const* rhs ) {
|
||||
return *lhs < *rhs;
|
||||
};
|
||||
std::set<TestCaseInfo const*, decltype( testInfoCmp )&> seenTests(
|
||||
testInfoCmp );
|
||||
for ( auto const& test : tests ) {
|
||||
const auto infoPtr = &test.getTestCaseInfo();
|
||||
const auto prev = seenTests.insert( infoPtr );
|
||||
CATCH_ENFORCE( prev.second,
|
||||
"error: test case \""
|
||||
<< infoPtr->name << "\", with tags \""
|
||||
<< infoPtr->tagsAsString()
|
||||
<< "\" already defined.\n"
|
||||
<< "\tFirst seen at "
|
||||
<< ( *prev.first )->lineInfo << "\n"
|
||||
<< "\tRedefined at " << infoPtr->lineInfo );
|
||||
}
|
||||
}
|
||||
|
||||
static bool matchTest( TestCaseHandle const& testCase,
|
||||
TestSpec const& testSpec,
|
||||
IConfig const& config ) {
|
||||
return testSpec.matches( testCase.getTestCaseInfo() ) &&
|
||||
isThrowSafe( testCase, config );
|
||||
}
|
||||
|
||||
} // end unnamed namespace
|
||||
|
||||
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) {
|
||||
switch (config.runOrder()) {
|
||||
case TestRunOrder::Declared:
|
||||
@ -79,29 +112,6 @@ namespace Catch {
|
||||
return !testCase.getTestCaseInfo().throws() || config.allowThrows();
|
||||
}
|
||||
|
||||
bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) {
|
||||
return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config );
|
||||
}
|
||||
|
||||
void
|
||||
enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& tests ) {
|
||||
auto testInfoCmp = []( TestCaseInfo const* lhs,
|
||||
TestCaseInfo const* rhs ) {
|
||||
return *lhs < *rhs;
|
||||
};
|
||||
std::set<TestCaseInfo const*, decltype(testInfoCmp) &> seenTests(testInfoCmp);
|
||||
for ( auto const& test : tests ) {
|
||||
const auto infoPtr = &test.getTestCaseInfo();
|
||||
const auto prev = seenTests.insert( infoPtr );
|
||||
CATCH_ENFORCE(
|
||||
prev.second,
|
||||
"error: test case \"" << infoPtr->name << "\", with tags \""
|
||||
<< infoPtr->tagsAsString() << "\" already defined.\n"
|
||||
<< "\tFirst seen at " << ( *prev.first )->lineInfo << "\n"
|
||||
<< "\tRedefined at " << infoPtr->lineInfo );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
|
||||
std::vector<TestCaseHandle> filtered;
|
||||
filtered.reserve( testCases.size() );
|
||||
@ -142,11 +152,4 @@ namespace Catch {
|
||||
return m_sortedFunctions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
void TestInvokerAsFunction::invoke() const {
|
||||
m_testAsFunction();
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
||||
|
@ -8,23 +8,22 @@
|
||||
#ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
|
||||
#define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_test_registry.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
class TestCaseHandle;
|
||||
class IConfig;
|
||||
class ITestInvoker;
|
||||
class TestCaseHandle;
|
||||
class TestSpec;
|
||||
|
||||
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases );
|
||||
|
||||
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
|
||||
bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
|
||||
|
||||
void enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& functions );
|
||||
|
||||
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
|
||||
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
|
||||
@ -53,18 +52,6 @@ namespace Catch {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TestInvokerAsFunction final : public ITestInvoker {
|
||||
using TestType = void(*)();
|
||||
TestType m_testAsFunction;
|
||||
public:
|
||||
TestInvokerAsFunction(TestType testAsFunction) noexcept:
|
||||
m_testAsFunction(testAsFunction) {}
|
||||
|
||||
void invoke() const override;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
|
@ -113,7 +113,7 @@ namespace TestCaseTracking {
|
||||
|
||||
//! Returns true if tracker run to completion (successfully or not)
|
||||
virtual bool isComplete() const = 0;
|
||||
//! Returns true if tracker run to completion succesfully
|
||||
//! Returns true if tracker run to completion successfully
|
||||
bool isSuccessfullyCompleted() const {
|
||||
return m_runState == CompletedSuccessfully;
|
||||
}
|
||||
|
@ -20,4 +20,12 @@ namespace Catch {
|
||||
#endif
|
||||
}
|
||||
|
||||
void throw_test_skip_exception() {
|
||||
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
|
||||
throw Catch::TestSkipException();
|
||||
#else
|
||||
CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
|
@ -12,6 +12,8 @@ namespace Catch {
|
||||
|
||||
//! Used to signal that an assertion macro failed
|
||||
struct TestFailureException{};
|
||||
//! Used to signal that the remainder of a test should be skipped
|
||||
struct TestSkipException {};
|
||||
|
||||
/**
|
||||
* Outlines throwing of `TestFailureException` into a single TU
|
||||
@ -20,8 +22,12 @@ namespace Catch {
|
||||
*/
|
||||
[[noreturn]] void throw_test_failure_exception();
|
||||
|
||||
//! Used to signal that the remainder of a test should be skipped
|
||||
struct TestSkipException{};
|
||||
/**
|
||||
* Outlines throwing of `TestSkipException` into a single TU
|
||||
*
|
||||
* Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers.
|
||||
*/
|
||||
[[noreturn]] void throw_test_skip_exception();
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
#include <catch2/internal/catch_assertion_handler.hpp>
|
||||
#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
#include <catch2/internal/catch_source_line_info.hpp>
|
||||
@ -22,12 +23,6 @@
|
||||
|
||||
#if !defined(CATCH_CONFIG_DISABLE)
|
||||
|
||||
#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
|
||||
#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr
|
||||
#else
|
||||
#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -95,6 +90,7 @@
|
||||
if( catchAssertionHandler.allowThrows() ) \
|
||||
try { \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
||||
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
||||
static_cast<void>(__VA_ARGS__); \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||
@ -115,6 +111,7 @@
|
||||
if( catchAssertionHandler.allowThrows() ) \
|
||||
try { \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
||||
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
||||
static_cast<void>(expr); \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||
@ -141,6 +138,7 @@
|
||||
if( catchAssertionHandler.allowThrows() ) \
|
||||
try { \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
|
||||
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
|
||||
static_cast<void>(__VA_ARGS__); \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <catch2/internal/catch_test_registry.hpp>
|
||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||
#include <catch2/catch_test_case_info.hpp>
|
||||
#include <catch2/internal/catch_test_case_registry_impl.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
@ -17,9 +16,10 @@
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
ITestInvoker::~ITestInvoker() = default;
|
||||
|
||||
namespace {
|
||||
StringRef extractClassName( StringRef classOrMethodName ) {
|
||||
static StringRef extractClassName( StringRef classOrMethodName ) {
|
||||
if ( !startsWith( classOrMethodName, '&' ) ) {
|
||||
return classOrMethodName;
|
||||
}
|
||||
@ -46,6 +46,18 @@ namespace Catch {
|
||||
static_cast<std::size_t>( startIdx ),
|
||||
static_cast<std::size_t>( classNameSize ) );
|
||||
}
|
||||
|
||||
class TestInvokerAsFunction final : public ITestInvoker {
|
||||
using TestType = void ( * )();
|
||||
TestType m_testAsFunction;
|
||||
|
||||
public:
|
||||
TestInvokerAsFunction( TestType testAsFunction ) noexcept:
|
||||
m_testAsFunction( testAsFunction ) {}
|
||||
|
||||
void invoke() const override { m_testAsFunction(); }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) {
|
||||
|
@ -8,9 +8,10 @@
|
||||
#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED
|
||||
#define CATCH_TEST_REGISTRY_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_config_static_analysis_support.hpp>
|
||||
#include <catch2/internal/catch_source_line_info.hpp>
|
||||
#include <catch2/internal/catch_noncopyable.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_test_invoker.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
#include <catch2/internal/catch_unique_name.hpp>
|
||||
@ -72,6 +73,9 @@ struct AutoReg : Detail::NonCopyable {
|
||||
void TestName::test()
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
|
||||
static void TestName(); \
|
||||
@ -84,19 +88,40 @@ struct AutoReg : Detail::NonCopyable {
|
||||
#define INTERNAL_CATCH_TESTCASE( ... ) \
|
||||
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ )
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
namespace { \
|
||||
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
|
||||
Catch::makeTestInvoker( &QualifiedMethod ), \
|
||||
CATCH_INTERNAL_LINEINFO, \
|
||||
"&" #QualifiedMethod##_catch_sr, \
|
||||
Catch::NameAndTags{ __VA_ARGS__ } ); \
|
||||
} /* NOLINT */ \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
||||
|
||||
|
||||
// Dummy registrator for the dumy test case macros
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
struct DummyUse {
|
||||
DummyUse( void ( * )( int ) );
|
||||
};
|
||||
} // namespace Detail
|
||||
} // namespace Catch
|
||||
|
||||
// Note that both the presence of the argument and its exact name are
|
||||
// necessary for the section support.
|
||||
|
||||
// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE`
|
||||
// tests can compile. The redefined `TEST_CASE` shadows this with param.
|
||||
static int catchInternalSectionHint = 0;
|
||||
|
||||
# define INTERNAL_CATCH_TESTCASE2( fname ) \
|
||||
static void fname( int ); \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \
|
||||
dummyUser )( &fname ); \
|
||||
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
|
||||
static void fname( [[maybe_unused]] int catchInternalSectionHint ) \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
# define INTERNAL_CATCH_TESTCASE( ... ) \
|
||||
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ) )
|
||||
|
||||
|
||||
#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
|
||||
@ -118,6 +143,22 @@ struct AutoReg : Detail::NonCopyable {
|
||||
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
|
||||
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
|
||||
namespace { \
|
||||
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
|
||||
Catch::makeTestInvoker( &QualifiedMethod ), \
|
||||
CATCH_INTERNAL_LINEINFO, \
|
||||
"&" #QualifiedMethod##_catch_sr, \
|
||||
Catch::NameAndTags{ __VA_ARGS__ } ); \
|
||||
} /* NOLINT */ \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
|
||||
do { \
|
||||
|
22
src/catch2/internal/catch_test_run_info.hpp
Normal file
22
src/catch2/internal/catch_test_run_info.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED
|
||||
#define CATCH_TEST_RUN_INFO_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct TestRunInfo {
|
||||
constexpr TestRunInfo(StringRef _name) : name(_name) {}
|
||||
StringRef name;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED
|
@ -59,7 +59,7 @@ namespace Catch {
|
||||
// Calculates the length of the current line
|
||||
void calcLength();
|
||||
|
||||
// Returns current indention width
|
||||
// Returns current indentation width
|
||||
size_t indentSize() const;
|
||||
|
||||
// Creates an indented and (optionally) suffixed string from
|
||||
|
@ -129,7 +129,7 @@ namespace Catch {
|
||||
|
||||
/**
|
||||
* Creates a matcher that checks if all elements in a range are equal
|
||||
* to all elements in another range, in some permuation.
|
||||
* to all elements in another range, in some permutation.
|
||||
*
|
||||
* Uses to provided predicate `predicate` to do the comparisons
|
||||
*/
|
||||
|
@ -85,11 +85,10 @@ namespace Matchers {
|
||||
// - a more general approach would be via a compare template that defaults
|
||||
// to using !=. but could be specialised for, e.g. std::vector<T> etc
|
||||
// - then just call that directly
|
||||
if (m_comparator.size() != v.size())
|
||||
return false;
|
||||
for (std::size_t i = 0; i < v.size(); ++i)
|
||||
if (m_comparator[i] != v[i])
|
||||
return false;
|
||||
if ( m_comparator.size() != v.size() ) { return false; }
|
||||
for ( std::size_t i = 0; i < v.size(); ++i ) {
|
||||
if ( !( m_comparator[i] == v[i] ) ) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string describe() const override {
|
||||
|
@ -8,9 +8,14 @@
|
||||
#ifndef CATCH_MATCHERS_IMPL_HPP_INCLUDED
|
||||
#define CATCH_MATCHERS_IMPL_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_test_macro_impl.hpp>
|
||||
#include <catch2/internal/catch_assertion_handler.hpp>
|
||||
#include <catch2/internal/catch_source_line_info.hpp>
|
||||
#include <catch2/internal/catch_decomposer.hpp>
|
||||
#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
template<typename ArgT, typename MatcherT>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user