Compare commits

...

69 Commits

Author SHA1 Message Date
e4b16053a6 Escape Catch2 test names in catch_discover_tests tests 2023-06-15 14:19:39 +02:00
42ee66b5e6 Fix handling of semicolon and backslash characters in CMake test discovery (#2676)
This PR fixes the handling of semicolon and backslash characters in test names in the CMake test discovery

Closes #2674
2023-06-14 23:40:10 +02:00
a0c6a28460 Fix possible FP in catch_discover_tests tests 2023-06-14 23:31:41 +02:00
c8363143e7 Add test scaffolding for catch_discover_tests 2023-06-14 21:14:33 +02:00
7a52dfa77b Fix typo in cross-docs links 2023-06-11 19:37:15 +02:00
9131736630 Bazel support: Update skylib 2023-06-08 13:30:50 +02:00
0631b607ee Test & document SKIP in generator constructor
Closes #1593
2023-05-31 15:12:43 +02:00
dff7513b28 Static analysis cleanup in tests 2023-05-29 21:45:30 +02:00
bf5aa7b383 Experimental static analysis support in TEST_CASE and SECTION
Closes #2681
2023-05-29 21:45:28 +02:00
dba9197ec7 Add new config option: STATIC_ANALYSIS_SUPPORT 2023-05-29 00:55:20 +02:00
f60c15364b Add macro for suppressing Wshadow 2023-05-28 21:07:31 +02:00
b3cf1bfb5d Avoid unused variable warning in GeneratorsImpl tests 2023-05-28 21:05:01 +02:00
73b93ce6bc Include catch_user_config.hpp in all catch_config_* files 2023-05-28 21:04:16 +02:00
8008625d7e Merge pull request #2693 from Ali-Amir/u/ali/optional-meson-unit-tests
Add option to disable building unit tests in Meson build file.
2023-05-27 12:00:52 +02:00
ce7b153021 Add option to disable building unit tests in Meson build file. 2023-05-26 10:05:06 -07:00
535205e2ac Suppress -Wunused-result warning in gcc
See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425.
2023-05-23 23:31:55 +02:00
689fdcd7dc Fix some tests never being run 2023-05-20 22:03:48 +02:00
a153fce724 Improve error messages for TEST_CASE tag parsing errors
Also removes a duplicated test case checking for empty tag error.

Related to #2650
2023-05-20 21:13:48 +02:00
06c0e1cfab Merge pull request #2689 from ThePhD/fix/includes/header-exception
🛠💚 Add <exception> header where strictly necessary
2023-05-16 18:41:19 +02:00
05d7eb5a00 🛠 Add <exception> header where strictly necessary 2023-05-16 12:18:57 -04:00
f53bb3ae7b meson: require version >=0.54.1
See discussion in https://github.com/mesonbuild/wrapdb/pull/1016.
2023-05-15 17:41:56 +02:00
ce8a7b3390 Merge pull request #2687 from ChrisThrasher/sfml
Add SFML to the list of open source users
2023-05-15 13:44:59 +02:00
6dce539fad Add SFML to the list of open source users 2023-05-13 14:34:34 -06:00
5a40b2275c Update CatchConfigOptions.cmake
Fix CMake warning

CMake Warning (dev) at catch2-src/CMake/CatchConfigOptions.cmake:71 (set):
  uninitialized variable 'BUILD_SHARED_LIBS'
2023-05-12 13:52:45 +02:00
598895d048 Fix Wredundant-decls
Closes #2682
2023-05-12 09:51:13 +02:00
0dc82e08df Move CATCH_INTERNAL_STRINGIFY macro into its own header 2023-05-07 20:58:54 +02:00
8ca504cbc9 Move AssertionResult when passing it inside RunContext 2023-05-06 23:58:48 +02:00
c57b5cdf43 Move-enable Catch::optional
This avoids copies in couple places through Catch2, e.g. reporter
spec handling, and moving around `AssertionResult` in `RunContext`.
2023-05-06 15:22:02 +02:00
d84777c9cb Fix assertionStarting events being sent after the expr is evaluated
Closes #2678
2023-05-06 11:48:41 +02:00
51fdbedd13 Internal linkage for outlier_variance 2023-05-01 13:21:47 +02:00
10f0a58643 Some template instantiation reductions 2023-05-01 13:21:45 +02:00
fe64c28925 Reduce compilation costs of benchmarks
We replaced some simple std::algorithm usage by loops, and reduced
header inclusion.
2023-05-01 13:21:41 +02:00
7d07efc92b Clean up iterator usage in benchmarks
Specifically we turned `mean`, `classify_outliers`, `jackknife`,
into concrete functions that take only `const_iterator` from vecs,
instead of generic iterators over anything.

I also changed `resample` to take `const_iterator` instead of
plain `iterator`, and similar for `standard_deviation`, and
`analyse_samples`.
2023-05-01 13:21:36 +02:00
f3c678c0ab Constexprify constants in estimate_clock.hpp 2023-05-01 13:21:33 +02:00
46539b6d9b Fix spelling 2023-04-29 12:55:51 +02:00
10596b2278 Fix unreachable-code-return warnings 2023-04-20 14:53:03 +02:00
897fe2a01b cmake: Improve unreachable-code warnings
Enable CI to report -Wunreachable-code-aggressive warnings in clang
builds which covers all,  -Wunreachable-code, -code-break,
-code-return.
2023-04-20 14:53:03 +02:00
aad926baf8 Catch.cmake: Add new DISCOVERY_MODE option to catch_discover_tests
Introducing a new DISCOVERY_MODE mode option, which provides greater
control over when catch_discover_tests perforsm test discovery.

It has two supported modes:
* POST_BUILD: The default behavior, which adds a POST_BUILD command
  to perform test discovery after the test has been built as was
  always done so far.

* PRE_TEST: New mode, which delays test discovery until test execution.
  The generated include file generates the appropriate CTest files at
  runtime and regenerates the CTest files when the executable is
  updated.
  This mode can be used in build-environments that don't allow for
  executing the linked binaries at build-time (like in a
  cross-compilation environment).

DISCOVERY_MODE can be controlled in two ways:
1. Setting the DISCOVERY_MODE when calling catch_discover_tests.

2. Setting the global CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE prior
   to calling gtest_discover_tests.

Closes #2493
2023-04-19 23:51:42 +02:00
4e8399d835 CatchAddTests.cmake: Refactor into callable method
Move test discovery logic into new catch_discover_tests_impl method
and make CatchAddTests aware of whether it is being launched in
CMake's script mode.

When launched in script mode, catch_discover_tests_impl is called
passing arguments obtained from the definitions passed into the call
to cmake. This preserves the existing behavior assumed by Catch.cmake.

Looking ahead, it also allows CatchAddTests to be included in
generated files and call catch_discover_tests_impl to perform test
discovery at test runtime with the new PRE_TEST discovery mode
introduced later.
2023-04-19 23:51:42 +02:00
9a2a4eadc0 Bump xml-format-version in XML reporter 2023-04-10 21:59:50 +02:00
fb806da76f Add lineinfo to XML reporter output for INFO/WARN
Closes #1251
2023-04-10 16:32:07 +02:00
50bf00e266 Fix reporter detection in catch_discover_tests
Closes #2668
Instead of grepping for the reporter, use it in the process and check for the error code
2023-04-05 10:12:50 +02:00
9f08097f55 Cleanup internal includes by splitting out some event structs
* Split out BenchmarkInfo and BenchmarkStats to their own header
* Outline BenchmarkStats<> declaration to separate header
* Split out TestRunInfo into its own header

These changes let us remove the large `interfaces_reporter.hpp`
include from `benchmark.hpp`, and replace it with
`interfaces_capture.hpp` in `run_context.hpp`.

I also cleaned out `interfaces_repoter.hpp` from reporter headers
that depend on `reporter_common_base.hpp`. This will not change
anything in the actual inclusion set, but makes it logically
more consistent.
2023-03-31 19:31:51 +02:00
1f881ab464 Split ITestInvoker into its own header 2023-03-23 16:50:11 +01:00
c487b27d9d Reduce misc includes all around 2023-03-23 16:50:07 +01:00
3230760db2 Cleanup in translating exceptions to messages 2023-03-23 12:26:44 +01:00
b3ebce715e Cleanup benchmarking includes 2023-03-23 11:59:40 +01:00
d0f70fdfd6 Unify IReporterRegistry and ReporterRegistry
To keep the compilation firewall effect, the implementations
are hidden behind a PIMPL. In this case it is probably not
worth it, but we can inline it later if needed.
2023-03-22 23:58:44 +01:00
4f4ad8ada9 Sprinkle some constexpr around 2023-03-22 19:12:32 +01:00
5b665be643 Cut out catch_interfaces_capture.hpp include from the main include 2023-03-22 19:12:03 +01:00
2598116aa6 Mark various anonymous classes final 2023-03-20 22:56:43 +01:00
173aa3f1f4 Devirtualize Context 2023-03-20 20:46:41 +01:00
28437e1214 Remove pointless member variable from RunContext 2023-03-20 20:34:58 +01:00
3c8fb6bbb2 Internal linkage for generator trackers 2023-03-20 19:37:58 +01:00
72f3ce4db5 Outline the actual registering of listener factories to cpp file 2023-03-20 19:37:27 +01:00
62167d756e Reduce internal includes 2023-03-20 19:24:52 +01:00
6783411349 Fixed extras installation and shard impl location 2023-03-14 18:31:38 +01:00
7b4dd326c0 Remove obsolete comment in multireporter 2023-03-12 13:27:07 +01:00
1dfaa8abe7 Outline throwing of TestSkipException 2023-03-12 00:45:31 +01:00
ba94278bdd Inline trivial function in AssertionHandler 2023-03-12 00:41:25 +01:00
8e5a4b6f70 Remove superfluous pointer copy in AssertionStats constructor 2023-03-12 00:39:18 +01:00
9b884d8107 Fix refactoring 2023-03-11 23:59:47 +01:00
8a1b3b81cb Add wxWidgets as another Open Source project using Catch
wxWidgets uses Catch (v2 currently) for all of its tests, even though
some of them still use CppUnit-like macros for compatibility.
2023-03-11 21:29:51 +01:00
e5aabb6714 Add xmlwrapp to the list of Open Source projects using Catch
This library uses Catch (v2 currently) for its unit tests.
2023-03-11 21:29:51 +01:00
3a1ef14097 Use hasMessage() instead of getMessage().empty() 2023-03-11 21:27:11 +01:00
13fae1e2ff Move exception's translation into AssertionResultData message 2023-03-11 16:14:06 +01:00
3220ae6d4a Add support for the IAR compiler 2023-03-08 20:55:41 +01:00
0a0ebf5003 Support elements without op!= in VectorEquals
Closes #2648
2023-03-05 00:11:38 +01:00
69f35a5ac8 Bazel support: Update skylib version 2023-03-01 18:55:25 +01:00
146 changed files with 2572 additions and 1629 deletions

View File

@ -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

View File

@ -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": "",

View File

@ -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.")

View File

@ -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"

View File

@ -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}
)

