Compare commits

..

65 Commits

Author SHA1 Message Date
Martin Hořeňovský
5db31e587e WIP: devirtualize RunContext 2023-03-21 11:32:44 +01:00
Martin Hořeňovský
f3960c02ce Devirtualize RegistryHub 2023-03-21 11:30:22 +01:00
Martin Hořeňovský
02ce0a2eec Unify ITestCaseRegistry and TestRegistry
I also made a bunch of refactorings around the headers and includes
to simplify the main include path.
2023-03-21 11:30:19 +01:00
Martin Hořeňovský
cf4d84a349 Unify ITagAliasRegistry and TagAliasRegistry 2023-03-21 11:30:17 +01:00
Martin Hořeňovský
cfe859e0f3 Unify IExceptionTranslatorRegistry and ExceptionTranslatorRegistry 2023-03-21 11:30:12 +01:00
Martin Hořeňovský
31b291ba26 Unify IMutableEnumRegistry and EnumValuesRegistry
This time there was no need for compilation firewall, so we keep
everything inlined.
2023-03-21 11:30:08 +01:00
Martin Hořeňovský
ceb7ab6b20 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-21 11:30:06 +01:00
Martin Hořeňovský
2598116aa6 Mark various anonymous classes final 2023-03-20 22:56:43 +01:00
Martin Hořeňovský
173aa3f1f4 Devirtualize Context 2023-03-20 20:46:41 +01:00
Martin Hořeňovský
28437e1214 Remove pointless member variable from RunContext 2023-03-20 20:34:58 +01:00
Martin Hořeňovský
3c8fb6bbb2 Internal linkage for generator trackers 2023-03-20 19:37:58 +01:00
Martin Hořeňovský
72f3ce4db5 Outline the actual registering of listener factories to cpp file 2023-03-20 19:37:27 +01:00
Martin Hořeňovský
62167d756e Reduce internal includes 2023-03-20 19:24:52 +01:00
Bob Miller
6783411349 Fixed extras installation and shard impl location 2023-03-14 18:31:38 +01:00
Martin Hořeňovský
7b4dd326c0 Remove obsolete comment in multireporter 2023-03-12 13:27:07 +01:00
Martin Hořeňovský
1dfaa8abe7 Outline throwing of TestSkipException 2023-03-12 00:45:31 +01:00
Martin Hořeňovský
ba94278bdd Inline trivial function in AssertionHandler 2023-03-12 00:41:25 +01:00
Martin Hořeňovský
8e5a4b6f70 Remove superfluous pointer copy in AssertionStats constructor 2023-03-12 00:39:18 +01:00
Martin Hořeňovský
9b884d8107 Fix refactoring 2023-03-11 23:59:47 +01:00
Vadim Zeitlin
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
Vadim Zeitlin
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
Martin Hořeňovský
3a1ef14097 Use hasMessage() instead of getMessage().empty() 2023-03-11 21:27:11 +01:00
Martin Hořeňovský
13fae1e2ff Move exception's translation into AssertionResultData message 2023-03-11 16:14:06 +01:00
jushar
3220ae6d4a Add support for the IAR compiler 2023-03-08 20:55:41 +01:00
Martin Hořeňovský
0a0ebf5003 Support elements without op!= in VectorEquals
Closes #2648
2023-03-05 00:11:38 +01:00
Vertexwahn
69f35a5ac8 Bazel support: Update skylib version 2023-03-01 18:55:25 +01:00
Martin Hořeňovský
3f0283de7a v3.3.2 2023-02-27 15:12:49 +01:00
Martin Hořeňovský
6fbb3f0723 Add IsNaN matcher 2023-02-26 00:14:32 +01:00
Martin Hořeňovský
9ff3cde87b Simplify test name creation for list-templated test cases 2023-02-23 15:12:14 +01:00
Martin Hořeňovský
4d802ca58f Use StringRef UDL in more preprocessor-generated strings 2023-02-23 13:25:08 +01:00
Martin Hořeňovský
13711be7cf Use StringRef UDL for generated generator names 2023-02-23 13:25:07 +01:00
Martin Hořeňovský
27ba26f743 Merge pull request #2643 from kisielk/patch-1
cmake-integration.md: Use "tests" as test target name in all examples.
2023-02-22 20:59:12 +01:00
Martin Hořeňovský
a209bcfb54 Update build instructions in contributing.md
We now show the more modern `-S {source}` instead of the old and
undocumented `-H{source}` CMake flag, and also show the available
presets, instead of individually specifying the different testing
options.

Closes #2593
2023-02-22 20:11:45 +01:00
Martin Hořeňovský
584973a485 Early evaluate line loc in NameAndLoc::operator==
I do not know if checking the tracker name or the tracker's file
part of the location first would provide better results, but
in the common case, the line part of the location check should be
rather unique, because different `SECTION`s will have different
source lines where they are defined.

I also propagated this same check into `ITracker::findChild`,
because this significantly improves performance of section tracking
in Debug builds -> 10% in macro benchmark heavily focused on section
tracking. In Release build there is usually no difference, because
the inliner will inline `NameAndLoc::operator==` into `findChild`,
and then eliminate the redundant check. (If the inliner decides
against, then this still improves the performance on average).
2023-02-20 15:19:57 +01:00
Martin Hořeňovský
4f7c8cb28a Avoid copying NameAndLocationRef when passed as argument
`NameAndLocationRef` is pretty large type, so even in release build,
it is unlikely to be passed in registers. In addition to the fact
that some platforms currently do not allow passing even small types
in register (Windows ABI!!), it is better to pass it as a ref,
effectively passing around a pointer.
2023-02-20 15:17:35 +01:00
Martin Hořeňovský
e1dbad4c9e Inline StringRef::operator==
This enables its inlining even without LTO, which in turns enables
callers to determine that two StringRefs are unequals with simple
comparison of two numbers, without any function calls.
2023-02-20 15:05:09 +01:00
Martin Hořeňovský
2befd98da2 Inline some non-virtual functions in ITracker and TrackerContext 2023-02-20 15:02:50 +01:00
Martin Hořeňovský
00f259aeb2 Move captured output into TestCaseStats when sending testCaseEnded 2023-02-20 14:48:39 +01:00
Martin Hořeňovský
fed1436246 Avoid allocating trimmed name for SectionTracker 2023-02-20 14:32:46 +01:00
Martin Hořeňovský
0477326ad9 Directly construct empty string for invalid SectionInfo 2023-02-20 14:32:14 +01:00
Martin Hořeňovský
f04c93462b Small refactoring in AssertionResult 2023-02-20 14:32:12 +01:00
Martin Hořeňovský
1af351cea1 Remove unused TrackerContext::endRun function 2023-02-20 14:32:10 +01:00
Martin Hořeňovský
dcc9fa3f38 Use StringRef UDL for more string literals when expanding macros
* for the name of the listener when registering listener
* for the original expression in assertion macros
2023-02-20 14:31:26 +01:00
Martin Hořeňovský
bf6a15a69a Rewrite -# docs 2023-02-17 15:55:21 +01:00
Martin Hořeňovský
6135a78c31 Don't insert the foo part of [.foo] tag twice when parsing test spec 2023-02-13 22:16:53 +01:00
Martin Hořeňovský
e8ba329b6c Add support for iterator+sentinel pairs in Contains matcher 2023-02-10 23:25:45 +01:00
Martin Hořeňovský
4aa88299af Preconstruct error message in RunContext::handleIncomplete 2023-02-10 21:36:04 +01:00
Kamil Kisiel
4ff9be3bc5 cmake-integration.md: Use "tests" as test target name in all examples. 2023-02-10 11:09:26 -08:00
Martin Hořeňovský
76cdaa3b51 Merge pull request #2637 from jbadwaik/nvhpc_unused_warning
Suppress declared_but_not_referenced warning for NVHPC
2023-02-08 19:51:39 +01:00
Jayesh Badwaik (FZ Juelich)
644294df60 Suppress declared_but_not_referenced warning for NVHPC
Catch2 suppresses unused variable and equivalent warnings in a couple
  of places, but most importantly, in the declaration of autoRegistrar
  in test registry. This warning gets triggered by NVHPC compiler. The
  current patch adds three macros, namely:

      CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
      CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
      CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION

  for the NVHPC Compiler which in particular prevents that warning from
  occurring. The compiler is detected completely separately from the
  other compilers in this patch, because from what I found out, NVHPC
  defines __GNUC__ as well for some reason. (I suspect because it
  advertises itself as GNU compatible.)

  We also add a condition to make sure that the `__GNUC__` path is not
  taken by the NVHPC compiler.
2023-02-08 12:40:55 +01:00
Martin Hořeňovský
cefa8fcf32 Enable use of UnorderedRangeEquals with iterator+sentinel pairs 2023-02-06 15:34:38 +01:00
Martin Hořeňovský
772fa3f790 Add Catch::Detail::is_permutation that supports sentinels
Also split out helpers for testing matcher ranges (types whose
begin/end/empty/etc require ADL lookup, types whose iteration
uses iterator + sentinel pair, etc) into their own file.
2023-02-06 15:29:01 +01:00
Martin Hořeňovský
f3c0a3cd09 Fix RangeEquals matcher to handle iterator + sentinel ranges
Also added tests for types that require ADL lookup for their
`begin` and `end`.
2023-02-03 18:22:41 +01:00
Martin Hořeňovský
42d9d4533e Add test for empty result of filter generator 2023-02-01 18:27:41 +01:00
Martin Hořeňovský
618d44c448 Update docs about thread safe assertions 2023-02-01 15:24:47 +01:00
Martin Hořeňovský
388f7e1737 Cleanup unneeded allocations from reporters
The CompactReporter changes save 21 (430764 -> 430743) allocations
when running the SelfTest binary in default configuration. They
save about 500 allocations when running the binary with `-s`.
2023-01-30 15:30:36 +01:00
Martin Hořeňovský
2ab20a0e00 v3.3.1 2023-01-29 23:18:57 +01:00
Martin Hořeňovský
60264b8807 Avoid copying strings in sonarqube when sorting tests by file 2023-01-29 20:45:13 +01:00
Martin Hořeňovský
65ffee5189 Don't take ownership of SECTION's name for inactive sections
This eliminates 1945 (432709 -> 430764) allocations from running
`./tests/SelfTest -o /dev/null`. In general terms, this saves
an allocation every time an unvisited `SECTION` is passed, which
means that the saved allocations are quadratic in number of sibling
(same level) `SECTION`s in a test case.
2023-01-29 10:44:20 +01:00
Martin Hořeňovský
43f02027e4 Avoid allocations when looking for trackers
Now we delay allocating owning `NameAndLocation` instances until
we construct a new tracker (because a tracker's lifetime can be
significantly different from the underlying tracked-thing's name).

This saves 4239 allocations (436948 -> 432709) when running
`./tests/SelfTest -o /dev/null`, at some cost to code clarity
due to introducing a new ref type, `NameAndLocationRef`.
2023-01-29 10:14:20 +01:00
Martin Hořeňovský
906552f8c8 Clean up extraneous copies in Messages
This removes 109 allocations from running `tests/SelfTest`
(437057 -> 436948).
2023-01-28 22:14:37 +01:00
Martin Hořeňovský
356dfc1439 Move name and sample analysis in benchmarks into BenchmarkStats
This always saves 1 allocation per benchmark, and another two
allocations if the benchmark name is longer than the SSO buffer.
2023-01-28 21:40:59 +01:00
Martin Hořeňovský
e5d1eb757f Move AssertionResultData into AssertionResult in RunContext
When running `./tests/SelfTest -o /dev/null`, this saves 109
allocations (437167 -> 437058).
2023-01-28 19:57:38 +01:00
Martin Hořeňovský
2403f5620e Move SectionEndInfo into sectionEnded call in SECTION's destructor
When running `./tests/SelfTest -o /dev/null`, this saves 1272
allocations (437439 -> 437167). In general, this saves multiple
allocations per end of an entered `SECTION`, if the section name
was too long for SSO, because `RunContext::sectionEnded` can then
move the section's name further down the callstack.
2023-01-28 13:00:30 +01:00
Martin Hořeňovský
d58491c85a Move sectionInfo into sectionEndInfo when SECTION ends
When running `./tests/SelfTest -o /dev/null`, this saves 468
allocations (438907 -> 437439). In general, this saves 1 allocation
every time an entered `SECTION` ends and the section name was long
enough to move out of the SSO buffer.
2023-01-28 12:56:29 +01:00
140 changed files with 3175 additions and 1776 deletions

View File

@@ -31,7 +31,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif() endif()
project(Catch2 project(Catch2
VERSION 3.3.0 # CML version placeholder, don't delete VERSION 3.3.2 # CML version placeholder, don't delete
LANGUAGES CXX LANGUAGES CXX
# HOMEPAGE_URL is not supported until CMake version 3.12, which # HOMEPAGE_URL is not supported until CMake version 3.12, which
# we do not target yet. # we do not target yet.
@@ -148,6 +148,8 @@ if (NOT_SUBPROJECT)
"extras/ParseAndAddCatchTests.cmake" "extras/ParseAndAddCatchTests.cmake"
"extras/Catch.cmake" "extras/Catch.cmake"
"extras/CatchAddTests.cmake" "extras/CatchAddTests.cmake"
"extras/CatchShardTests.cmake"
"extras/CatchShardTestsImpl.cmake"
DESTINATION DESTINATION
${CATCH_CMAKE_CONFIG_DESTINATION} ${CATCH_CMAKE_CONFIG_DESTINATION}
) )

View File

@@ -4,12 +4,13 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive( http_archive(
name = "bazel_skylib", name = "bazel_skylib",
sha256 = "b8a1527901774180afc798aeb28c4634bdccf19c4d98e7bdd1ce79d1fe9aaad7",
urls = [ urls = [
"https://mirror.bazel.build/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.1/bazel-skylib-1.4.1.tar.gz",
"https://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.4.1/bazel-skylib-1.4.1.tar.gz",
], ],
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
) )
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace() bazel_skylib_workspace()

View File

@@ -90,12 +90,12 @@ cmake_minimum_required(VERSION 3.5)
project(baz LANGUAGES CXX VERSION 0.0.1) project(baz LANGUAGES CXX VERSION 0.0.1)
find_package(Catch2 REQUIRED) find_package(Catch2 REQUIRED)
add_executable(foo test.cpp) add_executable(tests test.cpp)
target_link_libraries(foo PRIVATE Catch2::Catch2) target_link_libraries(tests PRIVATE Catch2::Catch2)
include(CTest) include(CTest)
include(Catch) include(Catch)
catch_discover_tests(foo) catch_discover_tests(tests)
``` ```
When using `FetchContent`, `include(Catch)` will fail unless When using `FetchContent`, `include(Catch)` will fail unless
@@ -108,7 +108,7 @@ directory.
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest) include(CTest)
include(Catch) include(Catch)
catch_discover_tests() catch_discover_tests(tests)
``` ```
#### Customization #### Customization
@@ -222,12 +222,12 @@ cmake_minimum_required(VERSION 3.5)
project(baz LANGUAGES CXX VERSION 0.0.1) project(baz LANGUAGES CXX VERSION 0.0.1)
find_package(Catch2 REQUIRED) find_package(Catch2 REQUIRED)
add_executable(foo test.cpp) add_executable(tests test.cpp)
target_link_libraries(foo PRIVATE Catch2::Catch2) target_link_libraries(tests PRIVATE Catch2::Catch2)
include(CTest) include(CTest)
include(ParseAndAddCatchTests) include(ParseAndAddCatchTests)
ParseAndAddCatchTests(foo) ParseAndAddCatchTests(tests)
``` ```

View File

@@ -507,10 +507,13 @@ start of the first section.</br>
## Filenames as tags ## Filenames as tags
<pre>-#, --filenames-as-tags</pre> <pre>-#, --filenames-as-tags</pre>
When this option is used then every test is given an additional tag which is formed of the unqualified This option adds an extra tag to all test cases. The tag is `#` followed
filename it is found in, with any extension stripped, prefixed with the `#` character. by the unqualified filename the test case is defined in, with the _last_
extension stripped out.
For example, tests within the file `tests\SelfTest\UsageTests\BDD.tests.cpp`
will be given the `[#BDD.tests]` tag.
So, for example, tests within the file `~\Dev\MyProject\Ferrets.cpp` would be tagged `[#Ferrets]`.
<a id="colour-mode"></a> <a id="colour-mode"></a>
## Override output colouring ## Override output colouring

View File

@@ -55,6 +55,15 @@ tests from `SelfTest` through a specific reporter and then compare the
generated output with a known good output ("Baseline"). By default, new generated output with a known good output ("Baseline"). By default, new
tests should be placed here. tests should be placed here.
To configure a Catch2 build with just the basic tests, use the `basic-tests`
preset, like so:
```
# Assuming you are in Catch2's root folder
cmake -B basic-test-build -S . -DCMAKE_BUILD_TYPE=Debug --preset basic-tests
```
However, not all tests can be written as plain unit tests. For example, However, not all tests can be written as plain unit tests. For example,
checking that Catch2 orders tests randomly when asked to, and that this checking that Catch2 orders tests randomly when asked to, and that this
random ordering is subset-invariant, is better done as an integration random ordering is subset-invariant, is better done as an integration
@@ -76,21 +85,23 @@ configuration and require separate compilation.
Finally, CMake config tests test that you set Catch2's compile-time Finally, CMake config tests test that you set Catch2's compile-time
configuration options through CMake, using CMake options of the same name. configuration options through CMake, using CMake options of the same name.
None of these tests are enabled by default. To enable them, add These test categories can be enabled one by one, by passing
`-DCATCH_BUILD_EXAMPLES=ON`, `-DCATCH_BUILD_EXTRA_TESTS=ON`, and `-DCATCH_BUILD_EXAMPLES=ON`, `-DCATCH_BUILD_EXTRA_TESTS=ON`, and
`-DCATCH_ENABLE_CONFIGURE_TESTS=ON` when configuration the CMake build. `-DCATCH_ENABLE_CONFIGURE_TESTS=ON` when configuring the build.
Bringing this all together, the steps below should configure, build, Catch2 also provides a preset that promises to enable _all_ test types,
and run all tests in the `Debug` compilation. `all-tests`.
The snippet below will build & run all tests, in `Debug` compilation mode.
<!-- snippet: catch2-build-and-test --> <!-- snippet: catch2-build-and-test -->
<a id='snippet-catch2-build-and-test'></a> <a id='snippet-catch2-build-and-test'></a>
```sh ```sh
# 1. Regenerate the amalgamated distribution # 1. Regenerate the amalgamated distribution (some tests are built against it)
./tools/scripts/generateAmalgamatedFiles.py ./tools/scripts/generateAmalgamatedFiles.py
# 2. Configure the full test build # 2. Configure the full test build
cmake -Bdebug-build -H. -DCMAKE_BUILD_TYPE=Debug -DCATCH_DEVELOPMENT_BUILD=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_BUILD_EXTRA_TESTS=ON cmake -B debug-build -S . -DCMAKE_BUILD_TYPE=Debug --preset all-tests
# 3. Run the actual build # 3. Run the actual build
cmake --build debug-build cmake --build debug-build

View File

@@ -88,8 +88,8 @@ because only one thread passes the `REQUIRE` macro and this is not
REQUIRE(cnt == 16); REQUIRE(cnt == 16);
``` ```
Because C++11 provides the necessary tools to do this, we are planning We currently do not plan to support thread-safe assertions.
to remove this limitation in the future.
### Process isolation in a test ### Process isolation in a test
Catch does not support running tests in isolated (forked) processes. While this might in the future, the fact that Windows does not support forking and only allows full-on process creation and the desire to keep code as similar as possible across platforms, mean that this is likely to take significant development time, that is not currently available. Catch does not support running tests in isolated (forked) processes. While this might in the future, the fact that Windows does not support forking and only allows full-on process creation and the desire to keep code as similar as possible across platforms, mean that this is likely to take significant development time, that is not currently available.

View File

