Compare commits

...

44 Commits

Author SHA1 Message Date
ZXShady
33e6fd217a Remove recursion when stringifying std::tuple 2025-10-04 22:10:36 +02:00
ZXShady
a58df2d7c5 Outline part of formatting system_clock's time_point into cpp file 2025-10-04 22:10:36 +02:00
ZXShady
a9223b2bb3 Outline catch_strnlen's definition into catch_tostring.cpp 2025-10-04 22:10:36 +02:00
Martin Hořeňovský
363ca5af18 Add lifetime annotations to more places using StringRef 2025-10-04 16:38:07 +02:00
Martin Hořeňovský
cb6d713774 Add lifetimebound annotation to StringRef 2025-10-04 16:12:17 +02:00
Martin Hořeňovský
8e4ab5dd8f Annotate matcher combinators with CATCH_ATTR_LIFETIMEBOUND
The matcher combinators do not take ownership of the matchers
being combined, which can catch the users off-guard, when code like
this

```cpp
using Catch::Matchers::EndsWith;
using Catch::Matchers::ContainsSubstring;

auto combinedMatcher = EndsWith("as a service")
                       && ContainsSubstring("web scale");

REQUIRE_THAT( getSomeString(), combinedMatcher );
```

leads to use-after-free, as the `combinedMatcher` refers to matcher
temporaries that no longer exists. With this commit, users of Clang,
MSVC or other compiler that understands the `lifetimebound` attribute,
should get a warning.
2025-10-03 22:27:29 +02:00
Martin Hořeňovský
8219ed79f2 Add CATCH_ATTR_LIFETIMEBOUND macro polyfill over lifetimebound attr 2025-10-03 22:15:27 +02:00
Martin Hořeňovský
b3fb4b9fea v3.11.0 2025-09-30 10:54:31 +02:00
Martin Hořeňovský
6500dc8149 Add Tuple sponsorship banner to readme
Until December.
2025-09-30 10:34:21 +02:00
Martin Hořeňovský
4be9de6c54 Disable test for deprecated CATCH_CONFIG_BAZEL_SUPPORT option
Because it requires full rebuild of the base library, it adds about
20% to the build time of the test suite. Between the fact that the
option is deprecated, and that Bazel has added the `BAZEL_TEST`
env var _years_ ago, nobody should be using it, and the chance
of breakage is tiny, the test is not worth its compile-time cost.
2025-09-28 12:57:55 +02:00
Martin Hořeňovský
91b3b3bf40 Support Bazel's TEST_RANDOM_SEED
As with other Bazel env vars, it overrides the corresponding CLI
parameter if both are set.

Closes #3021
Closes #3024
2025-09-28 10:48:08 +02:00
Martin Hořeňovský
a00d654437 Speed up processing mostly visible strings in convertIntoString
This commit causes small (~5%) slowdown when processing strings
that consist mostly of escaped characters, but improves throughput
by about 100% for strings that consist mostly of characters that
do not need escaping.
2025-09-27 22:43:14 +02:00
Martin Hořeňovský
756ae05d30 Inline the getResultCapture helper into header.
We still keep the error check in the function, but hide it in an
outlined function inside a .cpp file, to promote inlining of the
retrieval part.

In the future, we should explore two things

1) Skipping over the context retrieval here, allowing direct access.
   I currently do not see a way to do this while keeping the
   "greppability" of mutable vs immutable accesses that is there now,
   but it would help a lot when inlining is not enabled.
2) Removing the error check, to make the function trivially inlinable,
   and without branches.

**runtime difference**

| --------- | Debug | Release |
|:----------|------:|--------:|
| Slow path |  0.98 |    1.07 |
| Fast path |  1.04 |    1.08 |

We lost bit of performance on the assertion slow path in debug mode,
but together with the previous commit, it comes out at net zero.
For other combinations, we see 5-10% perf improvement across the
two commits.
2025-09-27 13:24:46 +02:00
Martin Hořeňovský
a2e41916f2 Keep the main Context instance as static value, not pointer
This allows us to remove the lazy init checks, improving the inlining
potential when retrieving current context, thus slightly improving
the performance of assertions.

**runtime difference**

| --------- | Debug | Release |
|:----------|------:|--------:|
| Slow path |  1.01 |    0.98 |
| Fast path |  1.02 |    1.02 |

There is small slowdown in case of Release build + assertions taking
the slow path, but

1) going through the slow path is rare
2) Given the code change, I believe this to be artifact of the
   optimizer in the old GCC version I am using locally.