View File

@ -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"
}
}
]

View File

@ -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()

View File

@ -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`

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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>({ ...
*/

View File

@ -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()

View File

@ -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}")

View File

@ -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"
)

View 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
View File

@ -0,0 +1 @@
option('tests', type: 'boolean', value: true, description: 'Build the unit tests')

View File

@ -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})

View File

@ -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());

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
};

View File

@ -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() {

View File

@ -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,

View File

@ -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),

View File

@ -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>

View 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

View 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

View File

@ -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 {

View File

@ -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()),

View File

@ -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>

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -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>

View File

@ -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 )

View File

@ -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;

View File

@ -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 );

View File

@ -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 {

View File

@ -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>

View File

@ -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);

View File

@ -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;
}

View File

@ -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>

View 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

View File

@ -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 ) );
}
};

View File

@ -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
// ------

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -9,6 +9,5 @@
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
namespace Catch {
ITestInvoker::~ITestInvoker() = default;
ITestCaseRegistry::~ITestCaseRegistry() = default;
}

View File

@ -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

View File

@ -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 );

View File

@ -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;

View File

@ -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() );

View File

@ -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))

View File

@ -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

View 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

View File

@ -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

View File

@ -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.

View File

@ -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 ) {}

View File

@ -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;

View File

@ -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();

View File

@ -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
}

View File

@ -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;

View File

@ -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:

View File

@ -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() });

View File

@ -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>

View File

@ -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)];
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() ) };

View File

@ -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 ); }
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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)() ) {

View File

@ -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 { \

View 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

View File

@ -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

View File

@ -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
*/

View File

@ -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 {

View File

@ -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