@@ -141,18 +141,27 @@ are a permutation of the ones in `some_vec`.
### Floating point matchers ### Floating point matchers
Catch2 provides 3 matchers that target floating point numbers. These Catch2 provides 4 matchers that target floating point numbers. These
are: are:
* `WithinAbs(double target, double margin)`, * `WithinAbs(double target, double margin)`,
* `WithinULP(FloatingPoint target, uint64_t maxUlpDiff)`, and * `WithinULP(FloatingPoint target, uint64_t maxUlpDiff)`, and
* `WithinRel(FloatingPoint target, FloatingPoint eps)`. * `WithinRel(FloatingPoint target, FloatingPoint eps)`.
* `IsNaN()`
> `WithinRel` matcher was introduced in Catch2 2.10.0 > `WithinRel` matcher was introduced in Catch2 2.10.0
For more details, read [the docs on comparing floating point > `IsNaN` matcher was introduced in Catch2 3.3.2.
The first three serve to compare two floating pointe numbers. For more
details about how they work, read [the docs on comparing floating point
numbers](comparing-floating-point-numbers.md#floating-point-matchers). numbers](comparing-floating-point-numbers.md#floating-point-matchers).
`IsNaN` then does exactly what it says on the tin. It matches the input
if it is a NaN (Not a Number). The advantage of using it over just plain
`REQUIRE(std::isnan(x))`, is that if the check fails, with `REQUIRE` you
won't see the value of `x`, but with `REQUIRE_THAT(x, IsNaN())`, you will.
### Miscellaneous matchers ### Miscellaneous matchers

View File

@@ -110,6 +110,12 @@ A header-only TOML parser and serializer for modern C++.
### [Trompeloeil](https://github.com/rollbear/trompeloeil) ### [Trompeloeil](https://github.com/rollbear/trompeloeil)
A thread-safe header-only mocking framework for C++14. 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 ## Applications & Tools
### [App Mesh](https://github.com/laoshanxi/app-mesh) ### [App Mesh](https://github.com/laoshanxi/app-mesh)

View File

@@ -2,6 +2,8 @@
# Release notes # Release notes
**Contents**<br> **Contents**<br>
[3.3.2](#332)<br>
[3.3.1](#331)<br>
[3.3.0](#330)<br> [3.3.0](#330)<br>
[3.2.1](#321)<br> [3.2.1](#321)<br>
[3.2.0](#320)<br> [3.2.0](#320)<br>
@@ -55,6 +57,38 @@
## 3.3.2
### Improvements
* Further reduced allocations
* The compact, console, TAP and XML reporters perform less allocations in various cases
* Removed 1 allocation per entered `SECTION`/`TEST_CASE`.
* Removed 2 allocations per test case exit, if stdout/stderr is captured
* Improved performance
* Section tracking is 10%-25% faster than in v3.3.0
* Assertion handling is 5%-10% faster than in v3.3.0
* Test case registration is 1%-2% faster than in v3.3.0
* Tiny speedup for registering listeners
* Tiny speedup for `CAPTURE`, `TEST_CASE_METHOD`, `METHOD_AS_TEST_CASE`, and `TEMPLATE_LIST_TEST_*` macros.
* `Contains`, `RangeEquals` and `UnorderedRangeEquals` matchers now support ranges with iterator + sentinel pair
* Added `IsNaN` matcher
* Unlike `REQUIRE(isnan(x))`, `REQUIRE_THAT(x, IsNaN())` shows you the value of `x`.
* Suppressed `declared_but_not_referenced` warning for NVHPC (#2637)
### Fixes
* Fixed performance regression in section tracking introduced in v3.3.1
* Extreme cases would cause the tracking to run about 4x slower than in 3.3.0
## 3.3.1
### Improvements
* Reduced allocations and improved performance
* The exact improvements are dependent on your usage of Catch2.
* For example running Catch2's SelfTest binary performs 8k less allocations.
* The main improvement comes from smarter handling of `SECTION`s, especially sibling `SECTION`s
## 3.3.0 ## 3.3.0
### Improvements ### Improvements

View File

@@ -69,7 +69,8 @@ All tag names beginning with non-alphanumeric characters are reserved by Catch.
* `[!nonportable]` - Indicates that behaviour may vary between platforms or compilers. * `[!nonportable]` - Indicates that behaviour may vary between platforms or compilers.
* `[#<filename>]` - running with `-#` or `--filenames-as-tags` causes Catch to add the filename, prefixed with `#` (and with any extension stripped), as a tag to all contained tests, e.g. tests in testfile.cpp would all be tagged `[#testfile]`. * `[#<filename>]` - these tags are added to test cases when you run Catch2
with [`-#` or `--filenames-as-tags`](command-line.md#filenames-as-tags).
* `[@<alias>]` - tag aliases all begin with `@` (see below). * `[@<alias>]` - tag aliases all begin with `@` (see below).

View File

@@ -46,7 +46,7 @@ function(catch_add_sharded_tests TARGET)
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" 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( add_custom_command(
TARGET ${TARGET} POST_BUILD TARGET ${TARGET} POST_BUILD
@@ -64,3 +64,11 @@ function(catch_add_sharded_tests TARGET)
endfunction() 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

@@ -5,8 +5,8 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
// Catch v3.3.0 // Catch v3.3.2
// Generated: 2023-01-22 19:46:24.251531 // Generated: 2023-02-26 10:28:48.270752
// ---------------------------------------------------------- // ----------------------------------------------------------
// This file is an amalgamation of multiple different files. // This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly. // You probably shouldn't edit it directly.
@@ -428,9 +428,9 @@ namespace Catch {
return reconstructedExpression; return reconstructedExpression;
} }
AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data )
: m_info( info ), : m_info( info ),
m_resultData( data ) m_resultData( CATCH_MOVE(data) )
{} {}
// Result was a success // Result was a success
@@ -469,16 +469,15 @@ namespace Catch {
} }
std::string AssertionResult::getExpressionInMacro() const { std::string AssertionResult::getExpressionInMacro() const {
if ( m_info.macroName.empty() ) {
return static_cast<std::string>( m_info.capturedExpression );
}
std::string expr; std::string expr;
if( m_info.macroName.empty() )
expr = static_cast<std::string>(m_info.capturedExpression);
else {
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
expr += m_info.macroName; expr += m_info.macroName;
expr += "( "; expr += "( ";
expr += m_info.capturedExpression; expr += m_info.capturedExpression;
expr += " )"; expr += " )";
}
return expr; return expr;
} }
@@ -758,8 +757,8 @@ namespace Catch {
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
ScopedMessage::ScopedMessage( MessageBuilder const& builder ): ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
m_info( builder.m_info ) { m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str(); m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info ); getResultCapture().pushScopedMessage( m_info );
} }
@@ -2022,7 +2021,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 3, 3, 0, "", 0 ); static Version version( 3, 3, 2, "", 0 );
return version; return version;
} }
@@ -2179,18 +2178,17 @@ namespace Catch {
// Copy message into messages list. // Copy message into messages list.
// !TBD This should have been done earlier, somewhere // !TBD This should have been done earlier, somewhere
MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
builder << assertionResult.getMessage(); builder.m_info.message = static_cast<std::string>(assertionResult.getMessage());
builder.m_info.message = builder.m_stream.str();
infoMessages.push_back( builder.m_info ); infoMessages.push_back( CATCH_MOVE(builder.m_info) );
} }
} }
SectionStats::SectionStats( SectionInfo const& _sectionInfo, SectionStats::SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions, Counts const& _assertions,
double _durationInSeconds, double _durationInSeconds,
bool _missingAssertions ) bool _missingAssertions )
: sectionInfo( _sectionInfo ), : sectionInfo( CATCH_MOVE(_sectionInfo) ),
assertions( _assertions ), assertions( _assertions ),
durationInSeconds( _durationInSeconds ), durationInSeconds( _durationInSeconds ),
missingAssertions( _missingAssertions ) missingAssertions( _missingAssertions )
@@ -2199,13 +2197,13 @@ namespace Catch {
TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals, Totals const& _totals,
std::string const& _stdOut, std::string&& _stdOut,
std::string const& _stdErr, std::string&& _stdErr,
bool _aborting ) bool _aborting )
: testInfo( &_testInfo ), : testInfo( &_testInfo ),
totals( _totals ), totals( _totals ),
stdOut( _stdOut ), stdOut( CATCH_MOVE(_stdOut) ),
stdErr( _stdErr ), stdErr( CATCH_MOVE(_stdErr) ),
aborting( _aborting ) aborting( _aborting )
{} {}
@@ -4993,12 +4991,12 @@ namespace Catch {
struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
GeneratorBasePtr m_generator; GeneratorBasePtr m_generator;
GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( nameAndLocation, ctx, parent ) : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent )
{} {}
~GeneratorTracker() override; ~GeneratorTracker() override;
static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef const& nameAndLocation ) {
GeneratorTracker* tracker; GeneratorTracker* tracker;
ITracker& currentTracker = ctx.currentTracker(); ITracker& currentTracker = ctx.currentTracker();
@@ -5140,13 +5138,8 @@ namespace Catch {
Totals RunContext::runTest(TestCaseHandle const& testCase) { Totals RunContext::runTest(TestCaseHandle const& testCase) {
const Totals prevTotals = m_totals; const Totals prevTotals = m_totals;
std::string redirectedCout;
std::string redirectedCerr;
auto const& testInfo = testCase.getTestCaseInfo(); auto const& testInfo = testCase.getTestCaseInfo();
m_reporter->testCaseStarting(testInfo); m_reporter->testCaseStarting(testInfo);
m_activeTestCase = &testCase; m_activeTestCase = &testCase;
@@ -5188,9 +5181,11 @@ namespace Catch {
seedRng( *m_config ); seedRng( *m_config );
uint64_t testRuns = 0; uint64_t testRuns = 0;
std::string redirectedCout;
std::string redirectedCerr;
do { do {
m_trackerContext.startCycle(); m_trackerContext.startCycle();
m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo));
m_reporter->testCasePartialStarting(testInfo, testRuns); m_reporter->testCasePartialStarting(testInfo, testRuns);
@@ -5201,7 +5196,7 @@ namespace Catch {
redirectedCerr += oneRunCerr; redirectedCerr += oneRunCerr;
const auto singleRunTotals = m_totals.delta(beforeRunTotals); const auto singleRunTotals = m_totals.delta(beforeRunTotals);
auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting()); auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
m_reporter->testCasePartialEnded(statsForOneRun, testRuns); m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
++testRuns; ++testRuns;
@@ -5216,8 +5211,8 @@ namespace Catch {
m_totals.testCases += deltaTotals.testCases; m_totals.testCases += deltaTotals.testCases;
m_reporter->testCaseEnded(TestCaseStats(testInfo, m_reporter->testCaseEnded(TestCaseStats(testInfo,
deltaTotals, deltaTotals,
redirectedCout, CATCH_MOVE(redirectedCout),
redirectedCerr, CATCH_MOVE(redirectedCerr),
aborting())); aborting()));
m_activeTestCase = nullptr; m_activeTestCase = nullptr;
@@ -5261,12 +5256,17 @@ namespace Catch {
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
} }
bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) {
ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); ITracker& sectionTracker =
SectionTracker::acquire( m_trackerContext,
TestCaseTracking::NameAndLocationRef(
sectionName, sectionLineInfo ) );
if (!sectionTracker.isOpen()) if (!sectionTracker.isOpen())
return false; return false;
m_activeSections.push_back(&sectionTracker); m_activeSections.push_back(&sectionTracker);
SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) );
m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
m_reporter->sectionStarting(sectionInfo); m_reporter->sectionStarting(sectionInfo);
@@ -5281,8 +5281,8 @@ namespace Catch {
using namespace Generators; using namespace Generators;
GeneratorTracker* tracker = GeneratorTracker::acquire( GeneratorTracker* tracker = GeneratorTracker::acquire(
m_trackerContext, m_trackerContext,
TestCaseTracking::NameAndLocation( TestCaseTracking::NameAndLocationRef(
static_cast<std::string>( generatorName ), lineInfo ) ); generatorName, lineInfo ) );
m_lastAssertionInfo.lineInfo = lineInfo; m_lastAssertionInfo.lineInfo = lineInfo;
return tracker; return tracker;
} }
@@ -5299,7 +5299,7 @@ namespace Catch {
"Trying to create tracker for a genreator that already has one" ); "Trying to create tracker for a genreator that already has one" );
auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>( auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>(
nameAndLoc, m_trackerContext, &currentTracker ); CATCH_MOVE(nameAndLoc), m_trackerContext, &currentTracker );
auto ret = newTracker.get(); auto ret = newTracker.get();
currentTracker.addChild( CATCH_MOVE( newTracker ) ); currentTracker.addChild( CATCH_MOVE( newTracker ) );
@@ -5320,7 +5320,7 @@ namespace Catch {
return true; return true;
} }
void RunContext::sectionEnded(SectionEndInfo const & endInfo) { void RunContext::sectionEnded(SectionEndInfo&& endInfo) {
Counts assertions = m_totals.assertions - endInfo.prevAssertions; Counts assertions = m_totals.assertions - endInfo.prevAssertions;
bool missingAssertions = testForMissingAssertions(assertions); bool missingAssertions = testForMissingAssertions(assertions);
@@ -5329,19 +5329,20 @@ namespace Catch {
m_activeSections.pop_back(); m_activeSections.pop_back();
} }
m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions));
m_messages.clear(); m_messages.clear();
m_messageScopes.clear(); m_messageScopes.clear();
} }
void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) {
if (m_unfinishedSections.empty()) if ( m_unfinishedSections.empty() ) {
m_activeSections.back()->fail(); m_activeSections.back()->fail();
else } else {
m_activeSections.back()->close(); m_activeSections.back()->close();
}
m_activeSections.pop_back(); m_activeSections.pop_back();
m_unfinishedSections.push_back(endInfo); m_unfinishedSections.push_back(CATCH_MOVE(endInfo));
} }
void RunContext::benchmarkPreparing( StringRef name ) { void RunContext::benchmarkPreparing( StringRef name ) {
@@ -5365,8 +5366,8 @@ namespace Catch {
m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
} }
void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
m_messageScopes.emplace_back( builder ); m_messageScopes.emplace_back( CATCH_MOVE(builder) );
} }
std::string RunContext::getCurrentTestName() const { std::string RunContext::getCurrentTestName() const {
@@ -5391,7 +5392,7 @@ namespace Catch {
// Instead, fake a result data. // Instead, fake a result data.
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
tempResult.message = static_cast<std::string>(message); tempResult.message = static_cast<std::string>(message);
AssertionResult result(m_lastAssertionInfo, tempResult); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
assertionEnded(result); assertionEnded(result);
@@ -5403,7 +5404,7 @@ namespace Catch {
Counts assertions; Counts assertions;
assertions.failed = 1; assertions.failed = 1;
SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false);
m_reporter->sectionEnded(testCaseSectionStats); m_reporter->sectionEnded(testCaseSectionStats);
auto const& testInfo = m_activeTestCase->getTestCaseInfo(); auto const& testInfo = m_activeTestCase->getTestCaseInfo();
@@ -5482,7 +5483,7 @@ namespace Catch {
m_messages.clear(); m_messages.clear();
m_messageScopes.clear(); m_messageScopes.clear();
SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats); m_reporter->sectionEnded(testCaseSectionStats);
} }
@@ -5506,7 +5507,7 @@ namespace Catch {
itEnd = m_unfinishedSections.rend(); itEnd = m_unfinishedSections.rend();
it != itEnd; it != itEnd;
++it) ++it)
sectionEnded(*it); sectionEnded(CATCH_MOVE(*it));
m_unfinishedSections.clear(); m_unfinishedSections.clear();
} }
@@ -5542,7 +5543,7 @@ namespace Catch {
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( negated ) ); AssertionResultData data( resultType, LazyExpression( negated ) );
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
assertionEnded( assertionResult ); assertionEnded( assertionResult );
@@ -5560,7 +5561,8 @@ namespace Catch {
AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResultData data( resultType, LazyExpression( false ) );
data.message = static_cast<std::string>(message); data.message = static_cast<std::string>(message);
AssertionResult assertionResult{ m_lastAssertionInfo, data }; AssertionResult assertionResult{ m_lastAssertionInfo,
CATCH_MOVE( data ) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
if ( !assertionResult.isOk() ) { if ( !assertionResult.isOk() ) {
populateReaction( reaction ); populateReaction( reaction );
@@ -5586,7 +5588,7 @@ namespace Catch {
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = message; data.message = message;
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE(data) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
populateReaction( reaction ); populateReaction( reaction );
} }
@@ -5599,11 +5601,12 @@ namespace Catch {
void RunContext::handleIncomplete( void RunContext::handleIncomplete(
AssertionInfo const& info AssertionInfo const& info
) { ) {
using namespace std::string_literals;
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
} }
void RunContext::handleNonExpr( void RunContext::handleNonExpr(
@@ -5614,7 +5617,7 @@ namespace Catch {
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResultData data( resultType, LazyExpression( false ) );
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
if( !assertionResult.isOk() ) if( !assertionResult.isOk() )
@@ -5646,7 +5649,7 @@ namespace Catch {
Section::Section( SectionInfo&& info ): Section::Section( SectionInfo&& info ):
m_info( CATCH_MOVE( info ) ), m_info( CATCH_MOVE( info ) ),
m_sectionIncluded( m_sectionIncluded(
getResultCapture().sectionStarted( m_info, m_assertions ) ) { getResultCapture().sectionStarted( m_info.name, m_info.lineInfo, m_assertions ) ) {
// Non-"included" sections will not use the timing information // Non-"included" sections will not use the timing information
// anyway, so don't bother with the potential syscall. // anyway, so don't bother with the potential syscall.
if (m_sectionIncluded) { if (m_sectionIncluded) {
@@ -5654,13 +5657,31 @@ namespace Catch {
} }
} }
Section::Section( SourceLineInfo const& _lineInfo,
StringRef _name,
const char* const ):
m_info( { "invalid", static_cast<std::size_t>( -1 ) }, std::string{} ),
m_sectionIncluded(
getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) {
// We delay initialization the SectionInfo member until we know
// this section needs it, so we avoid allocating std::string for name.
// We also delay timer start to avoid the potential syscall unless we
// will actually use the result.
if ( m_sectionIncluded ) {
m_info.name = static_cast<std::string>( _name );
m_info.lineInfo = _lineInfo;
m_timer.start();
}
}
Section::~Section() { Section::~Section() {
if( m_sectionIncluded ) { if( m_sectionIncluded ) {
SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; SectionEndInfo endInfo{ CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds() };
if( uncaught_exceptions() ) if ( uncaught_exceptions() ) {
getResultCapture().sectionEndedEarly( endInfo ); getResultCapture().sectionEndedEarly( CATCH_MOVE(endInfo) );
else } else {
getResultCapture().sectionEnded( endInfo ); getResultCapture().sectionEnded( CATCH_MOVE( endInfo ) );
}
} }
} }
@@ -5876,10 +5897,6 @@ namespace Catch {
: StringRef( rawChars, std::strlen(rawChars) ) : StringRef( rawChars, std::strlen(rawChars) )
{} {}
auto StringRef::operator == ( StringRef other ) const noexcept -> bool {
return m_size == other.m_size
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
}
bool StringRef::operator<(StringRef rhs) const noexcept { bool StringRef::operator<(StringRef rhs) const noexcept {
if (m_size < rhs.m_size) { if (m_size < rhs.m_size) {
@@ -6155,8 +6172,8 @@ namespace Catch {
namespace Catch { namespace Catch {
namespace TestCaseTracking { namespace TestCaseTracking {
NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location )
: name( _name ), : name( CATCH_MOVE(_name) ),
location( _location ) location( _location )
{} {}
@@ -6171,14 +6188,17 @@ namespace TestCaseTracking {
m_children.push_back( CATCH_MOVE(child) ); m_children.push_back( CATCH_MOVE(child) );
} }
ITracker* ITracker::findChild( NameAndLocation const& nameAndLocation ) { ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) {
auto it = std::find_if( auto it = std::find_if(
m_children.begin(), m_children.begin(),
m_children.end(), m_children.end(),
[&nameAndLocation]( ITrackerPtr const& tracker ) { [&nameAndLocation]( ITrackerPtr const& tracker ) {
return tracker->nameAndLocation().location == auto const& tnameAndLoc = tracker->nameAndLocation();
nameAndLocation.location && if ( tnameAndLoc.location.line !=
tracker->nameAndLocation().name == nameAndLocation.name; nameAndLocation.location.line ) {
return false;
}
return tnameAndLoc == nameAndLocation;
} ); } );
return ( it != m_children.end() ) ? it->get() : nullptr; return ( it != m_children.end() ) ? it->get() : nullptr;
} }
@@ -6186,10 +6206,6 @@ namespace TestCaseTracking {
bool ITracker::isSectionTracker() const { return false; } bool ITracker::isSectionTracker() const { return false; }
bool ITracker::isGeneratorTracker() const { return false; } bool ITracker::isGeneratorTracker() const { return false; }
bool ITracker::isSuccessfullyCompleted() const {
return m_runState == CompletedSuccessfully;
}
bool ITracker::isOpen() const { bool ITracker::isOpen() const {
return m_runState != NotStarted && !isComplete(); return m_runState != NotStarted && !isComplete();
} }
@@ -6216,16 +6232,6 @@ namespace TestCaseTracking {
return *m_rootTracker; return *m_rootTracker;
} }
void TrackerContext::endRun() {
m_rootTracker.reset();
m_currentTracker = nullptr;
m_runState = NotStarted;
}
void TrackerContext::startCycle() {
m_currentTracker = m_rootTracker.get();
m_runState = Executing;
}
void TrackerContext::completeCycle() { void TrackerContext::completeCycle() {
m_runState = CompletedCycle; m_runState = CompletedCycle;
} }
@@ -6233,16 +6239,13 @@ namespace TestCaseTracking {
bool TrackerContext::completedCycle() const { bool TrackerContext::completedCycle() const {
return m_runState == CompletedCycle; return m_runState == CompletedCycle;
} }
ITracker& TrackerContext::currentTracker() {
return *m_currentTracker;
}
void TrackerContext::setCurrentTracker( ITracker* tracker ) { void TrackerContext::setCurrentTracker( ITracker* tracker ) {
m_currentTracker = tracker; m_currentTracker = tracker;
} }
TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
ITracker(nameAndLocation, parent), ITracker(CATCH_MOVE(nameAndLocation), parent),
m_ctx( ctx ) m_ctx( ctx )
{} {}
@@ -6302,13 +6305,14 @@ namespace TestCaseTracking {
m_ctx.setCurrentTracker( this ); m_ctx.setCurrentTracker( this );
} }
SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( nameAndLocation, ctx, parent ), : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
m_trimmed_name(trim(nameAndLocation.name)) m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name)))
{ {
if( parent ) { if( parent ) {
while( !parent->isSectionTracker() ) while ( !parent->isSectionTracker() ) {
parent = parent->parent(); parent = parent->parent();
}
SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
addNextFilters( parentSection.m_filters ); addNextFilters( parentSection.m_filters );
@@ -6328,24 +6332,30 @@ namespace TestCaseTracking {
bool SectionTracker::isSectionTracker() const { return true; } bool SectionTracker::isSectionTracker() const { return true; }
SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) {
SectionTracker* section; SectionTracker* tracker;
ITracker& currentTracker = ctx.currentTracker(); ITracker& currentTracker = ctx.currentTracker();
if ( ITracker* childTracker = if ( ITracker* childTracker =
currentTracker.findChild( nameAndLocation ) ) { currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker ); assert( childTracker );
assert( childTracker->isSectionTracker() ); assert( childTracker->isSectionTracker() );
section = static_cast<SectionTracker*>( childTracker ); tracker = static_cast<SectionTracker*>( childTracker );
} else { } else {
auto newSection = Catch::Detail::make_unique<SectionTracker>( auto newTracker = Catch::Detail::make_unique<SectionTracker>(
nameAndLocation, ctx, &currentTracker ); NameAndLocation{ static_cast<std::string>(nameAndLocation.name),
section = newSection.get(); nameAndLocation.location },
currentTracker.addChild( CATCH_MOVE( newSection ) ); ctx,
&currentTracker );
tracker = newTracker.get();
currentTracker.addChild( CATCH_MOVE( newTracker ) );
} }
if( !ctx.completedCycle() )
section->tryOpen(); if ( !ctx.completedCycle() ) {
return *section; tracker->tryOpen();
}
return *tracker;
} }
void SectionTracker::tryOpen() { void SectionTracker::tryOpen() {
@@ -6366,10 +6376,6 @@ namespace TestCaseTracking {
m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
} }
std::vector<StringRef> const& SectionTracker::getFilters() const {
return m_filters;
}
StringRef SectionTracker::trimmedName() const { StringRef SectionTracker::trimmedName() const {
return m_trimmed_name; return m_trimmed_name;
} }
@@ -6668,10 +6674,8 @@ namespace Catch {
token.erase(token.begin()); token.erase(token.begin());
if (m_exclusion) { if (m_exclusion) {
m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring)); m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
} else { } else {
m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring)); m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
} }
} }
if (m_exclusion) { if (m_exclusion) {
@@ -7614,6 +7618,18 @@ WithinRelMatcher WithinRel(float target) {
} }
bool IsNaNMatcher::match( double const& matchee ) const {
return std::isnan( matchee );
}
std::string IsNaNMatcher::describe() const {
using namespace std::string_literals;
return "is NaN"s;
}
IsNaNMatcher IsNaN() { return IsNaNMatcher(); }
} // namespace Matchers } // namespace Matchers
} // namespace Catch } // namespace Catch
@@ -8051,7 +8067,7 @@ private:
private: private:
std::ostream& stream; std::ostream& stream;
AssertionResult const& result; AssertionResult const& result;
std::vector<MessageInfo> messages; std::vector<MessageInfo> const& messages;
std::vector<MessageInfo>::const_iterator itMessage; std::vector<MessageInfo>::const_iterator itMessage;
bool printInfoMessages; bool printInfoMessages;
ColourImpl* colourImpl; ColourImpl* colourImpl;
@@ -8145,7 +8161,6 @@ public:
stats(_stats), stats(_stats),
result(_stats.assertionResult), result(_stats.assertionResult),
colour(Colour::None), colour(Colour::None),
message(result.getMessage()),
messages(_stats.infoMessages), messages(_stats.infoMessages),
colourImpl(colourImpl_), colourImpl(colourImpl_),
printInfoMessages(_printInfoMessages) { printInfoMessages(_printInfoMessages) {
@@ -8154,10 +8169,10 @@ public:
colour = Colour::Success; colour = Colour::Success;
passOrFail = "PASSED"_sr; passOrFail = "PASSED"_sr;
//if( result.hasMessage() ) //if( result.hasMessage() )
if (_stats.infoMessages.size() == 1) if (messages.size() == 1)
messageLabel = "with message"; messageLabel = "with message"_sr;
if (_stats.infoMessages.size() > 1) if (messages.size() > 1)
messageLabel = "with messages"; messageLabel = "with messages"_sr;
break; break;
case ResultWas::ExpressionFailed: case ResultWas::ExpressionFailed:
if (result.isOk()) { if (result.isOk()) {
@@ -8167,51 +8182,57 @@ public:
colour = Colour::Error; colour = Colour::Error;
passOrFail = "FAILED"_sr; passOrFail = "FAILED"_sr;
} }
if (_stats.infoMessages.size() == 1) if (messages.size() == 1)
messageLabel = "with message"; messageLabel = "with message"_sr;
if (_stats.infoMessages.size() > 1) if (messages.size() > 1)
messageLabel = "with messages"; messageLabel = "with messages"_sr;
break; break;
case ResultWas::ThrewException: case ResultWas::ThrewException:
colour = Colour::Error; colour = Colour::Error;
passOrFail = "FAILED"_sr; passOrFail = "FAILED"_sr;
messageLabel = "due to unexpected exception with "; // todo switch
if (_stats.infoMessages.size() == 1) switch (messages.size()) { case 0:
messageLabel += "message"; messageLabel = "due to unexpected exception with "_sr;
if (_stats.infoMessages.size() > 1) break;
messageLabel += "messages"; case 1:
messageLabel = "due to unexpected exception with message"_sr;
break;
default:
messageLabel = "due to unexpected exception with messages"_sr;
break;
}
break; break;
case ResultWas::FatalErrorCondition: case ResultWas::FatalErrorCondition:
colour = Colour::Error; colour = Colour::Error;
passOrFail = "FAILED"_sr; passOrFail = "FAILED"_sr;
messageLabel = "due to a fatal error condition"; messageLabel = "due to a fatal error condition"_sr;
break; break;
case ResultWas::DidntThrowException: case ResultWas::DidntThrowException:
colour = Colour::Error; colour = Colour::Error;
passOrFail = "FAILED"_sr; passOrFail = "FAILED"_sr;
messageLabel = "because no exception was thrown where one was expected"; messageLabel = "because no exception was thrown where one was expected"_sr;
break; break;
case ResultWas::Info: case ResultWas::Info:
messageLabel = "info"; messageLabel = "info"_sr;
break; break;
case ResultWas::Warning: case ResultWas::Warning:
messageLabel = "warning"; messageLabel = "warning"_sr;
break; break;
case ResultWas::ExplicitFailure: case ResultWas::ExplicitFailure:
passOrFail = "FAILED"_sr; passOrFail = "FAILED"_sr;
colour = Colour::Error; colour = Colour::Error;
if (_stats.infoMessages.size() == 1) if (messages.size() == 1)
messageLabel = "explicitly with message"; messageLabel = "explicitly with message"_sr;
if (_stats.infoMessages.size() > 1) if (messages.size() > 1)
messageLabel = "explicitly with messages"; messageLabel = "explicitly with messages"_sr;
break; break;
case ResultWas::ExplicitSkip: case ResultWas::ExplicitSkip:
colour = Colour::Skip; colour = Colour::Skip;
passOrFail = "SKIPPED"_sr; passOrFail = "SKIPPED"_sr;
if (_stats.infoMessages.size() == 1) if (messages.size() == 1)
messageLabel = "explicitly with message"; messageLabel = "explicitly with message"_sr;
if (_stats.infoMessages.size() > 1) if (messages.size() > 1)
messageLabel = "explicitly with messages"; messageLabel = "explicitly with messages"_sr;
break; break;
// These cases are here to prevent compiler warnings // These cases are here to prevent compiler warnings
case ResultWas::Unknown: case ResultWas::Unknown:
@@ -8275,9 +8296,8 @@ private:
AssertionResult const& result; AssertionResult const& result;
Colour::Code colour; Colour::Code colour;
StringRef passOrFail; StringRef passOrFail;
std::string messageLabel; StringRef messageLabel;
std::string message; std::vector<MessageInfo> const& messages;
std::vector<MessageInfo> messages;
ColourImpl* colourImpl; ColourImpl* colourImpl;
bool printInfoMessages; bool printInfoMessages;
}; };
@@ -8809,7 +8829,8 @@ namespace Catch {
void void
CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) { CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) {
SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); // We need a copy, because SectionStats expect to take ownership
SectionStats incompleteStats( SectionInfo(sectionInfo), Counts(), 0, false );
SectionNode* node; SectionNode* node;
if ( m_sectionStack.empty() ) { if ( m_sectionStack.empty() ) {
if ( !m_rootSection ) { if ( !m_rootSection ) {
@@ -9792,7 +9813,7 @@ namespace Catch {
} }
void SonarQubeReporter::writeRun( TestRunNode const& runNode ) { void SonarQubeReporter::writeRun( TestRunNode const& runNode ) {
std::map<std::string, std::vector<TestCaseNode const*>> testsPerFile; std::map<StringRef, std::vector<TestCaseNode const*>> testsPerFile;
for ( auto const& child : runNode.children ) { for ( auto const& child : runNode.children ) {
testsPerFile[child->value.testInfo->lineInfo.file].push_back( testsPerFile[child->value.testInfo->lineInfo.file].push_back(
@@ -9804,7 +9825,7 @@ namespace Catch {
} }
} }
void SonarQubeReporter::writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes) { void SonarQubeReporter::writeTestFile(StringRef filename, std::vector<TestCaseNode const*> const& testCaseNodes) {
XmlWriter::ScopedElement e = xml.scopedElement("file"); XmlWriter::ScopedElement e = xml.scopedElement("file");
xml.writeAttribute("path"_sr, filename); xml.writeAttribute("path"_sr, filename);
@@ -10103,7 +10124,7 @@ namespace Catch {
private: private:
std::ostream& stream; std::ostream& stream;
AssertionResult const& result; AssertionResult const& result;
std::vector<MessageInfo> messages; std::vector<MessageInfo> const& messages;
std::vector<MessageInfo>::const_iterator itMessage; std::vector<MessageInfo>::const_iterator itMessage;
bool printInfoMessages; bool printInfoMessages;
std::size_t counter; std::size_t counter;
@@ -10366,7 +10387,7 @@ namespace Catch {
void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
StreamingReporterBase::testCaseStarting(testInfo); StreamingReporterBase::testCaseStarting(testInfo);
m_xml.startElement( "TestCase" ) m_xml.startElement( "TestCase" )
.writeAttribute( "name"_sr, trim( testInfo.name ) ) .writeAttribute( "name"_sr, trim( StringRef(testInfo.name) ) )
.writeAttribute( "tags"_sr, testInfo.tagsAsString() ); .writeAttribute( "tags"_sr, testInfo.tagsAsString() );
writeSourceInfo( testInfo.lineInfo ); writeSourceInfo( testInfo.lineInfo );
@@ -10380,7 +10401,7 @@ namespace Catch {
StreamingReporterBase::sectionStarting( sectionInfo ); StreamingReporterBase::sectionStarting( sectionInfo );
if( m_sectionDepth++ > 0 ) { if( m_sectionDepth++ > 0 ) {
m_xml.startElement( "Section" ) m_xml.startElement( "Section" )
.writeAttribute( "name"_sr, trim( sectionInfo.name ) ); .writeAttribute( "name"_sr, trim( StringRef(sectionInfo.name) ) );
writeSourceInfo( sectionInfo.lineInfo ); writeSourceInfo( sectionInfo.lineInfo );
m_xml.ensureTagClosed(); m_xml.ensureTagClosed();
} }
@@ -10494,11 +10515,10 @@ namespace Catch {
if ( m_config->showDurations() == ShowDurations::Always ) if ( m_config->showDurations() == ShowDurations::Always )
e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() ); e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() );
if( !testCaseStats.stdOut.empty() ) if( !testCaseStats.stdOut.empty() )
m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); m_xml.scopedElement( "StdOut" ).writeText( trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline );
if( !testCaseStats.stdErr.empty() ) if( !testCaseStats.stdErr.empty() )
m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); m_xml.scopedElement( "StdErr" ).writeText( trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline );
m_xml.endElement(); m_xml.endElement();
} }

View File

@@ -5,8 +5,8 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
// Catch v3.3.0 // Catch v3.3.2
// Generated: 2023-01-22 19:46:23.163056 // Generated: 2023-02-26 10:28:46.785908
// ---------------------------------------------------------- // ----------------------------------------------------------
// This file is an amalgamation of multiple different files. // This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly. // You probably shouldn't edit it directly.
@@ -95,6 +95,8 @@ namespace Catch {
#include <iosfwd> #include <iosfwd>
#include <cassert> #include <cassert>
#include <cstring>
namespace Catch { namespace Catch {
/// A non-owning string class (similar to the forthcoming std::string_view) /// A non-owning string class (similar to the forthcoming std::string_view)
@@ -131,7 +133,10 @@ namespace Catch {
} }
public: // operators public: // operators
auto operator == ( StringRef other ) const noexcept -> bool; auto operator == ( StringRef other ) const noexcept -> bool {
return m_size == other.m_size
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
}
auto operator != (StringRef other) const noexcept -> bool { auto operator != (StringRef other) const noexcept -> bool {
return !(*this == other); return !(*this == other);
} }
@@ -352,7 +357,7 @@ namespace Catch {
// Only GCC compiler should be used in this block, so other compilers trying to // Only GCC compiler should be used in this block, so other compilers trying to
// mask themselves as GCC should be ignored. // mask themselves as GCC should be ignored.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
@@ -371,6 +376,12 @@ namespace Catch {
#endif #endif
#if defined(__NVCOMPILER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" )
#endif
#if defined(__CUDACC__) && !defined(__clang__) #if defined(__CUDACC__) && !defined(__clang__)
# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ # ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
// New pragmas introduced in CUDA 11.5+ // New pragmas introduced in CUDA 11.5+
@@ -1039,7 +1050,7 @@ namespace Catch {
class AssertionResult { class AssertionResult {
public: public:
AssertionResult() = delete; AssertionResult() = delete;
AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); AssertionResult( AssertionInfo const& info, AssertionResultData&& data );
bool isOk() const; bool isOk() const;
bool succeeded() const; bool succeeded() const;
@@ -1217,10 +1228,11 @@ namespace Catch {
public: public:
virtual ~IResultCapture(); virtual ~IResultCapture();
virtual bool sectionStarted( SectionInfo const& sectionInfo, virtual bool sectionStarted( StringRef sectionName,
SourceLineInfo const& sectionLineInfo,
Counts& assertions ) = 0; Counts& assertions ) = 0;
virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0;
virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0;
virtual IGeneratorTracker* virtual IGeneratorTracker*
acquireGeneratorTracker( StringRef generatorName, acquireGeneratorTracker( StringRef generatorName,
@@ -1238,7 +1250,7 @@ namespace Catch {
virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0;
virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual void handleFatalErrorCondition( StringRef message ) = 0;
@@ -1419,7 +1431,7 @@ namespace Catch {
}; };
struct SectionStats { struct SectionStats {
SectionStats( SectionInfo const& _sectionInfo, SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions, Counts const& _assertions,
double _durationInSeconds, double _durationInSeconds,
bool _missingAssertions ); bool _missingAssertions );
@@ -1433,8 +1445,8 @@ namespace Catch {
struct TestCaseStats { struct TestCaseStats {
TestCaseStats( TestCaseInfo const& _testInfo, TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals, Totals const& _totals,
std::string const& _stdOut, std::string&& _stdOut,
std::string const& _stdErr, std::string&& _stdErr,
bool _aborting ); bool _aborting );
TestCaseInfo const * testInfo; TestCaseInfo const * testInfo;
@@ -2691,7 +2703,7 @@ namespace Catch {
}); });
BenchmarkInfo info { BenchmarkInfo info {
name, CATCH_MOVE(name),
plan.estimated_duration.count(), plan.estimated_duration.count(),
plan.iterations_per_sample, plan.iterations_per_sample,
cfg->benchmarkSamples(), cfg->benchmarkSamples(),
@@ -2707,7 +2719,7 @@ namespace Catch {
}); });
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats); getResultCapture().benchmarkEnded(stats);
} CATCH_CATCH_ANON (TestFailureException) { } CATCH_CATCH_ANON (TestFailureException) {
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
@@ -4459,11 +4471,10 @@ namespace Catch {
ResultWas::OfType type ): ResultWas::OfType type ):
m_info(macroName, lineInfo, type) {} m_info(macroName, lineInfo, type) {}
template<typename T> template<typename T>
MessageBuilder& operator << ( T const& value ) { MessageBuilder&& operator << ( T const& value ) && {
m_stream << value; m_stream << value;
return *this; return CATCH_MOVE(*this);
} }
MessageInfo m_info; MessageInfo m_info;
@@ -4471,7 +4482,7 @@ namespace Catch {
class ScopedMessage { class ScopedMessage {
public: public:
explicit ScopedMessage( MessageBuilder const& builder ); explicit ScopedMessage( MessageBuilder&& builder );
ScopedMessage( ScopedMessage& duplicate ) = delete; ScopedMessage( ScopedMessage& duplicate ) = delete;
ScopedMessage( ScopedMessage&& old ) noexcept; ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage(); ~ScopedMessage();
@@ -4518,7 +4529,10 @@ namespace Catch {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ #define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ Catch::Capturer varName( macroName##_catch_sr, \
CATCH_INTERNAL_LINEINFO, \
Catch::ResultWas::Info, \
#__VA_ARGS__##_catch_sr ); \
varName.captureValues( 0, __VA_ARGS__ ) varName.captureValues( 0, __VA_ARGS__ )
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -5904,9 +5918,9 @@ namespace Catch {
#if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE)
#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr
#else #else
#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr
#endif #endif
#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
@@ -6071,6 +6085,9 @@ namespace Catch {
class Section : Detail::NonCopyable { class Section : Detail::NonCopyable {
public: public:
Section( SectionInfo&& info ); Section( SectionInfo&& info );
Section( SourceLineInfo const& _lineInfo,
StringRef _name,
const char* const = nullptr );
~Section(); ~Section();
// This indicates whether the section should be executed or not // This indicates whether the section should be executed or not
@@ -6089,7 +6106,7 @@ namespace Catch {
#define INTERNAL_CATCH_SECTION( ... ) \ #define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ #define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
@@ -6230,7 +6247,13 @@ struct AutoReg : Detail::NonCopyable {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ 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 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -6242,7 +6265,11 @@ struct AutoReg : Detail::NonCopyable {
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
void test(); \ void test(); \
}; \ }; \
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
Catch::makeTestInvoker( &TestName::test ), \
CATCH_INTERNAL_LINEINFO, \
#ClassName##_catch_sr, \
Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
} \ } \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
void TestName::test() void TestName::test()
@@ -6875,7 +6902,7 @@ struct AutoReg : Detail::NonCopyable {
void reg_tests() { \ void reg_tests() { \
size_t index = 0; \ size_t index = 0; \
using expander = size_t[]; \ using expander = size_t[]; \
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
} \ } \
};\ };\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
@@ -7010,7 +7037,7 @@ struct AutoReg : Detail::NonCopyable {
void reg_tests(){\ void reg_tests(){\
size_t index = 0;\ size_t index = 0;\
using expander = size_t[];\ using expander = size_t[];\
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
}\ }\
};\ };\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
@@ -7399,7 +7426,7 @@ namespace Catch {
#define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 3 #define CATCH_VERSION_MINOR 3
#define CATCH_VERSION_PATCH 0 #define CATCH_VERSION_PATCH 2
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED #endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -7751,16 +7778,19 @@ namespace Detail {
} // namespace Generators } // namespace Generators
} // namespace Catch } // namespace Catch
#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr
#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__)
#define GENERATE( ... ) \ #define GENERATE( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \ CATCH_INTERNAL_LINEINFO, \
[ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_COPY( ... ) \ #define GENERATE_COPY( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \ CATCH_INTERNAL_LINEINFO, \
[=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_REF( ... ) \ #define GENERATE_REF( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \ CATCH_INTERNAL_LINEINFO, \
[&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
@@ -8916,6 +8946,139 @@ namespace Detail {
#endif // CATCH_GETENV_HPP_INCLUDED #endif // CATCH_GETENV_HPP_INCLUDED
#ifndef CATCH_IS_PERMUTATION_HPP_INCLUDED
#define CATCH_IS_PERMUTATION_HPP_INCLUDED
#include <algorithm>
#include <iterator>
namespace Catch {
namespace Detail {
template <typename ForwardIter,
typename Sentinel,
typename T,
typename Comparator>
ForwardIter find_sentinel( ForwardIter start,
Sentinel sentinel,
T const& value,
Comparator cmp ) {
while ( start != sentinel ) {
if ( cmp( *start, value ) ) { break; }
++start;
}
return start;
}
template <typename ForwardIter,
typename Sentinel,
typename T,
typename Comparator>
std::ptrdiff_t count_sentinel( ForwardIter start,
Sentinel sentinel,
T const& value,
Comparator cmp ) {
std::ptrdiff_t count = 0;
while ( start != sentinel ) {
if ( cmp( *start, value ) ) { ++count; }
++start;
}
return count;
}
template <typename ForwardIter, typename Sentinel>
std::enable_if_t<!std::is_same<ForwardIter, Sentinel>::value,
std::ptrdiff_t>
sentinel_distance( ForwardIter iter, const Sentinel sentinel ) {
std::ptrdiff_t dist = 0;
while ( iter != sentinel ) {
++iter;
++dist;
}
return dist;
}
template <typename ForwardIter>
std::ptrdiff_t sentinel_distance( ForwardIter first,
ForwardIter last ) {
return std::distance( first, last );
}
template <typename ForwardIter1,
typename Sentinel1,
typename ForwardIter2,
typename Sentinel2,
typename Comparator>
bool check_element_counts( ForwardIter1 first_1,
const Sentinel1 end_1,
ForwardIter2 first_2,
const Sentinel2 end_2,
Comparator cmp ) {
auto cursor = first_1;
while ( cursor != end_1 ) {
if ( find_sentinel( first_1, cursor, *cursor, cmp ) ==
cursor ) {
// we haven't checked this element yet
const auto count_in_range_2 =
count_sentinel( first_2, end_2, *cursor, cmp );
// Not a single instance in 2nd range, so it cannot be a
// permutation of 1st range
if ( count_in_range_2 == 0 ) { return false; }
const auto count_in_range_1 =
count_sentinel( cursor, end_1, *cursor, cmp );
if ( count_in_range_1 != count_in_range_2 ) {
return false;
}
}
++cursor;
}
return true;
}
template <typename ForwardIter1,
typename Sentinel1,
typename ForwardIter2,
typename Sentinel2,
typename Comparator>
bool is_permutation( ForwardIter1 first_1,
const Sentinel1 end_1,
ForwardIter2 first_2,
const Sentinel2 end_2,
Comparator cmp ) {
// TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types
// TODO: Comparator has to be "both sides", e.g. a == b => b == a
// This skips shared prefix of the two ranges
while (first_1 != end_1 && first_2 != end_2 && cmp(*first_1, *first_2)) {
++first_1;
++first_2;
}
// We need to handle case where at least one of the ranges has no more elements
if (first_1 == end_1 || first_2 == end_2) {
return first_1 == end_1 && first_2 == end_2;
}
// pair counting is n**2, so we pay linear walk to compare the sizes first
auto dist_1 = sentinel_distance( first_1, end_1 );
auto dist_2 = sentinel_distance( first_2, end_2 );
if (dist_1 != dist_2) { return false; }
// Since we do not try to handle stronger iterators pair (e.g.
// bidir) optimally, the only thing left to do is to check counts in
// the remaining ranges.
return check_element_counts( first_1, end_1, first_2, end_2, cmp );
}
} // namespace Detail
} // namespace Catch
#endif // CATCH_IS_PERMUTATION_HPP_INCLUDED
#ifndef CATCH_ISTREAM_HPP_INCLUDED #ifndef CATCH_ISTREAM_HPP_INCLUDED
#define CATCH_ISTREAM_HPP_INCLUDED #define CATCH_ISTREAM_HPP_INCLUDED
@@ -9194,10 +9357,14 @@ namespace TestCaseTracking {
std::string name; std::string name;
SourceLineInfo location; SourceLineInfo location;
NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); NameAndLocation( std::string&& _name, SourceLineInfo const& _location );
friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
return lhs.name == rhs.name // This is a very cheap check that should have a very high hit rate.
&& lhs.location == rhs.location; // If we get to SourceLineInfo::operator==, we will redo it, but the
// cost of repeating is trivial at that point (we will be paying
// multiple strcmp/memcmps at that point).
if ( lhs.location.line != rhs.location.line ) { return false; }
return lhs.name == rhs.name && lhs.location == rhs.location;
} }
friend bool operator!=(NameAndLocation const& lhs, friend bool operator!=(NameAndLocation const& lhs,
NameAndLocation const& rhs) { NameAndLocation const& rhs) {
@@ -9205,6 +9372,37 @@ namespace TestCaseTracking {
} }
}; };
/**
* This is a variant of `NameAndLocation` that does not own the name string
*
* This avoids extra allocations when trying to locate a tracker by its
* name and location, as long as we make sure that trackers only keep
* around the owning variant.
*/
struct NameAndLocationRef {
StringRef name;
SourceLineInfo location;
constexpr NameAndLocationRef( StringRef name_,
SourceLineInfo location_ ):
name( name_ ), location( location_ ) {}
friend bool operator==( NameAndLocation const& lhs,
NameAndLocationRef const& rhs ) {
// This is a very cheap check that should have a very high hit rate.
// If we get to SourceLineInfo::operator==, we will redo it, but the
// cost of repeating is trivial at that point (we will be paying
// multiple strcmp/memcmps at that point).
if ( lhs.location.line != rhs.location.line ) { return false; }
return StringRef( lhs.name ) == rhs.name &&
lhs.location == rhs.location;
}
friend bool operator==( NameAndLocationRef const& lhs,
NameAndLocation const& rhs ) {
return rhs == lhs;
}
};
class ITracker; class ITracker;
using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>; using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>;
@@ -9229,8 +9427,8 @@ namespace TestCaseTracking {
CycleState m_runState = NotStarted; CycleState m_runState = NotStarted;
public: public:
ITracker( NameAndLocation const& nameAndLoc, ITracker* parent ): ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ):
m_nameAndLocation( nameAndLoc ), m_nameAndLocation( CATCH_MOVE(nameAndLoc) ),
m_parent( parent ) m_parent( parent )
{} {}
@@ -9251,7 +9449,9 @@ namespace TestCaseTracking {
//! Returns true if tracker run to completion (successfully or not) //! Returns true if tracker run to completion (successfully or not)
virtual bool isComplete() const = 0; virtual bool isComplete() const = 0;
//! Returns true if tracker run to completion succesfully //! Returns true if tracker run to completion succesfully
bool isSuccessfullyCompleted() const; bool isSuccessfullyCompleted() const {
return m_runState == CompletedSuccessfully;
}
//! Returns true if tracker has started but hasn't been completed //! Returns true if tracker has started but hasn't been completed
bool isOpen() const; bool isOpen() const;
//! Returns true iff tracker has started //! Returns true iff tracker has started
@@ -9269,7 +9469,7 @@ namespace TestCaseTracking {
* *
* Returns nullptr if not found. * Returns nullptr if not found.
*/ */
ITracker* findChild( NameAndLocation const& nameAndLocation ); ITracker* findChild( NameAndLocationRef const& nameAndLocation );
//! Have any children been added? //! Have any children been added?
bool hasChildren() const { bool hasChildren() const {
return !m_children.empty(); return !m_children.empty();
@@ -9310,13 +9510,15 @@ namespace TestCaseTracking {
public: public:
ITracker& startRun(); ITracker& startRun();
void endRun();
void startCycle(); void startCycle() {
m_currentTracker = m_rootTracker.get();
m_runState = Executing;
}
void completeCycle(); void completeCycle();
bool completedCycle() const; bool completedCycle() const;
ITracker& currentTracker(); ITracker& currentTracker() { return *m_currentTracker; }
void setCurrentTracker( ITracker* tracker ); void setCurrentTracker( ITracker* tracker );
}; };
@@ -9326,7 +9528,7 @@ namespace TestCaseTracking {
TrackerContext& m_ctx; TrackerContext& m_ctx;
public: public:
TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isComplete() const override; bool isComplete() const override;
@@ -9342,22 +9544,26 @@ namespace TestCaseTracking {
class SectionTracker : public TrackerBase { class SectionTracker : public TrackerBase {
std::vector<StringRef> m_filters; std::vector<StringRef> m_filters;
std::string m_trimmed_name; // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`.
// Currently it allocates owns the name, so this is safe. If it is later refactored
// to not own the name, the name still has to outlive the `ITracker` parent, so
// this should still be safe.
StringRef m_trimmed_name;
public: public:
SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isSectionTracker() const override; bool isSectionTracker() const override;
bool isComplete() const override; bool isComplete() const override;
static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation );
void tryOpen(); void tryOpen();
void addInitialFilters( std::vector<std::string> const& filters ); void addInitialFilters( std::vector<std::string> const& filters );
void addNextFilters( std::vector<StringRef> const& filters ); void addNextFilters( std::vector<StringRef> const& filters );
//! Returns filters active in this tracker //! Returns filters active in this tracker
std::vector<StringRef> const& getFilters() const; std::vector<StringRef> const& getFilters() const { return m_filters; }
//! Returns whitespace-trimmed name of the tracked section //! Returns whitespace-trimmed name of the tracked section
StringRef trimmedName() const; StringRef trimmedName() const;
}; };
@@ -9420,10 +9626,12 @@ namespace Catch {
ResultWas::OfType resultType, ResultWas::OfType resultType,
AssertionReaction &reaction ) override; AssertionReaction &reaction ) override;
bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; bool sectionStarted( StringRef sectionName,
SourceLineInfo const& sectionLineInfo,
Counts& assertions ) override;
void sectionEnded( SectionEndInfo const& endInfo ) override; void sectionEnded( SectionEndInfo&& endInfo ) override;
void sectionEndedEarly( SectionEndInfo const& endInfo ) override; void sectionEndedEarly( SectionEndInfo&& endInfo ) override;
IGeneratorTracker* IGeneratorTracker*
acquireGeneratorTracker( StringRef generatorName, acquireGeneratorTracker( StringRef generatorName,
@@ -9442,7 +9650,7 @@ namespace Catch {
void pushScopedMessage( MessageInfo const& message ) override; void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override; void popScopedMessage( MessageInfo const& message ) override;
void emplaceUnscopedMessage( MessageBuilder const& builder ) override; void emplaceUnscopedMessage( MessageBuilder&& builder ) override;
std::string getCurrentTestName() const override; std::string getCurrentTestName() const override;
@@ -10930,12 +11138,10 @@ namespace Catch {
template <typename RangeLike> template <typename RangeLike>
bool match( RangeLike&& rng ) const { bool match( RangeLike&& rng ) const {
using std::begin; using std::end; for ( auto&& elem : rng ) {
if ( m_eq( elem, m_desired ) ) { return true; }
return end(rng) != std::find_if(begin(rng), end(rng), }
[&](auto const& elem) { return false;
return m_eq(elem, m_desired);
});
} }
}; };
@@ -10987,7 +11193,7 @@ namespace Catch {
/** /**
* Creates a matcher that checks whether a range contains a specific element. * Creates a matcher that checks whether a range contains a specific element.
* *
* Uses `eq` to do the comparisons * Uses `eq` to do the comparisons, the element is provided on the rhs
*/ */
template <typename T, typename Equality> template <typename T, typename Equality>
ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) { ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
@@ -11076,6 +11282,11 @@ namespace Matchers {
double m_margin; double m_margin;
}; };
//! Creates a matcher that accepts numbers within certain range of target
WithinAbsMatcher WithinAbs( double target, double margin );
class WithinUlpsMatcher final : public MatcherBase<double> { class WithinUlpsMatcher final : public MatcherBase<double> {
public: public:
WithinUlpsMatcher( double target, WithinUlpsMatcher( double target,
@@ -11089,6 +11300,13 @@ namespace Matchers {
Detail::FloatingPointKind m_type; Detail::FloatingPointKind m_type;
}; };
//! Creates a matcher that accepts doubles within certain ULP range of target
WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts floats within certain ULP range of target
WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
// Given IEEE-754 format for floats and doubles, we can assume // Given IEEE-754 format for floats and doubles, we can assume
// that float -> double promotion is lossless. Given this, we can // that float -> double promotion is lossless. Given this, we can
// assume that if we do the standard relative comparison of // assume that if we do the standard relative comparison of
@@ -11105,13 +11323,6 @@ namespace Matchers {
double m_epsilon; double m_epsilon;
}; };
//! Creates a matcher that accepts doubles within certain ULP range of target
WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts floats within certain ULP range of target
WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts numbers within certain range of target
WithinAbsMatcher WithinAbs(double target, double margin);
//! Creates a matcher that accepts doubles within certain relative range of target //! Creates a matcher that accepts doubles within certain relative range of target
WithinRelMatcher WithinRel(double target, double eps); WithinRelMatcher WithinRel(double target, double eps);
//! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target //! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target
@@ -11121,6 +11332,17 @@ namespace Matchers {
//! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target //! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target
WithinRelMatcher WithinRel(float target); WithinRelMatcher WithinRel(float target);
class IsNaNMatcher final : public MatcherBase<double> {
public:
IsNaNMatcher() = default;
bool match( double const& matchee ) const override;
std::string describe() const override;
};
IsNaNMatcher IsNaN();
} // namespace Matchers } // namespace Matchers
} // namespace Catch } // namespace Catch
@@ -11339,6 +11561,7 @@ namespace Catch {
#ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED #ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
#define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED #define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
@@ -11363,13 +11586,19 @@ namespace Catch {
template <typename RangeLike> template <typename RangeLike>
bool match( RangeLike&& rng ) const { bool match( RangeLike&& rng ) const {
using std::begin; auto rng_start = begin( rng );
using std::end; const auto rng_end = end( rng );
return std::equal( begin(m_desired), auto target_start = begin( m_desired );
end(m_desired), const auto target_end = end( m_desired );
begin(rng),
end(rng), while (rng_start != rng_end && target_start != target_end) {
m_predicate ); if (!m_predicate(*rng_start, *target_start)) {
return false;
}
++rng_start;
++target_start;
}
return rng_start == rng_end && target_start == target_end;
} }
std::string describe() const override { std::string describe() const override {
@@ -11397,7 +11626,7 @@ namespace Catch {
bool match( RangeLike&& rng ) const { bool match( RangeLike&& rng ) const {
using std::begin; using std::begin;
using std::end; using std::end;
return std::is_permutation( begin( m_desired ), return Catch::Detail::is_permutation( begin( m_desired ),
end( m_desired ), end( m_desired ),
begin( rng ), begin( rng ),
end( rng ), end( rng ),
@@ -12518,7 +12747,7 @@ namespace Catch {
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace { \ namespace { \
Catch::ListenerRegistrar<listenerType> INTERNAL_CATCH_UNIQUE_NAME( \ Catch::ListenerRegistrar<listenerType> INTERNAL_CATCH_UNIQUE_NAME( \
catch_internal_RegistrarFor )( #listenerType ); \ catch_internal_RegistrarFor )( #listenerType##_catch_sr ); \
} \ } \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
@@ -12565,7 +12794,7 @@ namespace Catch {
void writeRun( TestRunNode const& groupNode ); void writeRun( TestRunNode const& groupNode );
void writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes); void writeTestFile(StringRef filename, std::vector<TestCaseNode const*> const& testCaseNodes);
void writeTestCase(TestCaseNode const& testCaseNode); void writeTestCase(TestCaseNode const& testCaseNode);

View File

@@ -8,7 +8,7 @@
project( project(
'catch2', 'catch2',
'cpp', 'cpp',
version: '3.3.0', # CML version placeholder, don't delete version: '3.3.2', # CML version placeholder, don't delete
license: 'BSL-1.0', license: 'BSL-1.0',
meson_version: '>=0.50.0', meson_version: '>=0.50.0',
) )

View File

@@ -55,6 +55,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/catch_template_test_macros.hpp ${SOURCES_DIR}/catch_template_test_macros.hpp
${SOURCES_DIR}/catch_test_case_info.hpp ${SOURCES_DIR}/catch_test_case_info.hpp
${SOURCES_DIR}/catch_test_macros.hpp ${SOURCES_DIR}/catch_test_macros.hpp
${SOURCES_DIR}/catch_test_run_info.hpp
${SOURCES_DIR}/catch_test_spec.hpp ${SOURCES_DIR}/catch_test_spec.hpp
${SOURCES_DIR}/catch_timer.hpp ${SOURCES_DIR}/catch_timer.hpp
${SOURCES_DIR}/catch_tostring.hpp ${SOURCES_DIR}/catch_tostring.hpp
@@ -63,6 +64,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/catch_version.hpp ${SOURCES_DIR}/catch_version.hpp
${SOURCES_DIR}/catch_version_macros.hpp ${SOURCES_DIR}/catch_version_macros.hpp
${SOURCES_DIR}/internal/catch_assertion_handler.hpp ${SOURCES_DIR}/internal/catch_assertion_handler.hpp
${SOURCES_DIR}/internal/catch_benchmark_stats_fwd.hpp
${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.hpp ${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.hpp
${SOURCES_DIR}/internal/catch_case_sensitive.hpp ${SOURCES_DIR}/internal/catch_case_sensitive.hpp
${SOURCES_DIR}/internal/catch_clara.hpp ${SOURCES_DIR}/internal/catch_clara.hpp
@@ -88,6 +90,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_floating_point_helpers.hpp ${SOURCES_DIR}/internal/catch_floating_point_helpers.hpp
${SOURCES_DIR}/internal/catch_getenv.hpp ${SOURCES_DIR}/internal/catch_getenv.hpp
${SOURCES_DIR}/internal/catch_istream.hpp ${SOURCES_DIR}/internal/catch_istream.hpp
${SOURCES_DIR}/internal/catch_is_permutation.hpp
${SOURCES_DIR}/internal/catch_lazy_expr.hpp ${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp ${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_list.hpp ${SOURCES_DIR}/internal/catch_list.hpp
@@ -105,6 +108,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_preprocessor_remove_parens.hpp ${SOURCES_DIR}/internal/catch_preprocessor_remove_parens.hpp
${SOURCES_DIR}/internal/catch_random_number_generator.hpp ${SOURCES_DIR}/internal/catch_random_number_generator.hpp
${SOURCES_DIR}/internal/catch_random_seed_generation.hpp ${SOURCES_DIR}/internal/catch_random_seed_generation.hpp
${SOURCES_DIR}/internal/catch_registry_hub.hpp
${SOURCES_DIR}/internal/catch_reporter_registry.hpp ${SOURCES_DIR}/internal/catch_reporter_registry.hpp
${SOURCES_DIR}/internal/catch_reporter_spec_parser.hpp ${SOURCES_DIR}/internal/catch_reporter_spec_parser.hpp
${SOURCES_DIR}/internal/catch_result_type.hpp ${SOURCES_DIR}/internal/catch_result_type.hpp
@@ -144,7 +148,6 @@ set(IMPL_SOURCES
${SOURCES_DIR}/catch_config.cpp ${SOURCES_DIR}/catch_config.cpp
${SOURCES_DIR}/catch_get_random_seed.cpp ${SOURCES_DIR}/catch_get_random_seed.cpp
${SOURCES_DIR}/catch_message.cpp ${SOURCES_DIR}/catch_message.cpp
${SOURCES_DIR}/catch_registry_hub.cpp
${SOURCES_DIR}/catch_session.cpp ${SOURCES_DIR}/catch_session.cpp
${SOURCES_DIR}/catch_tag_alias_autoregistrar.cpp ${SOURCES_DIR}/catch_tag_alias_autoregistrar.cpp
${SOURCES_DIR}/catch_test_case_info.cpp ${SOURCES_DIR}/catch_test_case_info.cpp
@@ -152,6 +155,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/catch_timer.cpp ${SOURCES_DIR}/catch_timer.cpp
${SOURCES_DIR}/catch_tostring.cpp ${SOURCES_DIR}/catch_tostring.cpp
${SOURCES_DIR}/catch_totals.cpp ${SOURCES_DIR}/catch_totals.cpp
${SOURCES_DIR}/catch_translate_exception.cpp
${SOURCES_DIR}/catch_version.cpp ${SOURCES_DIR}/catch_version.cpp
${SOURCES_DIR}/internal/catch_assertion_handler.cpp ${SOURCES_DIR}/internal/catch_assertion_handler.cpp
${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.cpp ${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.cpp
@@ -179,6 +183,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_polyfills.cpp ${SOURCES_DIR}/internal/catch_polyfills.cpp
${SOURCES_DIR}/internal/catch_random_number_generator.cpp ${SOURCES_DIR}/internal/catch_random_number_generator.cpp
${SOURCES_DIR}/internal/catch_random_seed_generation.cpp ${SOURCES_DIR}/internal/catch_random_seed_generation.cpp
${SOURCES_DIR}/internal/catch_registry_hub.cpp
${SOURCES_DIR}/internal/catch_reporter_registry.cpp ${SOURCES_DIR}/internal/catch_reporter_registry.cpp
${SOURCES_DIR}/internal/catch_reporter_spec_parser.cpp ${SOURCES_DIR}/internal/catch_reporter_spec_parser.cpp
${SOURCES_DIR}/internal/catch_result_type.cpp ${SOURCES_DIR}/internal/catch_result_type.cpp
@@ -207,27 +212,19 @@ set(INTERNAL_FILES ${IMPL_SOURCES} ${IMPL_HEADERS})
set(INTERFACE_HEADERS set(INTERFACE_HEADERS
${SOURCES_DIR}/interfaces/catch_interfaces_all.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_all.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_capture.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_config.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_config.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_enum_values_registry.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_exception.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_exception.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.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_testcase.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_testcase.hpp
) )
set(INTERFACE_SOURCES set(INTERFACE_SOURCES
${SOURCES_DIR}/interfaces/catch_interfaces_capture.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_config.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_config.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_exception.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_exception.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_testcase.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_testcase.cpp
) )
set(INTERFACE_FILES ${INTERFACE_HEADERS} ${INTERFACE_SOURCES}) set(INTERFACE_FILES ${INTERFACE_HEADERS} ${INTERFACE_SOURCES})

View File

@@ -11,9 +11,11 @@
#define CATCH_BENCHMARK_HPP_INCLUDED #define CATCH_BENCHMARK_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_context.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp> #include <catch2/internal/catch_run_context.hpp>
#include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/internal/catch_unique_name.hpp> #include <catch2/internal/catch_unique_name.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/benchmark/catch_chronometer.hpp> #include <catch2/benchmark/catch_chronometer.hpp>
@@ -26,6 +28,7 @@
#include <catch2/benchmark/detail/catch_benchmark_function.hpp> #include <catch2/benchmark/detail/catch_benchmark_function.hpp>
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp> #include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <string> #include <string>
@@ -64,7 +67,7 @@ namespace Catch {
}); });
BenchmarkInfo info { BenchmarkInfo info {
name, CATCH_MOVE(name),
plan.estimated_duration.count(), plan.estimated_duration.count(),
plan.iterations_per_sample, plan.iterations_per_sample,
cfg->benchmarkSamples(), cfg->benchmarkSamples(),
@@ -80,7 +83,7 @@ namespace Catch {
}); });
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats); getResultCapture().benchmarkEnded(stats);
} CATCH_CATCH_ANON (TestFailureException) { } CATCH_CATCH_ANON (TestFailureException) {
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);

View File

@@ -10,7 +10,7 @@
#ifndef CATCH_OPTIMIZER_HPP_INCLUDED #ifndef CATCH_OPTIMIZER_HPP_INCLUDED
#define 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 # include <atomic> // atomic_thread_fence
#endif #endif
@@ -32,16 +32,23 @@ namespace Catch {
namespace Detail { namespace Detail {
inline void optimizer_barrier() { keep_memory(); } inline void optimizer_barrier() { keep_memory(); }
} // namespace Detail } // namespace Detail
#elif defined(_MSC_VER) #elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
#if defined(_MSVC_VER)
#pragma optimize("", off) #pragma optimize("", off)
#elif defined(__IAR_SYSTEMS_ICC__)
// For IAR the pragma only affects the following function
#pragma optimize=disable
#endif
template <typename T> template <typename T>
inline void keep_memory(T* p) { inline void keep_memory(T* p) {
// thanks @milleniumbug // thanks @milleniumbug
*reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p); *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
} }
// TODO equivalent keep_memory() // TODO equivalent keep_memory()
#if defined(_MSVC_VER)
#pragma optimize("", on) #pragma optimize("", on)
#endif
namespace Detail { namespace Detail {
inline void optimizer_barrier() { inline void optimizer_barrier() {

View File

@@ -12,8 +12,6 @@
#include <catch2/internal/catch_test_failure_exception.hpp> #include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_meta.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 <catch2/internal/catch_move_and_forward.hpp>
#include <type_traits> #include <type_traits>

View File

@@ -36,6 +36,7 @@
#include <catch2/catch_template_test_macros.hpp> #include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_case_info.hpp> #include <catch2/catch_test_case_info.hpp>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <catch2/catch_test_run_info.hpp>
#include <catch2/catch_test_spec.hpp> #include <catch2/catch_test_spec.hpp>
#include <catch2/catch_timer.hpp> #include <catch2/catch_timer.hpp>
#include <catch2/catch_tostring.hpp> #include <catch2/catch_tostring.hpp>
@@ -46,6 +47,7 @@
#include <catch2/generators/catch_generators_all.hpp> #include <catch2/generators/catch_generators_all.hpp>
#include <catch2/interfaces/catch_interfaces_all.hpp> #include <catch2/interfaces/catch_interfaces_all.hpp>
#include <catch2/internal/catch_assertion_handler.hpp> #include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/internal/catch_benchmark_stats_fwd.hpp>
#include <catch2/internal/catch_case_insensitive_comparisons.hpp> #include <catch2/internal/catch_case_insensitive_comparisons.hpp>
#include <catch2/internal/catch_case_sensitive.hpp> #include <catch2/internal/catch_case_sensitive.hpp>
#include <catch2/internal/catch_clara.hpp> #include <catch2/internal/catch_clara.hpp>
@@ -70,6 +72,7 @@
#include <catch2/internal/catch_fatal_condition_handler.hpp> #include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp> #include <catch2/internal/catch_floating_point_helpers.hpp>
#include <catch2/internal/catch_getenv.hpp> #include <catch2/internal/catch_getenv.hpp>
#include <catch2/internal/catch_is_permutation.hpp>
#include <catch2/internal/catch_istream.hpp> #include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_lazy_expr.hpp> #include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp> #include <catch2/internal/catch_leak_detector.hpp>
@@ -88,6 +91,7 @@
#include <catch2/internal/catch_preprocessor_remove_parens.hpp> #include <catch2/internal/catch_preprocessor_remove_parens.hpp>
#include <catch2/internal/catch_random_number_generator.hpp> #include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/internal/catch_random_seed_generation.hpp> #include <catch2/internal/catch_random_seed_generation.hpp>
#include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/internal/catch_reporter_registry.hpp> #include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_reporter_spec_parser.hpp> #include <catch2/internal/catch_reporter_spec_parser.hpp>
#include <catch2/internal/catch_result_type.hpp> #include <catch2/internal/catch_result_type.hpp>

View File

@@ -7,6 +7,7 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_assertion_result.hpp> #include <catch2/catch_assertion_result.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
namespace Catch { namespace Catch {
@@ -26,9 +27,9 @@ namespace Catch {
return reconstructedExpression; return reconstructedExpression;
} }
AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data )
: m_info( info ), : m_info( info ),
m_resultData( data ) m_resultData( CATCH_MOVE(data) )
{} {}
// Result was a success // Result was a success
@@ -67,16 +68,15 @@ namespace Catch {
} }
std::string AssertionResult::getExpressionInMacro() const { std::string AssertionResult::getExpressionInMacro() const {
if ( m_info.macroName.empty() ) {
return static_cast<std::string>( m_info.capturedExpression );
}
std::string expr; std::string expr;
if( m_info.macroName.empty() )
expr = static_cast<std::string>(m_info.capturedExpression);
else {
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
expr += m_info.macroName; expr += m_info.macroName;
expr += "( "; expr += "( ";
expr += m_info.capturedExpression; expr += m_info.capturedExpression;
expr += " )"; expr += " )";
}
return expr; return expr;
} }

View File

@@ -35,7 +35,7 @@ namespace Catch {
class AssertionResult { class AssertionResult {
public: public:
AssertionResult() = delete; AssertionResult() = delete;
AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); AssertionResult( AssertionInfo const& info, AssertionResultData&& data );
bool isOk() const; bool isOk() const;
bool succeeded() const; bool succeeded() const;

View File

@@ -13,7 +13,7 @@
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_test_spec_parser.hpp> #include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp> #include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_getenv.hpp> #include <catch2/internal/catch_getenv.hpp>
#include <fstream> #include <fstream>
@@ -123,7 +123,7 @@ namespace Catch {
// Bazel support can modify the test specs, so parsing has to happen // Bazel support can modify the test specs, so parsing has to happen
// after reading Bazel env vars. // after reading Bazel env vars.
TestSpecParser parser( ITagAliasRegistry::get() ); TestSpecParser parser( TagAliasRegistry::get() );
if ( !m_data.testsOrTags.empty() ) { if ( !m_data.testsOrTags.empty() ) {
m_hasTestFilters = true; m_hasTestFilters = true;
for ( auto const& testOrTags : m_data.testsOrTags ) { for ( auto const& testOrTags : m_data.testsOrTags ) {

View File

@@ -6,10 +6,10 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_message.hpp> #include <catch2/catch_message.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_uncaught_exceptions.hpp> #include <catch2/internal/catch_uncaught_exceptions.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_run_context.hpp>
#include <cassert> #include <cassert>
#include <stack> #include <stack>
@@ -19,8 +19,8 @@ namespace Catch {
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
ScopedMessage::ScopedMessage( MessageBuilder const& builder ): ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
m_info( builder.m_info ) { m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str(); m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info ); getResultCapture().pushScopedMessage( m_info );
} }

View File

@@ -12,7 +12,7 @@
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stream_end_stop.hpp> #include <catch2/internal/catch_stream_end_stop.hpp>
#include <catch2/internal/catch_message_info.hpp> #include <catch2/internal/catch_message_info.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp> #include <catch2/internal/catch_run_context.hpp>
#include <catch2/catch_tostring.hpp> #include <catch2/catch_tostring.hpp>
#include <string> #include <string>
@@ -39,11 +39,10 @@ namespace Catch {
ResultWas::OfType type ): ResultWas::OfType type ):
m_info(macroName, lineInfo, type) {} m_info(macroName, lineInfo, type) {}
template<typename T> template<typename T>
MessageBuilder& operator << ( T const& value ) { MessageBuilder&& operator << ( T const& value ) && {
m_stream << value; m_stream << value;
return *this; return CATCH_MOVE(*this);
} }
MessageInfo m_info; MessageInfo m_info;
@@ -51,7 +50,7 @@ namespace Catch {
class ScopedMessage { class ScopedMessage {
public: public:
explicit ScopedMessage( MessageBuilder const& builder ); explicit ScopedMessage( MessageBuilder&& builder );
ScopedMessage( ScopedMessage& duplicate ) = delete; ScopedMessage( ScopedMessage& duplicate ) = delete;
ScopedMessage( ScopedMessage&& old ) noexcept; ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage(); ~ScopedMessage();
@@ -62,7 +61,7 @@ namespace Catch {
class Capturer { class Capturer {
std::vector<MessageInfo> m_messages; std::vector<MessageInfo> m_messages;
IResultCapture& m_resultCapture = getResultCapture(); RunContext& m_resultCapture = getResultCapture();
size_t m_captured = 0; size_t m_captured = 0;
public: public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
@@ -98,7 +97,10 @@ namespace Catch {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ #define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ Catch::Capturer varName( macroName##_catch_sr, \
CATCH_INTERNAL_LINEINFO, \
Catch::ResultWas::Info, \
#__VA_ARGS__##_catch_sr ); \
varName.captureValues( 0, __VA_ARGS__ ) varName.captureValues( 0, __VA_ARGS__ )
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@@ -1,104 +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
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_startup_exception_registry.hpp>
#include <catch2/internal/catch_singletons.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
namespace Catch {
namespace {
class RegistryHub : public IRegistryHub,
public IMutableRegistryHub,
private Detail::NonCopyable {
public: // IRegistryHub
RegistryHub() = default;
IReporterRegistry const& getReporterRegistry() const override {
return m_reporterRegistry;
}
ITestCaseRegistry const& getTestCaseRegistry() const override {
return m_testCaseRegistry;
}
IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
return m_exceptionTranslatorRegistry;
}
ITagAliasRegistry const& getTagAliasRegistry() const override {
return m_tagAliasRegistry;
}
StartupExceptionRegistry const& getStartupExceptionRegistry() const override {
return m_exceptionRegistry;
}
public: // IMutableRegistryHub
void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override {
m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) );
}
void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) override {
m_reporterRegistry.registerListener( CATCH_MOVE(factory) );
}
void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker ) override {
m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) );
}
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) override {
m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) );
}
void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
m_tagAliasRegistry.add( alias, tag, lineInfo );
}
void registerStartupException() noexcept override {
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
m_exceptionRegistry.add(std::current_exception());
#else
CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
#endif
}
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
return m_enumValuesRegistry;
}
private:
TestRegistry m_testCaseRegistry;
ReporterRegistry m_reporterRegistry;
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry;
StartupExceptionRegistry m_exceptionRegistry;
Detail::EnumValuesRegistry m_enumValuesRegistry;
};
}
using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>;
IRegistryHub const& getRegistryHub() {
return RegistryHubSingleton::get();
}
IMutableRegistryHub& getMutableRegistryHub() {
return RegistryHubSingleton::getMutable();
}
void cleanUp() {
cleanupSingletons();
cleanUpContext();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
}
} // end namespace Catch

View File

@@ -19,11 +19,14 @@
#include <catch2/internal/catch_textflow.hpp> #include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_windows_h_proxy.hpp> #include <catch2/internal/catch_windows_h_proxy.hpp>
#include <catch2/reporters/catch_reporter_multi.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/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_stdstreams.hpp> #include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_istream.hpp> #include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>

View File

@@ -8,7 +8,7 @@
#include <catch2/catch_tag_alias_autoregistrar.hpp> #include <catch2/catch_tag_alias_autoregistrar.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_registry_hub.hpp>
namespace Catch { namespace Catch {

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

@@ -8,6 +8,7 @@
#include <catch2/catch_test_spec.hpp> #include <catch2/catch_test_spec.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/catch_test_case_info.hpp> #include <catch2/catch_test_case_info.hpp>
#include <algorithm> #include <algorithm>

View File

@@ -16,9 +16,9 @@
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_config_wchar.hpp> #include <catch2/internal/catch_config_wchar.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_void_type.hpp> #include <catch2/internal/catch_void_type.hpp>
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
#include <string_view> #include <string_view>
@@ -648,7 +648,7 @@ struct ratio_string<std::milli> {
}; };
} }
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_registry_hub.hpp>
#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ #define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
namespace Catch { \ namespace Catch { \

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/internal/catch_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> #include <exception>
namespace Catch { namespace Catch {
namespace Detail {
void registerTranslatorImpl(
Detail::unique_ptr<IExceptionTranslator>&& translator );
}
class ExceptionTranslatorRegistrar { class ExceptionTranslatorRegistrar {
template<typename T> template<typename T>
@@ -48,9 +52,8 @@ namespace Catch {
public: public:
template<typename T> template<typename T>
ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) { ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
getMutableRegistryHub().registerTranslator( Detail::registerTranslatorImpl( Detail::make_unique<ExceptionTranslator<T>>(
Detail::make_unique<ExceptionTranslator<T>>(translateFunction) translateFunction ) );
);
} }
}; };

View File

@@ -36,7 +36,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 3, 3, 0, "", 0 ); static Version version( 3, 3, 2, "", 0 );
return version; return version;
} }

View File

@@ -10,6 +10,6 @@
#define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 3 #define CATCH_VERSION_MINOR 3
#define CATCH_VERSION_PATCH 0 #define CATCH_VERSION_PATCH 2
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED #endif // CATCH_VERSION_MACROS_HPP_INCLUDED

View File

@@ -9,7 +9,7 @@
#include <catch2/generators/catch_generators.hpp> #include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/generators/catch_generator_exception.hpp> #include <catch2/generators/catch_generator_exception.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp> #include <catch2/internal/catch_run_context.hpp>
namespace Catch { namespace Catch {

View File

@@ -14,7 +14,6 @@
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_unique_name.hpp> #include <catch2/internal/catch_unique_name.hpp>
#include <catch2/internal/catch_preprocessor.hpp>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
@@ -232,16 +231,19 @@ namespace Detail {
} // namespace Generators } // namespace Generators
} // namespace Catch } // namespace Catch
#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr
#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__)
#define GENERATE( ... ) \ #define GENERATE( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \ CATCH_INTERNAL_LINEINFO, \
[ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_COPY( ... ) \ #define GENERATE_COPY( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \ CATCH_INTERNAL_LINEINFO, \
[=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_REF( ... ) \ #define GENERATE_REF( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \ CATCH_INTERNAL_LINEINFO, \
[&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)

View File

@@ -22,16 +22,11 @@
#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED #ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED
#define CATCH_INTERFACES_ALL_HPP_INCLUDED #define CATCH_INTERFACES_ALL_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/interfaces/catch_interfaces_exception.hpp> #include <catch2/interfaces/catch_interfaces_exception.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp> #include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp> #include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.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_testcase.hpp> #include <catch2/interfaces/catch_interfaces_testcase.hpp>
#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED #endif // CATCH_INTERFACES_ALL_HPP_INCLUDED

View File

@@ -1,13 +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
#include <catch2/interfaces/catch_interfaces_capture.hpp>
namespace Catch {
IResultCapture::~IResultCapture() = default;
}

View File

@@ -1,109 +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_CAPTURE_HPP_INCLUDED
#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
#include <string>
#include <chrono>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
namespace Catch {
class AssertionResult;
struct AssertionInfo;
struct SectionInfo;
struct SectionEndInfo;
struct MessageInfo;
struct MessageBuilder;
struct Counts;
struct AssertionReaction;
struct SourceLineInfo;
class ITransientExpression;
class IGeneratorTracker;
struct BenchmarkInfo;
template <typename Duration = std::chrono::duration<double, std::nano>>
struct BenchmarkStats;
namespace Generators {
class GeneratorUntypedBase;
using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
}
class IResultCapture {
public:
virtual ~IResultCapture();
virtual bool sectionStarted( SectionInfo const& sectionInfo,
Counts& assertions ) = 0;
virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
virtual IGeneratorTracker*
acquireGeneratorTracker( StringRef generatorName,
SourceLineInfo const& lineInfo ) = 0;
virtual IGeneratorTracker*
createGeneratorTracker( StringRef generatorName,
SourceLineInfo lineInfo,
Generators::GeneratorBasePtr&& generator ) = 0;
virtual void benchmarkPreparing( StringRef name ) = 0;
virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
virtual void benchmarkFailed( StringRef error ) = 0;
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
virtual void handleFatalErrorCondition( StringRef message ) = 0;
virtual void handleExpr
( AssertionInfo const& info,
ITransientExpression const& expr,
AssertionReaction& reaction ) = 0;
virtual void handleMessage
( AssertionInfo const& info,
ResultWas::OfType resultType,
StringRef message,
AssertionReaction& reaction ) = 0;
virtual void handleUnexpectedExceptionNotThrown
( AssertionInfo const& info,
AssertionReaction& reaction ) = 0;
virtual void handleUnexpectedInflightException
( AssertionInfo const& info,
std::string const& message,
AssertionReaction& reaction ) = 0;
virtual void handleIncomplete
( AssertionInfo const& info ) = 0;
virtual void handleNonExpr
( AssertionInfo const &info,
ResultWas::OfType resultType,
AssertionReaction &reaction ) = 0;
virtual bool lastAssertionPassed() = 0;
virtual void assertionPassed() = 0;
// Deprecated, do not use:
virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0;
virtual void exceptionEarlyReported() = 0;
};
IResultCapture& getResultCapture();
}
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED

View File

@@ -1,47 +0,0 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#include <catch2/internal/catch_stringref.hpp>
#include <vector>
namespace Catch {
namespace Detail {
struct EnumInfo {
StringRef m_name;
std::vector<std::pair<int, StringRef>> m_values;
~EnumInfo();
StringRef lookup( int value ) const;
};
} // namespace Detail
class IMutableEnumValuesRegistry {
public:
virtual ~IMutableEnumValuesRegistry(); // = default;
virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
template<typename E>
Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
std::vector<int> intValues;
intValues.reserve( values.size() );
for( auto enumValue : values )
intValues.push_back( static_cast<int>( enumValue ) );
return registerEnum( enumName, allEnums, intValues );
}
};
} // Catch
#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED

View File

@@ -10,5 +10,4 @@
namespace Catch { namespace Catch {
IExceptionTranslator::~IExceptionTranslator() = default; IExceptionTranslator::~IExceptionTranslator() = default;
IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default;
} }

View File

@@ -8,15 +8,12 @@
#ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED #ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
#define 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 <catch2/internal/catch_unique_ptr.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
namespace Catch { namespace Catch {
using exceptionTranslateFunction = std::string(*)();
class IExceptionTranslator; class IExceptionTranslator;
using ExceptionTranslators = std::vector<Detail::unique_ptr<IExceptionTranslator const>>; using ExceptionTranslators = std::vector<Detail::unique_ptr<IExceptionTranslator const>>;
@@ -26,12 +23,6 @@ namespace Catch {
virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
}; };
class IExceptionTranslatorRegistry {
public:
virtual ~IExceptionTranslatorRegistry(); // = default
virtual std::string translateActiveException() const = 0;
};
} // namespace Catch } // namespace Catch
#endif // CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED #endif // CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED

View File

@@ -1,14 +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
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
namespace Catch {
IRegistryHub::~IRegistryHub() = default;
IMutableRegistryHub::~IMutableRegistryHub() = default;
}

View File

@@ -1,66 +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_REGISTRY_HUB_HPP_INCLUDED
#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED
#include <catch2/internal/catch_unique_ptr.hpp>
#include <string>
namespace Catch {
class TestCaseHandle;
struct TestCaseInfo;
class ITestCaseRegistry;
class IExceptionTranslatorRegistry;
class IExceptionTranslator;
class IReporterRegistry;
class IReporterFactory;
class ITagAliasRegistry;
class ITestInvoker;
class IMutableEnumValuesRegistry;
struct SourceLineInfo;
class StartupExceptionRegistry;
class EventListenerFactory;
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
class IRegistryHub {
public:
virtual ~IRegistryHub(); // = default
virtual IReporterRegistry const& getReporterRegistry() const = 0;
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
};
class IMutableRegistryHub {
public:
virtual ~IMutableRegistryHub(); // = default
virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0;
virtual void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) = 0;
virtual void registerTest(Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker) = 0;
virtual void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) = 0;
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
virtual void registerStartupException() noexcept = 0;
virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
};
IRegistryHub const& getRegistryHub();
IMutableRegistryHub& getMutableRegistryHub();
void cleanUp();
std::string translateActiveException();
}
#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED

View File

@@ -54,24 +54,21 @@ namespace Catch {
infoMessages( _infoMessages ), infoMessages( _infoMessages ),
totals( _totals ) totals( _totals )
{ {
assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
if( assertionResult.hasMessage() ) { if( assertionResult.hasMessage() ) {
// Copy message into messages list. // Copy message into messages list.
// !TBD This should have been done earlier, somewhere // !TBD This should have been done earlier, somewhere
MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
builder << assertionResult.getMessage(); builder.m_info.message = static_cast<std::string>(assertionResult.getMessage());
builder.m_info.message = builder.m_stream.str();
infoMessages.push_back( builder.m_info ); infoMessages.push_back( CATCH_MOVE(builder.m_info) );
} }
} }
SectionStats::SectionStats( SectionInfo const& _sectionInfo, SectionStats::SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions, Counts const& _assertions,
double _durationInSeconds, double _durationInSeconds,
bool _missingAssertions ) bool _missingAssertions )
: sectionInfo( _sectionInfo ), : sectionInfo( CATCH_MOVE(_sectionInfo) ),
assertions( _assertions ), assertions( _assertions ),
durationInSeconds( _durationInSeconds ), durationInSeconds( _durationInSeconds ),
missingAssertions( _missingAssertions ) missingAssertions( _missingAssertions )
@@ -80,13 +77,13 @@ namespace Catch {
TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals, Totals const& _totals,
std::string const& _stdOut, std::string&& _stdOut,
std::string const& _stdErr, std::string&& _stdErr,
bool _aborting ) bool _aborting )
: testInfo( &_testInfo ), : testInfo( &_testInfo ),
totals( _totals ), totals( _totals ),
stdOut( _stdOut ), stdOut( CATCH_MOVE(_stdOut) ),
stdErr( _stdErr ), stdErr( CATCH_MOVE(_stdErr) ),
aborting( _aborting ) aborting( _aborting )
{} {}

View File

@@ -9,6 +9,7 @@
#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED #define CATCH_INTERFACES_REPORTER_HPP_INCLUDED
#include <catch2/catch_section_info.hpp> #include <catch2/catch_section_info.hpp>
#include <catch2/catch_test_run_info.hpp>
#include <catch2/catch_totals.hpp> #include <catch2/catch_totals.hpp>
#include <catch2/catch_assertion_result.hpp> #include <catch2/catch_assertion_result.hpp>
#include <catch2/internal/catch_message_info.hpp> #include <catch2/internal/catch_message_info.hpp>
@@ -17,15 +18,18 @@
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/benchmark/catch_estimate.hpp> #include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp> #include <catch2/benchmark/catch_outlier_classification.hpp>
#include <catch2/internal/catch_benchmark_stats_fwd.hpp>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include <iosfwd> #include <iosfwd>
#include <chrono>
namespace Catch { namespace Catch {
struct BenchmarkInfo;
struct ReporterDescription; struct ReporterDescription;
struct ListenerDescription; struct ListenerDescription;
struct TagInfo; struct TagInfo;
@@ -57,11 +61,6 @@ namespace Catch {
std::map<std::string, std::string> m_customOptions; std::map<std::string, std::string> m_customOptions;
}; };
struct TestRunInfo {
constexpr TestRunInfo(StringRef _name) : name(_name) {}
StringRef name;
};
struct AssertionStats { struct AssertionStats {
AssertionStats( AssertionResult const& _assertionResult, AssertionStats( AssertionResult const& _assertionResult,
std::vector<MessageInfo> const& _infoMessages, std::vector<MessageInfo> const& _infoMessages,
@@ -78,7 +77,7 @@ namespace Catch {
}; };
struct SectionStats { struct SectionStats {
SectionStats( SectionInfo const& _sectionInfo, SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions, Counts const& _assertions,
double _durationInSeconds, double _durationInSeconds,
bool _missingAssertions ); bool _missingAssertions );
@@ -92,8 +91,8 @@ namespace Catch {
struct TestCaseStats { struct TestCaseStats {
TestCaseStats( TestCaseInfo const& _testInfo, TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals, Totals const& _totals,
std::string const& _stdOut, std::string&& _stdOut,
std::string const& _stdErr, std::string&& _stdErr,
bool _aborting ); bool _aborting );
TestCaseInfo const * testInfo; TestCaseInfo const * testInfo;

View File

@@ -1,13 +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
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
namespace Catch {
IReporterRegistry::~IReporterRegistry() = default;
}

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

@@ -1,29 +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_TAG_ALIAS_REGISTRY_HPP_INCLUDED
#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED
#include <string>
namespace Catch {
struct TagAlias;
class ITagAliasRegistry {
public:
virtual ~ITagAliasRegistry(); // = default
// Nullptr if not present
virtual TagAlias const* find( std::string const& alias ) const = 0;
virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
static ITagAliasRegistry const& get();
};
} // end namespace Catch
#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED

View File

@@ -10,5 +10,4 @@
namespace Catch { namespace Catch {
ITestInvoker::~ITestInvoker() = default; ITestInvoker::~ITestInvoker() = default;
ITestCaseRegistry::~ITestCaseRegistry() = default;
} }

View File

@@ -8,36 +8,14 @@
#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED #ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED #define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
#include <vector>
namespace Catch { namespace Catch {
class TestSpec;
struct TestCaseInfo;
class ITestInvoker { class ITestInvoker {
public: public:
virtual void invoke () const = 0; virtual void invoke () const = 0;
virtual ~ITestInvoker(); // = default virtual ~ITestInvoker(); // = default
}; };
class TestCaseHandle;
class IConfig;
class ITestCaseRegistry {
public:
virtual ~ITestCaseRegistry(); // = default
// TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
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 #endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED

View File

@@ -11,7 +11,7 @@
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_debugger.hpp> #include <catch2/internal/catch_debugger.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp> #include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/internal/catch_run_context.hpp> #include <catch2/internal/catch_run_context.hpp>
#include <catch2/matchers/catch_matchers_string.hpp> #include <catch2/matchers/catch_matchers_string.hpp>
@@ -38,7 +38,7 @@ namespace Catch {
} }
void AssertionHandler::complete() { void AssertionHandler::complete() {
setCompleted(); m_completed = true;
if( m_reaction.shouldDebugBreak ) { if( m_reaction.shouldDebugBreak ) {
// If you find your debugger stopping you here then go one level up on the // If you find your debugger stopping you here then go one level up on the
@@ -51,16 +51,9 @@ namespace Catch {
throw_test_failure_exception(); throw_test_failure_exception();
} }
if ( m_reaction.shouldSkip ) { if ( m_reaction.shouldSkip ) {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) throw_test_skip_exception();
throw Catch::TestSkipException();
#else
CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
#endif
} }
} }
void AssertionHandler::setCompleted() {
m_completed = true;
}
void AssertionHandler::handleUnexpectedInflightException() { void AssertionHandler::handleUnexpectedInflightException() {
m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );

View File

@@ -10,14 +10,14 @@
#include <catch2/catch_assertion_info.hpp> #include <catch2/catch_assertion_info.hpp>
#include <catch2/internal/catch_decomposer.hpp> #include <catch2/internal/catch_decomposer.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp> #include <catch2/internal/catch_run_context.hpp>
#include <catch2/internal/catch_lazy_expr.hpp> #include <catch2/internal/catch_lazy_expr.hpp>
#include <string> #include <string>
namespace Catch { namespace Catch {
class IResultCapture; class RunContext;
struct AssertionReaction { struct AssertionReaction {
bool shouldDebugBreak = false; bool shouldDebugBreak = false;
@@ -29,7 +29,7 @@ namespace Catch {
AssertionInfo m_assertionInfo; AssertionInfo m_assertionInfo;
AssertionReaction m_reaction; AssertionReaction m_reaction;
bool m_completed = false; bool m_completed = false;
IResultCapture& m_resultCapture; RunContext& m_resultCapture;
public: public:
AssertionHandler AssertionHandler
@@ -59,7 +59,6 @@ namespace Catch {
void handleUnexpectedInflightException(); void handleUnexpectedInflightException();
void complete(); void complete();
void setCompleted();
// query // query
auto allowThrows() const -> bool; auto allowThrows() const -> bool;

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

@@ -9,8 +9,8 @@
#include <catch2/catch_config.hpp> #include <catch2/catch_config.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_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_console_colour.hpp>
#include <catch2/internal/catch_parse_numbers.hpp> #include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_reporter_spec_parser.hpp> #include <catch2/internal/catch_reporter_spec_parser.hpp>
@@ -144,7 +144,7 @@ namespace Catch {
auto const& reporterSpec = *parsed; auto const& reporterSpec = *parsed;
IReporterRegistry::FactoryMap const& factories = auto const& factories =
getRegistryHub().getReporterRegistry().getFactories(); getRegistryHub().getReporterRegistry().getFactories();
auto result = factories.find( reporterSpec.name() ); auto result = factories.find( reporterSpec.name() );

View File

@@ -41,7 +41,7 @@
// Only GCC compiler should be used in this block, so other compilers trying to // Only GCC compiler should be used in this block, so other compilers trying to
// mask themselves as GCC should be ignored. // mask themselves as GCC should be ignored.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
@@ -60,6 +60,12 @@
#endif #endif
#if defined(__NVCOMPILER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" )
#endif
#if defined(__CUDACC__) && !defined(__clang__) #if defined(__CUDACC__) && !defined(__clang__)
# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ # ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
// New pragmas introduced in CUDA 11.5+ // New pragmas introduced in CUDA 11.5+

View File

@@ -85,7 +85,7 @@ namespace Catch {
namespace { namespace {
//! A do-nothing implementation of colour, used as fallback for unknown //! A do-nothing implementation of colour, used as fallback for unknown
//! platforms, and when the user asks to deactivate all colours. //! platforms, and when the user asks to deactivate all colours.
class NoColourImpl : public ColourImpl { class NoColourImpl final : public ColourImpl {
public: public:
NoColourImpl( IStream* stream ): ColourImpl( stream ) {} NoColourImpl( IStream* stream ): ColourImpl( stream ) {}
@@ -103,7 +103,7 @@ namespace Catch {
namespace Catch { namespace Catch {
namespace { namespace {
class Win32ColourImpl : public ColourImpl { class Win32ColourImpl final : public ColourImpl {
public: public:
Win32ColourImpl(IStream* stream): Win32ColourImpl(IStream* stream):
ColourImpl(stream) { ColourImpl(stream) {
@@ -169,7 +169,7 @@ namespace {
namespace Catch { namespace Catch {
namespace { namespace {
class ANSIColourImpl : public ColourImpl { class ANSIColourImpl final : public ColourImpl {
public: public:
ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {}

View File

@@ -11,49 +11,27 @@
namespace Catch { namespace Catch {
class Context : public IMutableContext, private Detail::NonCopyable { Context* Context::currentContext = nullptr;
public: // IContext void cleanUpContext() {
IResultCapture* getResultCapture() override { delete Context::currentContext;
return m_resultCapture; Context::currentContext = nullptr;
} }
void Context::createContext() {
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()
{
currentContext = new Context(); currentContext = new Context();
} }
void cleanUpContext() { Context& getCurrentMutableContext() {
delete IMutableContext::currentContext; if ( !Context::currentContext ) { Context::createContext(); }
IMutableContext::currentContext = nullptr; // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
} }
IContext::~IContext() = default;
IMutableContext::~IMutableContext() = default;
Context::~Context() = default;
void Context::setResultCapture( RunContext* resultCapture ) {
m_resultCapture = resultCapture;
}
void Context::setConfig( IConfig const* config ) { m_config = config; }
SimplePcg32& sharedRng() { SimplePcg32& sharedRng() {
static SimplePcg32 s_rng; static SimplePcg32 s_rng;

View File

@@ -12,41 +12,34 @@
namespace Catch { namespace Catch {
class IResultCapture; class RunContext;
class IConfig; class IConfig;
class IContext { class Context {
public: IConfig const* m_config = nullptr;
virtual ~IContext(); // = default RunContext* m_resultCapture = nullptr;
virtual IResultCapture* getResultCapture() = 0; CATCH_EXPORT static Context* currentContext;
virtual IConfig const* getConfig() const = 0; friend Context& getCurrentMutableContext();
}; friend Context const& getCurrentContext();
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();
static void createContext(); static void createContext();
friend void cleanUpContext();
public:
RunContext* getResultCapture() const { return m_resultCapture; }
IConfig const* getConfig() const { return m_config; }
void setResultCapture( RunContext* resultCapture );
void setConfig( IConfig const* config );
}; };
inline IMutableContext& getCurrentMutableContext() Context& getCurrentMutableContext();
{
if( !IMutableContext::currentContext )
IMutableContext::createContext();
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *IMutableContext::currentContext;
}
inline IContext& getCurrentContext() inline Context const& getCurrentContext() {
{ // We duplicate the logic from `getCurrentMutableContext` here,
return getCurrentMutableContext(); // 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(); void cleanUpContext();

View File

@@ -12,22 +12,23 @@
namespace Catch { namespace Catch {
IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
namespace Detail { namespace Detail {
namespace { namespace {
// Extracts the actual name part of an enum instance // Extracts the actual name part of an enum instance
// In other words, it returns the Blue part of Bikeshed::Colour::Blue // In other words, it returns the Blue part of
StringRef extractInstanceName(StringRef enumInstance) { // Bikeshed::Colour::Blue
static StringRef extractInstanceName( StringRef enumInstance ) {
// Find last occurrence of ":" // Find last occurrence of ":"
size_t name_start = enumInstance.size(); size_t name_start = enumInstance.size();
while (name_start > 0 && enumInstance[name_start - 1] != ':') { while ( name_start > 0 &&
enumInstance[name_start - 1] != ':' ) {
--name_start; --name_start;
} }
return enumInstance.substr(name_start, enumInstance.size() - name_start); return enumInstance.substr( name_start,
} enumInstance.size() - name_start );
} }
} // end unnamed namespace
std::vector<StringRef> parseEnums( StringRef enums ) { std::vector<StringRef> parseEnums( StringRef enums ) {
auto enumValues = splitStringRef( enums, ',' ); auto enumValues = splitStringRef( enums, ',' );
@@ -39,17 +40,19 @@ namespace Catch {
return parsed; return parsed;
} }
EnumInfo::~EnumInfo() {} EnumInfo::~EnumInfo() = default;
StringRef EnumInfo::lookup( int value ) const { StringRef EnumInfo::lookup( int value ) const {
for ( auto const& valueToName : m_values ) { for ( auto const& valueToName : m_values ) {
if( valueToName.first == value ) if ( valueToName.first == value ) { return valueToName.second; }
return valueToName.second;
} }
return "{** unexpected enum value **}"_sr; return "{** unexpected enum value **}"_sr;
} }
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { Catch::Detail::unique_ptr<EnumInfo>
makeEnumInfo( StringRef enumName,
StringRef allValueNames,
std::vector<int> const& values ) {
auto enumInfo = Catch::Detail::make_unique<EnumInfo>(); auto enumInfo = Catch::Detail::make_unique<EnumInfo>();
enumInfo->m_name = enumName; enumInfo->m_name = enumName;
enumInfo->m_values.reserve( values.size() ); enumInfo->m_values.reserve( values.size() );
@@ -57,17 +60,22 @@ namespace Catch {
const auto valueNames = Catch::Detail::parseEnums( allValueNames ); const auto valueNames = Catch::Detail::parseEnums( allValueNames );
assert( valueNames.size() == values.size() ); assert( valueNames.size() == values.size() );
std::size_t i = 0; std::size_t i = 0;
for( auto value : values ) for ( auto value : values ) {
enumInfo->m_values.emplace_back( value, valueNames[i++] ); enumInfo->m_values.emplace_back( value, valueNames[i++] );
}
return enumInfo; return enumInfo;
} }
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { } // namespace Detail
m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
Detail::EnumInfo const&
EnumValuesRegistry::registerEnum( StringRef enumName,
StringRef allValueNames,
std::vector<int> const& values ) {
m_enumInfos.push_back(
Detail::makeEnumInfo( enumName, allValueNames, values ) );
return *m_enumInfos.back(); return *m_enumInfos.back();
} }
} // Detail } // namespace Catch
} // Catch

View File

@@ -8,29 +8,54 @@
#ifndef CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED #ifndef CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED #define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <vector> #include <vector>
namespace Catch { namespace Catch {
namespace Detail { namespace Detail {
struct EnumInfo {
StringRef m_name;
std::vector<std::pair<int, StringRef>> m_values;
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ); ~EnumInfo();
class EnumValuesRegistry : public IMutableEnumValuesRegistry { StringRef lookup( int value ) const;
std::vector<Catch::Detail::unique_ptr<EnumInfo>> m_enumInfos;
EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
}; };
Detail::unique_ptr<EnumInfo>
makeEnumInfo( StringRef enumName,
StringRef allValueNames,
std::vector<int> const& values );
std::vector<StringRef> parseEnums( StringRef enums ); std::vector<StringRef> parseEnums( StringRef enums );
} // Detail } // namespace Detail
} // Catch class EnumValuesRegistry {
std::vector<Catch::Detail::unique_ptr<Detail::EnumInfo>> m_enumInfos;
public:
Detail::EnumInfo const& registerEnum( StringRef enumName,
StringRef allEnums,
std::vector<int> const& values );
template <typename E>
Detail::EnumInfo const&
registerEnum( StringRef enumName,
StringRef allEnums,
std::initializer_list<E> values ) {
static_assert( sizeof( int ) >= sizeof( E ),
"Cannot serialize enum to int" );
std::vector<int> intValues;
intValues.reserve( values.size() );
for ( auto enumValue : values ) {
intValues.push_back( static_cast<int>( enumValue ) );
}
return registerEnum( enumName, allEnums, intValues );
}
};
} // namespace Catch
#endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED #endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED

View File

@@ -6,18 +6,41 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_exception_translator_registry.hpp> #include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/interfaces/catch_interfaces_exception.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp> #include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <vector>
namespace Catch { namespace Catch {
ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { 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() );
}
}
} }
struct ExceptionTranslatorRegistry::ExceptionTranslatorRegistryImpl {
std::vector<Detail::unique_ptr<IExceptionTranslator const>>
translators;
};
ExceptionTranslatorRegistry::ExceptionTranslatorRegistry():
m_impl( Detail::make_unique<ExceptionTranslatorRegistryImpl>() ) {}
ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default;
void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) { void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) {
m_translators.push_back( CATCH_MOVE( translator ) ); m_impl->translators.push_back( CATCH_MOVE( translator ) );
} }
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
@@ -37,7 +60,7 @@ namespace Catch {
// First we try user-registered translators. If none of them can // First we try user-registered translators. If none of them can
// handle the exception, it will be rethrown handled by our defaults. // handle the exception, it will be rethrown handled by our defaults.
try { try {
return tryTranslators(); return tryTranslators(m_impl->translators);
} }
// To avoid having to handle TFE explicitly everywhere, we just // To avoid having to handle TFE explicitly everywhere, we just
// rethrow it so that it goes back up the caller. // rethrow it so that it goes back up the caller.
@@ -61,22 +84,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 #else // ^^ Exceptions are enabled // Exceptions are disabled vv
std::string ExceptionTranslatorRegistry::translateActiveException() const { std::string ExceptionTranslatorRegistry::translateActiveException() const {
CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); 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 #endif

View File

@@ -8,23 +8,22 @@
#ifndef CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED #ifndef CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
#define CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED #define CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_exception.hpp>
#include <catch2/internal/catch_unique_ptr.hpp> #include <catch2/internal/catch_unique_ptr.hpp>
#include <vector>
#include <string> #include <string>
namespace Catch { namespace Catch {
class IExceptionTranslator;
class ExceptionTranslatorRegistry {
struct ExceptionTranslatorRegistryImpl;
Detail::unique_ptr<ExceptionTranslatorRegistryImpl> m_impl;
class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
public: public:
~ExceptionTranslatorRegistry() override; ExceptionTranslatorRegistry();
~ExceptionTranslatorRegistry();
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ); void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
std::string translateActiveException() const override; std::string translateActiveException() const;
std::string tryTranslators() const;
private:
ExceptionTranslators m_translators;
}; };
} }

View File

@@ -28,7 +28,7 @@
#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp> #include <catch2/internal/catch_run_context.hpp>
#include <catch2/internal/catch_windows_h_proxy.hpp> #include <catch2/internal/catch_windows_h_proxy.hpp>
#include <catch2/internal/catch_stdstreams.hpp> #include <catch2/internal/catch_stdstreams.hpp>

View File

@@ -0,0 +1,138 @@
// 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_IS_PERMUTATION_HPP_INCLUDED
#define CATCH_IS_PERMUTATION_HPP_INCLUDED
#include <algorithm>
#include <iterator>
namespace Catch {
namespace Detail {
template <typename ForwardIter,
typename Sentinel,
typename T,
typename Comparator>
ForwardIter find_sentinel( ForwardIter start,
Sentinel sentinel,
T const& value,
Comparator cmp ) {
while ( start != sentinel ) {
if ( cmp( *start, value ) ) { break; }
++start;
}
return start;
}
template <typename ForwardIter,
typename Sentinel,
typename T,
typename Comparator>
std::ptrdiff_t count_sentinel( ForwardIter start,
Sentinel sentinel,
T const& value,
Comparator cmp ) {
std::ptrdiff_t count = 0;
while ( start != sentinel ) {
if ( cmp( *start, value ) ) { ++count; }
++start;
}
return count;
}
template <typename ForwardIter, typename Sentinel>
std::enable_if_t<!std::is_same<ForwardIter, Sentinel>::value,
std::ptrdiff_t>
sentinel_distance( ForwardIter iter, const Sentinel sentinel ) {
std::ptrdiff_t dist = 0;
while ( iter != sentinel ) {
++iter;
++dist;
}
return dist;
}
template <typename ForwardIter>
std::ptrdiff_t sentinel_distance( ForwardIter first,
ForwardIter last ) {
return std::distance( first, last );
}
template <typename ForwardIter1,
typename Sentinel1,
typename ForwardIter2,
typename Sentinel2,
typename Comparator>
bool check_element_counts( ForwardIter1 first_1,
const Sentinel1 end_1,
ForwardIter2 first_2,
const Sentinel2 end_2,
Comparator cmp ) {
auto cursor = first_1;
while ( cursor != end_1 ) {
if ( find_sentinel( first_1, cursor, *cursor, cmp ) ==
cursor ) {
// we haven't checked this element yet
const auto count_in_range_2 =
count_sentinel( first_2, end_2, *cursor, cmp );
// Not a single instance in 2nd range, so it cannot be a
// permutation of 1st range
if ( count_in_range_2 == 0 ) { return false; }
const auto count_in_range_1 =
count_sentinel( cursor, end_1, *cursor, cmp );
if ( count_in_range_1 != count_in_range_2 ) {
return false;
}
}
++cursor;
}
return true;
}
template <typename ForwardIter1,
typename Sentinel1,
typename ForwardIter2,
typename Sentinel2,
typename Comparator>
bool is_permutation( ForwardIter1 first_1,
const Sentinel1 end_1,
ForwardIter2 first_2,
const Sentinel2 end_2,
Comparator cmp ) {
// TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types
// TODO: Comparator has to be "both sides", e.g. a == b => b == a
// This skips shared prefix of the two ranges
while (first_1 != end_1 && first_2 != end_2 && cmp(*first_1, *first_2)) {
++first_1;
++first_2;
}
// We need to handle case where at least one of the ranges has no more elements
if (first_1 == end_1 || first_2 == end_2) {
return first_1 == end_1 && first_2 == end_2;
}
// pair counting is n**2, so we pay linear walk to compare the sizes first
auto dist_1 = sentinel_distance( first_1, end_1 );
auto dist_2 = sentinel_distance( first_2, end_2 );
if (dist_1 != dist_2) { return false; }
// Since we do not try to handle stronger iterators pair (e.g.
// bidir) optimally, the only thing left to do is to check counts in
// the remaining ranges.
return check_element_counts( first_1, end_1, first_2, end_2, cmp );
}
} // namespace Detail
} // namespace Catch
#endif // CATCH_IS_PERMUTATION_HPP_INCLUDED

View File

@@ -24,7 +24,7 @@ namespace Catch {
namespace Detail { namespace Detail {
namespace { namespace {
template<typename WriterF, std::size_t bufferSize=256> template<typename WriterF, std::size_t bufferSize=256>
class StreamBufImpl : public std::streambuf { class StreamBufImpl final : public std::streambuf {
char data[bufferSize]; char data[bufferSize];
WriterF m_writer; WriterF m_writer;
@@ -72,7 +72,7 @@ namespace Detail {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
class FileStream : public IStream { class FileStream final : public IStream {
std::ofstream m_ofs; std::ofstream m_ofs;
public: public:
FileStream( std::string const& filename ) { FileStream( std::string const& filename ) {
@@ -89,7 +89,7 @@ namespace Detail {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
class CoutStream : public IStream { class CoutStream final : public IStream {
std::ostream m_os; std::ostream m_os;
public: public:
// Store the streambuf from cout up-front because // 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; Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
std::ostream m_os; std::ostream m_os;
public: public:

View File

@@ -7,7 +7,7 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_leak_detector.hpp> #include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/catch_user_config.hpp> #include <catch2/catch_user_config.hpp>
#ifdef CATCH_CONFIG_WINDOWS_CRTDBG #ifdef CATCH_CONFIG_WINDOWS_CRTDBG

View File

@@ -7,14 +7,14 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_list.hpp> #include <catch2/internal/catch_list.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.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_testcase.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp> #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_case_insensitive_comparisons.hpp> #include <catch2/internal/catch_case_insensitive_comparisons.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_context.hpp>
#include <catch2/catch_config.hpp> #include <catch2/catch_config.hpp>
#include <catch2/catch_test_spec.hpp> #include <catch2/catch_test_spec.hpp>
@@ -54,7 +54,7 @@ namespace Catch {
void listReporters(IEventListener& reporter) { void listReporters(IEventListener& reporter) {
std::vector<ReporterDescription> descriptions; std::vector<ReporterDescription> descriptions;
IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); auto const& factories = getRegistryHub().getReporterRegistry().getFactories();
descriptions.reserve(factories.size()); descriptions.reserve(factories.size());
for (auto const& fac : factories) { for (auto const& fac : factories) {
descriptions.push_back({ fac.first, fac.second->getDescription() }); 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_result_type.hpp>
#include <catch2/internal/catch_source_line_info.hpp> #include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp> #include <catch2/internal/catch_stringref.hpp>
#include <string> #include <string>

View File

@@ -0,0 +1,114 @@
// 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/internal/catch_registry_hub.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_startup_exception_registry.hpp>
#include <catch2/internal/catch_singletons.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
namespace Catch {
struct RegistryHub::RegistryHubImpl {
TestCaseRegistry testCaseRegistry;
ReporterRegistry reporterRegistry;
ExceptionTranslatorRegistry exceptionTranslatorRegistry;
TagAliasRegistry tagAliasRegistry;
StartupExceptionRegistry exceptionRegistry;
EnumValuesRegistry enumValuesRegistry;
};
RegistryHub::RegistryHub():
m_impl( Detail::make_unique<RegistryHubImpl>() ) {}
RegistryHub::~RegistryHub() = default;
ReporterRegistry const&
RegistryHub::getReporterRegistry() const {
return m_impl->reporterRegistry;
}
TestCaseRegistry const&
RegistryHub::getTestCaseRegistry() const {
return m_impl->testCaseRegistry;
}
ExceptionTranslatorRegistry const&
RegistryHub::getExceptionTranslatorRegistry() const {
return m_impl->exceptionTranslatorRegistry;
}
TagAliasRegistry const&
RegistryHub::getTagAliasRegistry() const {
return m_impl->tagAliasRegistry;
}
StartupExceptionRegistry const&
RegistryHub::getStartupExceptionRegistry() const {
return m_impl->exceptionRegistry;
}
void
RegistryHub::registerReporter( std::string const& name,
IReporterFactoryPtr factory ) {
m_impl->reporterRegistry.registerReporter( name, CATCH_MOVE( factory ) );
}
void RegistryHub::registerListener(
Detail::unique_ptr<EventListenerFactory> factory ) {
m_impl->reporterRegistry.registerListener( CATCH_MOVE( factory ) );
}
void RegistryHub::registerTest(
Detail::unique_ptr<TestCaseInfo>&& testInfo,
Detail::unique_ptr<ITestInvoker>&& invoker ) {
m_impl->testCaseRegistry.registerTest( CATCH_MOVE( testInfo ),
CATCH_MOVE( invoker ) );
}
void RegistryHub::registerTranslator(
Detail::unique_ptr<IExceptionTranslator>&& translator ) {
m_impl->exceptionTranslatorRegistry.registerTranslator(
CATCH_MOVE( translator ) );
}
void RegistryHub::registerTagAlias( std::string const& alias,
std::string const& tag,
SourceLineInfo const& lineInfo ) {
m_impl->tagAliasRegistry.add( alias, tag, lineInfo );
}
void RegistryHub::registerStartupException() noexcept {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
m_impl->exceptionRegistry.add( std::current_exception() );
#else
CATCH_INTERNAL_ERROR( "Attempted to register active exception under "
"CATCH_CONFIG_DISABLE_EXCEPTIONS!" );
#endif
}
EnumValuesRegistry& RegistryHub::getMutableEnumValuesRegistry() {
return m_impl->enumValuesRegistry;
}
using RegistryHubSingleton = Singleton<RegistryHub>;
RegistryHub const& getRegistryHub() {
return RegistryHubSingleton::get();
}
RegistryHub& getMutableRegistryHub() {
return RegistryHubSingleton::getMutable();
}
void cleanUp() {
cleanupSingletons();
cleanUpContext();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
}
} // end namespace Catch

View File

@@ -0,0 +1,67 @@
// 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_REGISTRY_HUB_HPP_INCLUDED
#define CATCH_REGISTRY_HUB_HPP_INCLUDED
#include <catch2/internal/catch_unique_ptr.hpp>
#include <string>
namespace Catch {
class TestCaseHandle;
struct TestCaseInfo;
class TestCaseRegistry;
class ExceptionTranslatorRegistry;
class IExceptionTranslator;
class ReporterRegistry;
class IReporterFactory;
class TagAliasRegistry;
class ITestInvoker;
class EnumValuesRegistry;
struct SourceLineInfo;
class StartupExceptionRegistry;
class EventListenerFactory;
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
class RegistryHub {
struct RegistryHubImpl;
Detail::unique_ptr<RegistryHubImpl> m_impl;
public:
RegistryHub();
~RegistryHub();
ReporterRegistry const& getReporterRegistry() const;
TestCaseRegistry const& getTestCaseRegistry() const;
TagAliasRegistry const& getTagAliasRegistry() const;
ExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const;
StartupExceptionRegistry const& getStartupExceptionRegistry() const;
void registerReporter( std::string const& name,
IReporterFactoryPtr factory );
void registerListener( Detail::unique_ptr<EventListenerFactory> factory );
void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo,
Detail::unique_ptr<ITestInvoker>&& invoker );
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
void registerTagAlias( std::string const& alias,
std::string const& tag,
SourceLineInfo const& lineInfo );
void registerStartupException() noexcept;
EnumValuesRegistry& getMutableEnumValuesRegistry();
};
RegistryHub const& getRegistryHub();
RegistryHub& getMutableRegistryHub();
void cleanUp();
std::string translateActiveException();
}
#endif // CATCH_REGISTRY_HUB_HPP_INCLUDED

View File

@@ -5,61 +5,86 @@
// https://www.boost.org/LICENSE_1_0.txt) // https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0 // 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_automake.hpp>
#include <catch2/reporters/catch_reporter_compact.hpp> #include <catch2/reporters/catch_reporter_compact.hpp>
#include <catch2/reporters/catch_reporter_console.hpp> #include <catch2/reporters/catch_reporter_console.hpp>
#include <catch2/reporters/catch_reporter_junit.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_sonarqube.hpp>
#include <catch2/reporters/catch_reporter_tap.hpp> #include <catch2/reporters/catch_reporter_tap.hpp>
#include <catch2/reporters/catch_reporter_teamcity.hpp> #include <catch2/reporters/catch_reporter_teamcity.hpp>
#include <catch2/reporters/catch_reporter_xml.hpp> #include <catch2/reporters/catch_reporter_xml.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_enforce.hpp>
namespace Catch { 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, // Because it is impossible to move out of initializer list,
// we have to add the elements manually // we have to add the elements manually
m_factories["Automake"] = Detail::make_unique<ReporterFactory<AutomakeReporter>>(); m_impl->factories["Automake"] =
m_factories["compact"] = Detail::make_unique<ReporterFactory<CompactReporter>>(); Detail::make_unique<ReporterFactory<AutomakeReporter>>();
m_factories["console"] = Detail::make_unique<ReporterFactory<ConsoleReporter>>(); m_impl->factories["compact"] =
m_factories["JUnit"] = Detail::make_unique<ReporterFactory<JunitReporter>>(); Detail::make_unique<ReporterFactory<CompactReporter>>();
m_factories["SonarQube"] = Detail::make_unique<ReporterFactory<SonarQubeReporter>>(); m_impl->factories["console"] =
m_factories["TAP"] = Detail::make_unique<ReporterFactory<TAPReporter>>(); Detail::make_unique<ReporterFactory<ConsoleReporter>>();
m_factories["TeamCity"] = Detail::make_unique<ReporterFactory<TeamCityReporter>>(); m_impl->factories["JUnit"] =
m_factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>(); 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; ReporterRegistry::~ReporterRegistry() = default;
IEventListenerPtr
IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const { ReporterRegistry::create( std::string const& name,
auto it = m_factories.find( name ); ReporterConfig&& config ) const {
if( it == m_factories.end() ) auto it = m_impl->factories.find( name );
return nullptr; if ( it == m_impl->factories.end() ) return nullptr;
return it->second->create( CATCH_MOVE( config ) ); return it->second->create( CATCH_MOVE( config ) );
return IEventListenerPtr();
} }
void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { void ReporterRegistry::registerReporter( std::string const& name,
IReporterFactoryPtr factory ) {
CATCH_ENFORCE( name.find( "::" ) == name.npos, CATCH_ENFORCE( name.find( "::" ) == name.npos,
"'::' is not allowed in reporter name: '" + name + '\'' ); "'::' 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" ); 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( void ReporterRegistry::registerListener(
Detail::unique_ptr<EventListenerFactory> factory ) { 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 { std::map<std::string,
return m_factories; IReporterFactoryPtr,
} Detail::CaseInsensitiveLess> const&
IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { ReporterRegistry::getFactories() const {
return m_listeners; 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 #ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED
#define CATCH_REPORTER_REGISTRY_HPP_INCLUDED #define CATCH_REPORTER_REGISTRY_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_reporter.hpp> #include <catch2/internal/catch_case_insensitive_comparisons.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp> #include <catch2/internal/catch_unique_ptr.hpp>
#include <map> #include <map>
#include <string>
#include <vector>
namespace Catch { 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: public:
ReporterRegistry(); 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 registerReporter( std::string const& name,
void registerListener( Detail::unique_ptr<EventListenerFactory> factory ); IReporterFactoryPtr factory );
FactoryMap const& getFactories() const override; void
Listeners const& getListeners() const override; registerListener( Detail::unique_ptr<EventListenerFactory> factory );
private: std::map<std::string,
FactoryMap m_factories; IReporterFactoryPtr,
Listeners m_listeners; Detail::CaseInsensitiveLess> const&
getFactories() const;
std::vector<Detail::unique_ptr<EventListenerFactory>> const&
getListeners() const;
}; };
}
} // end namespace Catch
#endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED #endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED

View File

@@ -7,7 +7,11 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_run_context.hpp> #include <catch2/internal/catch_run_context.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/catch_assertion_result.hpp>
#include <catch2/catch_user_config.hpp> #include <catch2/catch_user_config.hpp>
#include <catch2/catch_timer.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp> #include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
@@ -15,10 +19,14 @@
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp> #include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/internal/catch_random_number_generator.hpp> #include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/catch_timer.hpp>
#include <catch2/internal/catch_output_redirect.hpp> #include <catch2/internal/catch_output_redirect.hpp>
#include <catch2/internal/catch_assertion_handler.hpp> #include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp> #include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_optional.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/internal/catch_test_case_tracker.hpp>
#include <catch2/catch_message.hpp>
#include <cassert> #include <cassert>
#include <algorithm> #include <algorithm>
@@ -26,15 +34,22 @@
namespace Catch { namespace Catch {
namespace Generators { namespace Generators {
struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { namespace {
struct GeneratorTracker final : TestCaseTracking::TrackerBase,
IGeneratorTracker {
GeneratorBasePtr m_generator; GeneratorBasePtr m_generator;
GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) GeneratorTracker(
: TrackerBase( nameAndLocation, ctx, parent ) TestCaseTracking::NameAndLocation&& nameAndLocation,
{} TrackerContext& ctx,
~GeneratorTracker() override; ITracker* parent ):
TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {}
~GeneratorTracker() override = default;
static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { static GeneratorTracker*
acquire( TrackerContext& ctx,
TestCaseTracking::NameAndLocationRef const&
nameAndLocation ) {
GeneratorTracker* tracker; GeneratorTracker* tracker;
ITracker& currentTracker = ctx.currentTracker(); ITracker& currentTracker = ctx.currentTracker();
@@ -50,23 +65,23 @@ namespace Catch {
// //
// without it, the code above creates 5 nested generators. // without it, the code above creates 5 nested generators.
if ( currentTracker.nameAndLocation() == nameAndLocation ) { if ( currentTracker.nameAndLocation() == nameAndLocation ) {
auto thisTracker = auto thisTracker = currentTracker.parent()->findChild(
currentTracker.parent()->findChild( nameAndLocation ); nameAndLocation );
assert( thisTracker ); assert( thisTracker );
assert( thisTracker->isGeneratorTracker() ); assert( thisTracker->isGeneratorTracker() );
tracker = static_cast<GeneratorTracker*>( thisTracker ); tracker = static_cast<GeneratorTracker*>( thisTracker );
} else if ( ITracker* childTracker = } else if ( ITracker* childTracker =
currentTracker.findChild( nameAndLocation ) ) { currentTracker.findChild(
nameAndLocation ) ) {
assert( childTracker ); assert( childTracker );
assert( childTracker->isGeneratorTracker() ); assert( childTracker->isGeneratorTracker() );
tracker = static_cast<GeneratorTracker*>( childTracker ); tracker =
static_cast<GeneratorTracker*>( childTracker );
} else { } else {
return nullptr; return nullptr;
} }
if( !tracker->isComplete() ) { if ( !tracker->isComplete() ) { tracker->open(); }
tracker->open();
}
return tracker; return tracker;
} }
@@ -84,25 +99,26 @@ namespace Catch {
// This catches cases where `GENERATE` is placed between two // This catches cases where `GENERATE` is placed between two
// `SECTION`s. // `SECTION`s.
// **The check for m_children.empty cannot be removed**. // **The check for m_children.empty cannot be removed**.
// doing so would break `GENERATE` _not_ followed by `SECTION`s. // doing so would break `GENERATE` _not_ followed by
// `SECTION`s.
const bool should_wait_for_child = [&]() { const bool should_wait_for_child = [&]() {
// No children -> nobody to wait for // No children -> nobody to wait for
if ( m_children.empty() ) { if ( m_children.empty() ) { return false; }
return false;
}
// If at least one child started executing, don't wait // If at least one child started executing, don't wait
if ( std::find_if( if ( std::find_if(
m_children.begin(), m_children.begin(),
m_children.end(), m_children.end(),
[]( TestCaseTracking::ITrackerPtr const& tracker ) { []( TestCaseTracking::ITrackerPtr const&
tracker ) {
return tracker->hasStarted(); return tracker->hasStarted();
} ) != m_children.end() ) { } ) != m_children.end() ) {
return false; return false;
} }
// No children have started. We need to check if they _can_ // No children have started. We need to check if they
// start, and thus we should wait for them, or they cannot // _can_ start, and thus we should wait for them, or
// start (due to filters), and we shouldn't wait for them // they cannot start (due to filters), and we shouldn't
// wait for them
ITracker* parent = m_parent; ITracker* parent = m_parent;
// This is safe: there is always at least one section // This is safe: there is always at least one section
// tracker in a test case tracking tree // tracker in a test case tracking tree
@@ -116,17 +132,16 @@ namespace Catch {
static_cast<SectionTracker const&>( *parent ); static_cast<SectionTracker const&>( *parent );
auto const& filters = parentSection.getFilters(); auto const& filters = parentSection.getFilters();
// No filters -> no restrictions on running sections // No filters -> no restrictions on running sections
if ( filters.empty() ) { if ( filters.empty() ) { return true; }
return true;
}
for ( auto const& child : m_children ) { for ( auto const& child : m_children ) {
if ( child->isSectionTracker() && if ( child->isSectionTracker() &&
std::find( std::find( filters.begin(),
filters.begin(),
filters.end(), filters.end(),
static_cast<SectionTracker const&>( *child ) static_cast<SectionTracker const&>(
.trimmedName() ) != filters.end() ) { *child )
.trimmedName() ) !=
filters.end() ) {
return true; return true;
} }
} }
@@ -154,39 +169,47 @@ namespace Catch {
m_generator = CATCH_MOVE( generator ); m_generator = CATCH_MOVE( generator );
} }
}; };
GeneratorTracker::~GeneratorTracker() = default; } // namespace
} }
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
: m_runInfo(_config->name()), struct RunContext::RunContextImpl {
m_context(getCurrentMutableContext()), Optional<AssertionResult> lastResult;
FatalConditionHandler fatalConditionhandler;
TrackerContext trackerContext;
std::vector<MessageInfo> messages;
// Fake owners for unscoped messages
std::vector<ScopedMessage> messageScopes;
IEventListenerPtr reporter;
std::vector<SectionEndInfo> unfinishedSections;
std::vector<ITracker*> activeSections;
};
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter):
m_impl( Detail::make_unique<RunContextImpl>() ),
m_runInfo(_config->name()),
m_config(_config), m_config(_config),
m_reporter(CATCH_MOVE(reporter)),
m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) m_includeSuccessfulResults( m_config->includeSuccessfulResults() || reporter->getPreferences().shouldReportAllAssertions )
{ {
m_context.setResultCapture(this); getCurrentMutableContext().setResultCapture(this);
m_reporter->testRunStarting(m_runInfo); m_impl->reporter = CATCH_MOVE( reporter );
m_impl->reporter->testRunStarting(m_runInfo);
} }
RunContext::~RunContext() { RunContext::~RunContext() {
m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); m_impl->reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
} }
Totals RunContext::runTest(TestCaseHandle const& testCase) { Totals RunContext::runTest(TestCaseHandle const& testCase) {
const Totals prevTotals = m_totals; const Totals prevTotals = m_totals;
std::string redirectedCout;
std::string redirectedCerr;
auto const& testInfo = testCase.getTestCaseInfo(); auto const& testInfo = testCase.getTestCaseInfo();
m_impl->reporter->testCaseStarting( testInfo );
m_reporter->testCaseStarting(testInfo);
m_activeTestCase = &testCase; m_activeTestCase = &testCase;
ITracker& rootTracker = m_trackerContext.startRun(); ITracker& rootTracker = m_impl->trackerContext.startRun();
assert(rootTracker.isSectionTracker()); assert(rootTracker.isSectionTracker());
static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun()); static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
@@ -224,11 +247,13 @@ namespace Catch {
seedRng( *m_config ); seedRng( *m_config );
uint64_t testRuns = 0; uint64_t testRuns = 0;
std::string redirectedCout;
std::string redirectedCerr;
do { do {
m_trackerContext.startCycle(); m_impl->trackerContext.startCycle();
m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); m_testCaseTracker = &SectionTracker::acquire(m_impl->trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo));
m_reporter->testCasePartialStarting(testInfo, testRuns); m_impl->reporter->testCasePartialStarting( testInfo, testRuns );
const auto beforeRunTotals = m_totals; const auto beforeRunTotals = m_totals;
std::string oneRunCout, oneRunCerr; std::string oneRunCout, oneRunCerr;
@@ -237,9 +262,9 @@ namespace Catch {
redirectedCerr += oneRunCerr; redirectedCerr += oneRunCerr;
const auto singleRunTotals = m_totals.delta(beforeRunTotals); const auto singleRunTotals = m_totals.delta(beforeRunTotals);
auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting()); auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
m_reporter->testCasePartialEnded(statsForOneRun, testRuns); m_impl->reporter->testCasePartialEnded( statsForOneRun, testRuns );
++testRuns; ++testRuns;
} while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
@@ -250,10 +275,11 @@ namespace Catch {
deltaTotals.testCases.failed++; deltaTotals.testCases.failed++;
} }
m_totals.testCases += deltaTotals.testCases; m_totals.testCases += deltaTotals.testCases;
m_reporter->testCaseEnded(TestCaseStats(testInfo, m_impl->reporter->testCaseEnded(
TestCaseStats( testInfo,
deltaTotals, deltaTotals,
redirectedCout, CATCH_MOVE( redirectedCout ),
redirectedCerr, CATCH_MOVE( redirectedCerr ),
aborting() ) ); aborting() ) );
m_activeTestCase = nullptr; m_activeTestCase = nullptr;
@@ -283,29 +309,35 @@ namespace Catch {
m_lastAssertionPassed = true; m_lastAssertionPassed = true;
} }
m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); m_impl->reporter->assertionEnded(AssertionStats(result, m_impl->messages, m_totals));
if (result.getResultType() != ResultWas::Warning) if ( result.getResultType() != ResultWas::Warning ) {
m_messageScopes.clear(); m_impl->messageScopes.clear();
}
// Reset working state // Reset working state
resetAssertionInfo(); resetAssertionInfo();
m_lastResult = result; m_impl->lastResult = result;
} }
void RunContext::resetAssertionInfo() { void RunContext::resetAssertionInfo() {
m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.macroName = StringRef();
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
} }
bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) {
ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); ITracker& sectionTracker =
SectionTracker::acquire( m_impl->trackerContext,
TestCaseTracking::NameAndLocationRef(
sectionName, sectionLineInfo ) );
if (!sectionTracker.isOpen()) if (!sectionTracker.isOpen())
return false; return false;
m_activeSections.push_back(&sectionTracker); m_impl->activeSections.push_back(&sectionTracker);
SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) );
m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
m_reporter->sectionStarting(sectionInfo); m_impl->reporter->sectionStarting( sectionInfo );
assertions = m_totals.assertions; assertions = m_totals.assertions;
@@ -316,9 +348,9 @@ namespace Catch {
SourceLineInfo const& lineInfo ) { SourceLineInfo const& lineInfo ) {
using namespace Generators; using namespace Generators;
GeneratorTracker* tracker = GeneratorTracker::acquire( GeneratorTracker* tracker = GeneratorTracker::acquire(
m_trackerContext, m_impl->trackerContext,
TestCaseTracking::NameAndLocation( TestCaseTracking::NameAndLocationRef(
static_cast<std::string>( generatorName ), lineInfo ) ); generatorName, lineInfo ) );
m_lastAssertionInfo.lineInfo = lineInfo; m_lastAssertionInfo.lineInfo = lineInfo;
return tracker; return tracker;
} }
@@ -329,13 +361,13 @@ namespace Catch {
Generators::GeneratorBasePtr&& generator ) { Generators::GeneratorBasePtr&& generator ) {
auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo ); auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo );
auto& currentTracker = m_trackerContext.currentTracker(); auto& currentTracker = m_impl->trackerContext.currentTracker();
assert( assert(
currentTracker.nameAndLocation() != nameAndLoc && currentTracker.nameAndLocation() != nameAndLoc &&
"Trying to create tracker for a genreator that already has one" ); "Trying to create tracker for a genreator that already has one" );
auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>( auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>(
nameAndLoc, m_trackerContext, &currentTracker ); CATCH_MOVE(nameAndLoc), m_impl->trackerContext, &currentTracker );
auto ret = newTracker.get(); auto ret = newTracker.get();
currentTracker.addChild( CATCH_MOVE( newTracker ) ); currentTracker.addChild( CATCH_MOVE( newTracker ) );
@@ -349,60 +381,68 @@ namespace Catch {
return false; return false;
if (!m_config->warnAboutMissingAssertions()) if (!m_config->warnAboutMissingAssertions())
return false; return false;
if (m_trackerContext.currentTracker().hasChildren()) if (m_impl->trackerContext.currentTracker().hasChildren())
return false; return false;
m_totals.assertions.failed++; m_totals.assertions.failed++;
assertions.failed++; assertions.failed++;
return true; return true;
} }
void RunContext::sectionEnded(SectionEndInfo const & endInfo) { void RunContext::sectionEnded(SectionEndInfo&& endInfo) {
Counts assertions = m_totals.assertions - endInfo.prevAssertions; Counts assertions = m_totals.assertions - endInfo.prevAssertions;
bool missingAssertions = testForMissingAssertions(assertions); bool missingAssertions = testForMissingAssertions(assertions);
if (!m_activeSections.empty()) { if (!m_impl->activeSections.empty()) {
m_activeSections.back()->close(); m_impl->activeSections.back()->close();
m_activeSections.pop_back(); m_impl->activeSections.pop_back();
} }
m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); m_impl->reporter->sectionEnded(
m_messages.clear(); SectionStats( CATCH_MOVE( endInfo.sectionInfo ),
m_messageScopes.clear(); assertions,
endInfo.durationInSeconds,
missingAssertions ) );
m_impl->messages.clear();
m_impl->messageScopes.clear();
} }
void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) {
if (m_unfinishedSections.empty()) if ( m_impl->unfinishedSections.empty() ) {
m_activeSections.back()->fail(); m_impl->activeSections.back()->fail();
else } else {
m_activeSections.back()->close(); m_impl->activeSections.back()->close();
m_activeSections.pop_back(); }
m_impl->activeSections.pop_back();
m_unfinishedSections.push_back(endInfo); m_impl->unfinishedSections.push_back(CATCH_MOVE(endInfo));
} }
void RunContext::benchmarkPreparing( StringRef name ) { void RunContext::benchmarkPreparing( StringRef name ) {
m_reporter->benchmarkPreparing(name); m_impl->reporter->benchmarkPreparing( name );
} }
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
m_reporter->benchmarkStarting( info ); m_impl->reporter->benchmarkStarting( info );
} }
void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
m_reporter->benchmarkEnded( stats ); m_impl->reporter->benchmarkEnded( stats );
} }
void RunContext::benchmarkFailed( StringRef error ) { void RunContext::benchmarkFailed( StringRef error ) {
m_reporter->benchmarkFailed( error ); m_impl->reporter->benchmarkFailed( error );
} }
void RunContext::pushScopedMessage(MessageInfo const & message) { void RunContext::pushScopedMessage(MessageInfo const & message) {
m_messages.push_back(message); m_impl->messages.push_back(message);
} }
void RunContext::popScopedMessage(MessageInfo const & message) { void RunContext::popScopedMessage(MessageInfo const & message) {
m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); m_impl->messages.erase( std::remove( m_impl->messages.begin(),
m_impl->messages.end(),
message ),
m_impl->messages.end() );
} }
void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
m_messageScopes.emplace_back( builder ); m_impl->messageScopes.emplace_back( CATCH_MOVE(builder) );
} }
std::string RunContext::getCurrentTestName() const { std::string RunContext::getCurrentTestName() const {
@@ -412,7 +452,7 @@ namespace Catch {
} }
const AssertionResult * RunContext::getLastResult() const { const AssertionResult * RunContext::getLastResult() const {
return &(*m_lastResult); return &(*m_impl->lastResult);
} }
void RunContext::exceptionEarlyReported() { void RunContext::exceptionEarlyReported() {
@@ -421,13 +461,13 @@ namespace Catch {
void RunContext::handleFatalErrorCondition( StringRef message ) { void RunContext::handleFatalErrorCondition( StringRef message ) {
// First notify reporter that bad things happened // First notify reporter that bad things happened
m_reporter->fatalErrorEncountered(message); m_impl->reporter->fatalErrorEncountered(message);
// Don't rebuild the result -- the stringification itself can cause more fatal errors // Don't rebuild the result -- the stringification itself can cause more fatal errors
// Instead, fake a result data. // Instead, fake a result data.
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
tempResult.message = static_cast<std::string>(message); tempResult.message = static_cast<std::string>(message);
AssertionResult result(m_lastAssertionInfo, tempResult); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
assertionEnded(result); assertionEnded(result);
@@ -439,21 +479,21 @@ namespace Catch {
Counts assertions; Counts assertions;
assertions.failed = 1; assertions.failed = 1;
SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false);
m_reporter->sectionEnded(testCaseSectionStats); m_impl->reporter->sectionEnded(testCaseSectionStats);
auto const& testInfo = m_activeTestCase->getTestCaseInfo(); auto const& testInfo = m_activeTestCase->getTestCaseInfo();
Totals deltaTotals; Totals deltaTotals;
deltaTotals.testCases.failed = 1; deltaTotals.testCases.failed = 1;
deltaTotals.assertions.failed = 1; deltaTotals.assertions.failed = 1;
m_reporter->testCaseEnded(TestCaseStats(testInfo, m_impl->reporter->testCaseEnded(TestCaseStats(testInfo,
deltaTotals, deltaTotals,
std::string(), std::string(),
std::string(), std::string(),
false)); false));
m_totals.testCases.failed++; m_totals.testCases.failed++;
m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); m_impl->reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
} }
bool RunContext::lastAssertionPassed() { bool RunContext::lastAssertionPassed() {
@@ -464,7 +504,7 @@ namespace Catch {
m_lastAssertionPassed = true; m_lastAssertionPassed = true;
++m_totals.assertions.passed; ++m_totals.assertions.passed;
resetAssertionInfo(); resetAssertionInfo();
m_messageScopes.clear(); m_impl->messageScopes.clear();
} }
bool RunContext::aborting() const { bool RunContext::aborting() const {
@@ -474,7 +514,7 @@ namespace Catch {
void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
m_reporter->sectionStarting(testCaseSection); m_impl->reporter->sectionStarting(testCaseSection);
Counts prevAssertions = m_totals.assertions; Counts prevAssertions = m_totals.assertions;
double duration = 0; double duration = 0;
m_shouldReportUnexpected = true; m_shouldReportUnexpected = true;
@@ -482,7 +522,7 @@ namespace Catch {
Timer timer; Timer timer;
CATCH_TRY { CATCH_TRY {
if (m_reporter->getPreferences().shouldRedirectStdOut) { if (m_impl->reporter->getPreferences().shouldRedirectStdOut) {
#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) #if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
@@ -515,18 +555,18 @@ namespace Catch {
m_testCaseTracker->close(); m_testCaseTracker->close();
handleUnfinishedSections(); handleUnfinishedSections();
m_messages.clear(); m_impl->messages.clear();
m_messageScopes.clear(); m_impl->messageScopes.clear();
SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats); m_impl->reporter->sectionEnded(testCaseSectionStats);
} }
void RunContext::invokeActiveTestCase() { void RunContext::invokeActiveTestCase() {
// We need to engage a handler for signals/structured exceptions // We need to engage a handler for signals/structured exceptions
// before running the tests themselves, or the binary can crash // before running the tests themselves, or the binary can crash
// without failed test being reported. // without failed test being reported.
FatalConditionHandlerGuard _(&m_fatalConditionhandler); FatalConditionHandlerGuard _(&m_impl->fatalConditionhandler);
// We keep having issue where some compilers warn about an unused // We keep having issue where some compilers warn about an unused
// variable, even though the type has non-trivial constructor and // variable, even though the type has non-trivial constructor and
// destructor. This is annoying and ugly, but it makes them stfu. // destructor. This is annoying and ugly, but it makes them stfu.
@@ -538,12 +578,12 @@ namespace Catch {
void RunContext::handleUnfinishedSections() { void RunContext::handleUnfinishedSections() {
// If sections ended prematurely due to an exception we stored their // If sections ended prematurely due to an exception we stored their
// infos here so we can tear them down outside the unwind process. // infos here so we can tear them down outside the unwind process.
for (auto it = m_unfinishedSections.rbegin(), for (auto it = m_impl->unfinishedSections.rbegin(),
itEnd = m_unfinishedSections.rend(); itEnd = m_impl->unfinishedSections.rend();
it != itEnd; it != itEnd;
++it) ++it)
sectionEnded(*it); sectionEnded(CATCH_MOVE(*it));
m_unfinishedSections.clear(); m_impl->unfinishedSections.clear();
} }
void RunContext::handleExpr( void RunContext::handleExpr(
@@ -551,7 +591,7 @@ namespace Catch {
ITransientExpression const& expr, ITransientExpression const& expr,
AssertionReaction& reaction AssertionReaction& reaction
) { ) {
m_reporter->assertionStarting( info ); m_impl->reporter->assertionStarting( info );
bool negated = isFalseTest( info.resultDisposition ); bool negated = isFalseTest( info.resultDisposition );
bool result = expr.getResult() != negated; bool result = expr.getResult() != negated;
@@ -578,7 +618,7 @@ namespace Catch {
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( negated ) ); AssertionResultData data( resultType, LazyExpression( negated ) );
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
assertionEnded( assertionResult ); assertionEnded( assertionResult );
@@ -590,13 +630,14 @@ namespace Catch {
StringRef message, StringRef message,
AssertionReaction& reaction AssertionReaction& reaction
) { ) {
m_reporter->assertionStarting( info ); m_impl->reporter->assertionStarting( info );
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResultData data( resultType, LazyExpression( false ) );
data.message = static_cast<std::string>(message); data.message = static_cast<std::string>(message);
AssertionResult assertionResult{ m_lastAssertionInfo, data }; AssertionResult assertionResult{ m_lastAssertionInfo,
CATCH_MOVE( data ) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
if ( !assertionResult.isOk() ) { if ( !assertionResult.isOk() ) {
populateReaction( reaction ); populateReaction( reaction );
@@ -615,14 +656,14 @@ namespace Catch {
void RunContext::handleUnexpectedInflightException( void RunContext::handleUnexpectedInflightException(
AssertionInfo const& info, AssertionInfo const& info,
std::string const& message, std::string&& message,
AssertionReaction& reaction AssertionReaction& reaction
) { ) {
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = message; data.message = CATCH_MOVE(message);
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE(data) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
populateReaction( reaction ); populateReaction( reaction );
} }
@@ -635,11 +676,12 @@ namespace Catch {
void RunContext::handleIncomplete( void RunContext::handleIncomplete(
AssertionInfo const& info AssertionInfo const& info
) { ) {
using namespace std::string_literals;
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
} }
void RunContext::handleNonExpr( void RunContext::handleNonExpr(
@@ -650,7 +692,7 @@ namespace Catch {
m_lastAssertionInfo = info; m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResultData data( resultType, LazyExpression( false ) );
AssertionResult assertionResult{ info, data }; AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionEnded( assertionResult ); assertionEnded( assertionResult );
if( !assertionResult.isOk() ) if( !assertionResult.isOk() )
@@ -658,7 +700,7 @@ namespace Catch {
} }
IResultCapture& getResultCapture() { RunContext& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture()) if (auto* capture = getCurrentContext().getResultCapture())
return *capture; return *capture;
else else

View File

@@ -8,37 +8,60 @@
#ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED #ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED
#define CATCH_RUN_CONTEXT_HPP_INCLUDED #define CATCH_RUN_CONTEXT_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_reporter.hpp> #include <catch2/catch_test_run_info.hpp>
#include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/catch_totals.hpp> #include <catch2/catch_totals.hpp>
#include <catch2/internal/catch_test_case_tracker.hpp>
#include <catch2/catch_assertion_info.hpp> #include <catch2/catch_assertion_info.hpp>
#include <catch2/catch_assertion_result.hpp> #include <catch2/internal/catch_benchmark_stats_fwd.hpp>
#include <catch2/internal/catch_optional.hpp> #include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <string> #include <string>
#include <chrono>
namespace Catch { namespace Catch {
class IMutableContext; class TestCaseHandle;
class AssertionResult;
struct AssertionInfo;
struct SectionInfo;
struct SectionEndInfo;
struct MessageInfo;
struct MessageBuilder;
struct Counts;
struct AssertionReaction;
struct SourceLineInfo;
class ITransientExpression;
class IGeneratorTracker;
struct BenchmarkInfo;
namespace Generators {
class GeneratorUntypedBase;
using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
}
class IGeneratorTracker; class IGeneratorTracker;
class IConfig; class IConfig;
// Fixme: Take out the namespace?
namespace TestCaseTracking {
class ITracker;
}
using TestCaseTracking::ITracker;
/////////////////////////////////////////////////////////////////////////// class IEventListener;
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
class RunContext : public IResultCapture {
class RunContext {
struct RunContextImpl;
Detail::unique_ptr<RunContextImpl> m_impl;
public: public:
RunContext( RunContext const& ) = delete; RunContext( RunContext const& ) = delete;
RunContext& operator =( RunContext const& ) = delete; RunContext& operator =( RunContext const& ) = delete;
explicit RunContext( IConfig const* _config, IEventListenerPtr&& reporter ); explicit RunContext( IConfig const* _config, IEventListenerPtr&& reporter );
~RunContext() override; ~RunContext();
Totals runTest(TestCaseHandle const& testCase); Totals runTest(TestCaseHandle const& testCase);
@@ -48,61 +71,63 @@ namespace Catch {
void handleExpr void handleExpr
( AssertionInfo const& info, ( AssertionInfo const& info,
ITransientExpression const& expr, ITransientExpression const& expr,
AssertionReaction& reaction ) override; AssertionReaction& reaction );
void handleMessage void handleMessage
( AssertionInfo const& info, ( AssertionInfo const& info,
ResultWas::OfType resultType, ResultWas::OfType resultType,
StringRef message, StringRef message,
AssertionReaction& reaction ) override; AssertionReaction& reaction );
void handleUnexpectedExceptionNotThrown void handleUnexpectedExceptionNotThrown
( AssertionInfo const& info, ( AssertionInfo const& info,
AssertionReaction& reaction ) override; AssertionReaction& reaction );
void handleUnexpectedInflightException void handleUnexpectedInflightException
( AssertionInfo const& info, ( AssertionInfo const& info,
std::string const& message, std::string&& message,
AssertionReaction& reaction ) override; AssertionReaction& reaction );
void handleIncomplete void handleIncomplete
( AssertionInfo const& info ) override; ( AssertionInfo const& info );
void handleNonExpr void handleNonExpr
( AssertionInfo const &info, ( AssertionInfo const &info,
ResultWas::OfType resultType, ResultWas::OfType resultType,
AssertionReaction &reaction ) override; AssertionReaction &reaction );
bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; bool sectionStarted( StringRef sectionName,
SourceLineInfo const& sectionLineInfo,
Counts& assertions );
void sectionEnded( SectionEndInfo const& endInfo ) override; void sectionEnded( SectionEndInfo&& endInfo );
void sectionEndedEarly( SectionEndInfo const& endInfo ) override; void sectionEndedEarly( SectionEndInfo&& endInfo );
IGeneratorTracker* IGeneratorTracker*
acquireGeneratorTracker( StringRef generatorName, acquireGeneratorTracker( StringRef generatorName,
SourceLineInfo const& lineInfo ) override; SourceLineInfo const& lineInfo );
IGeneratorTracker* createGeneratorTracker( IGeneratorTracker* createGeneratorTracker(
StringRef generatorName, StringRef generatorName,
SourceLineInfo lineInfo, SourceLineInfo lineInfo,
Generators::GeneratorBasePtr&& generator ) override; Generators::GeneratorBasePtr&& generator );
void benchmarkPreparing( StringRef name ) override; void benchmarkPreparing( StringRef name );
void benchmarkStarting( BenchmarkInfo const& info ) override; void benchmarkStarting( BenchmarkInfo const& info );
void benchmarkEnded( BenchmarkStats<> const& stats ) override; void benchmarkEnded( BenchmarkStats<> const& stats );
void benchmarkFailed( StringRef error ) override; void benchmarkFailed( StringRef error );
void pushScopedMessage( MessageInfo const& message ) override; void pushScopedMessage( MessageInfo const& message );
void popScopedMessage( MessageInfo const& message ) override; void popScopedMessage( MessageInfo const& message );
void emplaceUnscopedMessage( MessageBuilder const& builder ) override; void emplaceUnscopedMessage( MessageBuilder&& builder );
std::string getCurrentTestName() const override; std::string getCurrentTestName() const;
const AssertionResult* getLastResult() const override; const AssertionResult* getLastResult() const;
void exceptionEarlyReported() override; void exceptionEarlyReported();
void handleFatalErrorCondition( StringRef message ) override; void handleFatalErrorCondition( StringRef message );
bool lastAssertionPassed() override; bool lastAssertionPassed();
void assertionPassed() override; void assertionPassed();
public: public:
// !TBD We need to do this another way! // !TBD We need to do this another way!
@@ -130,21 +155,12 @@ namespace Catch {
void handleUnfinishedSections(); void handleUnfinishedSections();
TestRunInfo m_runInfo; TestRunInfo m_runInfo;
IMutableContext& m_context;
TestCaseHandle const* m_activeTestCase = nullptr; TestCaseHandle const* m_activeTestCase = nullptr;
ITracker* m_testCaseTracker = nullptr; ITracker* m_testCaseTracker = nullptr;
Optional<AssertionResult> m_lastResult;
IConfig const* m_config; IConfig const* m_config;
Totals m_totals; Totals m_totals;
IEventListenerPtr m_reporter;
std::vector<MessageInfo> m_messages;
std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
AssertionInfo m_lastAssertionInfo; AssertionInfo m_lastAssertionInfo;
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;
FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false; bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true; bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults; bool m_includeSuccessfulResults;
@@ -152,6 +168,9 @@ namespace Catch {
void seedRng(IConfig const& config); void seedRng(IConfig const& config);
unsigned int rngSeed(); unsigned int rngSeed();
RunContext& getResultCapture();
} // end namespace Catch } // end namespace Catch
#endif // CATCH_RUN_CONTEXT_HPP_INCLUDED #endif // CATCH_RUN_CONTEXT_HPP_INCLUDED

View File

@@ -15,7 +15,7 @@ namespace Catch {
Section::Section( SectionInfo&& info ): Section::Section( SectionInfo&& info ):
m_info( CATCH_MOVE( info ) ), m_info( CATCH_MOVE( info ) ),
m_sectionIncluded( m_sectionIncluded(
getResultCapture().sectionStarted( m_info, m_assertions ) ) { getResultCapture().sectionStarted( m_info.name, m_info.lineInfo, m_assertions ) ) {
// Non-"included" sections will not use the timing information // Non-"included" sections will not use the timing information
// anyway, so don't bother with the potential syscall. // anyway, so don't bother with the potential syscall.
if (m_sectionIncluded) { if (m_sectionIncluded) {
@@ -23,13 +23,31 @@ namespace Catch {
} }
} }
Section::Section( SourceLineInfo const& _lineInfo,
StringRef _name,
const char* const ):
m_info( { "invalid", static_cast<std::size_t>( -1 ) }, std::string{} ),
m_sectionIncluded(
getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) {
// We delay initialization the SectionInfo member until we know
// this section needs it, so we avoid allocating std::string for name.
// We also delay timer start to avoid the potential syscall unless we
// will actually use the result.
if ( m_sectionIncluded ) {
m_info.name = static_cast<std::string>( _name );
m_info.lineInfo = _lineInfo;
m_timer.start();
}
}
Section::~Section() { Section::~Section() {
if( m_sectionIncluded ) { if( m_sectionIncluded ) {
SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; SectionEndInfo endInfo{ CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds() };
if( uncaught_exceptions() ) if ( uncaught_exceptions() ) {
getResultCapture().sectionEndedEarly( endInfo ); getResultCapture().sectionEndedEarly( CATCH_MOVE(endInfo) );
else } else {
getResultCapture().sectionEnded( endInfo ); getResultCapture().sectionEnded( CATCH_MOVE( endInfo ) );
}
} }
} }

View File

@@ -20,6 +20,9 @@ namespace Catch {
class Section : Detail::NonCopyable { class Section : Detail::NonCopyable {
public: public:
Section( SectionInfo&& info ); Section( SectionInfo&& info );
Section( SourceLineInfo const& _lineInfo,
StringRef _name,
const char* const = nullptr );
~Section(); ~Section();
// This indicates whether the section should be executed or not // This indicates whether the section should be executed or not
@@ -38,7 +41,7 @@ namespace Catch {
#define INTERNAL_CATCH_SECTION( ... ) \ #define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ #define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \

View File

@@ -17,10 +17,6 @@ namespace Catch {
: StringRef( rawChars, std::strlen(rawChars) ) : StringRef( rawChars, std::strlen(rawChars) )
{} {}
auto StringRef::operator == ( StringRef other ) const noexcept -> bool {
return m_size == other.m_size
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
}
bool StringRef::operator<(StringRef rhs) const noexcept { bool StringRef::operator<(StringRef rhs) const noexcept {
if (m_size < rhs.m_size) { if (m_size < rhs.m_size) {

View File

@@ -13,6 +13,8 @@
#include <iosfwd> #include <iosfwd>
#include <cassert> #include <cassert>
#include <cstring>
namespace Catch { namespace Catch {
/// A non-owning string class (similar to the forthcoming std::string_view) /// A non-owning string class (similar to the forthcoming std::string_view)
@@ -49,7 +51,10 @@ namespace Catch {
} }
public: // operators public: // operators
auto operator == ( StringRef other ) const noexcept -> bool; auto operator == ( StringRef other ) const noexcept -> bool {
return m_size == other.m_size
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
}
auto operator != (StringRef other) const noexcept -> bool { auto operator != (StringRef other) const noexcept -> bool {
return !(*this == other); return !(*this == other);
} }

View File

@@ -8,16 +8,24 @@
#include <catch2/internal/catch_tag_alias_registry.hpp> #include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_console_colour.hpp> #include <catch2/internal/catch_console_colour.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
namespace Catch { #include <map>
TagAliasRegistry::~TagAliasRegistry() {} namespace Catch {
struct TagAliasRegistry::TagAliasRegistryImpl {
std::map<std::string, TagAlias> registry;
};
TagAliasRegistry::TagAliasRegistry():
m_impl( Detail::make_unique<TagAliasRegistryImpl>() ){}
TagAliasRegistry::~TagAliasRegistry() = default;
TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
auto it = m_registry.find( alias ); auto it = m_impl->registry.find( alias );
if( it != m_registry.end() ) if( it != m_impl->registry.end() )
return &(it->second); return &(it->second);
else else
return nullptr; return nullptr;
@@ -25,7 +33,7 @@ namespace Catch {
std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
std::string expandedTestSpec = unexpandedTestSpec; std::string expandedTestSpec = unexpandedTestSpec;
for( auto const& registryKvp : m_registry ) { for( auto const& registryKvp : m_impl->registry ) {
std::size_t pos = expandedTestSpec.find( registryKvp.first ); std::size_t pos = expandedTestSpec.find( registryKvp.first );
if( pos != std::string::npos ) { if( pos != std::string::npos ) {
expandedTestSpec = expandedTestSpec.substr( 0, pos ) + expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
@@ -40,15 +48,13 @@ namespace Catch {
CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'),
"error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo );
CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, CATCH_ENFORCE( m_impl->registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second,
"error: tag alias, '" << alias << "' already registered.\n" "error: tag alias, '" << alias << "' already registered.\n"
<< "\tFirst seen at: " << find(alias)->lineInfo << "\n" << "\tFirst seen at: " << find(alias)->lineInfo << "\n"
<< "\tRedefined at: " << lineInfo ); << "\tRedefined at: " << lineInfo );
} }
ITagAliasRegistry::~ITagAliasRegistry() = default; TagAliasRegistry const& TagAliasRegistry::get() {
ITagAliasRegistry const& ITagAliasRegistry::get() {
return getRegistryHub().getTagAliasRegistry(); return getRegistryHub().getTagAliasRegistry();
} }

View File

@@ -8,24 +8,30 @@
#ifndef CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED #ifndef CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
#define CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED #define CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
#include <catch2/catch_tag_alias.hpp> #include <catch2/catch_tag_alias.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <map>
#include <string> #include <string>
namespace Catch { namespace Catch {
struct SourceLineInfo; struct SourceLineInfo;
class TagAliasRegistry : public ITagAliasRegistry { class TagAliasRegistry {
struct TagAliasRegistryImpl;
Detail::unique_ptr<TagAliasRegistryImpl> m_impl;
public: public:
~TagAliasRegistry() override; TagAliasRegistry();
TagAlias const* find( std::string const& alias ) const override; ~TagAliasRegistry(); // = default;
std::string expandAliases( std::string const& unexpandedTestSpec ) const override;
//! Nullptr if not present
TagAlias const* find( std::string const& alias ) const;
//! Returns the test spec but with expanded aliases
std::string expandAliases( std::string const& unexpandedTestSpec ) const;
void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
static TagAliasRegistry const& get();
private: private:
std::map<std::string, TagAlias> m_registry;
}; };
} // end namespace Catch } // end namespace Catch

View File

@@ -181,7 +181,7 @@
void reg_tests() { \ void reg_tests() { \
size_t index = 0; \ size_t index = 0; \
using expander = size_t[]; \ using expander = size_t[]; \
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
} \ } \
};\ };\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
@@ -316,7 +316,7 @@
void reg_tests(){\ void reg_tests(){\
size_t index = 0;\ size_t index = 0;\
using expander = size_t[];\ using expander = size_t[];\
(void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
}\ }\
};\ };\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\

View File

@@ -9,7 +9,8 @@
#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/internal/catch_random_number_generator.hpp> #include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/internal/catch_run_context.hpp> #include <catch2/internal/catch_run_context.hpp>
#include <catch2/internal/catch_sharding.hpp> #include <catch2/internal/catch_sharding.hpp>
@@ -23,6 +24,54 @@
namespace Catch { namespace Catch {
namespace {
static bool matchTest( TestCaseHandle const& testCase,
TestSpec const& testSpec,
IConfig const& config ) {
return testSpec.matches( testCase.getTestCaseInfo() ) &&
isThrowSafe( testCase, config );
}
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 );
}
}
} // namespace
struct TestCaseRegistry::TestCaseRegistryImpl {
std::vector<Detail::unique_ptr<TestCaseInfo>> owned_test_infos;
// Keeps a materialized vector for `getAllInfos`.
// We should get rid of that eventually (see interface note)
std::vector<TestCaseInfo*> viewed_test_infos;
std::vector<Detail::unique_ptr<ITestInvoker>> invokers;
std::vector<TestCaseHandle> handles;
mutable TestRunOrder currentSortOrder = TestRunOrder::Declared;
mutable std::vector<TestCaseHandle> sortedFunctions;
};
TestCaseRegistry::TestCaseRegistry():
m_impl( Detail::make_unique<TestCaseRegistryImpl>() ) {}
TestCaseRegistry ::~TestCaseRegistry() = default;
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) { std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) {
switch (config.runOrder()) { switch (config.runOrder()) {
case TestRunOrder::Declared: case TestRunOrder::Declared:
@@ -79,29 +128,6 @@ namespace Catch {
return !testCase.getTestCaseInfo().throws() || config.allowThrows(); 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> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
std::vector<TestCaseHandle> filtered; std::vector<TestCaseHandle> filtered;
filtered.reserve( testCases.size() ); filtered.reserve( testCases.size() );
@@ -117,36 +143,29 @@ namespace Catch {
return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
} }
void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) { void TestCaseRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
m_handles.emplace_back(testInfo.get(), testInvoker.get()); m_impl->handles.emplace_back(testInfo.get(), testInvoker.get());
m_viewed_test_infos.push_back(testInfo.get()); m_impl->viewed_test_infos.push_back(testInfo.get());
m_owned_test_infos.push_back(CATCH_MOVE(testInfo)); m_impl->owned_test_infos.push_back(CATCH_MOVE(testInfo));
m_invokers.push_back(CATCH_MOVE(testInvoker)); m_impl->invokers.push_back(CATCH_MOVE(testInvoker));
} }
std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const { std::vector<TestCaseInfo*> const& TestCaseRegistry::getAllInfos() const {
return m_viewed_test_infos; return m_impl->viewed_test_infos;
} }
std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const { std::vector<TestCaseHandle> const& TestCaseRegistry::getAllTests() const {
return m_handles; return m_impl->handles;
} }
std::vector<TestCaseHandle> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { std::vector<TestCaseHandle> const& TestCaseRegistry::getAllTestsSorted( IConfig const& config ) const {
if( m_sortedFunctions.empty() ) if( m_impl->sortedFunctions.empty() )
enforceNoDuplicateTestCases( m_handles ); enforceNoDuplicateTestCases( m_impl->handles );
if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { if( m_impl->currentSortOrder != config.runOrder() || m_impl->sortedFunctions.empty() ) {
m_sortedFunctions = sortTests( config, m_handles ); m_impl->sortedFunctions = sortTests( config, m_impl->handles );
m_currentSortOrder = config.runOrder(); m_impl->currentSortOrder = config.runOrder();
} }
return m_sortedFunctions; return m_impl->sortedFunctions;
}
///////////////////////////////////////////////////////////////////////////
void TestInvokerAsFunction::invoke() const {
m_testAsFunction();
} }
} // end namespace Catch } // end namespace Catch

View File

@@ -8,8 +8,7 @@
#ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
#define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
#include <catch2/internal/catch_test_registry.hpp> #include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <vector> #include <vector>
@@ -18,49 +17,28 @@ namespace Catch {
class TestCaseHandle; class TestCaseHandle;
class IConfig; class IConfig;
class TestSpec; class TestSpec;
class ITestInvoker;
struct TestCaseInfo;
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ); std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases );
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); 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> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config ); std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
class TestRegistry : public ITestCaseRegistry { class TestCaseRegistry {
struct TestCaseRegistryImpl;
Detail::unique_ptr<TestCaseRegistryImpl> m_impl;
public: public:
~TestRegistry() override = default; TestCaseRegistry();
~TestCaseRegistry(); // = default;
void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker ); void registerTest( Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker );
std::vector<TestCaseInfo*> const& getAllInfos() const override; std::vector<TestCaseInfo*> const& getAllInfos() const;
std::vector<TestCaseHandle> const& getAllTests() const override; std::vector<TestCaseHandle> const& getAllTests() const;
std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const override; std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const;
private:
std::vector<Detail::unique_ptr<TestCaseInfo>> m_owned_test_infos;
// Keeps a materialized vector for `getAllInfos`.
// We should get rid of that eventually (see interface note)
std::vector<TestCaseInfo*> m_viewed_test_infos;
std::vector<Detail::unique_ptr<ITestInvoker>> m_invokers;
std::vector<TestCaseHandle> m_handles;
mutable TestRunOrder m_currentSortOrder = TestRunOrder::Declared;
mutable std::vector<TestCaseHandle> m_sortedFunctions;
};
///////////////////////////////////////////////////////////////////////////
class TestInvokerAsFunction final : public ITestInvoker {
using TestType = void(*)();
TestType m_testAsFunction;
public:
TestInvokerAsFunction(TestType testAsFunction) noexcept:
m_testAsFunction(testAsFunction) {}
void invoke() const override;
}; };
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -22,8 +22,8 @@
namespace Catch { namespace Catch {
namespace TestCaseTracking { namespace TestCaseTracking {
NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location )
: name( _name ), : name( CATCH_MOVE(_name) ),
location( _location ) location( _location )
{} {}
@@ -38,14 +38,17 @@ namespace TestCaseTracking {
m_children.push_back( CATCH_MOVE(child) ); m_children.push_back( CATCH_MOVE(child) );
} }
ITracker* ITracker::findChild( NameAndLocation const& nameAndLocation ) { ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) {
auto it = std::find_if( auto it = std::find_if(
m_children.begin(), m_children.begin(),
m_children.end(), m_children.end(),
[&nameAndLocation]( ITrackerPtr const& tracker ) { [&nameAndLocation]( ITrackerPtr const& tracker ) {
return tracker->nameAndLocation().location == auto const& tnameAndLoc = tracker->nameAndLocation();
nameAndLocation.location && if ( tnameAndLoc.location.line !=
tracker->nameAndLocation().name == nameAndLocation.name; nameAndLocation.location.line ) {
return false;
}
return tnameAndLoc == nameAndLocation;
} ); } );
return ( it != m_children.end() ) ? it->get() : nullptr; return ( it != m_children.end() ) ? it->get() : nullptr;
} }
@@ -53,10 +56,6 @@ namespace TestCaseTracking {
bool ITracker::isSectionTracker() const { return false; } bool ITracker::isSectionTracker() const { return false; }
bool ITracker::isGeneratorTracker() const { return false; } bool ITracker::isGeneratorTracker() const { return false; }
bool ITracker::isSuccessfullyCompleted() const {
return m_runState == CompletedSuccessfully;
}
bool ITracker::isOpen() const { bool ITracker::isOpen() const {
return m_runState != NotStarted && !isComplete(); return m_runState != NotStarted && !isComplete();
} }
@@ -83,16 +82,6 @@ namespace TestCaseTracking {
return *m_rootTracker; return *m_rootTracker;
} }
void TrackerContext::endRun() {
m_rootTracker.reset();
m_currentTracker = nullptr;
m_runState = NotStarted;
}
void TrackerContext::startCycle() {
m_currentTracker = m_rootTracker.get();
m_runState = Executing;
}
void TrackerContext::completeCycle() { void TrackerContext::completeCycle() {
m_runState = CompletedCycle; m_runState = CompletedCycle;
} }
@@ -100,16 +89,13 @@ namespace TestCaseTracking {
bool TrackerContext::completedCycle() const { bool TrackerContext::completedCycle() const {
return m_runState == CompletedCycle; return m_runState == CompletedCycle;
} }
ITracker& TrackerContext::currentTracker() {
return *m_currentTracker;
}
void TrackerContext::setCurrentTracker( ITracker* tracker ) { void TrackerContext::setCurrentTracker( ITracker* tracker ) {
m_currentTracker = tracker; m_currentTracker = tracker;
} }
TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
ITracker(nameAndLocation, parent), ITracker(CATCH_MOVE(nameAndLocation), parent),
m_ctx( ctx ) m_ctx( ctx )
{} {}
@@ -169,13 +155,14 @@ namespace TestCaseTracking {
m_ctx.setCurrentTracker( this ); m_ctx.setCurrentTracker( this );
} }
SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( nameAndLocation, ctx, parent ), : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
m_trimmed_name(trim(nameAndLocation.name)) m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name)))
{ {
if( parent ) { if( parent ) {
while( !parent->isSectionTracker() ) while ( !parent->isSectionTracker() ) {
parent = parent->parent(); parent = parent->parent();
}
SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
addNextFilters( parentSection.m_filters ); addNextFilters( parentSection.m_filters );
@@ -195,24 +182,30 @@ namespace TestCaseTracking {
bool SectionTracker::isSectionTracker() const { return true; } bool SectionTracker::isSectionTracker() const { return true; }
SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) {
SectionTracker* section; SectionTracker* tracker;
ITracker& currentTracker = ctx.currentTracker(); ITracker& currentTracker = ctx.currentTracker();
if ( ITracker* childTracker = if ( ITracker* childTracker =
currentTracker.findChild( nameAndLocation ) ) { currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker ); assert( childTracker );
assert( childTracker->isSectionTracker() ); assert( childTracker->isSectionTracker() );
section = static_cast<SectionTracker*>( childTracker ); tracker = static_cast<SectionTracker*>( childTracker );
} else { } else {
auto newSection = Catch::Detail::make_unique<SectionTracker>( auto newTracker = Catch::Detail::make_unique<SectionTracker>(
nameAndLocation, ctx, &currentTracker ); NameAndLocation{ static_cast<std::string>(nameAndLocation.name),
section = newSection.get(); nameAndLocation.location },
currentTracker.addChild( CATCH_MOVE( newSection ) ); ctx,
&currentTracker );
tracker = newTracker.get();
currentTracker.addChild( CATCH_MOVE( newTracker ) );
} }
if( !ctx.completedCycle() )
section->tryOpen(); if ( !ctx.completedCycle() ) {
return *section; tracker->tryOpen();
}
return *tracker;
} }
void SectionTracker::tryOpen() { void SectionTracker::tryOpen() {
@@ -233,10 +226,6 @@ namespace TestCaseTracking {
m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
} }
std::vector<StringRef> const& SectionTracker::getFilters() const {
return m_filters;
}
StringRef SectionTracker::trimmedName() const { StringRef SectionTracker::trimmedName() const {
return m_trimmed_name; return m_trimmed_name;
} }

View File

@@ -22,10 +22,14 @@ namespace TestCaseTracking {
std::string name; std::string name;
SourceLineInfo location; SourceLineInfo location;
NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); NameAndLocation( std::string&& _name, SourceLineInfo const& _location );
friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
return lhs.name == rhs.name // This is a very cheap check that should have a very high hit rate.
&& lhs.location == rhs.location; // If we get to SourceLineInfo::operator==, we will redo it, but the
// cost of repeating is trivial at that point (we will be paying
// multiple strcmp/memcmps at that point).
if ( lhs.location.line != rhs.location.line ) { return false; }
return lhs.name == rhs.name && lhs.location == rhs.location;
} }
friend bool operator!=(NameAndLocation const& lhs, friend bool operator!=(NameAndLocation const& lhs,
NameAndLocation const& rhs) { NameAndLocation const& rhs) {
@@ -33,6 +37,37 @@ namespace TestCaseTracking {
} }
}; };
/**
* This is a variant of `NameAndLocation` that does not own the name string
*
* This avoids extra allocations when trying to locate a tracker by its
* name and location, as long as we make sure that trackers only keep
* around the owning variant.
*/
struct NameAndLocationRef {
StringRef name;
SourceLineInfo location;
constexpr NameAndLocationRef( StringRef name_,
SourceLineInfo location_ ):
name( name_ ), location( location_ ) {}
friend bool operator==( NameAndLocation const& lhs,
NameAndLocationRef const& rhs ) {
// This is a very cheap check that should have a very high hit rate.
// If we get to SourceLineInfo::operator==, we will redo it, but the
// cost of repeating is trivial at that point (we will be paying
// multiple strcmp/memcmps at that point).
if ( lhs.location.line != rhs.location.line ) { return false; }
return StringRef( lhs.name ) == rhs.name &&
lhs.location == rhs.location;
}
friend bool operator==( NameAndLocationRef const& lhs,
NameAndLocation const& rhs ) {
return rhs == lhs;
}
};
class ITracker; class ITracker;
using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>; using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>;
@@ -57,8 +92,8 @@ namespace TestCaseTracking {
CycleState m_runState = NotStarted; CycleState m_runState = NotStarted;
public: public:
ITracker( NameAndLocation const& nameAndLoc, ITracker* parent ): ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ):
m_nameAndLocation( nameAndLoc ), m_nameAndLocation( CATCH_MOVE(nameAndLoc) ),
m_parent( parent ) m_parent( parent )
{} {}
@@ -79,7 +114,9 @@ namespace TestCaseTracking {
//! Returns true if tracker run to completion (successfully or not) //! Returns true if tracker run to completion (successfully or not)
virtual bool isComplete() const = 0; virtual bool isComplete() const = 0;
//! Returns true if tracker run to completion succesfully //! Returns true if tracker run to completion succesfully
bool isSuccessfullyCompleted() const; bool isSuccessfullyCompleted() const {
return m_runState == CompletedSuccessfully;
}
//! Returns true if tracker has started but hasn't been completed //! Returns true if tracker has started but hasn't been completed
bool isOpen() const; bool isOpen() const;
//! Returns true iff tracker has started //! Returns true iff tracker has started
@@ -97,7 +134,7 @@ namespace TestCaseTracking {
* *
* Returns nullptr if not found. * Returns nullptr if not found.
*/ */
ITracker* findChild( NameAndLocation const& nameAndLocation ); ITracker* findChild( NameAndLocationRef const& nameAndLocation );
//! Have any children been added? //! Have any children been added?
bool hasChildren() const { bool hasChildren() const {
return !m_children.empty(); return !m_children.empty();
@@ -138,13 +175,15 @@ namespace TestCaseTracking {
public: public:
ITracker& startRun(); ITracker& startRun();
void endRun();
void startCycle(); void startCycle() {
m_currentTracker = m_rootTracker.get();
m_runState = Executing;
}
void completeCycle(); void completeCycle();
bool completedCycle() const; bool completedCycle() const;
ITracker& currentTracker(); ITracker& currentTracker() { return *m_currentTracker; }
void setCurrentTracker( ITracker* tracker ); void setCurrentTracker( ITracker* tracker );
}; };
@@ -154,7 +193,7 @@ namespace TestCaseTracking {
TrackerContext& m_ctx; TrackerContext& m_ctx;
public: public:
TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isComplete() const override; bool isComplete() const override;
@@ -170,22 +209,26 @@ namespace TestCaseTracking {
class SectionTracker : public TrackerBase { class SectionTracker : public TrackerBase {
std::vector<StringRef> m_filters; std::vector<StringRef> m_filters;
std::string m_trimmed_name; // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`.
// Currently it allocates owns the name, so this is safe. If it is later refactored
// to not own the name, the name still has to outlive the `ITracker` parent, so
// this should still be safe.
StringRef m_trimmed_name;
public: public:
SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isSectionTracker() const override; bool isSectionTracker() const override;
bool isComplete() const override; bool isComplete() const override;
static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation );
void tryOpen(); void tryOpen();
void addInitialFilters( std::vector<std::string> const& filters ); void addInitialFilters( std::vector<std::string> const& filters );
void addNextFilters( std::vector<StringRef> const& filters ); void addNextFilters( std::vector<StringRef> const& filters );
//! Returns filters active in this tracker //! Returns filters active in this tracker
std::vector<StringRef> const& getFilters() const; std::vector<StringRef> const& getFilters() const { return m_filters; }
//! Returns whitespace-trimmed name of the tracked section //! Returns whitespace-trimmed name of the tracked section
StringRef trimmedName() const; StringRef trimmedName() const;
}; };

View File

@@ -20,4 +20,12 @@ namespace Catch {
#endif #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 } // namespace Catch

View File

@@ -12,6 +12,8 @@ namespace Catch {
//! Used to signal that an assertion macro failed //! Used to signal that an assertion macro failed
struct TestFailureException{}; struct TestFailureException{};
//! Used to signal that the remainder of a test should be skipped
struct TestSkipException {};
/** /**
* Outlines throwing of `TestFailureException` into a single TU * Outlines throwing of `TestFailureException` into a single TU
@@ -20,8 +22,12 @@ namespace Catch {
*/ */
[[noreturn]] void throw_test_failure_exception(); [[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 } // namespace Catch

View File

@@ -10,7 +10,6 @@
#include <catch2/catch_user_config.hpp> #include <catch2/catch_user_config.hpp>
#include <catch2/internal/catch_assertion_handler.hpp> #include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_source_line_info.hpp> #include <catch2/internal/catch_source_line_info.hpp>
@@ -23,9 +22,9 @@
#if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE)
#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr
#else #else
#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr
#endif #endif
#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)

View File

@@ -8,8 +8,7 @@
#include <catch2/internal/catch_test_registry.hpp> #include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/catch_test_case_info.hpp> #include <catch2/catch_test_case_info.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp> #include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
@@ -19,7 +18,7 @@
namespace Catch { namespace Catch {
namespace { namespace {
StringRef extractClassName( StringRef classOrMethodName ) { static StringRef extractClassName( StringRef classOrMethodName ) {
if ( !startsWith( classOrMethodName, '&' ) ) { if ( !startsWith( classOrMethodName, '&' ) ) {
return classOrMethodName; return classOrMethodName;
} }
@@ -46,6 +45,18 @@ namespace Catch {
static_cast<std::size_t>( startIdx ), static_cast<std::size_t>( startIdx ),
static_cast<std::size_t>( classNameSize ) ); 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 } // namespace
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) { Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) {

View File

@@ -89,7 +89,13 @@ struct AutoReg : Detail::NonCopyable {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ 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 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -101,7 +107,11 @@ struct AutoReg : Detail::NonCopyable {
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
void test(); \ void test(); \
}; \ }; \
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
Catch::makeTestInvoker( &TestName::test ), \
CATCH_INTERNAL_LINEINFO, \
#ClassName##_catch_sr, \
Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
} \ } \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
void TestName::test() void TestName::test()

View File

@@ -8,13 +8,13 @@
#include <catch2/internal/catch_test_spec_parser.hpp> #include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp> #include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
namespace Catch { namespace Catch {
TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} TestSpecParser::TestSpecParser( TagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
TestSpecParser& TestSpecParser::parse( std::string const& arg ) { TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
m_mode = None; m_mode = None;
@@ -221,10 +221,8 @@ namespace Catch {
token.erase(token.begin()); token.erase(token.begin());
if (m_exclusion) { if (m_exclusion) {
m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring)); m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
} else { } else {
m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring)); m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
} }
} }
if (m_exclusion) { if (m_exclusion) {

View File

@@ -20,7 +20,7 @@
namespace Catch { namespace Catch {
class ITagAliasRegistry; class TagAliasRegistry;
class TestSpecParser { class TestSpecParser {
enum Mode{ None, Name, QuotedName, Tag, EscapedName }; enum Mode{ None, Name, QuotedName, Tag, EscapedName };
@@ -35,10 +35,10 @@ namespace Catch {
std::vector<std::size_t> m_escapeChars; std::vector<std::size_t> m_escapeChars;
TestSpec::Filter m_currentFilter; TestSpec::Filter m_currentFilter;
TestSpec m_testSpec; TestSpec m_testSpec;
ITagAliasRegistry const* m_tagAliases = nullptr; TagAliasRegistry const* m_tagAliases = nullptr;
public: public:
TestSpecParser( ITagAliasRegistry const& tagAliases ); TestSpecParser( TagAliasRegistry const& tagAliases );
TestSpecParser& parse( std::string const& arg ); TestSpecParser& parse( std::string const& arg );
TestSpec testSpec(); TestSpec testSpec();

View File

@@ -34,12 +34,10 @@ namespace Catch {
template <typename RangeLike> template <typename RangeLike>
bool match( RangeLike&& rng ) const { bool match( RangeLike&& rng ) const {
using std::begin; using std::end; for ( auto&& elem : rng ) {
if ( m_eq( elem, m_desired ) ) { return true; }
return end(rng) != std::find_if(begin(rng), end(rng), }
[&](auto const& elem) { return false;
return m_eq(elem, m_desired);
});
} }
}; };
@@ -91,7 +89,7 @@ namespace Catch {
/** /**
* Creates a matcher that checks whether a range contains a specific element. * Creates a matcher that checks whether a range contains a specific element.
* *
* Uses `eq` to do the comparisons * Uses `eq` to do the comparisons, the element is provided on the rhs
*/ */
template <typename T, typename Equality> template <typename T, typename Equality>
ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) { ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {

View File

@@ -225,5 +225,17 @@ WithinRelMatcher WithinRel(float target) {
} }
bool IsNaNMatcher::match( double const& matchee ) const {
return std::isnan( matchee );
}
std::string IsNaNMatcher::describe() const {
using namespace std::string_literals;
return "is NaN"s;
}
IsNaNMatcher IsNaN() { return IsNaNMatcher(); }
} // namespace Matchers } // namespace Matchers
} // namespace Catch } // namespace Catch

View File

@@ -27,6 +27,11 @@ namespace Matchers {
double m_margin; double m_margin;
}; };
//! Creates a matcher that accepts numbers within certain range of target
WithinAbsMatcher WithinAbs( double target, double margin );
class WithinUlpsMatcher final : public MatcherBase<double> { class WithinUlpsMatcher final : public MatcherBase<double> {
public: public:
WithinUlpsMatcher( double target, WithinUlpsMatcher( double target,
@@ -40,6 +45,13 @@ namespace Matchers {
Detail::FloatingPointKind m_type; Detail::FloatingPointKind m_type;
}; };
//! Creates a matcher that accepts doubles within certain ULP range of target
WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts floats within certain ULP range of target
WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
// Given IEEE-754 format for floats and doubles, we can assume // Given IEEE-754 format for floats and doubles, we can assume
// that float -> double promotion is lossless. Given this, we can // that float -> double promotion is lossless. Given this, we can
// assume that if we do the standard relative comparison of // assume that if we do the standard relative comparison of
@@ -56,13 +68,6 @@ namespace Matchers {
double m_epsilon; double m_epsilon;
}; };
//! Creates a matcher that accepts doubles within certain ULP range of target
WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts floats within certain ULP range of target
WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts numbers within certain range of target
WithinAbsMatcher WithinAbs(double target, double margin);
//! Creates a matcher that accepts doubles within certain relative range of target //! Creates a matcher that accepts doubles within certain relative range of target
WithinRelMatcher WithinRel(double target, double eps); WithinRelMatcher WithinRel(double target, double eps);
//! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target //! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target
@@ -72,6 +77,17 @@ namespace Matchers {
//! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target //! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target
WithinRelMatcher WithinRel(float target); WithinRelMatcher WithinRel(float target);
class IsNaNMatcher final : public MatcherBase<double> {
public:
IsNaNMatcher() = default;
bool match( double const& matchee ) const override;
std::string describe() const override;
};
IsNaNMatcher IsNaN();
} // namespace Matchers } // namespace Matchers
} // namespace Catch } // namespace Catch

View File

@@ -8,8 +8,10 @@
#ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED #ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
#define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED #define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
#include <algorithm> #include <catch2/internal/catch_is_permutation.hpp>
#include <catch2/matchers/catch_matchers_templated.hpp> #include <catch2/matchers/catch_matchers_templated.hpp>
#include <algorithm>
#include <utility> #include <utility>
namespace Catch { namespace Catch {
@@ -33,13 +35,19 @@ namespace Catch {
template <typename RangeLike> template <typename RangeLike>
bool match( RangeLike&& rng ) const { bool match( RangeLike&& rng ) const {
using std::begin; auto rng_start = begin( rng );
using std::end; const auto rng_end = end( rng );
return std::equal( begin(m_desired), auto target_start = begin( m_desired );
end(m_desired), const auto target_end = end( m_desired );
begin(rng),
end(rng), while (rng_start != rng_end && target_start != target_end) {
m_predicate ); if (!m_predicate(*rng_start, *target_start)) {
return false;
}
++rng_start;
++target_start;
}
return rng_start == rng_end && target_start == target_end;
} }
std::string describe() const override { std::string describe() const override {
@@ -67,7 +75,7 @@ namespace Catch {
bool match( RangeLike&& rng ) const { bool match( RangeLike&& rng ) const {
using std::begin; using std::begin;
using std::end; using std::end;
return std::is_permutation( begin( m_desired ), return Catch::Detail::is_permutation( begin( m_desired ),
end( m_desired ), end( m_desired ),
begin( rng ), begin( rng ),
end( rng ), end( rng ),

View File

@@ -85,11 +85,10 @@ namespace Matchers {
// - a more general approach would be via a compare template that defaults // - 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 // to using !=. but could be specialised for, e.g. std::vector<T> etc
// - then just call that directly // - then just call that directly
if (m_comparator.size() != v.size()) if ( m_comparator.size() != v.size() ) { return false; }
return false; for ( std::size_t i = 0; i < v.size(); ++i ) {
for (std::size_t i = 0; i < v.size(); ++i) if ( !( m_comparator[i] == v[i] ) ) { return false; }
if (m_comparator[i] != v[i]) }
return false;
return true; return true;
} }
std::string describe() const override { std::string describe() const override {

View File

@@ -8,7 +8,7 @@
#include <catch2/matchers/internal/catch_matchers_impl.hpp> #include <catch2/matchers/internal/catch_matchers_impl.hpp>
#include <catch2/matchers/catch_matchers.hpp> #include <catch2/matchers/catch_matchers.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp> #include <catch2/internal/catch_registry_hub.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
namespace Catch { namespace Catch {

View File

@@ -56,18 +56,14 @@ internal_headers = [
'generators/catch_generators_random.hpp', 'generators/catch_generators_random.hpp',
'generators/catch_generators_range.hpp', 'generators/catch_generators_range.hpp',
'interfaces/catch_interfaces_all.hpp', 'interfaces/catch_interfaces_all.hpp',
'interfaces/catch_interfaces_capture.hpp',
'interfaces/catch_interfaces_config.hpp', 'interfaces/catch_interfaces_config.hpp',
'interfaces/catch_interfaces_enum_values_registry.hpp',
'interfaces/catch_interfaces_exception.hpp', 'interfaces/catch_interfaces_exception.hpp',
'interfaces/catch_interfaces_generatortracker.hpp', 'interfaces/catch_interfaces_generatortracker.hpp',
'interfaces/catch_interfaces_registry_hub.hpp',
'interfaces/catch_interfaces_reporter.hpp', 'interfaces/catch_interfaces_reporter.hpp',
'interfaces/catch_interfaces_reporter_factory.hpp', 'interfaces/catch_interfaces_reporter_factory.hpp',
'interfaces/catch_interfaces_reporter_registry.hpp',
'interfaces/catch_interfaces_tag_alias_registry.hpp',
'interfaces/catch_interfaces_testcase.hpp', 'interfaces/catch_interfaces_testcase.hpp',
'internal/catch_assertion_handler.hpp', 'internal/catch_assertion_handler.hpp',
'internal/catch_benchmark_stats_fwd.hpp',
'internal/catch_case_insensitive_comparisons.hpp', 'internal/catch_case_insensitive_comparisons.hpp',
'internal/catch_case_sensitive.hpp', 'internal/catch_case_sensitive.hpp',
'internal/catch_clara.hpp', 'internal/catch_clara.hpp',
@@ -93,6 +89,7 @@ internal_headers = [
'internal/catch_floating_point_helpers.hpp', 'internal/catch_floating_point_helpers.hpp',
'internal/catch_getenv.hpp', 'internal/catch_getenv.hpp',
'internal/catch_istream.hpp', 'internal/catch_istream.hpp',
'internal/catch_is_permutation.hpp',
'internal/catch_lazy_expr.hpp', 'internal/catch_lazy_expr.hpp',
'internal/catch_leak_detector.hpp', 'internal/catch_leak_detector.hpp',
'internal/catch_list.hpp', 'internal/catch_list.hpp',
@@ -168,6 +165,7 @@ internal_headers = [
'catch_template_test_macros.hpp', 'catch_template_test_macros.hpp',
'catch_test_case_info.hpp', 'catch_test_case_info.hpp',
'catch_test_macros.hpp', 'catch_test_macros.hpp',
'catch_test_run_info.hpp',
'catch_test_spec.hpp', 'catch_test_spec.hpp',
'catch_timer.hpp', 'catch_timer.hpp',
'catch_tostring.hpp', 'catch_tostring.hpp',
@@ -181,14 +179,11 @@ internal_sources = files(
'generators/catch_generator_exception.cpp', 'generators/catch_generator_exception.cpp',
'generators/catch_generators.cpp', 'generators/catch_generators.cpp',
'generators/catch_generators_random.cpp', 'generators/catch_generators_random.cpp',
'interfaces/catch_interfaces_capture.cpp',
'interfaces/catch_interfaces_config.cpp', 'interfaces/catch_interfaces_config.cpp',
'interfaces/catch_interfaces_exception.cpp', 'interfaces/catch_interfaces_exception.cpp',
'interfaces/catch_interfaces_generatortracker.cpp', 'interfaces/catch_interfaces_generatortracker.cpp',
'interfaces/catch_interfaces_registry_hub.cpp',
'interfaces/catch_interfaces_reporter.cpp', 'interfaces/catch_interfaces_reporter.cpp',
'interfaces/catch_interfaces_reporter_factory.cpp', 'interfaces/catch_interfaces_reporter_factory.cpp',
'interfaces/catch_interfaces_reporter_registry.cpp',
'interfaces/catch_interfaces_testcase.cpp', 'interfaces/catch_interfaces_testcase.cpp',
'internal/catch_assertion_handler.cpp', 'internal/catch_assertion_handler.cpp',
'internal/catch_case_insensitive_comparisons.cpp', 'internal/catch_case_insensitive_comparisons.cpp',
@@ -216,6 +211,7 @@ internal_sources = files(
'internal/catch_polyfills.cpp', 'internal/catch_polyfills.cpp',
'internal/catch_random_number_generator.cpp', 'internal/catch_random_number_generator.cpp',
'internal/catch_random_seed_generation.cpp', 'internal/catch_random_seed_generation.cpp',
'internal/catch_registry_hub.cpp',
'internal/catch_reporter_registry.cpp', 'internal/catch_reporter_registry.cpp',
'internal/catch_reporter_spec_parser.cpp', 'internal/catch_reporter_spec_parser.cpp',
'internal/catch_result_type.cpp', 'internal/catch_result_type.cpp',
@@ -253,7 +249,6 @@ internal_sources = files(
'catch_config.cpp', 'catch_config.cpp',
'catch_get_random_seed.cpp', 'catch_get_random_seed.cpp',
'catch_message.cpp', 'catch_message.cpp',
'catch_registry_hub.cpp',
'catch_session.cpp', 'catch_session.cpp',
'catch_tag_alias_autoregistrar.cpp', 'catch_tag_alias_autoregistrar.cpp',
'catch_test_case_info.cpp', 'catch_test_case_info.cpp',
@@ -261,6 +256,7 @@ internal_sources = files(
'catch_timer.cpp', 'catch_timer.cpp',
'catch_tostring.cpp', 'catch_tostring.cpp',
'catch_totals.cpp', 'catch_totals.cpp',
'catch_translate_exception.cpp',
'catch_version.cpp', 'catch_version.cpp',
) )

Some files were not shown because too many files have changed in this diff Show More