2025-09-27 13:24:44 +02:00
Duncan Horn
0e772cc0d2 Allow composability of unhandled exception filters (#3033)
The change is very simple. If a handler previously existed, Catch2 will invoke it after printing out its output. I've also updated the comment to better reflect that it's returning EXCEPTION_CONTINUE_SEARCH even in scenarios where the exception is one that the library cares about.
2025-09-27 11:09:34 +02:00
Mason Wilie
3bcd0a4e74 Changed GetOptions to use sequence 2025-09-25 21:06:10 +02:00
Duncan Horn
f7e7fa0983 Fix ReusableStringStream to access the container under the lock (#3031)
Commit 582200a made `ReusableStringStream`'s index reservation thread safe, however it's still accessing the `m_streams` vector outside the lock. This makes it so that the `add` call returns the pointer in addition to the index so that the lock doesn't need to get acquired again until destruction.

The issue with accessing `m_streams` outside the lock is that `add` can call `push_back` on the vector, which might re-allocate. If this re-allocation occurs concurrently with anther thread trying to index into this array, you get UB (typically a null pointer read).
2025-09-25 13:31:49 +02:00
Martin Hořeňovský
b626e4c7ae Add simple runtime benchmarks
For now we add just two binaries, one with assertions taking the
fast path, one with assertions taking the slow path, and the ability
to run 1 of `REQUIRE(true)`, `REQUIRE_NOTHROW`, `REQUIRE_THROWS`
in a loop.

I also split off a CMake preset which enables more tests than the
basic `simple-tests` preset, but does not enable the most expensive
tests which force recompilation of Catch2 multiple times.
2025-09-23 17:17:33 +02:00
Martin Hořeňovský
434bf55d47 Make message push/pop static
Since the change to make the message macros thread-safe, and thus
thread-local, there is no need to handle messages through instance
of `RunContext`.
2025-09-23 11:46:26 +02:00
Martin Hořeňovský
9048c24fa7 Improve QNX support
This cherry-picks the source changes from PR made by @pkleymonov-qnx,
without including the build path changes that are irrelevant unless
we can add a QNX target to our CI.

Close #2953
2025-09-22 15:36:46 +02:00
Martin Hořeňovský
8ed8c431c6 Support Bazel's TEST_PREMATURE_EXIT_FILE and add it to CLI as well
This tells Catch2 to create an empty file at specified path before
the tests start, and delete it after the tests finish. This allows
callers to catch cases where the test binary silently exits before
finishing (e.g. via call to `exit(0)` inside the code under test),
by looking whether the file still exists.

Closes #3020
2025-09-22 14:02:45 +02:00
Silent
dc3a4ea41a Fix non-desktop Windows platforms not defining CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 correctly
Fixes building on non-desktop GDK platforms.
2025-09-17 20:25:26 +02:00
Martin Hořeňovský
cd93d202e0 Small refactoring in CAPTURE parsing 2025-08-26 12:40:13 +02:00
Martin Hořeňovský
227af796b4 Move MessageInfos into the run context
Together with the previous change, this means that `ScopedMessage`
only needs to keep around a single unsigned int.
2025-08-26 12:37:52 +02:00
Martin Hořeňovský
33adb4c779 Use only the ID of a message when removing it from context 2025-08-26 12:07:41 +02:00
Martin Hořeňovský
25319fd304 v3.10.0 2025-08-25 21:30:21 +02:00
Szapi
85c4bad86b Forbid deducing reference types for m_predicate in FilterGenerator (#3005)
Forbid deducing reference types for m_predicate in FilterGenerator to prevent dangling references.
This is needed for out-of-line predicates to work correctly instead of undefined behavior or crashes.

---------

Co-authored-by: Tek Mate <mate.tek@evosoft.com>
2025-08-23 21:55:38 +02:00
Martin Hořeňovský
582200a1f8 Make message macros (FAIL, WARN, INFO, etc) thread safe
This builds on the existing work to make assertion thread safe,
by adding an extra synchronization point in the holder of
`ReusableStringStream`'s stream instances, as those are used to
build the messages, and finishing the move of message scope holders
to be thread-local.
2025-08-23 11:08:18 +02:00
Martin Hořeňovský
f4e05a67bb Improve performance of writing XML
As with the JSON writer, the old code was made to be simple and
for each char just decided whether it needs escaping, or should be
written as-is. The new code instead looks for characters that need
escaping and batches writes of characters that do not.

This provides 4-8x speedup (length dependent) for writing strings
that do not need escaping, and keeps roughly the same performance
for those that do need escaping.
2025-08-22 17:03:35 +02:00
Martin Hořeňovský
fb2e4fbe41 Improve performance of writing JSON values
The old code was exceedingly simple, as it went char-by-char and
decided whether to write it to the output stream as-is, or escaped.
This caused a _lot_ of stream writes of individual characters.

The new code instead looks for characters that need escaping, and
bulk-writes the non-escaped characters in between them. This leads
to about the same performance for strings that comprise of only
escaped characters, and 3-10x improvement for strings without any
escaping needed.

In practice, we should expect the former rather than the latter,
but this is still nice improvement.
2025-08-22 17:00:40 +02:00
Martin Hořeňovský
78a9518a28 Don't add / to start of pkg-config file path when DESTDIR is unset
This broke installation on Windows, because the suddenly started
with a '/' instead of a drive letter.

Closes #3019
2025-08-21 20:58:55 +02:00
Shloka Jain
3e82ef9317 Fix color mode detection on FreeBSD by adding platform macro 2025-08-12 14:38:50 +02:00
Martin Hořeňovský
7cad6d7539 Handle DESTDIR env var when generating pkgconfig files
Having the ability to configure the installation path at config
time, and the ability to change the prefix at install time is not
enough, apparently people also use env var to redirect them instead.

Closes #3006
2025-08-09 23:05:18 +02:00
Martin Hořeňovský
644821ce28 v3.9.1 2025-08-09 00:30:23 +02:00
Martin Hořeňovský
03c62cdf2e Add tests for comparing & stringifying volatile pointers 2025-08-08 00:02:33 +02:00
Martin Hořeňovský
9a3d68315b Refactor CATCH_TRAP selection logic to prefer compiler-specific impls
Primarily this means that with Clang we use `__builtin_debugtrap`
if available.
2025-08-08 00:01:38 +02:00
Andrew Auclair
bcd4116df7 Update generators.md
Break specific purpose generators into random and range generator sections.
2025-08-06 19:31:42 +02:00
Martin Hořeňovský
9b3f508a1b Cleanup WIP changes from last commit 2025-08-02 10:21:41 +02:00
Martin Hořeňovský
c5e0ef4e67 Catch exceptions from StringMakers inside Detail::stringify
This stops tests failing falsely if the assertion passed, but the
stringification itself failed as the assertion was sent to the reporter.

I don't think that stringification should be fallible, but the
overhead in compilation ended up being small enough (<0.5% on `SelfTest`)
that it might be worth implementing, in case there is more users
with weird `StringMaker`s than just MongoDB.

Closes #2980
2025-08-02 10:11:52 +02:00
Martin Hořeňovský
17fe5eaa5c Fix StringMaker for time_point<system_clock> with non-default duration
Fixes #2685
2025-08-02 08:31:34 +02:00
Tim Lyon
fbfd13501c Fix warning in catch_unique_ptr::bool() 2025-08-02 00:15:06 +02:00
Martin Hořeňovský
ccabd4de89 Add enum types to what is captured by value by default
As it turns out, enums can be used to declare bitfields, and we
cannot form a reference to a bitfield in the cpature. Thus, we add
`std::is_enum` as a criteria to the default for `capture_by_value`,
so that enum-based bitfields are also captured by value and thus
decomposable.

Closes #3001
2025-07-30 20:20:38 +02:00
Martin Hořeňovský
a1c7ee115f Don't follow __assume(false) with std::terminate in NDEBUG builds
Having `std::terminate` as the backstop after `__assume(false)`
would trigger W4702 (unreachable code) with MSVC. As we want to
keep `__assume(false)` for the optimization hint in NDEBUG builds,
we have to avoid mixing in `std::terminate` for those builds.

Fixes #3007
2025-07-28 11:03:46 +02:00
Martin Hořeňovský
d547cae549 Fix bad error reporting for nested exceptions in default configuration
Bad handling of `TestFailureException` when translating unexpected
exceptions inside assertion macros led to the unexpected exceptions
handling erroring out through throwing the same exception again.

This was then backstopped by the machinery for handling uncaught
exceptions from assertions, which is normally used by the
`CATCH_CONFIG_FAST_COMPILE` machinery, where we assume that it can
only be invoked because the assertion macros are not configured to
catch assertions.

Closes #1292
2025-07-27 21:41:02 +02:00
90 changed files with 2902 additions and 665 deletions

View File

@@ -78,5 +78,5 @@ WarningsAsErrors: >-
readability-duplicate-include,
HeaderFilterRegex: '.*\.(c|cxx|cpp)$'
FormatStyle: none
CheckOptions: {}
CheckOptions: []
...

View File

@@ -20,6 +20,7 @@ cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATC
cmake_dependent_option(CATCH_BUILD_EXAMPLES "Build code examples" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_FUZZERS "Build fuzzers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_BENCHMARKS "Build the benchmarks" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
@@ -34,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
VERSION 3.9.0 # CML version placeholder, don't delete
VERSION 3.11.0 # CML version placeholder, don't delete
LANGUAGES CXX
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
DESCRIPTION "A modern, C++-native, unit test framework."
@@ -77,6 +78,11 @@ set(SELF_TEST_DIR ${CATCH_DIR}/tests/SelfTest)
# We need to bring-in the variables defined there to this scope
add_subdirectory(src)
if (CATCH_BUILD_BENCHMARKS)
set(CMAKE_FOLDER "benchmarks")
add_subdirectory(benchmarks)
endif()
# Build tests only if requested
if(BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
@@ -195,17 +201,23 @@ if(NOT_SUBPROJECT)
"set(include_dir \"${CMAKE_INSTALL_INCLUDEDIR}\")"
"set(lib_dir \"${CMAKE_INSTALL_LIBDIR}\")"
[[
message(STATUS "DESTDIR: $ENV{DESTDIR}")
set(DESTDIR_PREFIX "")
if (DEFINED ENV{DESTDIR})
set(DESTDIR_PREFIX "$ENV{DESTDIR}")
endif ()
message(STATUS "PREFIX: ${DESTDIR_PREFIX}")
set(lib_name "$<TARGET_FILE_BASE_NAME:Catch2>")
configure_file(
"${impl_pc_file}"
"${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2.pc"
"${DESTDIR_PREFIX}${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2.pc"
@ONLY
)
set(lib_name "$<TARGET_FILE_BASE_NAME:Catch2WithMain>")
configure_file(
"${main_pc_file}"
"${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2-with-main.pc"
"${DESTDIR_PREFIX}${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2-with-main.pc"
@ONLY
)
]]

View File

@@ -15,14 +15,23 @@
}
},
{
"name": "all-tests",
"name": "most-tests",
"inherits": "basic-tests",
"displayName": "Full development build",
"description": "Enables development build with examples and ALL tests",
"description": "Enables development build with extended set of tests (still relatively cheap to build)",
"cacheVariables": {
"CATCH_BUILD_EXAMPLES": "ON",
"CATCH_BUILD_EXTRA_TESTS": "ON",
"CATCH_BUILD_SURROGATES": "ON",
"CATCH_BUILD_BENCHMARKS": "ON"
}
},
{
"name": "all-tests",
"inherits": "most-tests",
"displayName": "Full development build",
"description": "Enables development build with examples and ALL tests",
"cacheVariables": {
"CATCH_ENABLE_CONFIGURE_TESTS": "ON",
"CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON"
}

View File

@@ -1,5 +1,16 @@
<a id="top"></a>
![Catch2 logo](data/artwork/catch2-logo-full-with-background.svg)
<table width="100%">
<tr>
<td align="center" width="50%"><img src="/data/artwork/catch2-logo-full-with-background.svg" width="100%"></td>
<td align="center" width="50%">
<figure>
<figcaption>Special thanks to:</figcaption>
<a href="https://tuple.app/catch2"><img src="/data/sponsors/github_repo_sponsorship.png" width="100%"></a>
</figure>
</td>
</tr>
</table>
[![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases)
[![Linux build status](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml)

16
benchmarks/CMakeLists.txt Normal file
View File

@@ -0,0 +1,16 @@
include(CatchMiscFunctions)
add_executable(AssertionsFastPath
runtime_assertion_benches.cpp
)
add_executable(AssertionsSlowPath
runtime_assertion_benches.cpp
assertion_listener.cpp
)
target_link_libraries(AssertionsFastPath PRIVATE Catch2::Catch2WithMain)
target_link_libraries(AssertionsSlowPath PRIVATE Catch2::Catch2WithMain)
list(APPEND CATCH_TEST_TARGETS AssertionsFastPath AssertionsSlowPath)
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)

View File

@@ -0,0 +1,28 @@
// 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/reporters/catch_reporter_event_listener.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>
/**
* Event listener that listens to all assertions, forcing assertion slow path
*/
class AssertionSlowPathListener : public Catch::EventListenerBase {
public:
static std::string getDescription() {
return "Validates ordering of Catch2's listener events";
}
AssertionSlowPathListener(Catch::IConfig const* config) :
EventListenerBase(config) {
m_preferences.shouldReportAllAssertions = true;
m_preferences.shouldReportAllAssertionStarts = true;
}
};
CATCH_REGISTER_LISTENER( AssertionSlowPathListener )

View File

@@ -0,0 +1,27 @@
// 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_test_macros.hpp>
TEST_CASE("Simple REQUIRE - 10M") {
for (size_t i = 0; i < 10'000'000; ++i) {
REQUIRE(true);
}
}
TEST_CASE("Simple NOTHROW - 10M") {
for (size_t i = 0; i < 10'000'000; ++i) {
REQUIRE_NOTHROW([](){}());
}
}
TEST_CASE("Simple THROWS - 10M") {
for (size_t i = 0; i < 10'000'000; ++i) {
REQUIRE_THROWS([]() { throw 1; }());
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

View File

@@ -66,17 +66,24 @@ test execution. Specifically it understands
* JUnit output path via `XML_OUTPUT_FILE`
* Test filtering via `TESTBRIDGE_TEST_ONLY`
* Test sharding via `TEST_SHARD_INDEX`, `TEST_TOTAL_SHARDS`, and `TEST_SHARD_STATUS_FILE`
* Creating a file to signal premature test exit via `TEST_PREMATURE_EXIT_FILE`
* Setting the RNG seed via `TEST_RANDOM_SEED`
> Support for `XML_OUTPUT_FILE` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 3.0.1
> Support for `TESTBRIDGE_TEST_ONLY` and sharding was introduced in Catch2 3.2.0
> Support for `TEST_PREMATURE_EXIT_FILE` and `TEST_RANDOM_SEED` was introduced in Catch2 3.11.0
This integration is enabled via either a [compile time configuration
option](configuration.md#bazel-support), or via `BAZEL_TEST` environment
variable set to "1".
> Support for `BAZEL_TEST` was [introduced](https://github.com/catchorg/Catch2/pull/2459) in Catch2 3.1.0
Note that if both the Bazel environment var and command line option for
something are used, the environment variable wins.
## Low-level tools

View File

@@ -32,6 +32,7 @@
[Test Sharding](#test-sharding)<br>
[Allow running the binary without tests](#allow-running-the-binary-without-tests)<br>
[Output verbosity](#output-verbosity)<br>
[Create file to guard against silent early termination](#create-file-to-guard-against-silent-early-termination)<br>
Catch works quite nicely without any command line options at all - but for those times when you want greater control the following options are available.
Click one of the following links to take you straight to that option - or scroll on to browse the available options.
@@ -649,6 +650,21 @@ ignored.
Verbosity defaults to _normal_.
## Create file to guard against silent early termination
<pre>--premature-exit-guard-file &lt;path&gt;</pre>
> Introduced in Catch2 3.11.0
Tells Catch2 to create an empty file at specified path before the tests
start, and delete it after the tests finish. If the file is present after
the process stops, it can be assumed that the testing binary exited
prematurely, e.g. due to the OOM killer.
All directories in the path must already exist. If this option is used
and Catch2 cannot create the file (e.g. the location is not writable),
the test run will fail.
---
[Home](Readme.md#top)

View File

@@ -114,11 +114,12 @@ a test case,
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
on elements from a different generator
* `ChunkGenerator<T>` -- returns chunks (inside `std::vector`) of n elements from a generator
* 4 specific purpose generators (defined in `catch2/generators/catch_generators_random.hpp`)
* 2 random generators (defined in `catch2/generators/catch_generators_random.hpp`)
* `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
* `RandomFloatGenerator<Float>` -- generates random Floats from range
* `RangeGenerator<T>(first, last)` -- generates all values inside a `[first, last)` arithmetic range (defined in `catch2/generators/catch_generators_range.hpp`)
* `IteratorGenerator<T>` -- copies and returns values from an iterator range (defined in `catch2/generators/catch_generators_range.hpp`)
* 2 range generators (defined in `catch2/generators/catch_generators_range.hpp`)
* `RangeGenerator<T>(first, last)` -- generates all values inside a `[first, last)` arithmetic range
* `IteratorGenerator<T>` -- copies and returns values from an iterator range
> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch2 2.7.0.

View File

@@ -2,6 +2,9 @@
# Release notes
**Contents**<br>
[3.11.0](#3110)<br>
[3.10.0](#3100)<br>
[3.9.1](#391)<br>
[3.9.0](#390)<br>
[3.8.1](#381)<br>
[3.8.0](#380)<br>
@@ -68,6 +71,54 @@
[Even Older versions](#even-older-versions)<br>
## 3.11.0
### Fixes
* Fixed building on non-desktop GDK platforms (#3029)
* Fixed message macros being susceptible to race in specific scenario (#3031)
* Catch2's SEH filter will call the previously installed filter after reporting the error (#3033)
### Improvements
* Handling of scoped messages (e.g. `CAPTURE`) is a bit faster.
* Better out-of-the-box support for QNX (#2953)
* Improved performance of assertions by up-to 10%
* Release mode assertion fast-path sees the biggest improvement.
* Faster processing of non-escaped strings in `--invisibles` mode.
* Added support for Bazel's `TEST_RANDOM_SEED` env var (#3021)
* Added support for Bazel's `TEST_PREMATURE_EXIT_FILE` env var (#3020)
* This creates a file that is deleted if the tests exit normally, but stays around if the process dies unexpectedly.
* This functionality is also exposed through CLI as `--premature-exit-guard-file`
### Miscellaneous
* **[Tuple.app](https://tuple.app/catch2) has sponsored Catch2**
## 3.10.0
### Fixes
* pkg-config files will take `DESTDIR` env var into account when selecting install destination (#3006, #3019)
* Changed `filter` to store the provided predicate by value (#3002, #3005)
* This is done to avoid dangling-by-default behaviour when `filter` is used inside `GENERATE_COPY`/`GENERATE_REF`.
### Improvements
* Escaping XML and JSON output is faster when the strings do not need escaping.
* The improvement starts at about 3x throughput, up to 10x for long strings.
* Message macros (`INFO`, `CAPTURE`, `WARN`, `SUCCEED`, etc) are now thread safe.
## 3.9.1
### Fixes
* Fixed bad error reporting for multiple nested assertions (#1292)
* Fixed W4702 (unreachable code) in the polyfill for std::unreachable (#3007)
* Fixed decomposition of assertions comparing enum-backed bitfields (#3001)
* Fixed StringMaker specialization for `time_point<system_clock>` with non-default duration type (#2685)
### Improvements
* Exceptions thrown during stringification of decomposed expression no longer fail the assertion (#2980)
* The selection logic for `CATCH_TRAP` prefers `__builtin_debugtrap` on all platforms when Catch2 is compiled with Clang
## 3.9.0
### Improvements

View File

@@ -52,6 +52,20 @@ TEST_CASE("failing test") {
}
```
Same applies for a `SKIP` nested inside an assertion:
```cpp
static bool do_skip() {
SKIP();
return true;
}
TEST_CASE("Another failing test") {
CHECK(do_skip());
}
```
### Interaction with Sections and Generators
Sections, nested sections as well as specific outputs from [generators](generators.md#top)

View File

@@ -2,7 +2,9 @@
# Thread safety in Catch2
**Contents**<br>
[Using assertion macros from multiple threads](#using-assertion-macros-from-multiple-threads)<br>
[Using assertion macros from spawned threads](#using-assertion-macros-from-spawned-threads)<br>
[Assertion-like message macros and spawned threads](#assertion-like-message-macros-and-spawned-threads)<br>
[Message macros and spawned threads](#message-macros-and-spawned-threads)<br>
[examples](#examples)<br>
[`STATIC_REQUIRE` and `STATIC_CHECK`](#static_require-and-static_check)<br>
[Fatal errors and multiple threads](#fatal-errors-and-multiple-threads)<br>
@@ -10,17 +12,18 @@
> Thread safe assertions were introduced in Catch2 3.9.0
Thread safety in Catch2 is currently limited to all the assertion macros.
Interacting with benchmark macros, message macros (e.g. `INFO` or `CAPTURE`),
sections macros, generator macros, or test case macros is not thread-safe.
The message macros are likely to be made thread-safe in the future, but
the way sections define test runs is incompatible with user being able
to spawn threads arbitrarily, thus that limitation is here to stay.
Thread safety in Catch2 is currently limited to all the assertion macros,
and to message or message-adjacent macros (e.g. `INFO` or `WARN`).
Interacting with benchmark macros, sections macros, generator macros, or
test case macros is not thread-safe. The way sections define paths through
the test is incompatible with user spawning threads arbitrarily, so this
limitation is here to stay.
**Important: thread safety in Catch2 is [opt-in](configuration.md#experimental-thread-safety)**
## Using assertion macros from multiple threads
## Using assertion macros from spawned threads
The full set of Catch2's runtime assertion macros is thread-safe. However,
it is important to keep in mind that their semantics might not support
@@ -30,7 +33,7 @@ Specifically, the `REQUIRE` family of assertion macros have semantics
of stopping the test execution on failure. This is done by throwing
an exception, but since the user-spawned thread will not have the test-level
try-catch block ready to catch the test failure exception, failing a
`REQUIRE` assertion inside this thread will terminate the process.
`REQUIRE` assertion inside user-spawned thread will terminate the process.
The `CHECK` family of assertions does not have this issue, because it
does not try to stop the test execution.
@@ -38,16 +41,36 @@ does not try to stop the test execution.
Note that `CHECKED_IF` and `CHECKED_ELSE` are also thread safe (internally
they are assertion macro + an if).
**`SKIP()`, `FAIL()`, `SUCCEED()` are not assertion macros, and are not
thread-safe.**
## Assertion-like message macros and spawned threads
> Assertion-like messages were made thread safe in Catch2 3.10.0
Similarly to assertion macros, not all assertion-like message macros can
be used from spawned thread.
`SKIP` and `FAIL` macros stop the test execution. Just like with `REQUIRE`,
this means that they cannot be used inside user-spawned threads. `SUCCEED`,
`FAIL_CHECK` and `WARN` do not attempt to stop the test execution and
thus can be used from any thread.
## Message macros and spawned threads
> Message macros were made thread safe in Catch2 3.10.0
Macros that add extra messages to following assertion, such as `INFO`
or `CAPTURE`, are all thread safe and can be used in any thread. Note
that these messages are per-thread, and thus `INFO` inside a user-spawned
thread will not be seen by the main thread, and vice versa.
## examples
### `REQUIRE` from main thread, `CHECK` from spawned threads
### `REQUIRE` from the main thread, `CHECK` from spawned threads
```cpp
TEST_CASE( "Failed REQUIRE in main thread is fine" ) {
TEST_CASE( "Failed REQUIRE in the main thread is fine" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
@@ -85,7 +108,7 @@ TEST_CASE( "Successful REQUIRE in spawned thread is fine" ) {
This will also work as expected, because the `REQUIRE` is successful.
```cpp
TEST_CASE( "Failed REQUIRE in spawned thread is fine" ) {
TEST_CASE( "Failed REQUIRE in spawned thread kills the process" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
@@ -99,12 +122,88 @@ TEST_CASE( "Failed REQUIRE in spawned thread is fine" ) {
This will fail catastrophically and terminate the process.
### INFO across threads
```cpp
TEST_CASE( "messages don't cross threads" ) {
std::jthread t1( [&]() {
for ( size_t i = 0; i < 100; ++i ) {
INFO( "spawned thread #1" );
CHECK( 1 == 1 );
}
} );
std::thread t2( [&]() {
for (size_t i = 0; i < 100; ++i) {
UNSCOPED_INFO( "spawned thread #2" );
}
} );
for (size_t i = 0; i < 100; ++i) {
CHECK( 1 == 2 );
}
}
```
None of the failed checks will show the "spawned thread #1" message, as
that message is for the `t1` thread. If the reporter shows passing
assertions (e.g. due to the tests being run with `-s`), you will see the
"spawned thread #1" message alongside the passing `CHECK( 1 == 1 )` assertion.
The message "spawned thread #2" will never be shown, because there are no
assertions in `t2`.
### FAIL/SKIP from the main thread
```cpp
TEST_CASE( "FAIL in the main thread is fine" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
for (size_t i = 0; i < 10; ++i) {
CHECK( true );
CHECK( false );
}
} );
}
FAIL();
}
```
This will work as expected, that is, the process will finish running
normally, the test case will fail and there will be 321 total assertions,
160 passing and 161 failing (`FAIL` counts as failed assertion).
However, when the main thread hits `FAIL`, it will wait for the other
threads to finish due to `std::jthread`'s destructor joining the spawned
thread. Due to this, using `SKIP` is not recommended once more threads
are spawned; while the main thread will bail from the test execution,
the spawned threads will keep running and may fail the test case.
### FAIL/SKIP from spawned threads
```cpp
TEST_CASE( "FAIL/SKIP in spawned thread kills the process" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
for (size_t i = 0; i < 10'000; ++i) {
FAIL();
}
} );
}
}
```
As with failing `REQUIRE`, both `FAIL` and `SKIP` in spawned threads
terminate the process.
## `STATIC_REQUIRE` and `STATIC_CHECK`
None of `STATIC_REQUIRE`, `STATIC_REQUIRE_FALSE`, `STATIC_CHECK`, and
`STATIC_CHECK_FALSE` are currently thread safe. This might be surprising
given that they are a compile-time checks, but they also rely on the
message macros to register the result with reporter at runtime.
All of `STATIC_REQUIRE`, `STATIC_REQUIRE_FALSE`, `STATIC_CHECK`, and
`STATIC_CHECK_FALSE` are thread safe in the delayed evaluation configuration.
## Fatal errors and multiple threads

View File

@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
// Catch v3.9.0
// Generated: 2025-07-24 22:00:25.173359
// Catch v3.11.0
// Generated: 2025-09-30 10:49:12.549018
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -825,6 +825,8 @@ namespace Catch {
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
}
// Reading bazel env vars can change some parts of the config data,
// so we have to process the bazel env before acting on the config.
if ( enableBazelEnvSupport() ) {
readBazelEnvVars();
}
@@ -889,6 +891,8 @@ namespace Catch {
bool Config::showHelp() const { return m_data.showHelp; }
std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
// IConfig interface
bool Config::allowThrows() const { return !m_data.noThrow; }
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
@@ -950,6 +954,26 @@ namespace Catch {
m_data.shardCount = bazelShardOptions->shardCount;
}
}
const auto bazelExitGuardFile = Detail::getEnv( "TEST_PREMATURE_EXIT_FILE" );
if (bazelExitGuardFile) {
m_data.prematureExitGuardFilePath = bazelExitGuardFile;
}
const auto bazelRandomSeed = Detail::getEnv( "TEST_RANDOM_SEED" );
if ( bazelRandomSeed ) {
auto parsedSeed = parseUInt( bazelRandomSeed, 0 );
if ( !parsedSeed ) {
// Currently we handle issues with parsing other Bazel Env
// options by warning and ignoring the issue. So we do the
// same for random seed option.
Catch::cerr()
<< "Warning: could not parse 'TEST_RANDOM_SEED' ('"
<< bazelRandomSeed << "') as proper seed.\n";
} else {
m_data.rngSeed = *parsedSeed;
}
}
}
} // end namespace Catch
@@ -975,28 +999,26 @@ namespace Catch {
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
m_messageId( builder.m_info.sequence ) {
MessageInfo info( CATCH_MOVE( builder.m_info ) );
info.message = builder.m_stream.str();
IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
m_info( CATCH_MOVE( old.m_info ) ) {
m_messageId( old.m_messageId ) {
old.m_moved = true;
}
ScopedMessage::~ScopedMessage() {
if ( !m_moved ){
getResultCapture().popScopedMessage(m_info);
}
if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
}
Capturer::Capturer( StringRef macroName,
SourceLineInfo const& lineInfo,
ResultWas::OfType resultType,
StringRef names ):
m_resultCapture( getResultCapture() ) {
StringRef names ) {
auto trimmed = [&] (size_t start, size_t end) {
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;
@@ -1042,8 +1064,8 @@ namespace Catch {
case ',':
if (start != pos && openings.empty()) {
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, pos);
m_messages.back().message += " := "_sr;
start = pos;
}
break;
@@ -1052,19 +1074,20 @@ namespace Catch {
}
assert(openings.empty() && "Mismatched openings");
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, names.size() - 1);
m_messages.back().message += " := "_sr;
}
Capturer::~Capturer() {
assert( m_captured == m_messages.size() );
for ( size_t i = 0; i < m_captured; ++i )
m_resultCapture.popScopedMessage( m_messages[i] );
for (auto const& message : m_messages) {
IResultCapture::popScopedMessage( message.sequence );
}
}
void Capturer::captureValue( size_t index, std::string const& value ) {
assert( index < m_messages.size() );
m_messages[index].message += value;
m_resultCapture.pushScopedMessage( m_messages[index] );
IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
m_captured++;
}
@@ -1148,7 +1171,6 @@ namespace Catch {
}
void cleanUp() {
cleanupSingletons();
cleanUpContext();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
@@ -1160,6 +1182,8 @@ namespace Catch {
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <iomanip>
#include <set>
@@ -1274,6 +1298,50 @@ namespace Catch {
}
}
// Creates empty file at path. The path must be writable, we do not
// try to create directories in path because that's hard in C++14.
void setUpGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
#if defined( _MSC_VER )
std::FILE* file = nullptr;
if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) {
char msgBuffer[100];
const auto err = errno;
std::string errMsg;
if ( !strerror_s( msgBuffer, err ) ) {
errMsg = msgBuffer;
} else {
errMsg = "Could not translate errno to a string";
}
#else
std::FILE* file = std::fopen( guardFilePath.c_str(), "w" );
if ( !file ) {
const auto err = errno;
const char* errMsg = std::strerror( err );
#endif
CATCH_RUNTIME_ERROR( "Could not open the exit guard file '"
<< guardFilePath << "' because '"
<< errMsg << "' (" << err << ')' );
}
const int ret = std::fclose( file );
CATCH_ENFORCE(
ret == 0,
"Error when closing the exit guard file: " << ret );
}
}
// Removes file at path. Assuming we created it in setUpGuardFile.
void tearDownGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
const int ret = std::remove( guardFilePath.c_str() );
CATCH_ENFORCE(
ret == 0,
"Error when removing the exit guard file: " << ret );
}
}
} // anon namespace
Session::Session() {
@@ -1392,6 +1460,7 @@ namespace Catch {
static_cast<void>(std::getchar());
}
int exitCode = runInternal();
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
static_cast<void>(std::getchar());
@@ -1432,6 +1501,10 @@ namespace Catch {
CATCH_TRY {
config(); // Force config to be constructed
// We need to retrieve potential Bazel config with the full Config
// constructor, so we have to create the guard file after it is created.
setUpGuardFile( m_config->getExitGuardFilePath() );
seedRng( *m_config );
if (m_configData.filenamesAsTags) {
@@ -1461,9 +1534,12 @@ namespace Catch {
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
auto const totals = tests.execute();
// If we got here, running the tests finished normally-enough.
// They might've failed, but that would've been reported elsewhere.
tearDownGuardFile( m_config->getExitGuardFilePath() );
if ( tests.hadUnmatchedTestSpecs()
&& m_config->warnAboutUnmatchedTestSpecs() ) {
// UnmatchedTestSpecExitCode
return UnmatchedTestSpecExitCode;
}
@@ -1973,35 +2049,35 @@ namespace Detail {
std::string ret;
// This is enough for the "don't escape invisibles" case, and a good
// lower bound on the "escape invisibles" case.
ret.reserve(string.size() + 2);
ret.reserve( string.size() + 2 );
if (!escapeInvisibles) {
if ( !escapeInvisibles ) {
ret += '"';
ret += string;
ret += '"';
return ret;
}
size_t last_start = 0;
auto write_to = [&]( size_t idx ) {
if ( last_start < idx ) {
ret += string.substr( last_start, idx - last_start );
}
last_start = idx + 1;
};
ret += '"';
for (char c : string) {
switch (c) {
case '\r':
ret.append("\\r");
break;
case '\n':
ret.append("\\n");
break;
case '\t':
ret.append("\\t");
break;
case '\f':
ret.append("\\f");
break;
default:
ret.push_back(c);
break;
for ( size_t i = 0; i < string.size(); ++i ) {
const char c = string[i];
if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
write_to( i );
if ( c == '\r' ) { ret.append( "\\r" ); }
if ( c == '\n' ) { ret.append( "\\n" ); }
if ( c == '\t' ) { ret.append( "\\t" ); }
if ( c == '\f' ) { ret.append( "\\f" ); }
}
}
write_to( string.size() );
ret += '"';
return ret;
@@ -2026,6 +2102,13 @@ namespace Detail {
rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
return rss.str();
}
std::string makeExceptionHappenedString() {
return "{ stringification failed with an exception: \"" +
translateActiveException() + "\" }";
}
} // end Detail namespace
@@ -2271,7 +2354,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 9, 0, "", 0 );
static Version version( 3, 11, 0, "", 0 );
return version;
}
@@ -2359,8 +2442,14 @@ namespace Catch {
namespace Catch {
namespace Detail {
void missingCaptureInstance() {
CATCH_INTERNAL_ERROR( "No result capture instance" );
}
} // namespace Detail
IResultCapture::~IResultCapture() = default;
}
} // namespace Catch
@@ -3353,6 +3442,9 @@ namespace Catch {
| Opt( config.allowZeroTests )
["--allow-running-no-tests"]
( "Treat 'No tests run' as a success" )
| Opt( config.prematureExitGuardFilePath, "path" )
["--premature-exit-guard-file"]
( "create a file before running tests and delete it during clean exit" )
| Arg( config.testsOrTags, "test name|pattern|tags" )
( "which test or tests to use" );
@@ -3507,7 +3599,11 @@ namespace {
#endif // Windows/ ANSI/ None
#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || defined( __GLIBC__ )
#if defined( CATCH_PLATFORM_LINUX ) \
|| defined( CATCH_PLATFORM_MAC ) \
|| defined( __GLIBC__ ) \
|| defined( __FreeBSD__ ) \
|| defined( CATCH_PLATFORM_QNX )
# define CATCH_INTERNAL_HAS_ISATTY
# include <unistd.h>
#endif
@@ -3631,20 +3727,10 @@ namespace Catch {
namespace Catch {
Context* Context::currentContext = nullptr;
void cleanUpContext() {
delete Context::currentContext;
Context::currentContext = nullptr;
}
void Context::createContext() {
currentContext = new Context();
}
Context Context::currentContext;
Context& getCurrentMutableContext() {
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
return Context::currentContext;
}
SimplePcg32& sharedRng() {
@@ -3749,7 +3835,7 @@ namespace Catch {
#endif
} // namespace Catch
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
#include <fstream>
#include <string>
@@ -3981,10 +4067,10 @@ namespace Catch {
// To avoid having to handle TFE explicitly everywhere, we just
// rethrow it so that it goes back up the caller.
catch( TestFailureException& ) {
std::rethrow_exception(std::current_exception());
return "{ nested assertion failed }";
}
catch( TestSkipException& ) {
std::rethrow_exception(std::current_exception());
return "{ nested SKIP() called }";
}
catch( std::exception const& ex ) {
return ex.what();
@@ -4083,23 +4169,27 @@ namespace Catch {
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
};
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name);
}
}
// If its not an exception we care about, pass it along.
// If a filter was previously registered, invoke it
if (previousTopLevelExceptionFilter) {
return previousTopLevelExceptionFilter(ExceptionInfo);
}
// Otherwise, pass along all exceptions.
// This stops us from eating debugger breaks etc.
return EXCEPTION_CONTINUE_SEARCH;
}
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
// For MSVC, we reserve part of the stack memory for handling
// memory overflow structured exception.
FatalConditionHandler::FatalConditionHandler() {
@@ -4451,6 +4541,33 @@ namespace Detail {
namespace Catch {
namespace {
static bool needsEscape( char c ) {
return c == '"' || c == '\\' || c == '\b' || c == '\f' ||
c == '\n' || c == '\r' || c == '\t';
}
static Catch::StringRef makeEscapeStringRef( char c ) {
if ( c == '"' ) {
return "\\\""_sr;
} else if ( c == '\\' ) {
return "\\\\"_sr;
} else if ( c == '\b' ) {
return "\\b"_sr;
} else if ( c == '\f' ) {
return "\\f"_sr;
} else if ( c == '\n' ) {
return "\\n"_sr;
} else if ( c == '\r' ) {
return "\\r"_sr;
} else if ( c == '\t' ) {
return "\\t"_sr;
}
Catch::Detail::Unreachable();
}
} // namespace
void JsonUtils::indent( std::ostream& os, std::uint64_t level ) {
for ( std::uint64_t i = 0; i < level; ++i ) {
os << " ";
@@ -4560,30 +4677,19 @@ namespace Catch {
void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
if ( quote ) { m_os << '"'; }
for (char c : value) {
// Escape list taken from https://www.json.org/json-en.html,
// string definition.
// Note that while forward slash _can_ be escaped, it does
// not have to be, if JSON is not further embedded somewhere
// where forward slash is meaningful.
if ( c == '"' ) {
m_os << "\\\"";
} else if ( c == '\\' ) {
m_os << "\\\\";
} else if ( c == '\b' ) {
m_os << "\\b";
} else if ( c == '\f' ) {
m_os << "\\f";
} else if ( c == '\n' ) {
m_os << "\\n";
} else if ( c == '\r' ) {
m_os << "\\r";
} else if ( c == '\t' ) {
m_os << "\\t";
} else {
m_os << c;
size_t current_start = 0;
for ( size_t i = 0; i < value.size(); ++i ) {
if ( needsEscape( value[i] ) ) {
if ( current_start < i ) {
m_os << value.substr( current_start, i - current_start );
}
m_os << makeEscapeStringRef( value[i] );
current_start = i + 1;
}
}
if ( current_start < value.size() ) {
m_os << value.substr( current_start, value.size() - current_start );
}
if ( quote ) { m_os << '"'; }
}
@@ -4780,17 +4886,18 @@ int main (int argc, char * argv[]) {
namespace Catch {
MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
: macroName( _macroName ),
lineInfo( _lineInfo ),
type( _type ),
sequence( ++globalCount )
{}
// This may need protecting if threading support is added
unsigned int MessageInfo::globalCount = 0;
// Messages are owned by their individual threads, so the counter should be thread-local as well.
// Alternative consideration: atomic, so threads don't share IDs and things are easier to debug.
thread_local unsigned int MessageInfo::globalCount = 0;
} // end namespace Catch
@@ -5540,6 +5647,7 @@ ReporterSpec::ReporterSpec(
#include <cstdio>
#include <sstream>
#include <tuple>
#include <vector>
namespace Catch {
@@ -5549,34 +5657,40 @@ namespace Catch {
std::vector<Detail::unique_ptr<std::ostringstream>> m_streams;
std::vector<std::size_t> m_unused;
std::ostringstream m_referenceStream; // Used for copy state/ flags from
Detail::Mutex m_mutex;
auto add() -> std::size_t {
auto add() -> std::pair<std::size_t, std::ostringstream*> {
Detail::LockGuard _( m_mutex );
if( m_unused.empty() ) {
m_streams.push_back( Detail::make_unique<std::ostringstream>() );
return m_streams.size()-1;
return { m_streams.size()-1, m_streams.back().get() };
}
else {
auto index = m_unused.back();
m_unused.pop_back();
return index;
return { index, m_streams[index].get() };
}
}
void release( std::size_t index ) {
m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
m_unused.push_back(index);
void release( std::size_t index, std::ostream* originalPtr ) {
assert( originalPtr );
originalPtr->copyfmt( m_referenceStream ); // Restore initial flags and other state
Detail::LockGuard _( m_mutex );
assert( originalPtr == m_streams[index].get() && "Mismatch between release index and stream ptr" );
m_unused.push_back( index );
}
};
ReusableStringStream::ReusableStringStream()
: m_index( Singleton<StringStreams>::getMutable().add() ),
m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
{}
ReusableStringStream::ReusableStringStream() {
std::tie( m_index, m_oss ) =
Singleton<StringStreams>::getMutable().add();
}
ReusableStringStream::~ReusableStringStream() {
static_cast<std::ostringstream*>( m_oss )->str("");
m_oss->clear();
Singleton<StringStreams>::getMutable().release( m_index );
Singleton<StringStreams>::getMutable().release( m_index, m_oss );
}
std::string ReusableStringStream::str() const {
@@ -5743,9 +5857,6 @@ namespace Catch {
// This also implies that messages are owned by their respective
// threads, and should not be shared across different threads.
//
// For simplicity, we disallow messages in multi-threaded contexts,
// but in the future we can enable them under this logic.
//
// This implies that various pieces of metadata referring to last
// assertion result/source location/message handling, etc
// should also be thread local. For now we just use naked globals
@@ -5754,15 +5865,27 @@ namespace Catch {
// This is used for the "if" part of CHECKED_IF/CHECKED_ELSE
static thread_local bool g_lastAssertionPassed = false;
// Should we clear message scopes before sending off the messages to
// reporter? Set in `assertionPassedFastPath` to avoid doing the full
// clear there for performance reasons.
static thread_local bool g_clearMessageScopes = false;
// This is the source location for last encountered macro. It is
// used to provide the users with more precise location of error
// when an unexpected exception/fatal error happens.
static thread_local SourceLineInfo g_lastKnownLineInfo("DummyLocation", static_cast<size_t>(-1));
}
// Should we clear message scopes before sending off the messages to
// reporter? Set in `assertionPassedFastPath` to avoid doing the full
// clear there for performance reasons.
static thread_local bool g_clearMessageScopes = false;
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
// Actual messages to be provided to the reporter
static thread_local std::vector<MessageInfo> g_messages;
// Owners for the UNSCOPED_X information macro
static thread_local std::vector<ScopedMessage> g_messageScopes;
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
} // namespace Detail
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
: m_runInfo(_config->name()),
@@ -5898,20 +6021,21 @@ namespace Catch {
Detail::g_lastAssertionPassed = true;
}
if ( Detail::g_clearMessageScopes ) {
Detail::g_messageScopes.clear();
Detail::g_clearMessageScopes = false;
}
// From here, we are touching shared state and need mutex.
Detail::LockGuard lock( m_assertionMutex );
{
if ( Detail::g_clearMessageScopes ) {
m_messageScopes.clear();
Detail::g_clearMessageScopes = false;
}
auto _ = scopedDeactivate( *m_outputRedirect );
updateTotalsFromAtomics();
m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) );
m_reporter->assertionEnded( AssertionStats( result, Detail::g_messages, m_totals ) );
}
if ( result.getResultType() != ResultWas::Warning ) {
m_messageScopes.clear();
Detail::g_messageScopes.clear();
}
// Reset working state. assertion info will be reset after
@@ -6044,28 +6168,6 @@ namespace Catch {
m_reporter->benchmarkFailed( error );
}
void RunContext::pushScopedMessage(MessageInfo const & message) {
m_messages.push_back(message);
}
void RunContext::popScopedMessage( MessageInfo const& message ) {
// Note: On average, it would probably be better to look for the message
// backwards. However, we do not expect to have to deal with more
// messages than low single digits, so the optimization is tiny,
// and we would have to hand-write the loop to avoid terrible
// codegen of reverse iterators in debug mode.
m_messages.erase(
std::find_if( m_messages.begin(),
m_messages.end(),
[id = message.sequence]( MessageInfo const& msg ) {
return msg.sequence == id;
} ) );
}
void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
m_messageScopes.emplace_back( CATCH_MOVE(builder) );
}
std::string RunContext::getCurrentTestName() const {
return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name
@@ -6222,10 +6324,10 @@ namespace Catch {
m_testCaseTracker->close();
handleUnfinishedSections();
m_messageScopes.clear();
Detail::g_messageScopes.clear();
// TBD: At this point, m_messages should be empty. Do we want to
// assert that this is true, or keep the defensive clear call?
m_messages.clear();
Detail::g_messages.clear();
SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats);
@@ -6392,11 +6494,26 @@ namespace Catch {
}
}
IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture())
return *capture;
else
CATCH_INTERNAL_ERROR("No result capture instance");
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
Detail::g_messages.push_back( CATCH_MOVE( message ) );
}
void IResultCapture::popScopedMessage( unsigned int messageId ) {
// Note: On average, it would probably be better to look for the message
// backwards. However, we do not expect to have to deal with more
// messages than low single digits, so the optimization is tiny,
// and we would have to hand-write the loop to avoid terrible
// codegen of reverse iterators in debug mode.
Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(),
Detail::g_messages.end(),
[=]( MessageInfo const& msg ) {
return msg.sequence ==
messageId;
} ) );
}
void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) );
}
void seedRng(IConfig const& config) {
@@ -7964,7 +8081,7 @@ namespace {
void hexEscapeChar(std::ostream& os, unsigned char c) {
std::ios_base::fmtflags f(os.flags());
os << "\\x"
os << "\\x"_sr
<< std::uppercase << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<int>(c);
os.flags(f);
@@ -7983,95 +8100,111 @@ namespace {
void XmlEncode::encodeTo( std::ostream& os ) const {
// Apostrophe escaping not necessary if we always use " to write attributes
// (see: http://www.w3.org/TR/xml/#syntax)
size_t last_start = 0;
auto write_to = [&]( size_t idx ) {
if ( last_start < idx ) {
os << m_str.substr( last_start, idx - last_start );
}
last_start = idx + 1;
};
for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
unsigned char c = static_cast<unsigned char>(m_str[idx]);
switch (c) {
case '<': os << "&lt;"; break;
case '&': os << "&amp;"; break;
for ( std::size_t idx = 0; idx < m_str.size(); ++idx ) {
unsigned char c = static_cast<unsigned char>( m_str[idx] );
switch ( c ) {
case '<':
write_to( idx );
os << "&lt;"_sr;
break;
case '&':
write_to( idx );
os << "&amp;"_sr;
break;
case '>':
// See: http://www.w3.org/TR/xml/#syntax
if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
os << "&gt;";
else
os << c;
if ( idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']' ) {
write_to( idx );
os << "&gt;"_sr;
}
break;
case '\"':
if (m_forWhat == ForAttributes)
os << "&quot;";
else
os << c;
if ( m_forWhat == ForAttributes ) {
write_to( idx );
os << "&quot;"_sr;
}
break;
default:
// Check for control characters and invalid utf-8
// Escape control characters in standard ascii
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
hexEscapeChar(os, c);
// see
// http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
if ( c < 0x09 || ( c > 0x0D && c < 0x20 ) || c == 0x7F ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
// Plain ASCII: Write it to stream
if (c < 0x7F) {
os << c;
if ( c < 0x7F ) {
break;
}
// UTF-8 territory
// Check if the encoding is valid and if it is not, hex escape bytes.
// Important: We do not check the exact decoded values for validity, only the encoding format
// First check that this bytes is a valid lead byte:
// This means that it is not encoded as 1111 1XXX
// Check if the encoding is valid and if it is not, hex escape
// bytes. Important: We do not check the exact decoded values for
// validity, only the encoding format First check that this bytes is
// a valid lead byte: This means that it is not encoded as 1111 1XXX
// Or as 10XX XXXX
if (c < 0xC0 ||
c >= 0xF8) {
hexEscapeChar(os, c);
if ( c < 0xC0 || c >= 0xF8 ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
auto encBytes = trailingBytes(c);
// Are there enough bytes left to avoid accessing out-of-bounds memory?
if (idx + encBytes - 1 >= m_str.size()) {
hexEscapeChar(os, c);
auto encBytes = trailingBytes( c );
// Are there enough bytes left to avoid accessing out-of-bounds
// memory?
if ( idx + encBytes - 1 >= m_str.size() ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
// The header is valid, check data
// The next encBytes bytes must together be a valid utf-8
// This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
// This means: bitpattern 10XX XXXX and the extracted value is sane
// (ish)
bool valid = true;
uint32_t value = headerValue(c);
for (std::size_t n = 1; n < encBytes; ++n) {
unsigned char nc = static_cast<unsigned char>(m_str[idx + n]);
valid &= ((nc & 0xC0) == 0x80);
value = (value << 6) | (nc & 0x3F);
uint32_t value = headerValue( c );
for ( std::size_t n = 1; n < encBytes; ++n ) {
unsigned char nc = static_cast<unsigned char>( m_str[idx + n] );
valid &= ( ( nc & 0xC0 ) == 0x80 );
value = ( value << 6 ) | ( nc & 0x3F );
}
if (
// Wrong bit pattern of following bytes
(!valid) ||
( !valid ) ||
// Overlong encodings
(value < 0x80) ||
(0x80 <= value && value < 0x800 && encBytes > 2) ||
(0x800 < value && value < 0x10000 && encBytes > 3) ||
( value < 0x80 ) ||
( 0x80 <= value && value < 0x800 && encBytes > 2 ) ||
( 0x800 < value && value < 0x10000 && encBytes > 3 ) ||
// Encoded value out of range
(value >= 0x110000)
) {
hexEscapeChar(os, c);
( value >= 0x110000 ) ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
// If we got here, this is in fact a valid(ish) utf-8 sequence
for (std::size_t n = 0; n < encBytes; ++n) {
os << m_str[idx + n];
}
idx += encBytes - 1;
break;
}
}
write_to( m_str.size() );
}
std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {

View File

@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
// Catch v3.9.0
// Generated: 2025-07-24 22:00:24.654688
// Catch v3.11.0
// Generated: 2025-09-30 10:49:11.225746
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -101,6 +101,9 @@
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX
#elif defined(__QNX__)
# define CATCH_PLATFORM_QNX
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
# define CATCH_PLATFORM_WINDOWS
@@ -308,13 +311,17 @@
# endif
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
# if defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# else
# if !defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
# endif
// Only some Windows platform families support the console
# if defined(WINAPI_FAMILY_PARTITION)
# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# endif
# endif
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
@@ -564,11 +571,9 @@ namespace Catch {
IConfig const* m_config = nullptr;
IResultCapture* m_resultCapture = nullptr;
CATCH_EXPORT static Context* currentContext;
CATCH_EXPORT static Context currentContext;
friend Context& getCurrentMutableContext();
friend Context const& getCurrentContext();
static void createContext();
friend void cleanUpContext();
public:
constexpr IResultCapture* getResultCapture() const {
@@ -579,21 +584,14 @@ namespace Catch {
m_resultCapture = resultCapture;
}
constexpr void setConfig( IConfig const* config ) { m_config = config; }
};
Context& getCurrentMutableContext();
inline Context const& getCurrentContext() {
// We duplicate the logic from `getCurrentMutableContext` here,
// to avoid paying the call overhead in debug mode.
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
return Context::currentContext;
}
void cleanUpContext();
class SimplePcg32;
SimplePcg32& sharedRng();
}
@@ -958,7 +956,7 @@ namespace Detail {
}
explicit operator bool() const {
return m_ptr;
return m_ptr != nullptr;
}
friend void swap(unique_ptr& lhs, unique_ptr& rhs) {
@@ -1069,10 +1067,9 @@ namespace Catch {
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&& builder ) = 0;
static void pushScopedMessage( MessageInfo&& message );
static void popScopedMessage( unsigned int messageId );
static void emplaceUnscopedMessage( MessageBuilder&& builder );
virtual void handleFatalErrorCondition( StringRef message ) = 0;
@@ -1108,7 +1105,18 @@ namespace Catch {
virtual void exceptionEarlyReported() = 0;
};
IResultCapture& getResultCapture();
namespace Detail {
[[noreturn]]
void missingCaptureInstance();
}
inline IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture()) {
return *capture;
} else {
Detail::missingCaptureInstance();
}
}
}
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
@@ -2151,9 +2159,7 @@ namespace Catch {
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats);
} CATCH_CATCH_ANON (TestFailureException const&) {
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
} CATCH_CATCH_ALL{
} CATCH_CATCH_ALL {
getResultCapture().benchmarkFailed(translateActiveException());
// We let the exception go further up so that the
// test case is marked as failed.
@@ -2566,11 +2572,17 @@ namespace Catch {
namespace Detail {
std::string makeExceptionHappenedString();
// This function dispatches all stringification requests inside of Catch.
// Should be preferably called fully qualified, like ::Catch::Detail::stringify
template <typename T>
std::string stringify(const T& e) {
return ::Catch::StringMaker<std::remove_cv_t<std::remove_reference_t<T>>>::convert(e);
std::string stringify( const T& e ) {
CATCH_TRY {
return ::Catch::StringMaker<
std::remove_cv_t<std::remove_reference_t<T>>>::convert( e );
}
CATCH_CATCH_ALL { return makeExceptionHappenedString(); }
}
template<typename E>
@@ -3053,17 +3065,19 @@ struct ratio_string<std::milli> {
template<typename Duration>
struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
auto converted = std::chrono::system_clock::to_time_t(time_point);
const auto systemish = std::chrono::time_point_cast<
std::chrono::system_clock::duration>( time_point );
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
#ifdef _MSC_VER
std::tm timeInfo = {};
const auto err = gmtime_s(&timeInfo, &converted);
const auto err = gmtime_s( &timeInfo, &as_time_t );
if ( err ) {
return "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc";
}
#else
std::tm* timeInfo = std::gmtime(&converted);
std::tm* timeInfo = std::gmtime( &as_time_t );
#endif
auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
@@ -3794,6 +3808,8 @@ namespace Catch {
std::vector<std::string> testsOrTags;
std::vector<std::string> sectionsToRun;
std::string prematureExitGuardFilePath;
};
@@ -3821,6 +3837,8 @@ namespace Catch {
bool showHelp() const;
std::string const& getExitGuardFilePath() const;
// IConfig interface
bool allowThrows() const override;
StringRef name() const override;
@@ -3955,6 +3973,7 @@ namespace Catch {
std::string message;
SourceLineInfo lineInfo;
ResultWas::OfType type;
// The "ID" of the message, used to know when to remove it from reporter context.
unsigned int sequence;
DEPRECATED( "Explicitly use the 'sequence' member instead" )
@@ -3966,7 +3985,7 @@ namespace Catch {
return sequence < other.sequence;
}
private:
static unsigned int globalCount;
static thread_local unsigned int globalCount;
};
} // end namespace Catch
@@ -4014,13 +4033,12 @@ namespace Catch {
ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage();
MessageInfo m_info;
unsigned int m_messageId;
bool m_moved = false;
};
class Capturer {
std::vector<MessageInfo> m_messages;
IResultCapture& m_resultCapture;
size_t m_captured = 0;
public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
@@ -4068,7 +4086,7 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
@@ -5238,10 +5256,11 @@ namespace Detail {
*
* 3) If a type has no linkage, we also cannot capture it by reference.
* The solution is once again to capture them by value. We handle
* the common cases by using `std::is_arithmetic` as the default
* for `Catch::capture_by_value`, but that is only a some-effort
* heuristic. But as with 2), users can specialize `capture_by_value`
* for their own types as needed.
* the common cases by using `std::is_arithmetic` and `std::is_enum`
* as the default for `Catch::capture_by_value`, but that is only a
* some-effort heuristic. These combine to capture all possible bitfield
* bases, and also some trait-like types. As with 2), users can
* specialize `capture_by_value` for their own types as needed.
*
* 4) To support C++20 and make the SFINAE on our decomposing operators
* work, the SFINAE has to happen in return type, rather than in
@@ -5293,13 +5312,22 @@ namespace Catch {
using RemoveCVRef_t = std::remove_cv_t<std::remove_reference_t<T>>;
}
// Note: There is nothing that stops us from extending this,
// e.g. to `std::is_scalar`, but the more encompassing
// traits are usually also more expensive. For now we
// keep this as it used to be and it can be changed later.
// Note: This is about as much as we can currently reasonably support.
// In an ideal world, we could capture by value small trivially
// copyable types, but the actual `std::is_trivially_copyable`
// trait is a huge mess with standard-violating results on
// GCC and Clang, which are unlikely to be fixed soon due to ABI
// concerns.
// `std::is_scalar` also causes issues due to the `is_pointer`
// component, which causes ambiguity issues with (references-to)
// function pointer. If those are resolved, we still need to
// disambiguate the overload set for arrays, through explicit
// overload for references to sized arrays.
template <typename T>
struct capture_by_value
: std::integral_constant<bool, std::is_arithmetic<T>{}> {};
: std::integral_constant<bool,
std::is_arithmetic<T>::value ||
std::is_enum<T>::value> {};
#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )
template <>
@@ -6241,9 +6269,13 @@ namespace Catch {
__assume( false );
# elif defined( __GNUC__ )
__builtin_unreachable();
# endif
# endif // ^^ NDEBUG
# else // vv platform without known optimization hint
std::terminate();
# endif
# else // ^^ NDEBUG
// For non-release builds, we prefer termination on bug over UB
std::terminate();
# endif //
}
} // namespace Detail
@@ -7446,7 +7478,7 @@ namespace Catch {
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 9
#define CATCH_VERSION_MINOR 11
#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -7862,8 +7894,9 @@ namespace Generators {
class FilterGenerator final : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
Predicate m_predicate;
static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
public:
template <typename P = Predicate>
template <typename P>
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
m_generator(CATCH_MOVE(generator)),
m_predicate(CATCH_FORWARD(pred))
@@ -7895,7 +7928,7 @@ namespace Generators {
template <typename T, typename Predicate>
GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, typename std::remove_reference<Predicate>::type>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
}
template <typename T>
@@ -9500,6 +9533,17 @@ namespace Catch {
bool isDebuggerActive();
}
#if !defined( CATCH_TRAP ) && defined( __clang__ ) && defined( __has_builtin )
# if __has_builtin( __builtin_debugtrap )
# define CATCH_TRAP() __builtin_debugtrap()
# endif
#endif
#if !defined( CATCH_TRAP ) && defined( _MSC_VER )
# define CATCH_TRAP() __debugbreak()
#endif
#if !defined(CATCH_TRAP) // If we couldn't use compiler-specific impl from above, we get into platform-specific options
#ifdef CATCH_PLATFORM_MAC
#if defined(__i386__) || defined(__x86_64__)
@@ -9524,7 +9568,7 @@ namespace Catch {
#define CATCH_TRAP() __asm__(".inst 0xde01")
#endif
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
// If we can use inline assembler, do it because this allows us to break
// directly at the location of the failing check instead of breaking inside
// raise() called from it, i.e. one stack frame below.
@@ -9535,15 +9579,15 @@ namespace Catch {
#define CATCH_TRAP() raise(SIGTRAP)
#endif
#elif defined(_MSC_VER)
#define CATCH_TRAP() __debugbreak()
#elif defined(__MINGW32__)
extern "C" __declspec(dllimport) void __stdcall DebugBreak();
#define CATCH_TRAP() DebugBreak()
#endif
#endif // ^^ CATCH_TRAP is not defined yet, so we define it
#ifndef CATCH_BREAK_INTO_DEBUGGER
#ifdef CATCH_TRAP
#if !defined(CATCH_BREAK_INTO_DEBUGGER)
#if defined(CATCH_TRAP)
#define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
#else
#define CATCH_BREAK_INTO_DEBUGGER() []{}()
@@ -10687,11 +10731,6 @@ namespace Catch {
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
void benchmarkFailed( StringRef error ) override;
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
void emplaceUnscopedMessage( MessageBuilder&& builder ) override;
std::string getCurrentTestName() const override;
const AssertionResult* getLastResult() const override;
@@ -10742,9 +10781,6 @@ namespace Catch {
Totals m_totals;
Detail::AtomicCounts m_atomicAssertionCount;
IEventListenerPtr m_reporter;
std::vector<MessageInfo> m_messages;
// Owners for the UNSCOPED_X information macro
std::vector<ScopedMessage> m_messageScopes;
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;

View File

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

View File

@@ -98,6 +98,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_lifetimebound.hpp
${SOURCES_DIR}/internal/catch_list.hpp
${SOURCES_DIR}/internal/catch_logical_traits.hpp
${SOURCES_DIR}/internal/catch_message_info.hpp

View File

@@ -86,9 +86,7 @@ namespace Catch {
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats);
} CATCH_CATCH_ANON (TestFailureException const&) {
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
} CATCH_CATCH_ALL{
} CATCH_CATCH_ALL {
getResultCapture().benchmarkFailed(translateActiveException());
// We let the exception go further up so that the
// test case is marked as failed.

View File

@@ -79,6 +79,7 @@
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_list.hpp>
#include <catch2/internal/catch_logical_traits.hpp>
#include <catch2/internal/catch_message_info.hpp>

View File

@@ -119,6 +119,8 @@ namespace Catch {
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
}
// Reading bazel env vars can change some parts of the config data,
// so we have to process the bazel env before acting on the config.
if ( enableBazelEnvSupport() ) {
readBazelEnvVars();
}
@@ -183,6 +185,8 @@ namespace Catch {
bool Config::showHelp() const { return m_data.showHelp; }
std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
// IConfig interface
bool Config::allowThrows() const { return !m_data.noThrow; }
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
@@ -244,6 +248,26 @@ namespace Catch {
m_data.shardCount = bazelShardOptions->shardCount;
}
}
const auto bazelExitGuardFile = Detail::getEnv( "TEST_PREMATURE_EXIT_FILE" );
if (bazelExitGuardFile) {
m_data.prematureExitGuardFilePath = bazelExitGuardFile;
}
const auto bazelRandomSeed = Detail::getEnv( "TEST_RANDOM_SEED" );
if ( bazelRandomSeed ) {
auto parsedSeed = parseUInt( bazelRandomSeed, 0 );
if ( !parsedSeed ) {
// Currently we handle issues with parsing other Bazel Env
// options by warning and ignoring the issue. So we do the
// same for random seed option.
Catch::cerr()
<< "Warning: could not parse 'TEST_RANDOM_SEED' ('"
<< bazelRandomSeed << "') as proper seed.\n";
} else {
m_data.rngSeed = *parsedSeed;
}
}
}
} // end namespace Catch

View File

@@ -87,6 +87,8 @@ namespace Catch {
std::vector<std::string> testsOrTags;
std::vector<std::string> sectionsToRun;
std::string prematureExitGuardFilePath;
};
@@ -114,6 +116,8 @@ namespace Catch {
bool showHelp() const;
std::string const& getExitGuardFilePath() const;
// IConfig interface
bool allowThrows() const override;
StringRef name() const override;

View File

@@ -19,28 +19,26 @@ namespace Catch {
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
m_messageId( builder.m_info.sequence ) {
MessageInfo info( CATCH_MOVE( builder.m_info ) );
info.message = builder.m_stream.str();
IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
m_info( CATCH_MOVE( old.m_info ) ) {
m_messageId( old.m_messageId ) {
old.m_moved = true;
}
ScopedMessage::~ScopedMessage() {
if ( !m_moved ){
getResultCapture().popScopedMessage(m_info);
}
if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
}
Capturer::Capturer( StringRef macroName,
SourceLineInfo const& lineInfo,
ResultWas::OfType resultType,
StringRef names ):
m_resultCapture( getResultCapture() ) {
StringRef names ) {
auto trimmed = [&] (size_t start, size_t end) {
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;
@@ -86,8 +84,8 @@ namespace Catch {
case ',':
if (start != pos && openings.empty()) {
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, pos);
m_messages.back().message += " := "_sr;
start = pos;
}
break;
@@ -96,19 +94,20 @@ namespace Catch {
}
assert(openings.empty() && "Mismatched openings");
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, names.size() - 1);
m_messages.back().message += " := "_sr;
}
Capturer::~Capturer() {
assert( m_captured == m_messages.size() );
for ( size_t i = 0; i < m_captured; ++i )
m_resultCapture.popScopedMessage( m_messages[i] );
for (auto const& message : m_messages) {
IResultCapture::popScopedMessage( message.sequence );
}
}
void Capturer::captureValue( size_t index, std::string const& value ) {
assert( index < m_messages.size() );
m_messages[index].message += value;
m_resultCapture.pushScopedMessage( m_messages[index] );
IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
m_captured++;
}

View File

@@ -57,13 +57,12 @@ namespace Catch {
ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage();
MessageInfo m_info;
unsigned int m_messageId;
bool m_moved = false;
};
class Capturer {
std::vector<MessageInfo> m_messages;
IResultCapture& m_resultCapture;
size_t m_captured = 0;
public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
@@ -111,7 +110,7 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)

View File

@@ -96,7 +96,6 @@ namespace Catch {
}
void cleanUp() {
cleanupSingletons();
cleanUpContext();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();

View File

@@ -26,6 +26,8 @@
#include <catch2/internal/catch_istream.hpp>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <iomanip>
#include <set>
@@ -140,6 +142,50 @@ namespace Catch {
}
}
// Creates empty file at path. The path must be writable, we do not
// try to create directories in path because that's hard in C++14.
void setUpGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
#if defined( _MSC_VER )
std::FILE* file = nullptr;
if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) {
char msgBuffer[100];
const auto err = errno;
std::string errMsg;
if ( !strerror_s( msgBuffer, err ) ) {
errMsg = msgBuffer;
} else {
errMsg = "Could not translate errno to a string";
}
#else
std::FILE* file = std::fopen( guardFilePath.c_str(), "w" );
if ( !file ) {
const auto err = errno;
const char* errMsg = std::strerror( err );
#endif
CATCH_RUNTIME_ERROR( "Could not open the exit guard file '"
<< guardFilePath << "' because '"
<< errMsg << "' (" << err << ')' );
}
const int ret = std::fclose( file );
CATCH_ENFORCE(
ret == 0,
"Error when closing the exit guard file: " << ret );
}
}
// Removes file at path. Assuming we created it in setUpGuardFile.
void tearDownGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
const int ret = std::remove( guardFilePath.c_str() );
CATCH_ENFORCE(
ret == 0,
"Error when removing the exit guard file: " << ret );
}
}
} // anon namespace
Session::Session() {
@@ -258,6 +304,7 @@ namespace Catch {
static_cast<void>(std::getchar());
}
int exitCode = runInternal();
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
static_cast<void>(std::getchar());
@@ -298,6 +345,10 @@ namespace Catch {
CATCH_TRY {
config(); // Force config to be constructed
// We need to retrieve potential Bazel config with the full Config
// constructor, so we have to create the guard file after it is created.
setUpGuardFile( m_config->getExitGuardFilePath() );
seedRng( *m_config );
if (m_configData.filenamesAsTags) {
@@ -327,9 +378,12 @@ namespace Catch {
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
auto const totals = tests.execute();
// If we got here, running the tests finished normally-enough.
// They might've failed, but that would've been reported elsewhere.
tearDownGuardFile( m_config->getExitGuardFilePath() );
if ( tests.hadUnmatchedTestSpecs()
&& m_config->warnAboutUnmatchedTestSpecs() ) {
// UnmatchedTestSpecExitCode
return UnmatchedTestSpecExitCode;
}

View File

@@ -8,6 +8,7 @@
#include <catch2/catch_tostring.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_polyfills.hpp>
@@ -56,39 +57,69 @@ namespace Detail {
}
} // end unnamed namespace
std::size_t catch_strnlen( const char* str, std::size_t n ) {
auto ret = std::char_traits<char>::find( str, n, '\0' );
if ( ret != nullptr ) { return static_cast<std::size_t>( ret - str ); }
return n;
}
std::string formatTimeT(std::time_t time) {
#ifdef _MSC_VER
std::tm timeInfo = {};
const auto err = gmtime_s( &timeInfo, &time );
if ( err ) {
return "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc";
}
#else
std::tm* timeInfo = std::gmtime( &time );
#endif
auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" );
char timeStamp[timeStampSize];
const char* const fmt = "%Y-%m-%dT%H:%M:%SZ";
#ifdef _MSC_VER
std::strftime( timeStamp, timeStampSize, fmt, &timeInfo );
#else
std::strftime( timeStamp, timeStampSize, fmt, timeInfo );
#endif
return std::string( timeStamp, timeStampSize - 1 );
}
std::string convertIntoString(StringRef string, bool escapeInvisibles) {
std::string ret;
// This is enough for the "don't escape invisibles" case, and a good
// lower bound on the "escape invisibles" case.
ret.reserve(string.size() + 2);
ret.reserve( string.size() + 2 );
if (!escapeInvisibles) {
if ( !escapeInvisibles ) {
ret += '"';
ret += string;
ret += '"';
return ret;
}
size_t last_start = 0;
auto write_to = [&]( size_t idx ) {
if ( last_start < idx ) {
ret += string.substr( last_start, idx - last_start );
}
last_start = idx + 1;
};
ret += '"';
for (char c : string) {
switch (c) {
case '\r':
ret.append("\\r");
break;
case '\n':
ret.append("\\n");
break;
case '\t':
ret.append("\\t");
break;
case '\f':
ret.append("\\f");
break;
default:
ret.push_back(c);
break;
for ( size_t i = 0; i < string.size(); ++i ) {
const char c = string[i];
if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
write_to( i );
if ( c == '\r' ) { ret.append( "\\r" ); }
if ( c == '\n' ) { ret.append( "\\n" ); }
if ( c == '\t' ) { ret.append( "\\t" ); }
if ( c == '\f' ) { ret.append( "\\f" ); }
}
}
write_to( string.size() );
ret += '"';
return ret;
@@ -113,6 +144,13 @@ namespace Detail {
rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
return rss.str();
}
std::string makeExceptionHappenedString() {
return "{ stringification failed with an exception: \"" +
translateActiveException() + "\" }";
}
} // end Detail namespace

View File

@@ -8,7 +8,7 @@
#ifndef CATCH_TOSTRING_HPP_INCLUDED
#define CATCH_TOSTRING_HPP_INCLUDED
#include <ctime>
#include <vector>
#include <cstddef>
#include <type_traits>
@@ -40,13 +40,9 @@ namespace Catch {
namespace Detail {
inline std::size_t catch_strnlen(const char *str, std::size_t n) {
auto ret = std::char_traits<char>::find(str, n, '\0');
if (ret != nullptr) {
return static_cast<std::size_t>(ret - str);
}
return n;
}
std::size_t catch_strnlen(const char *str, std::size_t n);
std::string formatTimeT( std::time_t time );
constexpr StringRef unprintableString = "{?}"_sr;
@@ -139,11 +135,17 @@ namespace Catch {
namespace Detail {
std::string makeExceptionHappenedString();
// This function dispatches all stringification requests inside of Catch.
// Should be preferably called fully qualified, like ::Catch::Detail::stringify
template <typename T>
std::string stringify(const T& e) {
return ::Catch::StringMaker<std::remove_cv_t<std::remove_reference_t<T>>>::convert(e);
std::string stringify( const T& e ) {
CATCH_TRY {
return ::Catch::StringMaker<
std::remove_cv_t<std::remove_reference_t<T>>>::convert( e );
}
CATCH_CATCH_ALL { return makeExceptionHappenedString(); }
}
template<typename E>
@@ -405,44 +407,38 @@ namespace Catch {
// Separate std::tuple specialization
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
#include <tuple>
# include <tuple>
# include <utility>
namespace Catch {
namespace Detail {
template<
typename Tuple,
std::size_t N = 0,
bool = (N < std::tuple_size<Tuple>::value)
>
struct TupleElementPrinter {
static void print(const Tuple& tuple, std::ostream& os) {
os << (N ? ", " : " ")
<< ::Catch::Detail::stringify(std::get<N>(tuple));
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
}
};
template <typename Tuple, std::size_t... Is>
void PrintTuple( const Tuple& tuple,
std::ostream& os,
std::index_sequence<Is...> ) {
// 1 + Account for when the tuple is empty
char a[1 + sizeof...( Is )] = {
( ( os << ( Is ? ", " : " " )
<< ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
'\0' )... };
(void)a;
}
template<
typename Tuple,
std::size_t N
>
struct TupleElementPrinter<Tuple, N, false> {
static void print(const Tuple&, std::ostream&) {}
};
} // namespace Detail
}
template<typename ...Types>
template <typename... Types>
struct StringMaker<std::tuple<Types...>> {
static std::string convert(const std::tuple<Types...>& tuple) {
static std::string convert( const std::tuple<Types...>& tuple ) {
ReusableStringStream rss;
rss << '{';
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
Detail::PrintTuple(
tuple,
rss.get(),
std::make_index_sequence<sizeof...( Types )>{} );
rss << " }";
return rss.str();
}
};
}
} // namespace Catch
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
@@ -626,29 +622,10 @@ struct ratio_string<std::milli> {
template<typename Duration>
struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
auto converted = std::chrono::system_clock::to_time_t(time_point);
#ifdef _MSC_VER
std::tm timeInfo = {};
const auto err = gmtime_s(&timeInfo, &converted);
if ( err ) {
return "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc";
}
#else
std::tm* timeInfo = std::gmtime(&converted);
#endif
auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
char timeStamp[timeStampSize];
const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
#ifdef _MSC_VER
std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
#else
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
#endif
return std::string(timeStamp, timeStampSize - 1);
const auto systemish = std::chrono::time_point_cast<
std::chrono::system_clock::duration>( time_point );
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
return ::Catch::Detail::formatTimeT( as_time_t );
}
};
}

View File

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

View File

@@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 9
#define CATCH_VERSION_MINOR 11
#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED

View File

@@ -58,8 +58,9 @@ namespace Generators {
class FilterGenerator final : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
Predicate m_predicate;
static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
public:
template <typename P = Predicate>
template <typename P>
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
m_generator(CATCH_MOVE(generator)),
m_predicate(CATCH_FORWARD(pred))
@@ -91,7 +92,7 @@ namespace Generators {
template <typename T, typename Predicate>
GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, typename std::remove_reference<Predicate>::type>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
}
template <typename T>

View File

@@ -7,7 +7,14 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_enforce.hpp>
namespace Catch {
namespace Detail {
void missingCaptureInstance() {
CATCH_INTERNAL_ERROR( "No result capture instance" );
}
} // namespace Detail
IResultCapture::~IResultCapture() = default;
}
} // namespace Catch

View File

@@ -10,6 +10,7 @@
#include <string>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
@@ -62,10 +63,9 @@ namespace Catch {
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&& builder ) = 0;
static void pushScopedMessage( MessageInfo&& message );
static void popScopedMessage( unsigned int messageId );
static void emplaceUnscopedMessage( MessageBuilder&& builder );
virtual void handleFatalErrorCondition( StringRef message ) = 0;
@@ -101,7 +101,18 @@ namespace Catch {
virtual void exceptionEarlyReported() = 0;
};
IResultCapture& getResultCapture();
namespace Detail {
[[noreturn]]
void missingCaptureInstance();
}
inline IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture()) {
return *capture;
} else {
Detail::missingCaptureInstance();
}
}
}
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED

View File

@@ -305,6 +305,9 @@ namespace Catch {
| Opt( config.allowZeroTests )
["--allow-running-no-tests"]
( "Treat 'No tests run' as a success" )
| Opt( config.prematureExitGuardFilePath, "path" )
["--premature-exit-guard-file"]
( "create a file before running tests and delete it during clean exit" )
| Arg( config.testsOrTags, "test name|pattern|tags" )
( "which test or tests to use" );

View File

@@ -220,13 +220,17 @@
# endif
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
# if defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# else
# if !defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
# endif
// Only some Windows platform families support the console
# if defined(WINAPI_FAMILY_PARTITION)
# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# endif
# endif
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor

View File

@@ -161,7 +161,11 @@ namespace {
#endif // Windows/ ANSI/ None
#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || defined( __GLIBC__ )
#if defined( CATCH_PLATFORM_LINUX ) \
|| defined( CATCH_PLATFORM_MAC ) \
|| defined( __GLIBC__ ) \
|| defined( __FreeBSD__ ) \
|| defined( CATCH_PLATFORM_QNX )
# define CATCH_INTERNAL_HAS_ISATTY
# include <unistd.h>
#endif

View File

@@ -6,25 +6,14 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/internal/catch_random_number_generator.hpp>
namespace Catch {
Context* Context::currentContext = nullptr;
void cleanUpContext() {
delete Context::currentContext;
Context::currentContext = nullptr;
}
void Context::createContext() {
currentContext = new Context();
}
Context Context::currentContext;
Context& getCurrentMutableContext() {
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
return Context::currentContext;
}
SimplePcg32& sharedRng() {

View File

@@ -19,11 +19,9 @@ namespace Catch {
IConfig const* m_config = nullptr;
IResultCapture* m_resultCapture = nullptr;
CATCH_EXPORT static Context* currentContext;
CATCH_EXPORT static Context currentContext;
friend Context& getCurrentMutableContext();
friend Context const& getCurrentContext();
static void createContext();
friend void cleanUpContext();
public:
constexpr IResultCapture* getResultCapture() const {
@@ -34,21 +32,14 @@ namespace Catch {
m_resultCapture = resultCapture;
}
constexpr void setConfig( IConfig const* config ) { m_config = config; }
};
Context& getCurrentMutableContext();
inline Context const& getCurrentContext() {
// We duplicate the logic from `getCurrentMutableContext` here,
// to avoid paying the call overhead in debug mode.
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
return Context::currentContext;
}
void cleanUpContext();
class SimplePcg32;
SimplePcg32& sharedRng();
}

View File

@@ -69,7 +69,7 @@
#endif
} // namespace Catch
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
#include <fstream>
#include <string>

View File

@@ -14,6 +14,17 @@ namespace Catch {
bool isDebuggerActive();
}
#if !defined( CATCH_TRAP ) && defined( __clang__ ) && defined( __has_builtin )
# if __has_builtin( __builtin_debugtrap )
# define CATCH_TRAP() __builtin_debugtrap()
# endif
#endif
#if !defined( CATCH_TRAP ) && defined( _MSC_VER )
# define CATCH_TRAP() __debugbreak()
#endif
#if !defined(CATCH_TRAP) // If we couldn't use compiler-specific impl from above, we get into platform-specific options
#ifdef CATCH_PLATFORM_MAC
#if defined(__i386__) || defined(__x86_64__)
@@ -38,7 +49,7 @@ namespace Catch {
#define CATCH_TRAP() __asm__(".inst 0xde01")
#endif
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
// If we can use inline assembler, do it because this allows us to break
// directly at the location of the failing check instead of breaking inside
// raise() called from it, i.e. one stack frame below.
@@ -49,15 +60,15 @@ namespace Catch {
#define CATCH_TRAP() raise(SIGTRAP)
#endif
#elif defined(_MSC_VER)
#define CATCH_TRAP() __debugbreak()
#elif defined(__MINGW32__)
extern "C" __declspec(dllimport) void __stdcall DebugBreak();
#define CATCH_TRAP() DebugBreak()
#endif
#endif // ^^ CATCH_TRAP is not defined yet, so we define it
#ifndef CATCH_BREAK_INTO_DEBUGGER
#ifdef CATCH_TRAP
#if !defined(CATCH_BREAK_INTO_DEBUGGER)
#if defined(CATCH_TRAP)
#define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
#else
#define CATCH_BREAK_INTO_DEBUGGER() []{}()

View File

@@ -78,10 +78,11 @@
*
* 3) If a type has no linkage, we also cannot capture it by reference.
* The solution is once again to capture them by value. We handle
* the common cases by using `std::is_arithmetic` as the default
* for `Catch::capture_by_value`, but that is only a some-effort
* heuristic. But as with 2), users can specialize `capture_by_value`
* for their own types as needed.
* the common cases by using `std::is_arithmetic` and `std::is_enum`
* as the default for `Catch::capture_by_value`, but that is only a
* some-effort heuristic. These combine to capture all possible bitfield
* bases, and also some trait-like types. As with 2), users can
* specialize `capture_by_value` for their own types as needed.
*
* 4) To support C++20 and make the SFINAE on our decomposing operators
* work, the SFINAE has to happen in return type, rather than in
@@ -133,13 +134,22 @@ namespace Catch {
using RemoveCVRef_t = std::remove_cv_t<std::remove_reference_t<T>>;
}
// Note: There is nothing that stops us from extending this,
// e.g. to `std::is_scalar`, but the more encompassing
// traits are usually also more expensive. For now we
// keep this as it used to be and it can be changed later.
// Note: This is about as much as we can currently reasonably support.
// In an ideal world, we could capture by value small trivially
// copyable types, but the actual `std::is_trivially_copyable`
// trait is a huge mess with standard-violating results on
// GCC and Clang, which are unlikely to be fixed soon due to ABI
// concerns.
// `std::is_scalar` also causes issues due to the `is_pointer`
// component, which causes ambiguity issues with (references-to)
// function pointer. If those are resolved, we still need to
// disambiguate the overload set for arrays, through explicit
// overload for references to sized arrays.
template <typename T>
struct capture_by_value
: std::integral_constant<bool, std::is_arithmetic<T>{}> {};
: std::integral_constant<bool,
std::is_arithmetic<T>::value ||
std::is_enum<T>::value> {};
#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )
template <>

View File

@@ -59,10 +59,10 @@ namespace Catch {
// To avoid having to handle TFE explicitly everywhere, we just
// rethrow it so that it goes back up the caller.
catch( TestFailureException& ) {
std::rethrow_exception(std::current_exception());
return "{ nested assertion failed }";
}
catch( TestSkipException& ) {
std::rethrow_exception(std::current_exception());
return "{ nested SKIP() called }";
}
catch( std::exception const& ex ) {
return ex.what();

View File

@@ -86,23 +86,27 @@ namespace Catch {
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
};
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name);
}
}
// If its not an exception we care about, pass it along.
// If a filter was previously registered, invoke it
if (previousTopLevelExceptionFilter) {
return previousTopLevelExceptionFilter(ExceptionInfo);
}
// Otherwise, pass along all exceptions.
// This stops us from eating debugger breaks etc.
return EXCEPTION_CONTINUE_SEARCH;
}
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
// For MSVC, we reserve part of the stack memory for handling
// memory overflow structured exception.
FatalConditionHandler::FatalConditionHandler() {

View File

@@ -7,8 +7,36 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_unreachable.hpp>
namespace Catch {
namespace {
static bool needsEscape( char c ) {
return c == '"' || c == '\\' || c == '\b' || c == '\f' ||
c == '\n' || c == '\r' || c == '\t';
}
static Catch::StringRef makeEscapeStringRef( char c ) {
if ( c == '"' ) {
return "\\\""_sr;
} else if ( c == '\\' ) {
return "\\\\"_sr;
} else if ( c == '\b' ) {
return "\\b"_sr;
} else if ( c == '\f' ) {
return "\\f"_sr;
} else if ( c == '\n' ) {
return "\\n"_sr;
} else if ( c == '\r' ) {
return "\\r"_sr;
} else if ( c == '\t' ) {
return "\\t"_sr;
}
Catch::Detail::Unreachable();
}
} // namespace
void JsonUtils::indent( std::ostream& os, std::uint64_t level ) {
for ( std::uint64_t i = 0; i < level; ++i ) {
os << " ";
@@ -118,30 +146,19 @@ namespace Catch {
void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
if ( quote ) { m_os << '"'; }
for (char c : value) {
// Escape list taken from https://www.json.org/json-en.html,
// string definition.
// Note that while forward slash _can_ be escaped, it does
// not have to be, if JSON is not further embedded somewhere
// where forward slash is meaningful.
if ( c == '"' ) {
m_os << "\\\"";
} else if ( c == '\\' ) {
m_os << "\\\\";
} else if ( c == '\b' ) {
m_os << "\\b";
} else if ( c == '\f' ) {
m_os << "\\f";
} else if ( c == '\n' ) {
m_os << "\\n";
} else if ( c == '\r' ) {
m_os << "\\r";
} else if ( c == '\t' ) {
m_os << "\\t";
} else {
m_os << c;
size_t current_start = 0;
for ( size_t i = 0; i < value.size(); ++i ) {
if ( needsEscape( value[i] ) ) {
if ( current_start < i ) {
m_os << value.substr( current_start, i - current_start );
}
m_os << makeEscapeStringRef( value[i] );
current_start = i + 1;
}
}
if ( current_start < value.size() ) {
m_os << value.substr( current_start, value.size() - current_start );
}
if ( quote ) { m_os << '"'; }
}

View File

@@ -0,0 +1,24 @@
// 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_LIFETIMEBOUND_HPP_INCLUDED
#define CATCH_LIFETIMEBOUND_HPP_INCLUDED
#if !defined( __has_cpp_attribute )
# define CATCH_ATTR_LIFETIMEBOUND
#elif __has_cpp_attribute( msvc::lifetimebound )
# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]]
#elif __has_cpp_attribute( clang::lifetimebound )
# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
#elif __has_cpp_attribute( lifetimebound )
# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]]
#else
# define CATCH_ATTR_LIFETIMEBOUND
#endif
#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED

View File

@@ -10,16 +10,17 @@
namespace Catch {
MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
: macroName( _macroName ),
lineInfo( _lineInfo ),
type( _type ),
sequence( ++globalCount )
{}
// This may need protecting if threading support is added
unsigned int MessageInfo::globalCount = 0;
// Messages are owned by their individual threads, so the counter should be thread-local as well.
// Alternative consideration: atomic, so threads don't share IDs and things are easier to debug.
thread_local unsigned int MessageInfo::globalCount = 0;
} // end namespace Catch

View File

@@ -26,6 +26,7 @@ namespace Catch {
std::string message;
SourceLineInfo lineInfo;
ResultWas::OfType type;
// The "ID" of the message, used to know when to remove it from reporter context.
unsigned int sequence;
DEPRECATED( "Explicitly use the 'sequence' member instead" )
@@ -37,7 +38,7 @@ namespace Catch {
return sequence < other.sequence;
}
private:
static unsigned int globalCount;
static thread_local unsigned int globalCount;
};
} // end namespace Catch

View File

@@ -25,6 +25,9 @@
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX
#elif defined(__QNX__)
# define CATCH_PLATFORM_QNX
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
# define CATCH_PLATFORM_WINDOWS

View File

@@ -7,10 +7,12 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_singletons.hpp>
#include <catch2/internal/catch_thread_support.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <cstdio>
#include <sstream>
#include <tuple>
#include <vector>
namespace Catch {
@@ -20,34 +22,40 @@ namespace Catch {
std::vector<Detail::unique_ptr<std::ostringstream>> m_streams;
std::vector<std::size_t> m_unused;
std::ostringstream m_referenceStream; // Used for copy state/ flags from
Detail::Mutex m_mutex;
auto add() -> std::size_t {
auto add() -> std::pair<std::size_t, std::ostringstream*> {
Detail::LockGuard _( m_mutex );
if( m_unused.empty() ) {
m_streams.push_back( Detail::make_unique<std::ostringstream>() );
return m_streams.size()-1;
return { m_streams.size()-1, m_streams.back().get() };
}
else {
auto index = m_unused.back();
m_unused.pop_back();
return index;
return { index, m_streams[index].get() };
}
}
void release( std::size_t index ) {
m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
m_unused.push_back(index);
void release( std::size_t index, std::ostream* originalPtr ) {
assert( originalPtr );
originalPtr->copyfmt( m_referenceStream ); // Restore initial flags and other state
Detail::LockGuard _( m_mutex );
assert( originalPtr == m_streams[index].get() && "Mismatch between release index and stream ptr" );
m_unused.push_back( index );
}
};
ReusableStringStream::ReusableStringStream()
: m_index( Singleton<StringStreams>::getMutable().add() ),
m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
{}
ReusableStringStream::ReusableStringStream() {
std::tie( m_index, m_oss ) =
Singleton<StringStreams>::getMutable().add();
}
ReusableStringStream::~ReusableStringStream() {
static_cast<std::ostringstream*>( m_oss )->str("");
m_oss->clear();
Singleton<StringStreams>::getMutable().release( m_index );
Singleton<StringStreams>::getMutable().release( m_index, m_oss );
}
std::string ReusableStringStream::str() const {

View File

@@ -172,9 +172,6 @@ namespace Catch {
// This also implies that messages are owned by their respective
// threads, and should not be shared across different threads.
//
// For simplicity, we disallow messages in multi-threaded contexts,
// but in the future we can enable them under this logic.
//
// This implies that various pieces of metadata referring to last
// assertion result/source location/message handling, etc
// should also be thread local. For now we just use naked globals
@@ -183,15 +180,27 @@ namespace Catch {
// This is used for the "if" part of CHECKED_IF/CHECKED_ELSE
static thread_local bool g_lastAssertionPassed = false;
// Should we clear message scopes before sending off the messages to
// reporter? Set in `assertionPassedFastPath` to avoid doing the full
// clear there for performance reasons.
static thread_local bool g_clearMessageScopes = false;
// This is the source location for last encountered macro. It is
// used to provide the users with more precise location of error
// when an unexpected exception/fatal error happens.
static thread_local SourceLineInfo g_lastKnownLineInfo("DummyLocation", static_cast<size_t>(-1));
}
// Should we clear message scopes before sending off the messages to
// reporter? Set in `assertionPassedFastPath` to avoid doing the full
// clear there for performance reasons.
static thread_local bool g_clearMessageScopes = false;
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
// Actual messages to be provided to the reporter
static thread_local std::vector<MessageInfo> g_messages;
// Owners for the UNSCOPED_X information macro
static thread_local std::vector<ScopedMessage> g_messageScopes;
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
} // namespace Detail
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
: m_runInfo(_config->name()),
@@ -327,20 +336,21 @@ namespace Catch {
Detail::g_lastAssertionPassed = true;
}
if ( Detail::g_clearMessageScopes ) {
Detail::g_messageScopes.clear();
Detail::g_clearMessageScopes = false;
}
// From here, we are touching shared state and need mutex.
Detail::LockGuard lock( m_assertionMutex );
{
if ( Detail::g_clearMessageScopes ) {
m_messageScopes.clear();
Detail::g_clearMessageScopes = false;
}
auto _ = scopedDeactivate( *m_outputRedirect );
updateTotalsFromAtomics();
m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) );
m_reporter->assertionEnded( AssertionStats( result, Detail::g_messages, m_totals ) );
}
if ( result.getResultType() != ResultWas::Warning ) {
m_messageScopes.clear();
Detail::g_messageScopes.clear();
}
// Reset working state. assertion info will be reset after
@@ -473,28 +483,6 @@ namespace Catch {
m_reporter->benchmarkFailed( error );
}
void RunContext::pushScopedMessage(MessageInfo const & message) {
m_messages.push_back(message);
}
void RunContext::popScopedMessage( MessageInfo const& message ) {
// Note: On average, it would probably be better to look for the message
// backwards. However, we do not expect to have to deal with more
// messages than low single digits, so the optimization is tiny,
// and we would have to hand-write the loop to avoid terrible
// codegen of reverse iterators in debug mode.
m_messages.erase(
std::find_if( m_messages.begin(),
m_messages.end(),
[id = message.sequence]( MessageInfo const& msg ) {
return msg.sequence == id;
} ) );
}
void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
m_messageScopes.emplace_back( CATCH_MOVE(builder) );
}
std::string RunContext::getCurrentTestName() const {
return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name
@@ -651,10 +639,10 @@ namespace Catch {
m_testCaseTracker->close();
handleUnfinishedSections();
m_messageScopes.clear();
Detail::g_messageScopes.clear();
// TBD: At this point, m_messages should be empty. Do we want to
// assert that this is true, or keep the defensive clear call?
m_messages.clear();
Detail::g_messages.clear();
SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats);
@@ -821,11 +809,26 @@ namespace Catch {
}
}
IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture())
return *capture;
else
CATCH_INTERNAL_ERROR("No result capture instance");
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
Detail::g_messages.push_back( CATCH_MOVE( message ) );
}
void IResultCapture::popScopedMessage( unsigned int messageId ) {
// Note: On average, it would probably be better to look for the message
// backwards. However, we do not expect to have to deal with more
// messages than low single digits, so the optimization is tiny,
// and we would have to hand-write the loop to avoid terrible
// codegen of reverse iterators in debug mode.
Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(),
Detail::g_messages.end(),
[=]( MessageInfo const& msg ) {
return msg.sequence ==
messageId;
} ) );
}
void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) );
}
void seedRng(IConfig const& config) {

View File

@@ -94,11 +94,6 @@ namespace Catch {
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
void benchmarkFailed( StringRef error ) override;
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
void emplaceUnscopedMessage( MessageBuilder&& builder ) override;
std::string getCurrentTestName() const override;
const AssertionResult* getLastResult() const override;
@@ -149,9 +144,6 @@ namespace Catch {
Totals m_totals;
Detail::AtomicCounts m_atomicAssertionCount;
IEventListenerPtr m_reporter;
std::vector<MessageInfo> m_messages;
// Owners for the UNSCOPED_X information macro
std::vector<ScopedMessage> m_messageScopes;
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;

View File

@@ -8,6 +8,7 @@
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
#define CATCH_STRING_MANIP_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <cstdint>
@@ -28,10 +29,10 @@ namespace Catch {
//! Returns a new string without whitespace at the start/end
std::string trim( std::string const& str );
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
StringRef trim(StringRef ref);
StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND );
// !!! Be aware, returns refs into original string - make sure original string outlives them
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
std::vector<StringRef> splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter );
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
/**
@@ -49,7 +50,7 @@ namespace Catch {
StringRef m_label;
public:
constexpr pluralise(std::uint64_t count, StringRef label):
constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND):
m_count(count),
m_label(label)
{}

View File

@@ -8,11 +8,12 @@
#ifndef CATCH_STRINGREF_HPP_INCLUDED
#define CATCH_STRINGREF_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <cstddef>
#include <string>
#include <iosfwd>
#include <cassert>
#include <cstring>
namespace Catch {
@@ -36,14 +37,16 @@ namespace Catch {
public: // construction
constexpr StringRef() noexcept = default;
StringRef( char const* rawChars ) noexcept;
StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept;
constexpr StringRef( char const* rawChars, size_type size ) noexcept
constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND,
size_type size ) noexcept
: m_start( rawChars ),
m_size( size )
{}
StringRef( std::string const& stdString ) noexcept
StringRef(
std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept
: m_start( stdString.c_str() ),
m_size( stdString.size() )
{}
@@ -89,7 +92,7 @@ namespace Catch {
}
// Returns the current start pointer. May not be null-terminated.
constexpr char const* data() const noexcept {
constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND {
return m_start;
}

View File

@@ -8,6 +8,7 @@
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stringref.hpp>
@@ -48,7 +49,7 @@ namespace TestCaseTracking {
StringRef name;
SourceLineInfo location;
constexpr NameAndLocationRef( StringRef name_,
constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND,
SourceLineInfo location_ ):
name( name_ ), location( location_ ) {}

View File

@@ -92,7 +92,7 @@ namespace Detail {
}
explicit operator bool() const {
return m_ptr;
return m_ptr != nullptr;
}
friend void swap(unique_ptr& lhs, unique_ptr& rhs) {

View File

@@ -39,9 +39,13 @@ namespace Catch {
__assume( false );
# elif defined( __GNUC__ )
__builtin_unreachable();
# endif
# endif // ^^ NDEBUG
# else // vv platform without known optimization hint
std::terminate();
# endif
# else // ^^ NDEBUG
// For non-release builds, we prefer termination on bug over UB
std::terminate();
# endif //
}
} // namespace Detail

View File

@@ -47,7 +47,7 @@ namespace {
void hexEscapeChar(std::ostream& os, unsigned char c) {
std::ios_base::fmtflags f(os.flags());
os << "\\x"
os << "\\x"_sr
<< std::uppercase << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<int>(c);
os.flags(f);
@@ -66,95 +66,111 @@ namespace {
void XmlEncode::encodeTo( std::ostream& os ) const {
// Apostrophe escaping not necessary if we always use " to write attributes
// (see: http://www.w3.org/TR/xml/#syntax)
size_t last_start = 0;
auto write_to = [&]( size_t idx ) {
if ( last_start < idx ) {
os << m_str.substr( last_start, idx - last_start );
}
last_start = idx + 1;
};
for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
unsigned char c = static_cast<unsigned char>(m_str[idx]);
switch (c) {
case '<': os << "&lt;"; break;
case '&': os << "&amp;"; break;
for ( std::size_t idx = 0; idx < m_str.size(); ++idx ) {
unsigned char c = static_cast<unsigned char>( m_str[idx] );
switch ( c ) {
case '<':
write_to( idx );
os << "&lt;"_sr;
break;
case '&':
write_to( idx );
os << "&amp;"_sr;
break;
case '>':
// See: http://www.w3.org/TR/xml/#syntax
if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
os << "&gt;";
else
os << c;
if ( idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']' ) {
write_to( idx );
os << "&gt;"_sr;
}
break;
case '\"':
if (m_forWhat == ForAttributes)
os << "&quot;";
else
os << c;
if ( m_forWhat == ForAttributes ) {
write_to( idx );
os << "&quot;"_sr;
}
break;
default:
// Check for control characters and invalid utf-8
// Escape control characters in standard ascii
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
hexEscapeChar(os, c);
// see
// http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
if ( c < 0x09 || ( c > 0x0D && c < 0x20 ) || c == 0x7F ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
// Plain ASCII: Write it to stream
if (c < 0x7F) {
os << c;
if ( c < 0x7F ) {
break;
}
// UTF-8 territory
// Check if the encoding is valid and if it is not, hex escape bytes.
// Important: We do not check the exact decoded values for validity, only the encoding format
// First check that this bytes is a valid lead byte:
// This means that it is not encoded as 1111 1XXX
// Check if the encoding is valid and if it is not, hex escape
// bytes. Important: We do not check the exact decoded values for
// validity, only the encoding format First check that this bytes is
// a valid lead byte: This means that it is not encoded as 1111 1XXX
// Or as 10XX XXXX
if (c < 0xC0 ||
c >= 0xF8) {
hexEscapeChar(os, c);
if ( c < 0xC0 || c >= 0xF8 ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
auto encBytes = trailingBytes(c);
// Are there enough bytes left to avoid accessing out-of-bounds memory?
if (idx + encBytes - 1 >= m_str.size()) {
hexEscapeChar(os, c);
auto encBytes = trailingBytes( c );
// Are there enough bytes left to avoid accessing out-of-bounds
// memory?
if ( idx + encBytes - 1 >= m_str.size() ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
// The header is valid, check data
// The next encBytes bytes must together be a valid utf-8
// This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
// This means: bitpattern 10XX XXXX and the extracted value is sane
// (ish)
bool valid = true;
uint32_t value = headerValue(c);
for (std::size_t n = 1; n < encBytes; ++n) {
unsigned char nc = static_cast<unsigned char>(m_str[idx + n]);
valid &= ((nc & 0xC0) == 0x80);
value = (value << 6) | (nc & 0x3F);
uint32_t value = headerValue( c );
for ( std::size_t n = 1; n < encBytes; ++n ) {
unsigned char nc = static_cast<unsigned char>( m_str[idx + n] );
valid &= ( ( nc & 0xC0 ) == 0x80 );
value = ( value << 6 ) | ( nc & 0x3F );
}
if (
// Wrong bit pattern of following bytes
(!valid) ||
( !valid ) ||
// Overlong encodings
(value < 0x80) ||
(0x80 <= value && value < 0x800 && encBytes > 2) ||
(0x800 < value && value < 0x10000 && encBytes > 3) ||
( value < 0x80 ) ||
( 0x80 <= value && value < 0x800 && encBytes > 2 ) ||
( 0x800 < value && value < 0x10000 && encBytes > 3 ) ||
// Encoded value out of range
(value >= 0x110000)
) {
hexEscapeChar(os, c);
( value >= 0x110000 ) ) {
write_to( idx );
hexEscapeChar( os, c );
break;
}
// If we got here, this is in fact a valid(ish) utf-8 sequence
for (std::size_t n = 0; n < encBytes; ++n) {
os << m_str[idx + n];
}
idx += encBytes - 1;
break;
}
}
write_to( m_str.size() );
}
std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {

View File

@@ -10,6 +10,7 @@
#include <catch2/matchers/internal/catch_matchers_impl.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <string>
#include <vector>
@@ -79,11 +80,15 @@ namespace Matchers {
return description;
}
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
friend MatchAllOf operator&&( MatchAllOf&& lhs,
MatcherBase<ArgT> const& rhs
CATCH_ATTR_LIFETIMEBOUND ) {
lhs.m_matchers.push_back(&rhs);
return CATCH_MOVE(lhs);
}
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
friend MatchAllOf
operator&&( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOf&& rhs ) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
return CATCH_MOVE(rhs);
}
@@ -131,11 +136,15 @@ namespace Matchers {
return description;
}
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
friend MatchAnyOf operator||( MatchAnyOf&& lhs,
MatcherBase<ArgT> const& rhs
CATCH_ATTR_LIFETIMEBOUND ) {
lhs.m_matchers.push_back(&rhs);
return CATCH_MOVE(lhs);
}
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
friend MatchAnyOf
operator||( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOf&& rhs ) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
return CATCH_MOVE(rhs);
}
@@ -155,7 +164,8 @@ namespace Matchers {
MatcherBase<ArgT> const& m_underlyingMatcher;
public:
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher
CATCH_ATTR_LIFETIMEBOUND ):
m_underlyingMatcher( underlyingMatcher )
{}
@@ -171,16 +181,22 @@ namespace Matchers {
} // namespace Detail
template <typename T>
Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
Detail::MatchAllOf<T>
operator&&( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchAllOf<T>{} && lhs && rhs;
}
template <typename T>
Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
Detail::MatchAnyOf<T>
operator||( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchAnyOf<T>{} || lhs || rhs;
}
template <typename T>
Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) {
Detail::MatchNotOf<T>
operator!( MatcherBase<T> const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchNotOf<T>{ matcher };
}

View File

@@ -11,6 +11,7 @@
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_logical_traits.hpp>
#include <array>
@@ -114,7 +115,8 @@ namespace Matchers {
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
: m_matchers{ {std::addressof(matchers)...} } {}
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
template<typename Arg>
@@ -136,8 +138,8 @@ namespace Matchers {
template<typename... MatchersRHS>
friend
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs,
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
}
@@ -145,8 +147,8 @@ namespace Matchers {
template<typename MatcherRHS>
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs,
MatcherRHS const& rhs) {
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
}
@@ -154,8 +156,8 @@ namespace Matchers {
template<typename MatcherLHS>
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
MatcherLHS const& lhs,
MatchAllOfGeneric<MatcherTs...>&& rhs) {
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
}
};
@@ -169,7 +171,8 @@ namespace Matchers {
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
: m_matchers{ {std::addressof(matchers)...} } {}
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
template<typename Arg>
@@ -190,8 +193,8 @@ namespace Matchers {
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
template<typename... MatchersRHS>
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs,
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
}
@@ -199,8 +202,8 @@ namespace Matchers {
template<typename MatcherRHS>
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs,
MatcherRHS const& rhs) {
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
}
@@ -208,8 +211,8 @@ namespace Matchers {
template<typename MatcherLHS>
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
MatcherLHS const& lhs,
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND) {
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
}
};
@@ -225,7 +228,8 @@ namespace Matchers {
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {}
explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND)
: m_matcher{matcher} {}
template<typename Arg>
bool match(Arg&& arg) const {
@@ -237,7 +241,9 @@ namespace Matchers {
}
//! Negating negation can just unwrap and return underlying matcher
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
friend MatcherT const&
operator!( MatchNotOfGeneric<MatcherT> const& matcher
CATCH_ATTR_LIFETIMEBOUND ) {
return matcher.m_matcher;
}
};
@@ -247,20 +253,22 @@ namespace Matchers {
// compose only generic matchers
template<typename MatcherLHS, typename MatcherRHS>
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
}
template<typename MatcherLHS, typename MatcherRHS>
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
}
//! Wrap provided generic matcher in generic negator
template<typename MatcherT>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
operator ! (MatcherT const& matcher) {
operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
}
@@ -268,25 +276,29 @@ namespace Matchers {
// compose mixed generic and non-generic matchers
template<typename MatcherLHS, typename ArgRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
}
template<typename ArgLHS, typename MatcherRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
operator&&( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
}
template<typename MatcherLHS, typename ArgRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
}
template<typename ArgLHS, typename MatcherRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
operator||( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs };
}

View File

@@ -105,6 +105,7 @@ internal_headers = [
'internal/catch_jsonwriter.hpp',
'internal/catch_lazy_expr.hpp',
'internal/catch_leak_detector.hpp',
'internal/catch_lifetimebound.hpp',
'internal/catch_list.hpp',
'internal/catch_logical_traits.hpp',
'internal/catch_message_info.hpp',

View File

@@ -662,5 +662,38 @@ foreach(reporterName # "Automake" - the simple .trs format does not support any
)
endforeach()
add_test(NAME "Bazel::RngSeedEnvVar::JustEnv"
COMMAND
$<TARGET_FILE:SelfTest> "Factorials are computed"
)
set_tests_properties("Bazel::RngSeedEnvVar::JustEnv"
PROPERTIES
ENVIRONMENT "BAZEL_TEST=1;TEST_RANDOM_SEED=18181818"
PASS_REGULAR_EXPRESSION "Randomness seeded to: 18181818"
)
add_test(NAME "Bazel::RngSeedEnvVar::EnvHasPriorityOverCLI"
COMMAND
$<TARGET_FILE:SelfTest> "Factorials are computed"
--rng-seed 17171717
)
set_tests_properties("Bazel::RngSeedEnvVar::EnvHasPriorityOverCLI"
PROPERTIES
ENVIRONMENT "BAZEL_TEST=1;TEST_RANDOM_SEED=18181818"
PASS_REGULAR_EXPRESSION "Randomness seeded to: 18181818"
)
add_test(NAME "Bazel::RngSeedEnvVar::MalformedValueIsIgnored"
COMMAND
$<TARGET_FILE:SelfTest> "Factorials are computed"
--rng-seed 17171717
)
set_tests_properties("Bazel::RngSeedEnvVar::MalformedValueIsIgnored"
PROPERTIES
ENVIRONMENT "BAZEL_TEST=1;TEST_RANDOM_SEED=XOXOXOXO"
PASS_REGULAR_EXPRESSION "Randomness seeded to: 17171717"
)
list(APPEND CATCH_TEST_TARGETS SelfTest)
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)

View File

@@ -95,7 +95,7 @@ foreach(target DisabledExceptions-DefaultHandler DisabledExceptions-CustomHandle
target_compile_options(${target}
PUBLIC
$<$<CXX_COMPILER_ID:MSVC>:/EHs-c-;/D_HAS_EXCEPTIONS=0>
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:AppleClang>>:-fno-exceptions>
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:QCC>,$<CXX_COMPILER_ID:AppleClang>>:-fno-exceptions>
)
target_link_libraries(${target} Catch2_buildall_interface)
endforeach()
@@ -109,18 +109,6 @@ set_tests_properties(CATCH_CONFIG_DISABLE_EXCEPTIONS-1
FAIL_REGULAR_EXPRESSION "abort;terminate;fatal"
)
add_executable(BazelReporter ${TESTS_DIR}/X30-BazelReporter.cpp)
target_compile_definitions(BazelReporter PRIVATE CATCH_CONFIG_BAZEL_SUPPORT)
target_link_libraries(BazelReporter Catch2_buildall_interface)
add_test(NAME CATCH_CONFIG_BAZEL_REPORTER-1
COMMAND
Python3::Interpreter "${CATCH_DIR}/tests/TestScripts/testBazelReporter.py" $<TARGET_FILE:BazelReporter> "${CMAKE_CURRENT_BINARY_DIR}"
)
set_tests_properties(CATCH_CONFIG_BAZEL_REPORTER-1
PROPERTIES
LABELS "uses-python"
)
# We must now test this works without the build flag.
add_executable(BazelReporterNoCatchConfig ${TESTS_DIR}/X30-BazelReporter.cpp)
target_link_libraries(BazelReporterNoCatchConfig Catch2WithMain)
@@ -429,6 +417,50 @@ set_tests_properties(Reporters::CrashInJunitReporter
LABELS "uses-signals"
)
add_executable(QuickExitInTest ${TESTS_DIR}/X40-QuickExit.cpp)
target_link_libraries(QuickExitInTest PRIVATE Catch2::Catch2WithMain)
add_test(
NAME BazelEnv::TEST_PREMATURE_EXIT_FILE
COMMAND
Python3::Interpreter
"${CATCH_DIR}/tests/TestScripts/testBazelExitGuardFile.py"
$<TARGET_FILE:QuickExitInTest>
"${CMAKE_CURRENT_BINARY_DIR}"
"bazel"
)
set_tests_properties(BazelEnv::TEST_PREMATURE_EXIT_FILE
PROPERTIES
LABELS "uses-python"
)
add_test(
NAME PrematureExitGuardFileCanBeUsedFromCLI::CheckAfterCrash
COMMAND
Python3::Interpreter
"${CATCH_DIR}/tests/TestScripts/testBazelExitGuardFile.py"
$<TARGET_FILE:QuickExitInTest>
"${CMAKE_CURRENT_BINARY_DIR}"
"cli"
)
set_tests_properties(PrematureExitGuardFileCanBeUsedFromCLI::CheckAfterCrash
PROPERTIES
LABELS "uses-python"
)
add_test(
NAME PrematureExitGuardFileCanBeUsedFromCLI::CheckWithoutCrash
COMMAND
Python3::Interpreter
"${CATCH_DIR}/tests/TestScripts/testBazelExitGuardFile.py"
$<TARGET_FILE:QuickExitInTest>
"${CMAKE_CURRENT_BINARY_DIR}"
"no-crash"
)
set_tests_properties(PrematureExitGuardFileCanBeUsedFromCLI::CheckWithoutCrash
PROPERTIES
LABELS "uses-python"
)
add_executable(AssertionStartingEventGoesBeforeAssertionIsEvaluated
X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp
)

View File

@@ -0,0 +1,28 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
/**\file
* Call ~~quick_exit~~ inside a test.
*
* This is used to test whether Catch2 properly creates the crash guard
* file based on provided arguments.
*/
#include <catch2/catch_test_macros.hpp>
#include <cstdlib>
TEST_CASE("quick_exit", "[quick_exit]") {
// Return 0 as fake "successful" exit, while there should be a guard
// file created and kept.
std::exit(0);
// We cannot use quick_exit because libstdc++ on older MacOS versions didn't support it yet.
// std::quick_exit(0);
}
TEST_CASE("pass") {}

View File

@@ -26,6 +26,7 @@ Nor would this
:test-result: PASS #2152 - ULP checks between differently signed values were wrong - double
:test-result: PASS #2152 - ULP checks between differently signed values were wrong - float
:test-result: XFAIL #2615 - Throwing in constructor generator fails test case but does not abort
:test-result: PASS #3001: Enum-based bitfields can be captured
:test-result: XFAIL #748 - captures with unexpected exceptions
:test-result: PASS #809
:test-result: PASS #833
@@ -95,6 +96,8 @@ Nor would this
:test-result: PASS Approximate comparisons with mixed numeric types
:test-result: PASS Arbitrary predicate matcher
:test-result: PASS Assertion macros support bit operators and bool conversions
:test-result: XFAIL Assertions can be nested - CHECK
:test-result: XFAIL Assertions can be nested - REQUIRE
:test-result: PASS Assertions then sections
:test-result: PASS Basic use of the Contains range matcher
:test-result: PASS Basic use of the Empty range matcher
@@ -119,6 +122,7 @@ Nor would this
:test-result: PASS Combining templated and concrete matchers
:test-result: PASS Combining templated matchers
:test-result: PASS Commas in various macros are allowed
:test-result: PASS Comparing (and stringifying) volatile pointers works
:test-result: PASS Comparing function pointers
:test-result: PASS Comparison ops
:test-result: PASS Comparison with explicitly convertible types
@@ -150,9 +154,11 @@ Nor would this
:test-result: PASS Exception matchers that succeed
:test-result: PASS Exception message can be matched
:test-result: PASS Exception messages can be tested for
:test-result: PASS Exception thrown inside stringify does not fail the test
:test-result: PASS Exceptions matchers
:test-result: FAIL Expected exceptions that don't throw or unexpected exceptions fail the test
:test-result: FAIL FAIL aborts the test
:test-result: XFAIL FAIL can be nested in assertion
:test-result: FAIL FAIL does not require an argument
:test-result: FAIL FAIL_CHECK does not abort the test
:test-result: PASS Factorials are computed
@@ -408,6 +414,7 @@ b1!
:test-result: PASS stringify( vectors<has_maker_and_operator> )
:test-result: PASS stringify( vectors<has_operator> )
:test-result: PASS strlen3
:test-result: PASS system_clock timepoint with non-default duration
:test-result: PASS tables
:test-result: PASS tags with dots in later positions are not parsed as hidden
:test-result: SKIP tests can be skipped dynamically at runtime

View File

@@ -24,6 +24,7 @@
:test-result: PASS #2152 - ULP checks between differently signed values were wrong - double
:test-result: PASS #2152 - ULP checks between differently signed values were wrong - float
:test-result: XFAIL #2615 - Throwing in constructor generator fails test case but does not abort
:test-result: PASS #3001: Enum-based bitfields can be captured
:test-result: XFAIL #748 - captures with unexpected exceptions
:test-result: PASS #809
:test-result: PASS #833
@@ -93,6 +94,8 @@
:test-result: PASS Approximate comparisons with mixed numeric types
:test-result: PASS Arbitrary predicate matcher
:test-result: PASS Assertion macros support bit operators and bool conversions
:test-result: XFAIL Assertions can be nested - CHECK
:test-result: XFAIL Assertions can be nested - REQUIRE
:test-result: PASS Assertions then sections
:test-result: PASS Basic use of the Contains range matcher
:test-result: PASS Basic use of the Empty range matcher
@@ -117,6 +120,7 @@
:test-result: PASS Combining templated and concrete matchers
:test-result: PASS Combining templated matchers
:test-result: PASS Commas in various macros are allowed
:test-result: PASS Comparing (and stringifying) volatile pointers works
:test-result: PASS Comparing function pointers
:test-result: PASS Comparison ops
:test-result: PASS Comparison with explicitly convertible types
@@ -148,9 +152,11 @@
:test-result: PASS Exception matchers that succeed
:test-result: PASS Exception message can be matched
:test-result: PASS Exception messages can be tested for
:test-result: PASS Exception thrown inside stringify does not fail the test
:test-result: PASS Exceptions matchers
:test-result: FAIL Expected exceptions that don't throw or unexpected exceptions fail the test
:test-result: FAIL FAIL aborts the test
:test-result: XFAIL FAIL can be nested in assertion
:test-result: FAIL FAIL does not require an argument
:test-result: FAIL FAIL_CHECK does not abort the test
:test-result: PASS Factorials are computed
@@ -397,6 +403,7 @@
:test-result: PASS stringify( vectors<has_maker_and_operator> )
:test-result: PASS stringify( vectors<has_operator> )
:test-result: PASS strlen3
:test-result: PASS system_clock timepoint with non-default duration
:test-result: PASS tables
:test-result: PASS tags with dots in later positions are not parsed as hidden
:test-result: SKIP tests can be skipped dynamically at runtime

View File

@@ -85,6 +85,8 @@ Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smalle
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
Generators.tests.cpp:<line number>: failed: unexpected exception with message: 'failure to init'; expression was: {Unknown expression after the reported line}
Compilation.tests.cpp:<line number>: passed: bf.e == 1 for: 1 == 1
Compilation.tests.cpp:<line number>: passed: 1 == bf.e for: 1 == 1
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'expected exception'; expression was: {Unknown expression after the reported line}
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'answer := 42'; expression was: thisThrows() with 1 message: 'expected exception'
Exception.tests.cpp:<line number>: passed: thisThrows() with 1 message: 'answer := 42'
@@ -332,6 +334,11 @@ Compilation.tests.cpp:<line number>: passed: !(lhs & rhs) for: !(Val: 1 & Val: 2
Compilation.tests.cpp:<line number>: passed: HasBitOperators{ 1 } & HasBitOperators{ 1 } for: Val: 1 & Val: 1
Compilation.tests.cpp:<line number>: passed: lhs ^ rhs for: Val: 1 ^ Val: 2
Compilation.tests.cpp:<line number>: passed: !(lhs ^ lhs) for: !(Val: 1 ^ Val: 1)
AssertionHandler.tests.cpp:<line number>: failed: i > 10 for: 2 > 10
AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
AssertionHandler.tests.cpp:<line number>: passed: true
AssertionHandler.tests.cpp:<line number>: failed: i > 10 for: 2 > 10
AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: true
@@ -476,6 +483,13 @@ Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2} == std::vector<in
Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2 } == { 1, 2 }
Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2 } == { 1, 2 }
Misc.tests.cpp:<line number>: passed: !(ptr) for: !0
Misc.tests.cpp:<line number>: passed: ptr == ptr for: 0 == 0
Misc.tests.cpp:<line number>: passed: !(ptr != ptr) for: !(0 != 0)
Misc.tests.cpp:<line number>: passed: !(ptr < ptr) for: !(0 < 0)
Misc.tests.cpp:<line number>: passed: ptr <= ptr for: 0 <= 0
Misc.tests.cpp:<line number>: passed: !(ptr > ptr) for: !(0 > 0)
Misc.tests.cpp:<line number>: passed: ptr >= ptr for: 0 >= 0
Tricky.tests.cpp:<line number>: passed: a for: 0x<hex digits>
Tricky.tests.cpp:<line number>: passed: a == &foo for: 0x<hex digits> == 0x<hex digits>
RandomNumberGeneration.tests.cpp:<line number>: passed: SimplePcg32{} == SimplePcg32{} for: {?} == {?}
@@ -628,6 +642,9 @@ Exception.tests.cpp:<line number>: passed: thisThrows(), StartsWith( "expected"
Exception.tests.cpp:<line number>: passed: thisThrows(), EndsWith( "exception" ) for: "expected exception" ends with: "exception"
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
ToString.tests.cpp:<line number>: passed: tos == tos for: { stringification failed with an exception: "Invalid" }
==
{ stringification failed with an exception: "Invalid" }
Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) for: DerivedException::what exception message matches "DerivedException::what"
Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, !Message( "derivedexception::what" ) for: DerivedException::what not exception message matches "derivedexception::what"
Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 2 ), SpecialException, !Message( "DerivedException::what" ) for: SpecialException::what not exception message matches "DerivedException::what"
@@ -636,6 +653,8 @@ Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'e
Exception.tests.cpp:<line number>: failed: expected exception, got none; expression was: thisDoesntThrow(), std::domain_error
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows()
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'This is a failure'
AssertionHandler.tests.cpp:<line number>: failed: explicitly with 1 message: 'Throw a Catch::TestFailureException'
AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: '{ nested assertion failed }'; expression was: do_fail()
Message.tests.cpp:<line number>: failed: explicitly
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'This is a failure'
Message.tests.cpp:<line number>: warning: 'This message appears in the output'
@@ -805,6 +824,10 @@ GeneratorsImpl.tests.cpp:<line number>: passed: filter([](int) { return false; }
GeneratorsImpl.tests.cpp:<line number>: passed: filter([](int) { return false; }, values({ 1, 2, 3 })), Catch::GeneratorException
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
@@ -2757,6 +2780,9 @@ Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 3 =
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 3 == 3
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 5 == 5
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 4 == 4
ToStringChrono.tests.cpp:<line number>: passed: tp1 == tp2 for: {iso8601-timestamp}
==
{iso8601-timestamp}
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 6 == 6
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
@@ -2862,7 +2888,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
test cases: 428 | 313 passed | 95 failed | 6 skipped | 14 failed as expected
assertions: 2281 | 2089 passed | 157 failed | 35 failed as expected
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected

View File

@@ -83,6 +83,8 @@ Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smalle
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
Generators.tests.cpp:<line number>: failed: unexpected exception with message: 'failure to init'; expression was: {Unknown expression after the reported line}
Compilation.tests.cpp:<line number>: passed: bf.e == 1 for: 1 == 1
Compilation.tests.cpp:<line number>: passed: 1 == bf.e for: 1 == 1
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'expected exception'; expression was: {Unknown expression after the reported line}
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'answer := 42'; expression was: thisThrows() with 1 message: 'expected exception'
Exception.tests.cpp:<line number>: passed: thisThrows() with 1 message: 'answer := 42'
@@ -330,6 +332,11 @@ Compilation.tests.cpp:<line number>: passed: !(lhs & rhs) for: !(Val: 1 & Val: 2
Compilation.tests.cpp:<line number>: passed: HasBitOperators{ 1 } & HasBitOperators{ 1 } for: Val: 1 & Val: 1
Compilation.tests.cpp:<line number>: passed: lhs ^ rhs for: Val: 1 ^ Val: 2
Compilation.tests.cpp:<line number>: passed: !(lhs ^ lhs) for: !(Val: 1 ^ Val: 1)
AssertionHandler.tests.cpp:<line number>: failed: i > 10 for: 2 > 10
AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
AssertionHandler.tests.cpp:<line number>: passed: true
AssertionHandler.tests.cpp:<line number>: failed: i > 10 for: 2 > 10
AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: true
@@ -474,6 +481,13 @@ Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2} == std::vector<in
Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2 } == { 1, 2 }
Tricky.tests.cpp:<line number>: passed: true
Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2 } == { 1, 2 }
Misc.tests.cpp:<line number>: passed: !(ptr) for: !0
Misc.tests.cpp:<line number>: passed: ptr == ptr for: 0 == 0
Misc.tests.cpp:<line number>: passed: !(ptr != ptr) for: !(0 != 0)
Misc.tests.cpp:<line number>: passed: !(ptr < ptr) for: !(0 < 0)
Misc.tests.cpp:<line number>: passed: ptr <= ptr for: 0 <= 0
Misc.tests.cpp:<line number>: passed: !(ptr > ptr) for: !(0 > 0)
Misc.tests.cpp:<line number>: passed: ptr >= ptr for: 0 >= 0
Tricky.tests.cpp:<line number>: passed: a for: 0x<hex digits>
Tricky.tests.cpp:<line number>: passed: a == &foo for: 0x<hex digits> == 0x<hex digits>
RandomNumberGeneration.tests.cpp:<line number>: passed: SimplePcg32{} == SimplePcg32{} for: {?} == {?}
@@ -626,6 +640,9 @@ Exception.tests.cpp:<line number>: passed: thisThrows(), StartsWith( "expected"
Exception.tests.cpp:<line number>: passed: thisThrows(), EndsWith( "exception" ) for: "expected exception" ends with: "exception"
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
Exception.tests.cpp:<line number>: passed: thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
ToString.tests.cpp:<line number>: passed: tos == tos for: { stringification failed with an exception: "Invalid" }
==
{ stringification failed with an exception: "Invalid" }
Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) for: DerivedException::what exception message matches "DerivedException::what"
Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, !Message( "derivedexception::what" ) for: DerivedException::what not exception message matches "derivedexception::what"
Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 2 ), SpecialException, !Message( "DerivedException::what" ) for: SpecialException::what not exception message matches "DerivedException::what"
@@ -634,6 +651,8 @@ Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'e
Exception.tests.cpp:<line number>: failed: expected exception, got none; expression was: thisDoesntThrow(), std::domain_error
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows()
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'This is a failure'
AssertionHandler.tests.cpp:<line number>: failed: explicitly with 1 message: 'Throw a Catch::TestFailureException'
AssertionHandler.tests.cpp:<line number>: failed: unexpected exception with message: '{ nested assertion failed }'; expression was: do_fail()
Message.tests.cpp:<line number>: failed: explicitly
Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'This is a failure'
Message.tests.cpp:<line number>: warning: 'This message appears in the output'
@@ -803,6 +822,10 @@ GeneratorsImpl.tests.cpp:<line number>: passed: filter([](int) { return false; }
GeneratorsImpl.tests.cpp:<line number>: passed: filter([](int) { return false; }, values({ 1, 2, 3 })), Catch::GeneratorException
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
@@ -2746,6 +2769,9 @@ Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 3 =
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 3 == 3
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 5 == 5
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 4 == 4
ToStringChrono.tests.cpp:<line number>: passed: tp1 == tp2 for: {iso8601-timestamp}
==
{iso8601-timestamp}
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 6 == 6
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
@@ -2851,7 +2877,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
Misc.tests.cpp:<line number>: passed:
Misc.tests.cpp:<line number>: passed:
test cases: 428 | 313 passed | 95 failed | 6 skipped | 14 failed as expected
assertions: 2281 | 2089 passed | 157 failed | 35 failed as expected
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected

View File

@@ -347,6 +347,38 @@ Exception.tests.cpp:<line number>: FAILED:
due to unexpected exception with message:
unexpected exception
-------------------------------------------------------------------------------
Assertions can be nested - CHECK
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
AssertionHandler.tests.cpp:<line number>: FAILED:
CHECK( foo( 2 ) == 2 )
due to unexpected exception with message:
{ nested assertion failed }
-------------------------------------------------------------------------------
Assertions can be nested - REQUIRE
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( foo( 2 ) == 2 )
due to unexpected exception with message:
{ nested assertion failed }
-------------------------------------------------------------------------------
Captures do not leave block with an exception
-------------------------------------------------------------------------------
@@ -619,6 +651,21 @@ Message.tests.cpp:<line number>: FAILED:
explicitly with message:
This is a failure
-------------------------------------------------------------------------------
FAIL can be nested in assertion
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
explicitly with message:
Throw a Catch::TestFailureException
AssertionHandler.tests.cpp:<line number>: FAILED:
CHECK_NOTHROW( do_fail() )
due to unexpected exception with message:
{ nested assertion failed }
-------------------------------------------------------------------------------
FAIL does not require an argument
-------------------------------------------------------------------------------
@@ -1672,6 +1719,6 @@ due to unexpected exception with message:
Why would you throw a std::string?
===============================================================================
test cases: 428 | 331 passed | 76 failed | 7 skipped | 14 failed as expected
assertions: 2260 | 2089 passed | 136 failed | 35 failed as expected
test cases: 435 | 335 passed | 76 failed | 7 skipped | 17 failed as expected
assertions: 2282 | 2105 passed | 136 failed | 41 failed as expected

View File

@@ -772,6 +772,22 @@ Generators.tests.cpp:<line number>: FAILED:
due to unexpected exception with message:
failure to init
-------------------------------------------------------------------------------
#3001: Enum-based bitfields can be captured
-------------------------------------------------------------------------------
Compilation.tests.cpp:<line number>
...............................................................................
Compilation.tests.cpp:<line number>: PASSED:
REQUIRE( bf.e == 1 )
with expansion:
1 == 1
Compilation.tests.cpp:<line number>: PASSED:
REQUIRE( 1 == bf.e )
with expansion:
1 == 1
-------------------------------------------------------------------------------
#748 - captures with unexpected exceptions
outside assertions
@@ -2566,6 +2582,41 @@ Compilation.tests.cpp:<line number>: PASSED:
with expansion:
!(Val: 1 ^ Val: 1)
-------------------------------------------------------------------------------
Assertions can be nested - CHECK
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
AssertionHandler.tests.cpp:<line number>: FAILED:
CHECK( foo( 2 ) == 2 )
due to unexpected exception with message:
{ nested assertion failed }
AssertionHandler.tests.cpp:<line number>: PASSED:
CHECK( true )
-------------------------------------------------------------------------------
Assertions can be nested - REQUIRE
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( foo( 2 ) == 2 )
due to unexpected exception with message:
{ nested assertion failed }
-------------------------------------------------------------------------------
Assertions then sections
-------------------------------------------------------------------------------
@@ -3504,6 +3555,47 @@ Tricky.tests.cpp:<line number>: PASSED:
with expansion:
{ 1, 2 } == { 1, 2 }
-------------------------------------------------------------------------------
Comparing (and stringifying) volatile pointers works
-------------------------------------------------------------------------------
Misc.tests.cpp:<line number>
...............................................................................
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr )
with expansion:
!0
Misc.tests.cpp:<line number>: PASSED:
REQUIRE( ptr == ptr )
with expansion:
0 == 0
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr != ptr )
with expansion:
!(0 != 0)
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr < ptr )
with expansion:
!(0 < 0)
Misc.tests.cpp:<line number>: PASSED:
REQUIRE( ptr <= ptr )
with expansion:
0 <= 0
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr > ptr )
with expansion:
!(0 > 0)
Misc.tests.cpp:<line number>: PASSED:
REQUIRE( ptr >= ptr )
with expansion:
0 >= 0
-------------------------------------------------------------------------------
Comparing function pointers
-------------------------------------------------------------------------------
@@ -4505,6 +4597,19 @@ Exception.tests.cpp:<line number>: PASSED:
with expansion:
"expected exception" contains: "except" (case insensitive)
-------------------------------------------------------------------------------
Exception thrown inside stringify does not fail the test
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
CHECK( tos == tos )
with expansion:
{ stringification failed with an exception: "Invalid" }
==
{ stringification failed with an exception: "Invalid" }
-------------------------------------------------------------------------------
Exceptions matchers
-------------------------------------------------------------------------------
@@ -4561,6 +4666,21 @@ Message.tests.cpp:<line number>: FAILED:
explicitly with message:
This is a failure
-------------------------------------------------------------------------------
FAIL can be nested in assertion
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
explicitly with message:
Throw a Catch::TestFailureException
AssertionHandler.tests.cpp:<line number>: FAILED:
CHECK_NOTHROW( do_fail() )
due to unexpected exception with message:
{ nested assertion failed }
-------------------------------------------------------------------------------
FAIL does not require an argument
-------------------------------------------------------------------------------
@@ -5934,6 +6054,34 @@ GeneratorsImpl.tests.cpp:<line number>: PASSED:
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS_AS( filter([](int) { return false; }, values({ 1, 2, 3 })), Catch::GeneratorException )
-------------------------------------------------------------------------------
Generators internals
Filter generator
Out-of-line predicates are copied into the generator
-------------------------------------------------------------------------------
GeneratorsImpl.tests.cpp:<line number>
...............................................................................
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( gen.get() == 1 )
with expansion:
1 == 1
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( gen.next() )
with expansion:
true
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( gen.get() == 3 )
with expansion:
3 == 3
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( gen.next() )
with expansion:
!false
-------------------------------------------------------------------------------
Generators internals
Take generator
@@ -18343,6 +18491,19 @@ Generators.tests.cpp:<line number>: PASSED:
with expansion:
4 == 4
-------------------------------------------------------------------------------
system_clock timepoint with non-default duration
-------------------------------------------------------------------------------
ToStringChrono.tests.cpp:<line number>
...............................................................................
ToStringChrono.tests.cpp:<line number>: PASSED:
CHECK( tp1 == tp2 )
with expansion:
{iso8601-timestamp}
==
{iso8601-timestamp}
-------------------------------------------------------------------------------
tables
-------------------------------------------------------------------------------
@@ -19134,6 +19295,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 428 | 313 passed | 95 failed | 6 skipped | 14 failed as expected
assertions: 2281 | 2089 passed | 157 failed | 35 failed as expected
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected

View File

@@ -770,6 +770,22 @@ Generators.tests.cpp:<line number>: FAILED:
due to unexpected exception with message:
failure to init
-------------------------------------------------------------------------------
#3001: Enum-based bitfields can be captured
-------------------------------------------------------------------------------
Compilation.tests.cpp:<line number>
...............................................................................
Compilation.tests.cpp:<line number>: PASSED:
REQUIRE( bf.e == 1 )
with expansion:
1 == 1
Compilation.tests.cpp:<line number>: PASSED:
REQUIRE( 1 == bf.e )
with expansion:
1 == 1
-------------------------------------------------------------------------------
#748 - captures with unexpected exceptions
outside assertions
@@ -2564,6 +2580,41 @@ Compilation.tests.cpp:<line number>: PASSED:
with expansion:
!(Val: 1 ^ Val: 1)
-------------------------------------------------------------------------------
Assertions can be nested - CHECK
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
AssertionHandler.tests.cpp:<line number>: FAILED:
CHECK( foo( 2 ) == 2 )
due to unexpected exception with message:
{ nested assertion failed }
AssertionHandler.tests.cpp:<line number>: PASSED:
CHECK( true )
-------------------------------------------------------------------------------
Assertions can be nested - REQUIRE
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
AssertionHandler.tests.cpp:<line number>: FAILED:
REQUIRE( foo( 2 ) == 2 )
due to unexpected exception with message:
{ nested assertion failed }
-------------------------------------------------------------------------------
Assertions then sections
-------------------------------------------------------------------------------
@@ -3502,6 +3553,47 @@ Tricky.tests.cpp:<line number>: PASSED:
with expansion:
{ 1, 2 } == { 1, 2 }
-------------------------------------------------------------------------------
Comparing (and stringifying) volatile pointers works
-------------------------------------------------------------------------------
Misc.tests.cpp:<line number>
...............................................................................
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr )
with expansion:
!0
Misc.tests.cpp:<line number>: PASSED:
REQUIRE( ptr == ptr )
with expansion:
0 == 0
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr != ptr )
with expansion:
!(0 != 0)
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr < ptr )
with expansion:
!(0 < 0)
Misc.tests.cpp:<line number>: PASSED:
REQUIRE( ptr <= ptr )
with expansion:
0 <= 0
Misc.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( ptr > ptr )
with expansion:
!(0 > 0)
Misc.tests.cpp:<line number>: PASSED:
REQUIRE( ptr >= ptr )
with expansion:
0 >= 0
-------------------------------------------------------------------------------
Comparing function pointers
-------------------------------------------------------------------------------
@@ -4503,6 +4595,19 @@ Exception.tests.cpp:<line number>: PASSED:
with expansion:
"expected exception" contains: "except" (case insensitive)
-------------------------------------------------------------------------------
Exception thrown inside stringify does not fail the test
-------------------------------------------------------------------------------
ToString.tests.cpp:<line number>
...............................................................................
ToString.tests.cpp:<line number>: PASSED:
CHECK( tos == tos )
with expansion:
{ stringification failed with an exception: "Invalid" }
==
{ stringification failed with an exception: "Invalid" }
-------------------------------------------------------------------------------
Exceptions matchers
-------------------------------------------------------------------------------
@@ -4559,6 +4664,21 @@ Message.tests.cpp:<line number>: FAILED:
explicitly with message:
This is a failure
-------------------------------------------------------------------------------
FAIL can be nested in assertion
-------------------------------------------------------------------------------
AssertionHandler.tests.cpp:<line number>
...............................................................................
AssertionHandler.tests.cpp:<line number>: FAILED:
explicitly with message:
Throw a Catch::TestFailureException
AssertionHandler.tests.cpp:<line number>: FAILED:
CHECK_NOTHROW( do_fail() )
due to unexpected exception with message:
{ nested assertion failed }
-------------------------------------------------------------------------------
FAIL does not require an argument
-------------------------------------------------------------------------------
@@ -5932,6 +6052,34 @@ GeneratorsImpl.tests.cpp:<line number>: PASSED:
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_THROWS_AS( filter([](int) { return false; }, values({ 1, 2, 3 })), Catch::GeneratorException )
-------------------------------------------------------------------------------
Generators internals
Filter generator
Out-of-line predicates are copied into the generator
-------------------------------------------------------------------------------
GeneratorsImpl.tests.cpp:<line number>
...............................................................................
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( gen.get() == 1 )
with expansion:
1 == 1
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( gen.next() )
with expansion:
true
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE( gen.get() == 3 )
with expansion:
3 == 3
GeneratorsImpl.tests.cpp:<line number>: PASSED:
REQUIRE_FALSE( gen.next() )
with expansion:
!false
-------------------------------------------------------------------------------
Generators internals
Take generator
@@ -18332,6 +18480,19 @@ Generators.tests.cpp:<line number>: PASSED:
with expansion:
4 == 4
-------------------------------------------------------------------------------
system_clock timepoint with non-default duration
-------------------------------------------------------------------------------
ToStringChrono.tests.cpp:<line number>
...............................................................................
ToStringChrono.tests.cpp:<line number>: PASSED:
CHECK( tp1 == tp2 )
with expansion:
{iso8601-timestamp}
==
{iso8601-timestamp}
-------------------------------------------------------------------------------
tables
-------------------------------------------------------------------------------
@@ -19123,6 +19284,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 428 | 313 passed | 95 failed | 6 skipped | 14 failed as expected
assertions: 2281 | 2089 passed | 157 failed | 35 failed as expected
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected

View File

@@ -772,6 +772,22 @@ Generators.tests.cpp:<line number>: FAILED:
due to unexpected exception with message:
failure to init
-------------------------------------------------------------------------------
#3001: Enum-based bitfields can be captured
-------------------------------------------------------------------------------
Compilation.tests.cpp:<line number>
...............................................................................
Compilation.tests.cpp:<line number>: PASSED:
REQUIRE( bf.e == 1 )
with expansion:
1 == 1
Compilation.tests.cpp:<line number>: PASSED:
REQUIRE( 1 == bf.e )
with expansion:
1 == 1
-------------------------------------------------------------------------------
#748 - captures with unexpected exceptions
outside assertions
@@ -952,6 +968,6 @@ Condition.tests.cpp:<line number>: FAILED:
CHECK( true != true )
===============================================================================
test cases: 33 | 27 passed | 3 failed | 3 failed as expected
assertions: 102 | 94 passed | 4 failed | 4 failed as expected
test cases: 34 | 28 passed | 3 failed | 3 failed as expected
assertions: 104 | 96 passed | 4 failed | 4 failed as expected

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact
>
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2293" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2315" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="random-seed" value="1"/>
<property name="filters" value="&quot;*&quot; ~[!nonportable] ~[!benchmark] ~[approvals]"/>
@@ -60,6 +60,7 @@ failure to init
at Generators.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="#3001: Enum-based bitfields can be captured" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
@@ -390,6 +391,38 @@ at Exception.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Arbitrary predicate matcher/Function pointer" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Arbitrary predicate matcher/Lambdas + different type" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertion macros support bit operators and bool conversions" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertions can be nested - CHECK" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
<failure message="i > 10" type="REQUIRE">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</failure>
<error message="foo( 2 ) == 2" type="CHECK">
FAILED:
CHECK( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="Assertions can be nested - REQUIRE" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
<failure message="i > 10" type="REQUIRE">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</failure>
<error message="foo( 2 ) == 2" type="REQUIRE">
FAILED:
REQUIRE( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="Assertions then sections" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertions then sections/A section" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertions then sections/A section/Another section" time="{duration}" status="run"/>
@@ -455,6 +488,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Combining templated and concrete matchers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Combining templated matchers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Commas in various macros are allowed" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparing (and stringifying) volatile pointers works" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparing function pointers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparison ops" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparison with explicitly convertible types" time="{duration}" status="run"/>
@@ -711,6 +745,7 @@ at Matchers.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Exception messages can be tested for/exact match" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exception messages can be tested for/different case" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exception messages can be tested for/wildcarded" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exception thrown inside stringify does not fail the test" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exceptions matchers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Expected exceptions that don't throw or unexpected exceptions fail the test" time="{duration}" status="run">
<error message="thisThrows(), std::string" type="CHECK_THROWS_AS">
@@ -738,6 +773,20 @@ This is a failure
at Message.tests.cpp:<line number>
</failure>
</testcase>
<testcase classname="<exe-name>.global" name="FAIL can be nested in assertion" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
<failure type="FAIL">
FAILED:
Throw a Catch::TestFailureException
at AssertionHandler.tests.cpp:<line number>
</failure>
<error message="do_fail()" type="CHECK_NOTHROW">
FAILED:
CHECK_NOTHROW( do_fail() )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="FAIL does not require an argument" time="{duration}" status="run">
<failure type="FAIL">
FAILED:
@@ -797,6 +846,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Simple filtering" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Filter out multiple elements at the start and end" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Throws on construction if it can't get initial element" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Out-of-line predicates are copied into the generator" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator/Take less" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator/Take more" time="{duration}" status="run"/>
@@ -2286,6 +2336,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_maker_and_operator> )" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_operator> )" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="strlen3" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="system_clock timepoint with non-default duration" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="tables" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="tags with dots in later positions are not parsed as hidden" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="tests can be skipped dynamically at runtime" time="{duration}" status="run">

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2293" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2315" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="random-seed" value="1"/>
<property name="filters" value="&quot;*&quot; ~[!nonportable] ~[!benchmark] ~[approvals]"/>
@@ -59,6 +59,7 @@ failure to init
at Generators.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="#3001: Enum-based bitfields can be captured" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
@@ -389,6 +390,38 @@ at Exception.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Arbitrary predicate matcher/Function pointer" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Arbitrary predicate matcher/Lambdas + different type" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertion macros support bit operators and bool conversions" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertions can be nested - CHECK" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
<failure message="i > 10" type="REQUIRE">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</failure>
<error message="foo( 2 ) == 2" type="CHECK">
FAILED:
CHECK( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="Assertions can be nested - REQUIRE" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
<failure message="i > 10" type="REQUIRE">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</failure>
<error message="foo( 2 ) == 2" type="REQUIRE">
FAILED:
REQUIRE( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="Assertions then sections" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertions then sections/A section" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Assertions then sections/A section/Another section" time="{duration}" status="run"/>
@@ -454,6 +487,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Combining templated and concrete matchers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Combining templated matchers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Commas in various macros are allowed" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparing (and stringifying) volatile pointers works" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparing function pointers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparison ops" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Comparison with explicitly convertible types" time="{duration}" status="run"/>
@@ -710,6 +744,7 @@ at Matchers.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Exception messages can be tested for/exact match" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exception messages can be tested for/different case" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exception messages can be tested for/wildcarded" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exception thrown inside stringify does not fail the test" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Exceptions matchers" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Expected exceptions that don't throw or unexpected exceptions fail the test" time="{duration}" status="run">
<error message="thisThrows(), std::string" type="CHECK_THROWS_AS">
@@ -737,6 +772,20 @@ This is a failure
at Message.tests.cpp:<line number>
</failure>
</testcase>
<testcase classname="<exe-name>.global" name="FAIL can be nested in assertion" time="{duration}" status="run">
<skipped message="TEST_CASE tagged with !mayfail"/>
<failure type="FAIL">
FAILED:
Throw a Catch::TestFailureException
at AssertionHandler.tests.cpp:<line number>
</failure>
<error message="do_fail()" type="CHECK_NOTHROW">
FAILED:
CHECK_NOTHROW( do_fail() )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</error>
</testcase>
<testcase classname="<exe-name>.global" name="FAIL does not require an argument" time="{duration}" status="run">
<failure type="FAIL">
FAILED:
@@ -796,6 +845,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Simple filtering" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Filter out multiple elements at the start and end" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Throws on construction if it can't get initial element" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator/Out-of-line predicates are copied into the generator" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator/Take less" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator/Take more" time="{duration}" status="run"/>
@@ -2285,6 +2335,7 @@ at Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_maker_and_operator> )" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_operator> )" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="strlen3" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="system_clock timepoint with non-default duration" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="tables" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="tags with dots in later positions are not parsed as hidden" time="{duration}" status="run"/>
<testcase classname="<exe-name>.global" name="tests can be skipped dynamically at runtime" time="{duration}" status="run">

View File

@@ -3,6 +3,49 @@
<testExecutions version="1"loose text artifact
>
<file path="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp">
<testCase name="Assertions can be nested - CHECK" duration="{duration}">
<skipped message="REQUIRE(i > 10)">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</skipped>
<skipped message="CHECK(foo( 2 ) == 2)">
FAILED:
CHECK( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</skipped>
</testCase>
<testCase name="Assertions can be nested - REQUIRE" duration="{duration}">
<skipped message="REQUIRE(i > 10)">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</skipped>
<skipped message="REQUIRE(foo( 2 ) == 2)">
FAILED:
REQUIRE( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</skipped>
</testCase>
<testCase name="FAIL can be nested in assertion" duration="{duration}">
<skipped message="FAIL()">
FAILED:
Throw a Catch::TestFailureException
at AssertionHandler.tests.cpp:<line number>
</skipped>
<skipped message="CHECK_NOTHROW(do_fail())">
FAILED:
CHECK_NOTHROW( do_fail() )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</skipped>
</testCase>
<testCase name="Incomplete AssertionHandler" duration="{duration}">
<skipped message="REQUIRE(Dummy)">
FAILED:
@@ -110,6 +153,7 @@ at AssertionHandler.tests.cpp:<line number>
<testCase name="Generators internals/Filter generator/Simple filtering" duration="{duration}"/>
<testCase name="Generators internals/Filter generator/Filter out multiple elements at the start and end" duration="{duration}"/>
<testCase name="Generators internals/Filter generator/Throws on construction if it can't get initial element" duration="{duration}"/>
<testCase name="Generators internals/Filter generator/Out-of-line predicates are copied into the generator" duration="{duration}"/>
<testCase name="Generators internals/Take generator" duration="{duration}"/>
<testCase name="Generators internals/Take generator/Take less" duration="{duration}"/>
<testCase name="Generators internals/Take generator/Take more" duration="{duration}"/>
@@ -344,6 +388,7 @@ at AssertionHandler.tests.cpp:<line number>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp">
<testCase name="Directly creating an EnumInfo" duration="{duration}"/>
<testCase name="Exception thrown inside stringify does not fail the test" duration="{duration}"/>
<testCase name="Range type with sentinel" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - char" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - signed char" duration="{duration}"/>
@@ -612,6 +657,7 @@ at Class.tests.cpp:<line number>
<testCase name="#1319: Sections can have description (even if it is not saved/SectionName" duration="{duration}"/>
<testCase name="#1403" duration="{duration}"/>
<testCase name="#1548" duration="{duration}"/>
<testCase name="#3001: Enum-based bitfields can be captured" duration="{duration}"/>
<testCase name="#809" duration="{duration}"/>
<testCase name="#833" duration="{duration}"/>
<testCase name="#872" duration="{duration}"/>
@@ -1889,6 +1935,7 @@ at Misc.tests.cpp:<line number>
<testCase name="A couple of nested sections followed by a failure/Outer" duration="{duration}"/>
<testCase name="A couple of nested sections followed by a failure/Outer/Inner" duration="{duration}"/>
<testCase name="An empty test with no assertions" duration="{duration}"/>
<testCase name="Comparing (and stringifying) volatile pointers works" duration="{duration}"/>
<testCase name="Factorials are computed" duration="{duration}"/>
<testCase name="ManuallyRegistered" duration="{duration}"/>
<testCase name="Nice descriptive name" duration="{duration}"/>
@@ -2254,6 +2301,7 @@ at Skip.tests.cpp:<line number>
<testCase name="Stringifying std::chrono::duration helpers" duration="{duration}"/>
<testCase name="Stringifying std::chrono::duration with weird ratios" duration="{duration}"/>
<testCase name="Stringifying std::chrono::time_point&lt;system_clock>" duration="{duration}"/>
<testCase name="system_clock timepoint with non-default duration" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp">
<testCase name="Capture and info messages" duration="{duration}"/>

View File

@@ -2,6 +2,49 @@
<!-- filters='"*" ~[!nonportable] ~[!benchmark] ~[approvals]' rng-seed=1 -->
<testExecutions version="1">
<file path="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp">
<testCase name="Assertions can be nested - CHECK" duration="{duration}">
<skipped message="REQUIRE(i > 10)">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</skipped>
<skipped message="CHECK(foo( 2 ) == 2)">
FAILED:
CHECK( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</skipped>
</testCase>
<testCase name="Assertions can be nested - REQUIRE" duration="{duration}">
<skipped message="REQUIRE(i > 10)">
FAILED:
REQUIRE( i > 10 )
with expansion:
2 > 10
at AssertionHandler.tests.cpp:<line number>
</skipped>
<skipped message="REQUIRE(foo( 2 ) == 2)">
FAILED:
REQUIRE( foo( 2 ) == 2 )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</skipped>
</testCase>
<testCase name="FAIL can be nested in assertion" duration="{duration}">
<skipped message="FAIL()">
FAILED:
Throw a Catch::TestFailureException
at AssertionHandler.tests.cpp:<line number>
</skipped>
<skipped message="CHECK_NOTHROW(do_fail())">
FAILED:
CHECK_NOTHROW( do_fail() )
{ nested assertion failed }
at AssertionHandler.tests.cpp:<line number>
</skipped>
</testCase>
<testCase name="Incomplete AssertionHandler" duration="{duration}">
<skipped message="REQUIRE(Dummy)">
FAILED:
@@ -109,6 +152,7 @@ at AssertionHandler.tests.cpp:<line number>
<testCase name="Generators internals/Filter generator/Simple filtering" duration="{duration}"/>
<testCase name="Generators internals/Filter generator/Filter out multiple elements at the start and end" duration="{duration}"/>
<testCase name="Generators internals/Filter generator/Throws on construction if it can't get initial element" duration="{duration}"/>
<testCase name="Generators internals/Filter generator/Out-of-line predicates are copied into the generator" duration="{duration}"/>
<testCase name="Generators internals/Take generator" duration="{duration}"/>
<testCase name="Generators internals/Take generator/Take less" duration="{duration}"/>
<testCase name="Generators internals/Take generator/Take more" duration="{duration}"/>
@@ -343,6 +387,7 @@ at AssertionHandler.tests.cpp:<line number>
</file>
<file path="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp">
<testCase name="Directly creating an EnumInfo" duration="{duration}"/>
<testCase name="Exception thrown inside stringify does not fail the test" duration="{duration}"/>
<testCase name="Range type with sentinel" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - char" duration="{duration}"/>
<testCase name="Stringifying char arrays with statically known sizes - signed char" duration="{duration}"/>
@@ -611,6 +656,7 @@ at Class.tests.cpp:<line number>
<testCase name="#1319: Sections can have description (even if it is not saved/SectionName" duration="{duration}"/>
<testCase name="#1403" duration="{duration}"/>
<testCase name="#1548" duration="{duration}"/>
<testCase name="#3001: Enum-based bitfields can be captured" duration="{duration}"/>
<testCase name="#809" duration="{duration}"/>
<testCase name="#833" duration="{duration}"/>
<testCase name="#872" duration="{duration}"/>
@@ -1888,6 +1934,7 @@ at Misc.tests.cpp:<line number>
<testCase name="A couple of nested sections followed by a failure/Outer" duration="{duration}"/>
<testCase name="A couple of nested sections followed by a failure/Outer/Inner" duration="{duration}"/>
<testCase name="An empty test with no assertions" duration="{duration}"/>
<testCase name="Comparing (and stringifying) volatile pointers works" duration="{duration}"/>
<testCase name="Factorials are computed" duration="{duration}"/>
<testCase name="ManuallyRegistered" duration="{duration}"/>
<testCase name="Nice descriptive name" duration="{duration}"/>
@@ -2253,6 +2300,7 @@ at Skip.tests.cpp:<line number>
<testCase name="Stringifying std::chrono::duration helpers" duration="{duration}"/>
<testCase name="Stringifying std::chrono::duration with weird ratios" duration="{duration}"/>
<testCase name="Stringifying std::chrono::time_point&lt;system_clock>" duration="{duration}"/>
<testCase name="system_clock timepoint with non-default duration" duration="{duration}"/>
</file>
<file path="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp">
<testCase name="Capture and info messages" duration="{duration}"/>

View File

@@ -166,6 +166,10 @@ ok {test-number} - smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.
ok {test-number} - smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
# #2615 - Throwing in constructor generator fails test case but does not abort
not ok {test-number} - unexpected exception with message: 'failure to init'; expression was: {Unknown expression after the reported line}
# #3001: Enum-based bitfields can be captured
ok {test-number} - bf.e == 1 for: 1 == 1
# #3001: Enum-based bitfields can be captured
ok {test-number} - 1 == bf.e for: 1 == 1
# #748 - captures with unexpected exceptions
not ok {test-number} - unexpected exception with message: 'expected exception'; expression was: {Unknown expression after the reported line}
# #748 - captures with unexpected exceptions
@@ -612,6 +616,16 @@ ok {test-number} - HasBitOperators{ 1 } & HasBitOperators{ 1 } for: Val: 1 & Val
ok {test-number} - lhs ^ rhs for: Val: 1 ^ Val: 2
# Assertion macros support bit operators and bool conversions
ok {test-number} - !(lhs ^ lhs) for: !(Val: 1 ^ Val: 1)
# Assertions can be nested - CHECK
not ok {test-number} - i > 10 for: 2 > 10
# Assertions can be nested - CHECK
not ok {test-number} - unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
# Assertions can be nested - CHECK
ok {test-number} - true
# Assertions can be nested - REQUIRE
not ok {test-number} - i > 10 for: 2 > 10
# Assertions can be nested - REQUIRE
not ok {test-number} - unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
# Assertions then sections
ok {test-number} - true
# Assertions then sections
@@ -852,6 +866,20 @@ ok {test-number} - std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2
ok {test-number} - true
# Commas in various macros are allowed
ok {test-number} - std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2 } == { 1, 2 }
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr) for: !0
# Comparing (and stringifying) volatile pointers works
ok {test-number} - ptr == ptr for: 0 == 0
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr != ptr) for: !(0 != 0)
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr < ptr) for: !(0 < 0)
# Comparing (and stringifying) volatile pointers works
ok {test-number} - ptr <= ptr for: 0 <= 0
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr > ptr) for: !(0 > 0)
# Comparing (and stringifying) volatile pointers works
ok {test-number} - ptr >= ptr for: 0 >= 0
# Comparing function pointers
ok {test-number} - a for: 0x<hex digits>
# Comparing function pointers
@@ -1116,6 +1144,8 @@ ok {test-number} - thisThrows(), EndsWith( "exception" ) for: "expected exceptio
ok {test-number} - thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
# Exception messages can be tested for
ok {test-number} - thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
# Exception thrown inside stringify does not fail the test
ok {test-number} - tos == tos for: { stringification failed with an exception: "Invalid" } == { stringification failed with an exception: "Invalid" }
# Exceptions matchers
ok {test-number} - throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) for: DerivedException::what exception message matches "DerivedException::what"
# Exceptions matchers
@@ -1132,6 +1162,10 @@ not ok {test-number} - expected exception, got none; expression was: thisDoesntT
not ok {test-number} - unexpected exception with message: 'expected exception'; expression was: thisThrows()
# FAIL aborts the test
not ok {test-number} - explicitly with 1 message: 'This is a failure'
# FAIL can be nested in assertion
not ok {test-number} - explicitly with 1 message: 'Throw a Catch::TestFailureException'
# FAIL can be nested in assertion
not ok {test-number} - unexpected exception with message: '{ nested assertion failed }'; expression was: do_fail()
# FAIL does not require an argument
not ok {test-number} - explicitly
# FAIL_CHECK does not abort the test
@@ -1471,6 +1505,14 @@ ok {test-number} - gen.get() == 1 for: 1 == 1
# Generators internals
ok {test-number} - gen.next() for: true
# Generators internals
ok {test-number} - gen.get() == 3 for: 3 == 3
# Generators internals
ok {test-number} - !(gen.next()) for: !false
# Generators internals
ok {test-number} - gen.get() == 1 for: 1 == 1
# Generators internals
ok {test-number} - gen.next() for: true
# Generators internals
ok {test-number} - gen.get() == 2 for: 2 == 2
# Generators internals
ok {test-number} - !(gen.next()) for: !false
@@ -4393,6 +4435,8 @@ ok {test-number} - data.str.size() == data.len for: 3 == 3
ok {test-number} - data.str.size() == data.len for: 5 == 5
# strlen3
ok {test-number} - data.str.size() == data.len for: 4 == 4
# system_clock timepoint with non-default duration
ok {test-number} - tp1 == tp2 for: {iso8601-timestamp} == {iso8601-timestamp}
# tables
ok {test-number} - strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
# tables
@@ -4583,5 +4627,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
ok {test-number} -
# xmlentitycheck
ok {test-number} -
1..2293
1..2315

View File

@@ -164,6 +164,10 @@ ok {test-number} - smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.
ok {test-number} - smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
# #2615 - Throwing in constructor generator fails test case but does not abort
not ok {test-number} - unexpected exception with message: 'failure to init'; expression was: {Unknown expression after the reported line}
# #3001: Enum-based bitfields can be captured
ok {test-number} - bf.e == 1 for: 1 == 1
# #3001: Enum-based bitfields can be captured
ok {test-number} - 1 == bf.e for: 1 == 1
# #748 - captures with unexpected exceptions
not ok {test-number} - unexpected exception with message: 'expected exception'; expression was: {Unknown expression after the reported line}
# #748 - captures with unexpected exceptions
@@ -610,6 +614,16 @@ ok {test-number} - HasBitOperators{ 1 } & HasBitOperators{ 1 } for: Val: 1 & Val
ok {test-number} - lhs ^ rhs for: Val: 1 ^ Val: 2
# Assertion macros support bit operators and bool conversions
ok {test-number} - !(lhs ^ lhs) for: !(Val: 1 ^ Val: 1)
# Assertions can be nested - CHECK
not ok {test-number} - i > 10 for: 2 > 10
# Assertions can be nested - CHECK
not ok {test-number} - unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
# Assertions can be nested - CHECK
ok {test-number} - true
# Assertions can be nested - REQUIRE
not ok {test-number} - i > 10 for: 2 > 10
# Assertions can be nested - REQUIRE
not ok {test-number} - unexpected exception with message: '{ nested assertion failed }'; expression was: foo( 2 ) == 2
# Assertions then sections
ok {test-number} - true
# Assertions then sections
@@ -850,6 +864,20 @@ ok {test-number} - std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2
ok {test-number} - true
# Commas in various macros are allowed
ok {test-number} - std::vector<int>{1, 2} == std::vector<int>{1, 2} for: { 1, 2 } == { 1, 2 }
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr) for: !0
# Comparing (and stringifying) volatile pointers works
ok {test-number} - ptr == ptr for: 0 == 0
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr != ptr) for: !(0 != 0)
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr < ptr) for: !(0 < 0)
# Comparing (and stringifying) volatile pointers works
ok {test-number} - ptr <= ptr for: 0 <= 0
# Comparing (and stringifying) volatile pointers works
ok {test-number} - !(ptr > ptr) for: !(0 > 0)
# Comparing (and stringifying) volatile pointers works
ok {test-number} - ptr >= ptr for: 0 >= 0
# Comparing function pointers
ok {test-number} - a for: 0x<hex digits>
# Comparing function pointers
@@ -1114,6 +1142,8 @@ ok {test-number} - thisThrows(), EndsWith( "exception" ) for: "expected exceptio
ok {test-number} - thisThrows(), ContainsSubstring( "except" ) for: "expected exception" contains: "except"
# Exception messages can be tested for
ok {test-number} - thisThrows(), ContainsSubstring( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
# Exception thrown inside stringify does not fail the test
ok {test-number} - tos == tos for: { stringification failed with an exception: "Invalid" } == { stringification failed with an exception: "Invalid" }
# Exceptions matchers
ok {test-number} - throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) for: DerivedException::what exception message matches "DerivedException::what"
# Exceptions matchers
@@ -1130,6 +1160,10 @@ not ok {test-number} - expected exception, got none; expression was: thisDoesntT
not ok {test-number} - unexpected exception with message: 'expected exception'; expression was: thisThrows()
# FAIL aborts the test
not ok {test-number} - explicitly with 1 message: 'This is a failure'
# FAIL can be nested in assertion
not ok {test-number} - explicitly with 1 message: 'Throw a Catch::TestFailureException'
# FAIL can be nested in assertion
not ok {test-number} - unexpected exception with message: '{ nested assertion failed }'; expression was: do_fail()
# FAIL does not require an argument
not ok {test-number} - explicitly
# FAIL_CHECK does not abort the test
@@ -1469,6 +1503,14 @@ ok {test-number} - gen.get() == 1 for: 1 == 1
# Generators internals
ok {test-number} - gen.next() for: true
# Generators internals
ok {test-number} - gen.get() == 3 for: 3 == 3
# Generators internals
ok {test-number} - !(gen.next()) for: !false
# Generators internals
ok {test-number} - gen.get() == 1 for: 1 == 1
# Generators internals
ok {test-number} - gen.next() for: true
# Generators internals
ok {test-number} - gen.get() == 2 for: 2 == 2
# Generators internals
ok {test-number} - !(gen.next()) for: !false
@@ -4382,6 +4424,8 @@ ok {test-number} - data.str.size() == data.len for: 3 == 3
ok {test-number} - data.str.size() == data.len for: 5 == 5
# strlen3
ok {test-number} - data.str.size() == data.len for: 4 == 4
# system_clock timepoint with non-default duration
ok {test-number} - tp1 == tp2 for: {iso8601-timestamp} == {iso8601-timestamp}
# tables
ok {test-number} - strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
# tables
@@ -4572,5 +4616,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
ok {test-number} -
# xmlentitycheck
ok {test-number} -
1..2293
1..2315

View File

@@ -55,6 +55,8 @@
##teamcity[testStarted name='#2615 - Throwing in constructor generator fails test case but does not abort']
##teamcity[testIgnored name='#2615 - Throwing in constructor generator fails test case but does not abort' message='Generators.tests.cpp:<line number>|n...............................................................................|n|nGenerators.tests.cpp:<line number>|nunexpected exception with message:|n "failure to init"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='#2615 - Throwing in constructor generator fails test case but does not abort' duration="{duration}"]
##teamcity[testStarted name='#3001: Enum-based bitfields can be captured']
##teamcity[testFinished name='#3001: Enum-based bitfields can be captured' duration="{duration}"]
##teamcity[testStarted name='#748 - captures with unexpected exceptions']
##teamcity[testIgnored name='#748 - captures with unexpected exceptions' message='-------------------------------------------------------------------------------|noutside assertions|n-------------------------------------------------------------------------------|nException.tests.cpp:<line number>|n...............................................................................|n|nException.tests.cpp:<line number>|nunexpected exception with message:|n "expected exception"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='#748 - captures with unexpected exceptions' message='-------------------------------------------------------------------------------|ninside REQUIRE_NOTHROW|n-------------------------------------------------------------------------------|nException.tests.cpp:<line number>|n...............................................................................|n|nException.tests.cpp:<line number>|nunexpected exception with messages:|n "answer := 42"|n "expected exception"|n REQUIRE_NOTHROW( thisThrows() )|nwith expansion:|n thisThrows()|n- failure ignore as test marked as |'ok to fail|'|n']
@@ -225,6 +227,14 @@
##teamcity[testFinished name='Arbitrary predicate matcher' duration="{duration}"]
##teamcity[testStarted name='Assertion macros support bit operators and bool conversions']
##teamcity[testFinished name='Assertion macros support bit operators and bool conversions' duration="{duration}"]
##teamcity[testStarted name='Assertions can be nested - CHECK']
##teamcity[testIgnored name='Assertions can be nested - CHECK' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nexpression failed|n REQUIRE( i > 10 )|nwith expansion:|n 2 > 10|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='Assertions can be nested - CHECK' message='AssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "{ nested assertion failed }"|n CHECK( foo( 2 ) == 2 )|nwith expansion:|n foo( 2 ) == 2|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='Assertions can be nested - CHECK' duration="{duration}"]
##teamcity[testStarted name='Assertions can be nested - REQUIRE']
##teamcity[testIgnored name='Assertions can be nested - REQUIRE' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nexpression failed|n REQUIRE( i > 10 )|nwith expansion:|n 2 > 10|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='Assertions can be nested - REQUIRE' message='AssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "{ nested assertion failed }"|n REQUIRE( foo( 2 ) == 2 )|nwith expansion:|n foo( 2 ) == 2|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='Assertions can be nested - REQUIRE' duration="{duration}"]
##teamcity[testStarted name='Assertions then sections']
##teamcity[testFinished name='Assertions then sections' duration="{duration}"]
##teamcity[testStarted name='Basic use of the Contains range matcher']
@@ -275,6 +285,8 @@
##teamcity[testFinished name='Combining templated matchers' duration="{duration}"]
##teamcity[testStarted name='Commas in various macros are allowed']
##teamcity[testFinished name='Commas in various macros are allowed' duration="{duration}"]
##teamcity[testStarted name='Comparing (and stringifying) volatile pointers works']
##teamcity[testFinished name='Comparing (and stringifying) volatile pointers works' duration="{duration}"]
##teamcity[testStarted name='Comparing function pointers']
##teamcity[testFinished name='Comparing function pointers' duration="{duration}"]
##teamcity[testStarted name='Comparison ops']
@@ -366,6 +378,8 @@
##teamcity[testFinished name='Exception message can be matched' duration="{duration}"]
##teamcity[testStarted name='Exception messages can be tested for']
##teamcity[testFinished name='Exception messages can be tested for' duration="{duration}"]
##teamcity[testStarted name='Exception thrown inside stringify does not fail the test']
##teamcity[testFinished name='Exception thrown inside stringify does not fail the test' duration="{duration}"]
##teamcity[testStarted name='Exceptions matchers']
##teamcity[testFinished name='Exceptions matchers' duration="{duration}"]
##teamcity[testStarted name='Expected exceptions that don|'t throw or unexpected exceptions fail the test']
@@ -376,6 +390,10 @@
##teamcity[testStarted name='FAIL aborts the test']
##teamcity[testFailed name='FAIL aborts the test' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexplicit failure with message:|n "This is a failure"']
##teamcity[testFinished name='FAIL aborts the test' duration="{duration}"]
##teamcity[testStarted name='FAIL can be nested in assertion']
##teamcity[testIgnored name='FAIL can be nested in assertion' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nexplicit failure with message:|n "Throw a Catch::TestFailureException"- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='FAIL can be nested in assertion' message='AssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "{ nested assertion failed }"|n CHECK_NOTHROW( do_fail() )|nwith expansion:|n do_fail()|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='FAIL can be nested in assertion' duration="{duration}"]
##teamcity[testStarted name='FAIL does not require an argument']
##teamcity[testFailed name='FAIL does not require an argument' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexplicit failure']
##teamcity[testFinished name='FAIL does not require an argument' duration="{duration}"]
@@ -985,6 +1003,8 @@ loose text artifact
##teamcity[testFinished name='stringify( vectors<has_operator> )' duration="{duration}"]
##teamcity[testStarted name='strlen3']
##teamcity[testFinished name='strlen3' duration="{duration}"]
##teamcity[testStarted name='system_clock timepoint with non-default duration']
##teamcity[testFinished name='system_clock timepoint with non-default duration' duration="{duration}"]
##teamcity[testStarted name='tables']
##teamcity[testFinished name='tables' duration="{duration}"]
##teamcity[testStarted name='tags with dots in later positions are not parsed as hidden']

View File

@@ -55,6 +55,8 @@
##teamcity[testStarted name='#2615 - Throwing in constructor generator fails test case but does not abort']
##teamcity[testIgnored name='#2615 - Throwing in constructor generator fails test case but does not abort' message='Generators.tests.cpp:<line number>|n...............................................................................|n|nGenerators.tests.cpp:<line number>|nunexpected exception with message:|n "failure to init"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='#2615 - Throwing in constructor generator fails test case but does not abort' duration="{duration}"]
##teamcity[testStarted name='#3001: Enum-based bitfields can be captured']
##teamcity[testFinished name='#3001: Enum-based bitfields can be captured' duration="{duration}"]
##teamcity[testStarted name='#748 - captures with unexpected exceptions']
##teamcity[testIgnored name='#748 - captures with unexpected exceptions' message='-------------------------------------------------------------------------------|noutside assertions|n-------------------------------------------------------------------------------|nException.tests.cpp:<line number>|n...............................................................................|n|nException.tests.cpp:<line number>|nunexpected exception with message:|n "expected exception"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='#748 - captures with unexpected exceptions' message='-------------------------------------------------------------------------------|ninside REQUIRE_NOTHROW|n-------------------------------------------------------------------------------|nException.tests.cpp:<line number>|n...............................................................................|n|nException.tests.cpp:<line number>|nunexpected exception with messages:|n "answer := 42"|n "expected exception"|n REQUIRE_NOTHROW( thisThrows() )|nwith expansion:|n thisThrows()|n- failure ignore as test marked as |'ok to fail|'|n']
@@ -225,6 +227,14 @@
##teamcity[testFinished name='Arbitrary predicate matcher' duration="{duration}"]
##teamcity[testStarted name='Assertion macros support bit operators and bool conversions']
##teamcity[testFinished name='Assertion macros support bit operators and bool conversions' duration="{duration}"]
##teamcity[testStarted name='Assertions can be nested - CHECK']
##teamcity[testIgnored name='Assertions can be nested - CHECK' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nexpression failed|n REQUIRE( i > 10 )|nwith expansion:|n 2 > 10|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='Assertions can be nested - CHECK' message='AssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "{ nested assertion failed }"|n CHECK( foo( 2 ) == 2 )|nwith expansion:|n foo( 2 ) == 2|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='Assertions can be nested - CHECK' duration="{duration}"]
##teamcity[testStarted name='Assertions can be nested - REQUIRE']
##teamcity[testIgnored name='Assertions can be nested - REQUIRE' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nexpression failed|n REQUIRE( i > 10 )|nwith expansion:|n 2 > 10|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='Assertions can be nested - REQUIRE' message='AssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "{ nested assertion failed }"|n REQUIRE( foo( 2 ) == 2 )|nwith expansion:|n foo( 2 ) == 2|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='Assertions can be nested - REQUIRE' duration="{duration}"]
##teamcity[testStarted name='Assertions then sections']
##teamcity[testFinished name='Assertions then sections' duration="{duration}"]
##teamcity[testStarted name='Basic use of the Contains range matcher']
@@ -275,6 +285,8 @@
##teamcity[testFinished name='Combining templated matchers' duration="{duration}"]
##teamcity[testStarted name='Commas in various macros are allowed']
##teamcity[testFinished name='Commas in various macros are allowed' duration="{duration}"]
##teamcity[testStarted name='Comparing (and stringifying) volatile pointers works']
##teamcity[testFinished name='Comparing (and stringifying) volatile pointers works' duration="{duration}"]
##teamcity[testStarted name='Comparing function pointers']
##teamcity[testFinished name='Comparing function pointers' duration="{duration}"]
##teamcity[testStarted name='Comparison ops']
@@ -366,6 +378,8 @@
##teamcity[testFinished name='Exception message can be matched' duration="{duration}"]
##teamcity[testStarted name='Exception messages can be tested for']
##teamcity[testFinished name='Exception messages can be tested for' duration="{duration}"]
##teamcity[testStarted name='Exception thrown inside stringify does not fail the test']
##teamcity[testFinished name='Exception thrown inside stringify does not fail the test' duration="{duration}"]
##teamcity[testStarted name='Exceptions matchers']
##teamcity[testFinished name='Exceptions matchers' duration="{duration}"]
##teamcity[testStarted name='Expected exceptions that don|'t throw or unexpected exceptions fail the test']
@@ -376,6 +390,10 @@
##teamcity[testStarted name='FAIL aborts the test']
##teamcity[testFailed name='FAIL aborts the test' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexplicit failure with message:|n "This is a failure"']
##teamcity[testFinished name='FAIL aborts the test' duration="{duration}"]
##teamcity[testStarted name='FAIL can be nested in assertion']
##teamcity[testIgnored name='FAIL can be nested in assertion' message='AssertionHandler.tests.cpp:<line number>|n...............................................................................|n|nAssertionHandler.tests.cpp:<line number>|nexplicit failure with message:|n "Throw a Catch::TestFailureException"- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testIgnored name='FAIL can be nested in assertion' message='AssertionHandler.tests.cpp:<line number>|nunexpected exception with message:|n "{ nested assertion failed }"|n CHECK_NOTHROW( do_fail() )|nwith expansion:|n do_fail()|n- failure ignore as test marked as |'ok to fail|'|n']
##teamcity[testFinished name='FAIL can be nested in assertion' duration="{duration}"]
##teamcity[testStarted name='FAIL does not require an argument']
##teamcity[testFailed name='FAIL does not require an argument' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexplicit failure']
##teamcity[testFinished name='FAIL does not require an argument' duration="{duration}"]
@@ -984,6 +1002,8 @@
##teamcity[testFinished name='stringify( vectors<has_operator> )' duration="{duration}"]
##teamcity[testStarted name='strlen3']
##teamcity[testFinished name='strlen3' duration="{duration}"]
##teamcity[testStarted name='system_clock timepoint with non-default duration']
##teamcity[testFinished name='system_clock timepoint with non-default duration' duration="{duration}"]
##teamcity[testStarted name='tables']
##teamcity[testFinished name='tables' duration="{duration}"]
##teamcity[testStarted name='tags with dots in later positions are not parsed as hidden']

View File

@@ -681,6 +681,25 @@ Nor would this
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="#3001: Enum-based bitfields can be captured" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
<Original>
bf.e == 1
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
<Original>
1 == bf.e
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="#748 - captures with unexpected exceptions" tags="[!shouldfail][!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
<Section name="outside assertions" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
<Expression success="false" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
@@ -2716,6 +2735,58 @@ Approx( 1.23399996757507324 )
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Assertions can be nested - CHECK" tags="[!shouldfail][assertions]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
i > 10
</Original>
<Expanded>
2 > 10
</Expanded>
</Expression>
<Expression success="false" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
foo( 2 ) == 2
</Original>
<Expanded>
foo( 2 ) == 2
</Expanded>
<Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
{ nested assertion failed }
</Exception>
</Expression>
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
true
</Original>
<Expanded>
true
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Assertions can be nested - REQUIRE" tags="[!shouldfail][assertions]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
i > 10
</Original>
<Expanded>
2 > 10
</Expanded>
</Expression>
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
foo( 2 ) == 2
</Original>
<Expanded>
foo( 2 ) == 2
</Expanded>
<Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
{ nested assertion failed }
</Exception>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Assertions then sections" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original>
@@ -3810,6 +3881,65 @@ C
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Comparing (and stringifying) volatile pointers works" tags="[volatile]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr)
</Original>
<Expanded>
!0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
ptr == ptr
</Original>
<Expanded>
0 == 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr != ptr)
</Original>
<Expanded>
!(0 != 0)
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr &lt; ptr)
</Original>
<Expanded>
!(0 &lt; 0)
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
ptr &lt;= ptr
</Original>
<Expanded>
0 &lt;= 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr > ptr)
</Original>
<Expanded>
!(0 > 0)
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
ptr >= ptr
</Original>
<Expanded>
0 >= 0
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Comparing function pointers" tags="[function pointer][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original>
@@ -5050,6 +5180,19 @@ Approx( 1.30000000000000004 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Exception thrown inside stringify does not fail the test" tags="[toString]" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
<Original>
tos == tos
</Original>
<Expanded>
{ stringification failed with an exception: "Invalid" }
==
{ stringification failed with an exception: "Invalid" }
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Exceptions matchers" tags="[!throws][exceptions][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
@@ -5124,6 +5267,23 @@ Approx( 1.30000000000000004 )
</Failure>
<OverallResult success="false" skips="0"/>
</TestCase>
<TestCase name="FAIL can be nested in assertion" tags="[!shouldfail][assertions]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Failure filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
Throw a Catch::TestFailureException
</Failure>
<Expression success="false" type="CHECK_NOTHROW" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
do_fail()
</Original>
<Expanded>
do_fail()
</Expanded>
<Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
{ nested assertion failed }
</Exception>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="FAIL does not require an argument" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
<Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" />
<OverallResult success="false" skips="0"/>
@@ -6756,6 +6916,44 @@ Approx( 1.30000000000000004 )
</Section>
<OverallResults successes="2" failures="0" expectedFailures="0" skipped="false"/>
</Section>
<Section name="Filter generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Section name="Out-of-line predicates are copied into the generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
gen.get() == 1
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
gen.next()
</Original>
<Expanded>
true
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
gen.get() == 3
</Original>
<Expanded>
3 == 3
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
!(gen.next())
</Original>
<Expanded>
!false
</Expanded>
</Expression>
<OverallResults successes="4" failures="0" expectedFailures="0" skipped="false"/>
</Section>
<OverallResults successes="4" failures="0" expectedFailures="0" skipped="false"/>
</Section>
<Section name="Take generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Section name="Take less" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
@@ -21236,6 +21434,19 @@ Approx( -1.95996398454005449 )
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="system_clock timepoint with non-default duration" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
<Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
<Original>
tp1 == tp2
</Original>
<Expanded>
{iso8601-timestamp}
==
{iso8601-timestamp}
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="tables" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
<Original>
@@ -22113,6 +22324,6 @@ Approx( -1.95996398454005449 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<OverallResults successes="2089" failures="157" expectedFailures="35" skips="12"/>
<OverallResultsCases successes="313" failures="95" expectedFailures="14" skips="6"/>
<OverallResults successes="2105" failures="157" expectedFailures="41" skips="12"/>
<OverallResultsCases successes="317" failures="95" expectedFailures="17" skips="6"/>
</Catch2TestRun>

View File

@@ -681,6 +681,25 @@ Nor would this
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="#3001: Enum-based bitfields can be captured" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
<Original>
bf.e == 1
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
<Original>
1 == bf.e
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="#748 - captures with unexpected exceptions" tags="[!shouldfail][!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
<Section name="outside assertions" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
<Expression success="false" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
@@ -2716,6 +2735,58 @@ Approx( 1.23399996757507324 )
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Assertions can be nested - CHECK" tags="[!shouldfail][assertions]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
i > 10
</Original>
<Expanded>
2 > 10
</Expanded>
</Expression>
<Expression success="false" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
foo( 2 ) == 2
</Original>
<Expanded>
foo( 2 ) == 2
</Expanded>
<Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
{ nested assertion failed }
</Exception>
</Expression>
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
true
</Original>
<Expanded>
true
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Assertions can be nested - REQUIRE" tags="[!shouldfail][assertions]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
i > 10
</Original>
<Expanded>
2 > 10
</Expanded>
</Expression>
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
foo( 2 ) == 2
</Original>
<Expanded>
foo( 2 ) == 2
</Expanded>
<Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
{ nested assertion failed }
</Exception>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Assertions then sections" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original>
@@ -3810,6 +3881,65 @@ C
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Comparing (and stringifying) volatile pointers works" tags="[volatile]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr)
</Original>
<Expanded>
!0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
ptr == ptr
</Original>
<Expanded>
0 == 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr != ptr)
</Original>
<Expanded>
!(0 != 0)
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr &lt; ptr)
</Original>
<Expanded>
!(0 &lt; 0)
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
ptr &lt;= ptr
</Original>
<Expanded>
0 &lt;= 0
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
!(ptr > ptr)
</Original>
<Expanded>
!(0 > 0)
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
<Original>
ptr >= ptr
</Original>
<Expanded>
0 >= 0
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Comparing function pointers" tags="[function pointer][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
<Original>
@@ -5050,6 +5180,19 @@ Approx( 1.30000000000000004 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Exception thrown inside stringify does not fail the test" tags="[toString]" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
<Original>
tos == tos
</Original>
<Expanded>
{ stringification failed with an exception: "Invalid" }
==
{ stringification failed with an exception: "Invalid" }
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="Exceptions matchers" tags="[!throws][exceptions][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
@@ -5124,6 +5267,23 @@ Approx( 1.30000000000000004 )
</Failure>
<OverallResult success="false" skips="0"/>
</TestCase>
<TestCase name="FAIL can be nested in assertion" tags="[!shouldfail][assertions]" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Failure filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
Throw a Catch::TestFailureException
</Failure>
<Expression success="false" type="CHECK_NOTHROW" filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
<Original>
do_fail()
</Original>
<Expanded>
do_fail()
</Expanded>
<Exception filename="tests/<exe-name>/IntrospectiveTests/AssertionHandler.tests.cpp" >
{ nested assertion failed }
</Exception>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="FAIL does not require an argument" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
<Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" />
<OverallResult success="false" skips="0"/>
@@ -6756,6 +6916,44 @@ Approx( 1.30000000000000004 )
</Section>
<OverallResults successes="2" failures="0" expectedFailures="0" skipped="false"/>
</Section>
<Section name="Filter generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Section name="Out-of-line predicates are copied into the generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
gen.get() == 1
</Original>
<Expanded>
1 == 1
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
gen.next()
</Original>
<Expanded>
true
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
gen.get() == 3
</Original>
<Expanded>
3 == 3
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Original>
!(gen.next())
</Original>
<Expanded>
!false
</Expanded>
</Expression>
<OverallResults successes="4" failures="0" expectedFailures="0" skipped="false"/>
</Section>
<OverallResults successes="4" failures="0" expectedFailures="0" skipped="false"/>
</Section>
<Section name="Take generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Section name="Take less" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
@@ -21235,6 +21433,19 @@ Approx( -1.95996398454005449 )
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="system_clock timepoint with non-default duration" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
<Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
<Original>
tp1 == tp2
</Original>
<Expanded>
{iso8601-timestamp}
==
{iso8601-timestamp}
</Expanded>
</Expression>
<OverallResult success="true" skips="0"/>
</TestCase>
<TestCase name="tables" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
<Original>
@@ -22112,6 +22323,6 @@ Approx( -1.95996398454005449 )
</Section>
<OverallResult success="true" skips="0"/>
</TestCase>
<OverallResults successes="2089" failures="157" expectedFailures="35" skips="12"/>
<OverallResultsCases successes="313" failures="95" expectedFailures="14" skips="6"/>
<OverallResults successes="2105" failures="157" expectedFailures="41" skips="12"/>
<OverallResultsCases successes="317" failures="95" expectedFailures="17" skips="6"/>
</Catch2TestRun>

View File

@@ -15,3 +15,28 @@ TEST_CASE( "Incomplete AssertionHandler", "[assertion-handler][!shouldfail]" ) {
"Dummy",
Catch::ResultDisposition::Normal );
}
static int foo( int i ) {
REQUIRE( i > 10 );
return 42;
}
TEST_CASE( "Assertions can be nested - CHECK", "[assertions][!shouldfail]" ) {
CHECK( foo( 2 ) == 2 );
// We should hit this, because CHECK continues on failure
CHECK( true );
}
TEST_CASE( "Assertions can be nested - REQUIRE", "[assertions][!shouldfail]" ) {
REQUIRE( foo( 2 ) == 2 );
// We should not hit this, because REQUIRE does not continue on failure
CHECK( true );
}
static void do_fail() { FAIL( "Throw a Catch::TestFailureException" ); }
TEST_CASE( "FAIL can be nested in assertion", "[assertions][!shouldfail]" ) {
// Fails, but the error message makes sense.
CHECK_NOTHROW( do_fail() );
}

View File

@@ -85,6 +85,19 @@ TEST_CASE("Generators internals", "[generators][internals]") {
filter([](int) { return false; }, values({ 1, 2, 3 })),
Catch::GeneratorException);
}
// Non-trivial usage
SECTION("Out-of-line predicates are copied into the generator") {
auto evilNumber = Catch::Detail::make_unique<int>(2);
auto gen = [&]{
const auto predicate = [&](int i) { return i != *evilNumber; };
return filter(predicate, values({ 2, 1, 2, 3, 2, 2 }));
}();
REQUIRE(gen.get() == 1);
REQUIRE(gen.next());
REQUIRE(gen.get() == 3);
REQUIRE_FALSE(gen.next());
}
}
SECTION("Take generator") {
SECTION("Take less") {

View File

@@ -7,6 +7,8 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_test_macros.hpp>
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
@@ -17,7 +19,6 @@ namespace {
static std::ostream& operator<<( std::ostream& os, Custom const& ) {
return os << "custom";
}
} // namespace
TEST_CASE( "JsonWriter", "[JSON][JsonWriter]" ) {
@@ -150,3 +151,28 @@ TEST_CASE( "JsonWriter escapes characters in strings properly", "[JsonWriter]" )
REQUIRE( sstream.str() == "\"\\\\/\\t\\r\\n\"" );
}
}
TEST_CASE( "JsonWriter benchmarks", "[JsonWriter][!benchmark]" ) {
const auto input_length = GENERATE( as<size_t>{}, 10, 100, 10'000 );
std::string test_input( input_length, 'a' );
BENCHMARK_ADVANCED( "write string, no-escaping, len=" +
std::to_string( input_length ) )(
Catch::Benchmark::Chronometer meter ) {
std::stringstream sstream;
meter.measure( [&]( int ) {
Catch::JsonValueWriter( sstream ).write( test_input );
} );
};
std::string escape_input( input_length, '\b' );
BENCHMARK_ADVANCED( "write string, all-escaped, len=" +
std::to_string( input_length ) )(
Catch::Benchmark::Chronometer meter ) {
std::stringstream sstream;
meter.measure( [&]( int ) {
Catch::JsonValueWriter( sstream ).write( escape_input );
} );
};
}
} // namespace

View File

@@ -6,11 +6,13 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <chrono>
@@ -118,3 +120,47 @@ TEST_CASE( "#2944 - Stringifying dates before 1970 should not crash", "[.approva
Equals( "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc" ) );
}
namespace {
struct ThrowsOnStringification {
friend bool operator==( ThrowsOnStringification,
ThrowsOnStringification ) {
return true;
}
};
}
template <>
struct Catch::StringMaker<ThrowsOnStringification> {
static std::string convert(ThrowsOnStringification) {
throw std::runtime_error( "Invalid" );
}
};
TEST_CASE( "Exception thrown inside stringify does not fail the test", "[toString]" ) {
ThrowsOnStringification tos;
CHECK( tos == tos );
}
TEST_CASE( "string escaping benchmark", "[toString][!benchmark]" ) {
const auto input_length = GENERATE( as<size_t>{}, 10, 100, 10'000, 100'000 );
std::string test_input( input_length, 'a' );
BENCHMARK( "no-escape string, no-escaping, len=" +
std::to_string( input_length ) ) {
return Catch::Detail::convertIntoString( test_input, false );
};
BENCHMARK( "no-escape string, escaping, len=" +
std::to_string( input_length ) ) {
return Catch::Detail::convertIntoString( test_input, true );
};
std::string escape_input( input_length, '\r' );
BENCHMARK( "full escape string, no-escaping, len=" +
std::to_string( input_length ) ) {
return Catch::Detail::convertIntoString( escape_input, false );
};
BENCHMARK( "full escape string, escaping, len=" +
std::to_string( input_length ) ) {
return Catch::Detail::convertIntoString( escape_input, true );
};
}

View File

@@ -7,13 +7,16 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_test_macros.hpp>
#include <catch2/internal/catch_xmlwriter.hpp>
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_xmlwriter.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <sstream>
namespace {
static std::string encode( std::string const& str, Catch::XmlEncode::ForWhat forWhat = Catch::XmlEncode::ForTextNodes ) {
Catch::ReusableStringStream oss;
oss << Catch::XmlEncode( str, forWhat );
@@ -181,3 +184,20 @@ TEST_CASE("XmlWriter escapes attributes properly", "[XML][XmlWriter][approvals]"
REQUIRE_THAT(stream.str(),
ContainsSubstring(R"(some-attribute="Special chars need escaping: &lt; > ' &quot; &amp;")"));
}
TEST_CASE( "XmlWriter benchmarks", "[XML][XmlWriter][!benchmark]" ) {
const auto input_length = GENERATE( as<size_t>{}, 10, 100, 10'000 );
std::string test_input( input_length, 'a' );
BENCHMARK_ADVANCED( "write string, no-escaping, len=" +
std::to_string( input_length ) ) {
return encode( test_input );
};
std::string escape_input( input_length, '\b' );
BENCHMARK_ADVANCED( "write string, all-escaped, len=" +
std::to_string( input_length ) ) {
return encode( escape_input );
};
}
} // namespace

View File

@@ -144,6 +144,23 @@ TEST_CASE("#1027: Bitfields can be captured") {
REQUIRE(0 == y.v);
}
TEST_CASE( "#3001: Enum-based bitfields can be captured" ) {
enum E {
ZERO = 0,
ONE = 1,
TWO = 2,
};
struct BF {
E e : 2;
};
BF bf{};
bf.e = ONE;
REQUIRE( bf.e == 1 );
REQUIRE( 1 == bf.e );
}
// Comparison operators can return non-booleans.
// This is unusual, but should be supported.
TEST_CASE("#1147") {

View File

@@ -562,3 +562,15 @@ TEST_CASE("Validate SEH behavior - no crash for stack unwinding", "[approvals][!
}
#endif // _MSC_VER
TEST_CASE( "Comparing (and stringifying) volatile pointers works",
"[volatile]" ) {
volatile int* ptr = nullptr;
REQUIRE_FALSE( ptr );
REQUIRE( ptr == ptr );
REQUIRE_FALSE( ptr != ptr );
REQUIRE_FALSE( ptr < ptr );
REQUIRE( ptr <= ptr );
REQUIRE_FALSE( ptr > ptr );
REQUIRE( ptr >= ptr );
}

View File

@@ -49,3 +49,9 @@ TEST_CASE("Stringifying std::chrono::time_point<Clock>", "[toString][chrono][!no
auto later2 = now2 + std::chrono::minutes(2);
REQUIRE(now2 != later2);
}
TEST_CASE( "system_clock timepoint with non-default duration", "[toString][chrono]" ) {
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>
tp1, tp2;
CHECK( tp1 == tp2 );
}

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python3
# Copyright Catch2 Authors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
import os
import sys
import subprocess
def generate_path_suffix() -> str:
return os.urandom(16).hex()[:16]
def run_common(cmd, env = None):
cmd_env = env if env is not None else os.environ.copy()
print('Running:', cmd)
try:
ret = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
universal_newlines=True,
env=cmd_env
)
except subprocess.SubprocessError as ex:
print('Could not run "{}"'.format(cmd))
print("Return code: {}".format(ex.returncode))
print("stdout: {}".format(ex.stdout))
print("stderr: {}".format(ex.stderr))
raise
def test_bazel_env_vars(bin_path, guard_path):
env = os.environ.copy()
env["TEST_PREMATURE_EXIT_FILE"] = guard_path
env["BAZEL_TEST"] = '1'
run_common([bin_path], env)
def test_cli_parameter(bin_path, guard_path):
cmd = [
bin_path,
'--premature-exit-guard-file',
guard_path
]
run_common(cmd)
def test_no_crash(bin_path, guard_path):
cmd = [
bin_path,
'--premature-exit-guard-file',
guard_path,
# Disable the quick-exit test
'~[quick_exit]'
]
run_common(cmd)
checks = {
'bazel': (test_bazel_env_vars, True),
'cli': (test_cli_parameter, True),
'no-crash': (test_no_crash, False),
}
bin_path = os.path.abspath(sys.argv[1])
output_dir = os.path.abspath(sys.argv[2])
test_kind = sys.argv[3]
guard_file_path = os.path.join(output_dir, f"guard_file.{generate_path_suffix()}")
print(f'Guard file path: "{guard_file_path}"')
check_func, file_should_exist = checks[test_kind]
check_func(bin_path, guard_file_path)
assert os.path.exists(guard_file_path) == file_should_exist
# With randomly generated file suffix, we should not run into name conflicts.
# However, we try to cleanup anyway, to avoid having infinity files in
# long living build directories.
if file_should_exist:
try:
os.remove(guard_file_path)
except Exception as ex:
print(f'Could not remove file {guard_file_path} because: {ex}')