mirror of
https://github.com/catchorg/Catch2.git
synced 2025-10-05 19:41:09 +02:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
33e6fd217a | ||
|
a58df2d7c5 | ||
|
a9223b2bb3 | ||
|
363ca5af18 | ||
|
cb6d713774 | ||
|
8e4ab5dd8f | ||
|
8219ed79f2 | ||
|
b3fb4b9fea | ||
|
6500dc8149 | ||
|
4be9de6c54 | ||
|
91b3b3bf40 | ||
|
a00d654437 | ||
|
756ae05d30 | ||
|
a2e41916f2 | ||
|
0e772cc0d2 | ||
|
3bcd0a4e74 | ||
|
f7e7fa0983 | ||
|
b626e4c7ae | ||
|
434bf55d47 | ||
|
9048c24fa7 | ||
|
8ed8c431c6 | ||
|
dc3a4ea41a | ||
|
cd93d202e0 | ||
|
227af796b4 | ||
|
33adb4c779 |
@@ -78,5 +78,5 @@ WarningsAsErrors: >-
|
|||||||
readability-duplicate-include,
|
readability-duplicate-include,
|
||||||
HeaderFilterRegex: '.*\.(c|cxx|cpp)$'
|
HeaderFilterRegex: '.*\.(c|cxx|cpp)$'
|
||||||
FormatStyle: none
|
FormatStyle: none
|
||||||
CheckOptions: {}
|
CheckOptions: []
|
||||||
...
|
...
|
||||||
|
@@ -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_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_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_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_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_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF)
|
||||||
cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
|
cmake_dependent_option(CATCH_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()
|
endif()
|
||||||
|
|
||||||
project(Catch2
|
project(Catch2
|
||||||
VERSION 3.10.0 # CML version placeholder, don't delete
|
VERSION 3.11.0 # CML version placeholder, don't delete
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
|
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
|
||||||
DESCRIPTION "A modern, C++-native, unit test framework."
|
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
|
# We need to bring-in the variables defined there to this scope
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
if (CATCH_BUILD_BENCHMARKS)
|
||||||
|
set(CMAKE_FOLDER "benchmarks")
|
||||||
|
add_subdirectory(benchmarks)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Build tests only if requested
|
# Build tests only if requested
|
||||||
if(BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT)
|
if(BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT)
|
||||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||||
|
@@ -15,14 +15,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "all-tests",
|
"name": "most-tests",
|
||||||
"inherits": "basic-tests",
|
"inherits": "basic-tests",
|
||||||
"displayName": "Full development build",
|
"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": {
|
"cacheVariables": {
|
||||||
"CATCH_BUILD_EXAMPLES": "ON",
|
"CATCH_BUILD_EXAMPLES": "ON",
|
||||||
"CATCH_BUILD_EXTRA_TESTS": "ON",
|
"CATCH_BUILD_EXTRA_TESTS": "ON",
|
||||||
"CATCH_BUILD_SURROGATES": "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_CONFIGURE_TESTS": "ON",
|
||||||
"CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON"
|
"CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON"
|
||||||
}
|
}
|
||||||
|
13
README.md
13
README.md
@@ -1,5 +1,16 @@
|
|||||||
<a id="top"></a>
|
<a id="top"></a>
|
||||||

|
|
||||||
|
<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>
|
||||||
|
|
||||||
[](https://github.com/catchorg/catch2/releases)
|
[](https://github.com/catchorg/catch2/releases)
|
||||||
[](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml)
|
[](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml)
|
||||||
|
16
benchmarks/CMakeLists.txt
Normal file
16
benchmarks/CMakeLists.txt
Normal 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)
|
28
benchmarks/assertion_listener.cpp
Normal file
28
benchmarks/assertion_listener.cpp
Normal 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 )
|
27
benchmarks/runtime_assertion_benches.cpp
Normal file
27
benchmarks/runtime_assertion_benches.cpp
Normal 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; }());
|
||||||
|
}
|
||||||
|
}
|
BIN
data/sponsors/github_repo_sponsorship.png
Normal file
BIN
data/sponsors/github_repo_sponsorship.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 264 KiB |
@@ -66,17 +66,24 @@ test execution. Specifically it understands
|
|||||||
* JUnit output path via `XML_OUTPUT_FILE`
|
* JUnit output path via `XML_OUTPUT_FILE`
|
||||||
* Test filtering via `TESTBRIDGE_TEST_ONLY`
|
* Test filtering via `TESTBRIDGE_TEST_ONLY`
|
||||||
* Test sharding via `TEST_SHARD_INDEX`, `TEST_TOTAL_SHARDS`, and `TEST_SHARD_STATUS_FILE`
|
* 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 `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 `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
|
This integration is enabled via either a [compile time configuration
|
||||||
option](configuration.md#bazel-support), or via `BAZEL_TEST` environment
|
option](configuration.md#bazel-support), or via `BAZEL_TEST` environment
|
||||||
variable set to "1".
|
variable set to "1".
|
||||||
|
|
||||||
> Support for `BAZEL_TEST` was [introduced](https://github.com/catchorg/Catch2/pull/2459) in Catch2 3.1.0
|
> 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
|
## Low-level tools
|
||||||
|
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
[Test Sharding](#test-sharding)<br>
|
[Test Sharding](#test-sharding)<br>
|
||||||
[Allow running the binary without tests](#allow-running-the-binary-without-tests)<br>
|
[Allow running the binary without tests](#allow-running-the-binary-without-tests)<br>
|
||||||
[Output verbosity](#output-verbosity)<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.
|
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.
|
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_.
|
Verbosity defaults to _normal_.
|
||||||
|
|
||||||
|
|
||||||
|
## Create file to guard against silent early termination
|
||||||
|
<pre>--premature-exit-guard-file <path></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)
|
[Home](Readme.md#top)
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
# Release notes
|
# Release notes
|
||||||
**Contents**<br>
|
**Contents**<br>
|
||||||
|
[3.11.0](#3110)<br>
|
||||||
[3.10.0](#3100)<br>
|
[3.10.0](#3100)<br>
|
||||||
[3.9.1](#391)<br>
|
[3.9.1](#391)<br>
|
||||||
[3.9.0](#390)<br>
|
[3.9.0](#390)<br>
|
||||||
@@ -70,6 +71,28 @@
|
|||||||
[Even Older versions](#even-older-versions)<br>
|
[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
|
## 3.10.0
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
// Catch v3.10.0
|
// Catch v3.11.0
|
||||||
// Generated: 2025-08-24 16:18:04.775778
|
// Generated: 2025-09-30 10:49:12.549018
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// This file is an amalgamation of multiple different files.
|
// This file is an amalgamation of multiple different files.
|
||||||
// You probably shouldn't edit it directly.
|
// You probably shouldn't edit it directly.
|
||||||
@@ -825,6 +825,8 @@ namespace Catch {
|
|||||||
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
|
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() ) {
|
if ( enableBazelEnvSupport() ) {
|
||||||
readBazelEnvVars();
|
readBazelEnvVars();
|
||||||
}
|
}
|
||||||
@@ -889,6 +891,8 @@ namespace Catch {
|
|||||||
|
|
||||||
bool Config::showHelp() const { return m_data.showHelp; }
|
bool Config::showHelp() const { return m_data.showHelp; }
|
||||||
|
|
||||||
|
std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
|
||||||
|
|
||||||
// IConfig interface
|
// IConfig interface
|
||||||
bool Config::allowThrows() const { return !m_data.noThrow; }
|
bool Config::allowThrows() const { return !m_data.noThrow; }
|
||||||
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
|
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;
|
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
|
} // end namespace Catch
|
||||||
@@ -975,28 +999,26 @@ namespace Catch {
|
|||||||
|
|
||||||
|
|
||||||
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
|
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
|
||||||
m_info( CATCH_MOVE(builder.m_info) ) {
|
m_messageId( builder.m_info.sequence ) {
|
||||||
m_info.message = builder.m_stream.str();
|
MessageInfo info( CATCH_MOVE( builder.m_info ) );
|
||||||
getResultCapture().pushScopedMessage( m_info );
|
info.message = builder.m_stream.str();
|
||||||
|
IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
|
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
|
||||||
m_info( CATCH_MOVE( old.m_info ) ) {
|
m_messageId( old.m_messageId ) {
|
||||||
old.m_moved = true;
|
old.m_moved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedMessage::~ScopedMessage() {
|
ScopedMessage::~ScopedMessage() {
|
||||||
if ( !m_moved ){
|
if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
|
||||||
getResultCapture().popScopedMessage(m_info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Capturer::Capturer( StringRef macroName,
|
Capturer::Capturer( StringRef macroName,
|
||||||
SourceLineInfo const& lineInfo,
|
SourceLineInfo const& lineInfo,
|
||||||
ResultWas::OfType resultType,
|
ResultWas::OfType resultType,
|
||||||
StringRef names ):
|
StringRef names ) {
|
||||||
m_resultCapture( getResultCapture() ) {
|
|
||||||
auto trimmed = [&] (size_t start, size_t end) {
|
auto trimmed = [&] (size_t start, size_t end) {
|
||||||
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
|
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
|
||||||
++start;
|
++start;
|
||||||
@@ -1042,8 +1064,8 @@ namespace Catch {
|
|||||||
case ',':
|
case ',':
|
||||||
if (start != pos && openings.empty()) {
|
if (start != pos && openings.empty()) {
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
m_messages.emplace_back(macroName, lineInfo, resultType);
|
||||||
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
|
m_messages.back().message += trimmed(start, pos);
|
||||||
m_messages.back().message += " := ";
|
m_messages.back().message += " := "_sr;
|
||||||
start = pos;
|
start = pos;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1052,20 +1074,20 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
assert(openings.empty() && "Mismatched openings");
|
assert(openings.empty() && "Mismatched openings");
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
m_messages.emplace_back(macroName, lineInfo, resultType);
|
||||||
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
|
m_messages.back().message += trimmed(start, names.size() - 1);
|
||||||
m_messages.back().message += " := ";
|
m_messages.back().message += " := "_sr;
|
||||||
}
|
}
|
||||||
Capturer::~Capturer() {
|
Capturer::~Capturer() {
|
||||||
assert( m_captured == m_messages.size() );
|
assert( m_captured == m_messages.size() );
|
||||||
for ( size_t i = 0; i < m_captured; ++i ) {
|
for (auto const& message : m_messages) {
|
||||||
m_resultCapture.popScopedMessage( m_messages[i] );
|
IResultCapture::popScopedMessage( message.sequence );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Capturer::captureValue( size_t index, std::string const& value ) {
|
void Capturer::captureValue( size_t index, std::string const& value ) {
|
||||||
assert( index < m_messages.size() );
|
assert( index < m_messages.size() );
|
||||||
m_messages[index].message += value;
|
m_messages[index].message += value;
|
||||||
m_resultCapture.pushScopedMessage( m_messages[index] );
|
IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
|
||||||
m_captured++;
|
m_captured++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1149,7 +1171,6 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
void cleanUp() {
|
void cleanUp() {
|
||||||
cleanupSingletons();
|
cleanupSingletons();
|
||||||
cleanUpContext();
|
|
||||||
}
|
}
|
||||||
std::string translateActiveException() {
|
std::string translateActiveException() {
|
||||||
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
|
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
|
||||||
@@ -1161,6 +1182,8 @@ namespace Catch {
|
|||||||
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <set>
|
#include <set>
|
||||||
@@ -1275,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
|
} // anon namespace
|
||||||
|
|
||||||
Session::Session() {
|
Session::Session() {
|
||||||
@@ -1393,6 +1460,7 @@ namespace Catch {
|
|||||||
static_cast<void>(std::getchar());
|
static_cast<void>(std::getchar());
|
||||||
}
|
}
|
||||||
int exitCode = runInternal();
|
int exitCode = runInternal();
|
||||||
|
|
||||||
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
|
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
|
||||||
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
|
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
|
||||||
static_cast<void>(std::getchar());
|
static_cast<void>(std::getchar());
|
||||||
@@ -1433,6 +1501,10 @@ namespace Catch {
|
|||||||
CATCH_TRY {
|
CATCH_TRY {
|
||||||
config(); // Force config to be constructed
|
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 );
|
seedRng( *m_config );
|
||||||
|
|
||||||
if (m_configData.filenamesAsTags) {
|
if (m_configData.filenamesAsTags) {
|
||||||
@@ -1462,9 +1534,12 @@ namespace Catch {
|
|||||||
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
|
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
|
||||||
auto const totals = tests.execute();
|
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()
|
if ( tests.hadUnmatchedTestSpecs()
|
||||||
&& m_config->warnAboutUnmatchedTestSpecs() ) {
|
&& m_config->warnAboutUnmatchedTestSpecs() ) {
|
||||||
// UnmatchedTestSpecExitCode
|
|
||||||
return UnmatchedTestSpecExitCode;
|
return UnmatchedTestSpecExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1974,35 +2049,35 @@ namespace Detail {
|
|||||||
std::string ret;
|
std::string ret;
|
||||||
// This is enough for the "don't escape invisibles" case, and a good
|
// This is enough for the "don't escape invisibles" case, and a good
|
||||||
// lower bound on the "escape invisibles" case.
|
// lower bound on the "escape invisibles" case.
|
||||||
ret.reserve(string.size() + 2);
|
ret.reserve( string.size() + 2 );
|
||||||
|
|
||||||
if (!escapeInvisibles) {
|
if ( !escapeInvisibles ) {
|
||||||
ret += '"';
|
ret += '"';
|
||||||
ret += string;
|
ret += string;
|
||||||
ret += '"';
|
ret += '"';
|
||||||
return 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 += '"';
|
ret += '"';
|
||||||
for (char c : string) {
|
for ( size_t i = 0; i < string.size(); ++i ) {
|
||||||
switch (c) {
|
const char c = string[i];
|
||||||
case '\r':
|
if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
|
||||||
ret.append("\\r");
|
write_to( i );
|
||||||
break;
|
if ( c == '\r' ) { ret.append( "\\r" ); }
|
||||||
case '\n':
|
if ( c == '\n' ) { ret.append( "\\n" ); }
|
||||||
ret.append("\\n");
|
if ( c == '\t' ) { ret.append( "\\t" ); }
|
||||||
break;
|
if ( c == '\f' ) { ret.append( "\\f" ); }
|
||||||
case '\t':
|
|
||||||
ret.append("\\t");
|
|
||||||
break;
|
|
||||||
case '\f':
|
|
||||||
ret.append("\\f");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret.push_back(c);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
write_to( string.size() );
|
||||||
ret += '"';
|
ret += '"';
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2279,7 +2354,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version const& libraryVersion() {
|
Version const& libraryVersion() {
|
||||||
static Version version( 3, 10, 0, "", 0 );
|
static Version version( 3, 11, 0, "", 0 );
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2367,8 +2442,14 @@ namespace Catch {
|
|||||||
|
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
namespace Detail {
|
||||||
|
void missingCaptureInstance() {
|
||||||
|
CATCH_INTERNAL_ERROR( "No result capture instance" );
|
||||||
|
}
|
||||||
|
} // namespace Detail
|
||||||
|
|
||||||
IResultCapture::~IResultCapture() = default;
|
IResultCapture::~IResultCapture() = default;
|
||||||
}
|
} // namespace Catch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -3361,6 +3442,9 @@ namespace Catch {
|
|||||||
| Opt( config.allowZeroTests )
|
| Opt( config.allowZeroTests )
|
||||||
["--allow-running-no-tests"]
|
["--allow-running-no-tests"]
|
||||||
( "Treat 'No tests run' as a success" )
|
( "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" )
|
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||||
( "which test or tests to use" );
|
( "which test or tests to use" );
|
||||||
|
|
||||||
@@ -3515,7 +3599,11 @@ namespace {
|
|||||||
#endif // Windows/ ANSI/ None
|
#endif // Windows/ ANSI/ None
|
||||||
|
|
||||||
|
|
||||||
#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || defined( __GLIBC__ ) || defined(__FreeBSD__)
|
#if defined( CATCH_PLATFORM_LINUX ) \
|
||||||
|
|| defined( CATCH_PLATFORM_MAC ) \
|
||||||
|
|| defined( __GLIBC__ ) \
|
||||||
|
|| defined( __FreeBSD__ ) \
|
||||||
|
|| defined( CATCH_PLATFORM_QNX )
|
||||||
# define CATCH_INTERNAL_HAS_ISATTY
|
# define CATCH_INTERNAL_HAS_ISATTY
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -3639,20 +3727,10 @@ namespace Catch {
|
|||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
Context* Context::currentContext = nullptr;
|
Context Context::currentContext;
|
||||||
|
|
||||||
void cleanUpContext() {
|
|
||||||
delete Context::currentContext;
|
|
||||||
Context::currentContext = nullptr;
|
|
||||||
}
|
|
||||||
void Context::createContext() {
|
|
||||||
currentContext = new Context();
|
|
||||||
}
|
|
||||||
|
|
||||||
Context& getCurrentMutableContext() {
|
Context& getCurrentMutableContext() {
|
||||||
if ( !Context::currentContext ) { Context::createContext(); }
|
return Context::currentContext;
|
||||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
|
||||||
return *Context::currentContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimplePcg32& sharedRng() {
|
SimplePcg32& sharedRng() {
|
||||||
@@ -3757,7 +3835,7 @@ namespace Catch {
|
|||||||
#endif
|
#endif
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
#elif defined(CATCH_PLATFORM_LINUX)
|
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -4091,23 +4169,27 @@ namespace Catch {
|
|||||||
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
|
{ 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) {
|
static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||||
for (auto const& def : signalDefs) {
|
for (auto const& def : signalDefs) {
|
||||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
|
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
|
||||||
reportFatal(def.name);
|
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.
|
// This stops us from eating debugger breaks etc.
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
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
|
// For MSVC, we reserve part of the stack memory for handling
|
||||||
// memory overflow structured exception.
|
// memory overflow structured exception.
|
||||||
FatalConditionHandler::FatalConditionHandler() {
|
FatalConditionHandler::FatalConditionHandler() {
|
||||||
@@ -5565,6 +5647,7 @@ ReporterSpec::ReporterSpec(
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
@@ -5576,16 +5659,16 @@ namespace Catch {
|
|||||||
std::ostringstream m_referenceStream; // Used for copy state/ flags from
|
std::ostringstream m_referenceStream; // Used for copy state/ flags from
|
||||||
Detail::Mutex m_mutex;
|
Detail::Mutex m_mutex;
|
||||||
|
|
||||||
auto add() -> std::size_t {
|
auto add() -> std::pair<std::size_t, std::ostringstream*> {
|
||||||
Detail::LockGuard _( m_mutex );
|
Detail::LockGuard _( m_mutex );
|
||||||
if( m_unused.empty() ) {
|
if( m_unused.empty() ) {
|
||||||
m_streams.push_back( Detail::make_unique<std::ostringstream>() );
|
m_streams.push_back( Detail::make_unique<std::ostringstream>() );
|
||||||
return m_streams.size()-1;
|
return { m_streams.size()-1, m_streams.back().get() };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto index = m_unused.back();
|
auto index = m_unused.back();
|
||||||
m_unused.pop_back();
|
m_unused.pop_back();
|
||||||
return index;
|
return { index, m_streams[index].get() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5599,10 +5682,10 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ReusableStringStream::ReusableStringStream()
|
ReusableStringStream::ReusableStringStream() {
|
||||||
: m_index( Singleton<StringStreams>::getMutable().add() ),
|
std::tie( m_index, m_oss ) =
|
||||||
m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
|
Singleton<StringStreams>::getMutable().add();
|
||||||
{}
|
}
|
||||||
|
|
||||||
ReusableStringStream::~ReusableStringStream() {
|
ReusableStringStream::~ReusableStringStream() {
|
||||||
static_cast<std::ostringstream*>( m_oss )->str("");
|
static_cast<std::ostringstream*>( m_oss )->str("");
|
||||||
@@ -6085,28 +6168,6 @@ namespace Catch {
|
|||||||
m_reporter->benchmarkFailed( error );
|
m_reporter->benchmarkFailed( error );
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunContext::pushScopedMessage( MessageInfo const& message ) {
|
|
||||||
Detail::g_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.
|
|
||||||
Detail::g_messages.erase(
|
|
||||||
std::find_if( Detail::g_messages.begin(),
|
|
||||||
Detail::g_messages.end(),
|
|
||||||
[id = message.sequence]( MessageInfo const& msg ) {
|
|
||||||
return msg.sequence == id;
|
|
||||||
} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
|
|
||||||
Detail::g_messageScopes.emplace_back( CATCH_MOVE(builder) );
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RunContext::getCurrentTestName() const {
|
std::string RunContext::getCurrentTestName() const {
|
||||||
return m_activeTestCase
|
return m_activeTestCase
|
||||||
? m_activeTestCase->getTestCaseInfo().name
|
? m_activeTestCase->getTestCaseInfo().name
|
||||||
@@ -6433,11 +6494,26 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IResultCapture& getResultCapture() {
|
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
|
||||||
if (auto* capture = getCurrentContext().getResultCapture())
|
Detail::g_messages.push_back( CATCH_MOVE( message ) );
|
||||||
return *capture;
|
}
|
||||||
else
|
|
||||||
CATCH_INTERNAL_ERROR("No result capture instance");
|
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) {
|
void seedRng(IConfig const& config) {
|
||||||
|
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
// Catch v3.10.0
|
// Catch v3.11.0
|
||||||
// Generated: 2025-08-24 16:18:04.055916
|
// Generated: 2025-09-30 10:49:11.225746
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// This file is an amalgamation of multiple different files.
|
// This file is an amalgamation of multiple different files.
|
||||||
// You probably shouldn't edit it directly.
|
// You probably shouldn't edit it directly.
|
||||||
@@ -101,6 +101,9 @@
|
|||||||
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
||||||
# define CATCH_PLATFORM_LINUX
|
# define CATCH_PLATFORM_LINUX
|
||||||
|
|
||||||
|
#elif defined(__QNX__)
|
||||||
|
# define CATCH_PLATFORM_QNX
|
||||||
|
|
||||||
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
|
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
# define CATCH_PLATFORM_WINDOWS
|
# define CATCH_PLATFORM_WINDOWS
|
||||||
|
|
||||||
@@ -308,13 +311,17 @@
|
|||||||
# endif
|
# endif
|
||||||
|
|
||||||
// Universal Windows platform does not support SEH
|
// Universal Windows platform does not support SEH
|
||||||
// Or console colours (or console at all...)
|
# if !defined(CATCH_PLATFORM_WINDOWS_UWP)
|
||||||
# if defined(CATCH_PLATFORM_WINDOWS_UWP)
|
|
||||||
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
|
|
||||||
# else
|
|
||||||
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
|
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
|
||||||
# endif
|
# 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 preprocessor needs some workaround for __VA_ARGS__
|
||||||
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
|
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
|
||||||
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
|
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
|
||||||
@@ -564,11 +571,9 @@ namespace Catch {
|
|||||||
IConfig const* m_config = nullptr;
|
IConfig const* m_config = nullptr;
|
||||||
IResultCapture* m_resultCapture = nullptr;
|
IResultCapture* m_resultCapture = nullptr;
|
||||||
|
|
||||||
CATCH_EXPORT static Context* currentContext;
|
CATCH_EXPORT static Context currentContext;
|
||||||
friend Context& getCurrentMutableContext();
|
friend Context& getCurrentMutableContext();
|
||||||
friend Context const& getCurrentContext();
|
friend Context const& getCurrentContext();
|
||||||
static void createContext();
|
|
||||||
friend void cleanUpContext();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr IResultCapture* getResultCapture() const {
|
constexpr IResultCapture* getResultCapture() const {
|
||||||
@@ -579,21 +584,14 @@ namespace Catch {
|
|||||||
m_resultCapture = resultCapture;
|
m_resultCapture = resultCapture;
|
||||||
}
|
}
|
||||||
constexpr void setConfig( IConfig const* config ) { m_config = config; }
|
constexpr void setConfig( IConfig const* config ) { m_config = config; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Context& getCurrentMutableContext();
|
Context& getCurrentMutableContext();
|
||||||
|
|
||||||
inline Context const& getCurrentContext() {
|
inline Context const& getCurrentContext() {
|
||||||
// We duplicate the logic from `getCurrentMutableContext` here,
|
return Context::currentContext;
|
||||||
// to avoid paying the call overhead in debug mode.
|
|
||||||
if ( !Context::currentContext ) { Context::createContext(); }
|
|
||||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
|
||||||
return *Context::currentContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanUpContext();
|
|
||||||
|
|
||||||
class SimplePcg32;
|
class SimplePcg32;
|
||||||
SimplePcg32& sharedRng();
|
SimplePcg32& sharedRng();
|
||||||
}
|
}
|
||||||
@@ -1069,10 +1067,9 @@ namespace Catch {
|
|||||||
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
|
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
|
||||||
virtual void benchmarkFailed( StringRef error ) = 0;
|
virtual void benchmarkFailed( StringRef error ) = 0;
|
||||||
|
|
||||||
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
|
static void pushScopedMessage( MessageInfo&& message );
|
||||||
virtual void popScopedMessage( MessageInfo const& message ) = 0;
|
static void popScopedMessage( unsigned int messageId );
|
||||||
|
static void emplaceUnscopedMessage( MessageBuilder&& builder );
|
||||||
virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
|
|
||||||
|
|
||||||
virtual void handleFatalErrorCondition( StringRef message ) = 0;
|
virtual void handleFatalErrorCondition( StringRef message ) = 0;
|
||||||
|
|
||||||
@@ -1108,7 +1105,18 @@ namespace Catch {
|
|||||||
virtual void exceptionEarlyReported() = 0;
|
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
|
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
|
||||||
@@ -3800,6 +3808,8 @@ namespace Catch {
|
|||||||
|
|
||||||
std::vector<std::string> testsOrTags;
|
std::vector<std::string> testsOrTags;
|
||||||
std::vector<std::string> sectionsToRun;
|
std::vector<std::string> sectionsToRun;
|
||||||
|
|
||||||
|
std::string prematureExitGuardFilePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -3827,6 +3837,8 @@ namespace Catch {
|
|||||||
|
|
||||||
bool showHelp() const;
|
bool showHelp() const;
|
||||||
|
|
||||||
|
std::string const& getExitGuardFilePath() const;
|
||||||
|
|
||||||
// IConfig interface
|
// IConfig interface
|
||||||
bool allowThrows() const override;
|
bool allowThrows() const override;
|
||||||
StringRef name() const override;
|
StringRef name() const override;
|
||||||
@@ -3961,6 +3973,7 @@ namespace Catch {
|
|||||||
std::string message;
|
std::string message;
|
||||||
SourceLineInfo lineInfo;
|
SourceLineInfo lineInfo;
|
||||||
ResultWas::OfType type;
|
ResultWas::OfType type;
|
||||||
|
// The "ID" of the message, used to know when to remove it from reporter context.
|
||||||
unsigned int sequence;
|
unsigned int sequence;
|
||||||
|
|
||||||
DEPRECATED( "Explicitly use the 'sequence' member instead" )
|
DEPRECATED( "Explicitly use the 'sequence' member instead" )
|
||||||
@@ -4020,13 +4033,12 @@ namespace Catch {
|
|||||||
ScopedMessage( ScopedMessage&& old ) noexcept;
|
ScopedMessage( ScopedMessage&& old ) noexcept;
|
||||||
~ScopedMessage();
|
~ScopedMessage();
|
||||||
|
|
||||||
MessageInfo m_info;
|
unsigned int m_messageId;
|
||||||
bool m_moved = false;
|
bool m_moved = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Capturer {
|
class Capturer {
|
||||||
std::vector<MessageInfo> m_messages;
|
std::vector<MessageInfo> m_messages;
|
||||||
IResultCapture& m_resultCapture;
|
|
||||||
size_t m_captured = 0;
|
size_t m_captured = 0;
|
||||||
public:
|
public:
|
||||||
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
|
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
|
||||||
@@ -4074,7 +4086,7 @@ namespace Catch {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
|
#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)
|
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
|
||||||
@@ -7466,7 +7478,7 @@ namespace Catch {
|
|||||||
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||||
|
|
||||||
#define CATCH_VERSION_MAJOR 3
|
#define CATCH_VERSION_MAJOR 3
|
||||||
#define CATCH_VERSION_MINOR 10
|
#define CATCH_VERSION_MINOR 11
|
||||||
#define CATCH_VERSION_PATCH 0
|
#define CATCH_VERSION_PATCH 0
|
||||||
|
|
||||||
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||||
@@ -7882,8 +7894,9 @@ namespace Generators {
|
|||||||
class FilterGenerator final : public IGenerator<T> {
|
class FilterGenerator final : public IGenerator<T> {
|
||||||
GeneratorWrapper<T> m_generator;
|
GeneratorWrapper<T> m_generator;
|
||||||
Predicate m_predicate;
|
Predicate m_predicate;
|
||||||
|
static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
|
||||||
public:
|
public:
|
||||||
template <typename P = Predicate>
|
template <typename P>
|
||||||
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
|
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
|
||||||
m_generator(CATCH_MOVE(generator)),
|
m_generator(CATCH_MOVE(generator)),
|
||||||
m_predicate(CATCH_FORWARD(pred))
|
m_predicate(CATCH_FORWARD(pred))
|
||||||
@@ -7915,7 +7928,7 @@ namespace Generators {
|
|||||||
|
|
||||||
template <typename T, typename Predicate>
|
template <typename T, typename Predicate>
|
||||||
GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
|
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>
|
template <typename T>
|
||||||
@@ -9555,7 +9568,7 @@ namespace Catch {
|
|||||||
#define CATCH_TRAP() __asm__(".inst 0xde01")
|
#define CATCH_TRAP() __asm__(".inst 0xde01")
|
||||||
#endif
|
#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
|
// 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
|
// directly at the location of the failing check instead of breaking inside
|
||||||
// raise() called from it, i.e. one stack frame below.
|
// raise() called from it, i.e. one stack frame below.
|
||||||
@@ -10718,11 +10731,6 @@ namespace Catch {
|
|||||||
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
|
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
|
||||||
void benchmarkFailed( StringRef error ) 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;
|
std::string getCurrentTestName() const override;
|
||||||
|
|
||||||
const AssertionResult* getLastResult() const override;
|
const AssertionResult* getLastResult() const override;
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
project(
|
project(
|
||||||
'catch2',
|
'catch2',
|
||||||
'cpp',
|
'cpp',
|
||||||
version: '3.10.0', # CML version placeholder, don't delete
|
version: '3.11.0', # CML version placeholder, don't delete
|
||||||
license: 'BSL-1.0',
|
license: 'BSL-1.0',
|
||||||
meson_version: '>=0.54.1',
|
meson_version: '>=0.54.1',
|
||||||
)
|
)
|
||||||
|
@@ -98,6 +98,7 @@ set(IMPL_HEADERS
|
|||||||
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
|
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
|
||||||
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
|
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
|
||||||
${SOURCES_DIR}/internal/catch_leak_detector.hpp
|
${SOURCES_DIR}/internal/catch_leak_detector.hpp
|
||||||
|
${SOURCES_DIR}/internal/catch_lifetimebound.hpp
|
||||||
${SOURCES_DIR}/internal/catch_list.hpp
|
${SOURCES_DIR}/internal/catch_list.hpp
|
||||||
${SOURCES_DIR}/internal/catch_logical_traits.hpp
|
${SOURCES_DIR}/internal/catch_logical_traits.hpp
|
||||||
${SOURCES_DIR}/internal/catch_message_info.hpp
|
${SOURCES_DIR}/internal/catch_message_info.hpp
|
||||||
|
@@ -79,6 +79,7 @@
|
|||||||
#include <catch2/internal/catch_jsonwriter.hpp>
|
#include <catch2/internal/catch_jsonwriter.hpp>
|
||||||
#include <catch2/internal/catch_lazy_expr.hpp>
|
#include <catch2/internal/catch_lazy_expr.hpp>
|
||||||
#include <catch2/internal/catch_leak_detector.hpp>
|
#include <catch2/internal/catch_leak_detector.hpp>
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_list.hpp>
|
#include <catch2/internal/catch_list.hpp>
|
||||||
#include <catch2/internal/catch_logical_traits.hpp>
|
#include <catch2/internal/catch_logical_traits.hpp>
|
||||||
#include <catch2/internal/catch_message_info.hpp>
|
#include <catch2/internal/catch_message_info.hpp>
|
||||||
|
@@ -119,6 +119,8 @@ namespace Catch {
|
|||||||
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
|
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() ) {
|
if ( enableBazelEnvSupport() ) {
|
||||||
readBazelEnvVars();
|
readBazelEnvVars();
|
||||||
}
|
}
|
||||||
@@ -183,6 +185,8 @@ namespace Catch {
|
|||||||
|
|
||||||
bool Config::showHelp() const { return m_data.showHelp; }
|
bool Config::showHelp() const { return m_data.showHelp; }
|
||||||
|
|
||||||
|
std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
|
||||||
|
|
||||||
// IConfig interface
|
// IConfig interface
|
||||||
bool Config::allowThrows() const { return !m_data.noThrow; }
|
bool Config::allowThrows() const { return !m_data.noThrow; }
|
||||||
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
|
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;
|
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
|
} // end namespace Catch
|
||||||
|
@@ -87,6 +87,8 @@ namespace Catch {
|
|||||||
|
|
||||||
std::vector<std::string> testsOrTags;
|
std::vector<std::string> testsOrTags;
|
||||||
std::vector<std::string> sectionsToRun;
|
std::vector<std::string> sectionsToRun;
|
||||||
|
|
||||||
|
std::string prematureExitGuardFilePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -114,6 +116,8 @@ namespace Catch {
|
|||||||
|
|
||||||
bool showHelp() const;
|
bool showHelp() const;
|
||||||
|
|
||||||
|
std::string const& getExitGuardFilePath() const;
|
||||||
|
|
||||||
// IConfig interface
|
// IConfig interface
|
||||||
bool allowThrows() const override;
|
bool allowThrows() const override;
|
||||||
StringRef name() const override;
|
StringRef name() const override;
|
||||||
|
@@ -19,28 +19,26 @@ namespace Catch {
|
|||||||
|
|
||||||
|
|
||||||
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
|
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
|
||||||
m_info( CATCH_MOVE(builder.m_info) ) {
|
m_messageId( builder.m_info.sequence ) {
|
||||||
m_info.message = builder.m_stream.str();
|
MessageInfo info( CATCH_MOVE( builder.m_info ) );
|
||||||
getResultCapture().pushScopedMessage( m_info );
|
info.message = builder.m_stream.str();
|
||||||
|
IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
|
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
|
||||||
m_info( CATCH_MOVE( old.m_info ) ) {
|
m_messageId( old.m_messageId ) {
|
||||||
old.m_moved = true;
|
old.m_moved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedMessage::~ScopedMessage() {
|
ScopedMessage::~ScopedMessage() {
|
||||||
if ( !m_moved ){
|
if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
|
||||||
getResultCapture().popScopedMessage(m_info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Capturer::Capturer( StringRef macroName,
|
Capturer::Capturer( StringRef macroName,
|
||||||
SourceLineInfo const& lineInfo,
|
SourceLineInfo const& lineInfo,
|
||||||
ResultWas::OfType resultType,
|
ResultWas::OfType resultType,
|
||||||
StringRef names ):
|
StringRef names ) {
|
||||||
m_resultCapture( getResultCapture() ) {
|
|
||||||
auto trimmed = [&] (size_t start, size_t end) {
|
auto trimmed = [&] (size_t start, size_t end) {
|
||||||
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
|
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
|
||||||
++start;
|
++start;
|
||||||
@@ -86,8 +84,8 @@ namespace Catch {
|
|||||||
case ',':
|
case ',':
|
||||||
if (start != pos && openings.empty()) {
|
if (start != pos && openings.empty()) {
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
m_messages.emplace_back(macroName, lineInfo, resultType);
|
||||||
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
|
m_messages.back().message += trimmed(start, pos);
|
||||||
m_messages.back().message += " := ";
|
m_messages.back().message += " := "_sr;
|
||||||
start = pos;
|
start = pos;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -96,20 +94,20 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
assert(openings.empty() && "Mismatched openings");
|
assert(openings.empty() && "Mismatched openings");
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
m_messages.emplace_back(macroName, lineInfo, resultType);
|
||||||
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
|
m_messages.back().message += trimmed(start, names.size() - 1);
|
||||||
m_messages.back().message += " := ";
|
m_messages.back().message += " := "_sr;
|
||||||
}
|
}
|
||||||
Capturer::~Capturer() {
|
Capturer::~Capturer() {
|
||||||
assert( m_captured == m_messages.size() );
|
assert( m_captured == m_messages.size() );
|
||||||
for ( size_t i = 0; i < m_captured; ++i ) {
|
for (auto const& message : m_messages) {
|
||||||
m_resultCapture.popScopedMessage( m_messages[i] );
|
IResultCapture::popScopedMessage( message.sequence );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Capturer::captureValue( size_t index, std::string const& value ) {
|
void Capturer::captureValue( size_t index, std::string const& value ) {
|
||||||
assert( index < m_messages.size() );
|
assert( index < m_messages.size() );
|
||||||
m_messages[index].message += value;
|
m_messages[index].message += value;
|
||||||
m_resultCapture.pushScopedMessage( m_messages[index] );
|
IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
|
||||||
m_captured++;
|
m_captured++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -57,13 +57,12 @@ namespace Catch {
|
|||||||
ScopedMessage( ScopedMessage&& old ) noexcept;
|
ScopedMessage( ScopedMessage&& old ) noexcept;
|
||||||
~ScopedMessage();
|
~ScopedMessage();
|
||||||
|
|
||||||
MessageInfo m_info;
|
unsigned int m_messageId;
|
||||||
bool m_moved = false;
|
bool m_moved = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Capturer {
|
class Capturer {
|
||||||
std::vector<MessageInfo> m_messages;
|
std::vector<MessageInfo> m_messages;
|
||||||
IResultCapture& m_resultCapture;
|
|
||||||
size_t m_captured = 0;
|
size_t m_captured = 0;
|
||||||
public:
|
public:
|
||||||
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
|
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
|
||||||
@@ -111,7 +110,7 @@ namespace Catch {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
|
#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)
|
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
|
||||||
|
@@ -96,7 +96,6 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
void cleanUp() {
|
void cleanUp() {
|
||||||
cleanupSingletons();
|
cleanupSingletons();
|
||||||
cleanUpContext();
|
|
||||||
}
|
}
|
||||||
std::string translateActiveException() {
|
std::string translateActiveException() {
|
||||||
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
|
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
|
||||||
|
@@ -26,6 +26,8 @@
|
|||||||
#include <catch2/internal/catch_istream.hpp>
|
#include <catch2/internal/catch_istream.hpp>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <set>
|
#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
|
} // anon namespace
|
||||||
|
|
||||||
Session::Session() {
|
Session::Session() {
|
||||||
@@ -258,6 +304,7 @@ namespace Catch {
|
|||||||
static_cast<void>(std::getchar());
|
static_cast<void>(std::getchar());
|
||||||
}
|
}
|
||||||
int exitCode = runInternal();
|
int exitCode = runInternal();
|
||||||
|
|
||||||
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
|
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
|
||||||
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
|
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
|
||||||
static_cast<void>(std::getchar());
|
static_cast<void>(std::getchar());
|
||||||
@@ -298,6 +345,10 @@ namespace Catch {
|
|||||||
CATCH_TRY {
|
CATCH_TRY {
|
||||||
config(); // Force config to be constructed
|
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 );
|
seedRng( *m_config );
|
||||||
|
|
||||||
if (m_configData.filenamesAsTags) {
|
if (m_configData.filenamesAsTags) {
|
||||||
@@ -327,9 +378,12 @@ namespace Catch {
|
|||||||
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
|
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
|
||||||
auto const totals = tests.execute();
|
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()
|
if ( tests.hadUnmatchedTestSpecs()
|
||||||
&& m_config->warnAboutUnmatchedTestSpecs() ) {
|
&& m_config->warnAboutUnmatchedTestSpecs() ) {
|
||||||
// UnmatchedTestSpecExitCode
|
|
||||||
return UnmatchedTestSpecExitCode;
|
return UnmatchedTestSpecExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -57,39 +57,69 @@ namespace Detail {
|
|||||||
}
|
}
|
||||||
} // end unnamed namespace
|
} // 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 convertIntoString(StringRef string, bool escapeInvisibles) {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
// This is enough for the "don't escape invisibles" case, and a good
|
// This is enough for the "don't escape invisibles" case, and a good
|
||||||
// lower bound on the "escape invisibles" case.
|
// lower bound on the "escape invisibles" case.
|
||||||
ret.reserve(string.size() + 2);
|
ret.reserve( string.size() + 2 );
|
||||||
|
|
||||||
if (!escapeInvisibles) {
|
if ( !escapeInvisibles ) {
|
||||||
ret += '"';
|
ret += '"';
|
||||||
ret += string;
|
ret += string;
|
||||||
ret += '"';
|
ret += '"';
|
||||||
return 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 += '"';
|
ret += '"';
|
||||||
for (char c : string) {
|
for ( size_t i = 0; i < string.size(); ++i ) {
|
||||||
switch (c) {
|
const char c = string[i];
|
||||||
case '\r':
|
if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
|
||||||
ret.append("\\r");
|
write_to( i );
|
||||||
break;
|
if ( c == '\r' ) { ret.append( "\\r" ); }
|
||||||
case '\n':
|
if ( c == '\n' ) { ret.append( "\\n" ); }
|
||||||
ret.append("\\n");
|
if ( c == '\t' ) { ret.append( "\\t" ); }
|
||||||
break;
|
if ( c == '\f' ) { ret.append( "\\f" ); }
|
||||||
case '\t':
|
|
||||||
ret.append("\\t");
|
|
||||||
break;
|
|
||||||
case '\f':
|
|
||||||
ret.append("\\f");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret.push_back(c);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
write_to( string.size() );
|
||||||
ret += '"';
|
ret += '"';
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
#ifndef CATCH_TOSTRING_HPP_INCLUDED
|
#ifndef CATCH_TOSTRING_HPP_INCLUDED
|
||||||
#define CATCH_TOSTRING_HPP_INCLUDED
|
#define CATCH_TOSTRING_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -40,13 +40,9 @@ namespace Catch {
|
|||||||
|
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
|
|
||||||
inline std::size_t catch_strnlen(const char *str, std::size_t n) {
|
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) {
|
std::string formatTimeT( std::time_t time );
|
||||||
return static_cast<std::size_t>(ret - str);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr StringRef unprintableString = "{?}"_sr;
|
constexpr StringRef unprintableString = "{?}"_sr;
|
||||||
|
|
||||||
@@ -411,44 +407,38 @@ namespace Catch {
|
|||||||
|
|
||||||
// Separate std::tuple specialization
|
// Separate std::tuple specialization
|
||||||
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
||||||
#include <tuple>
|
# include <tuple>
|
||||||
|
# include <utility>
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
template<
|
template <typename Tuple, std::size_t... Is>
|
||||||
typename Tuple,
|
void PrintTuple( const Tuple& tuple,
|
||||||
std::size_t N = 0,
|
std::ostream& os,
|
||||||
bool = (N < std::tuple_size<Tuple>::value)
|
std::index_sequence<Is...> ) {
|
||||||
>
|
// 1 + Account for when the tuple is empty
|
||||||
struct TupleElementPrinter {
|
char a[1 + sizeof...( Is )] = {
|
||||||
static void print(const Tuple& tuple, std::ostream& os) {
|
( ( os << ( Is ? ", " : " " )
|
||||||
os << (N ? ", " : " ")
|
<< ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
|
||||||
<< ::Catch::Detail::stringify(std::get<N>(tuple));
|
'\0' )... };
|
||||||
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
|
(void)a;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
template<
|
} // namespace Detail
|
||||||
typename Tuple,
|
|
||||||
std::size_t N
|
|
||||||
>
|
|
||||||
struct TupleElementPrinter<Tuple, N, false> {
|
|
||||||
static void print(const Tuple&, std::ostream&) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
template <typename... Types>
|
||||||
|
|
||||||
|
|
||||||
template<typename ...Types>
|
|
||||||
struct StringMaker<std::tuple<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;
|
ReusableStringStream rss;
|
||||||
rss << '{';
|
rss << '{';
|
||||||
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
|
Detail::PrintTuple(
|
||||||
|
tuple,
|
||||||
|
rss.get(),
|
||||||
|
std::make_index_sequence<sizeof...( Types )>{} );
|
||||||
rss << " }";
|
rss << " }";
|
||||||
return rss.str();
|
return rss.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace Catch
|
||||||
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
|
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
|
||||||
@@ -635,28 +625,7 @@ struct ratio_string<std::milli> {
|
|||||||
const auto systemish = std::chrono::time_point_cast<
|
const auto systemish = std::chrono::time_point_cast<
|
||||||
std::chrono::system_clock::duration>( time_point );
|
std::chrono::system_clock::duration>( time_point );
|
||||||
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
|
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
|
||||||
|
return ::Catch::Detail::formatTimeT( as_time_t );
|
||||||
#ifdef _MSC_VER
|
|
||||||
std::tm timeInfo = {};
|
|
||||||
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( &as_time_t );
|
|
||||||
#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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version const& libraryVersion() {
|
Version const& libraryVersion() {
|
||||||
static Version version( 3, 10, 0, "", 0 );
|
static Version version( 3, 11, 0, "", 0 );
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||||
|
|
||||||
#define CATCH_VERSION_MAJOR 3
|
#define CATCH_VERSION_MAJOR 3
|
||||||
#define CATCH_VERSION_MINOR 10
|
#define CATCH_VERSION_MINOR 11
|
||||||
#define CATCH_VERSION_PATCH 0
|
#define CATCH_VERSION_PATCH 0
|
||||||
|
|
||||||
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||||
|
@@ -7,7 +7,14 @@
|
|||||||
// SPDX-License-Identifier: BSL-1.0
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||||
|
#include <catch2/internal/catch_enforce.hpp>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
namespace Detail {
|
||||||
|
void missingCaptureInstance() {
|
||||||
|
CATCH_INTERNAL_ERROR( "No result capture instance" );
|
||||||
|
}
|
||||||
|
} // namespace Detail
|
||||||
|
|
||||||
IResultCapture::~IResultCapture() = default;
|
IResultCapture::~IResultCapture() = default;
|
||||||
}
|
} // namespace Catch
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_context.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
#include <catch2/internal/catch_result_type.hpp>
|
#include <catch2/internal/catch_result_type.hpp>
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||||
@@ -62,10 +63,9 @@ namespace Catch {
|
|||||||
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
|
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
|
||||||
virtual void benchmarkFailed( StringRef error ) = 0;
|
virtual void benchmarkFailed( StringRef error ) = 0;
|
||||||
|
|
||||||
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
|
static void pushScopedMessage( MessageInfo&& message );
|
||||||
virtual void popScopedMessage( MessageInfo const& message ) = 0;
|
static void popScopedMessage( unsigned int messageId );
|
||||||
|
static void emplaceUnscopedMessage( MessageBuilder&& builder );
|
||||||
virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
|
|
||||||
|
|
||||||
virtual void handleFatalErrorCondition( StringRef message ) = 0;
|
virtual void handleFatalErrorCondition( StringRef message ) = 0;
|
||||||
|
|
||||||
@@ -101,7 +101,18 @@ namespace Catch {
|
|||||||
virtual void exceptionEarlyReported() = 0;
|
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
|
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
|
||||||
|
@@ -305,6 +305,9 @@ namespace Catch {
|
|||||||
| Opt( config.allowZeroTests )
|
| Opt( config.allowZeroTests )
|
||||||
["--allow-running-no-tests"]
|
["--allow-running-no-tests"]
|
||||||
( "Treat 'No tests run' as a success" )
|
( "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" )
|
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||||
( "which test or tests to use" );
|
( "which test or tests to use" );
|
||||||
|
|
||||||
|
@@ -220,13 +220,17 @@
|
|||||||
# endif
|
# endif
|
||||||
|
|
||||||
// Universal Windows platform does not support SEH
|
// Universal Windows platform does not support SEH
|
||||||
// Or console colours (or console at all...)
|
# if !defined(CATCH_PLATFORM_WINDOWS_UWP)
|
||||||
# if defined(CATCH_PLATFORM_WINDOWS_UWP)
|
|
||||||
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
|
|
||||||
# else
|
|
||||||
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
|
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
|
||||||
# endif
|
# 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 preprocessor needs some workaround for __VA_ARGS__
|
||||||
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
|
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
|
||||||
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
|
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
|
||||||
|
@@ -161,7 +161,11 @@ namespace {
|
|||||||
#endif // Windows/ ANSI/ None
|
#endif // Windows/ ANSI/ None
|
||||||
|
|
||||||
|
|
||||||
#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || defined( __GLIBC__ ) || defined(__FreeBSD__)
|
#if defined( CATCH_PLATFORM_LINUX ) \
|
||||||
|
|| defined( CATCH_PLATFORM_MAC ) \
|
||||||
|
|| defined( __GLIBC__ ) \
|
||||||
|
|| defined( __FreeBSD__ ) \
|
||||||
|
|| defined( CATCH_PLATFORM_QNX )
|
||||||
# define CATCH_INTERNAL_HAS_ISATTY
|
# define CATCH_INTERNAL_HAS_ISATTY
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
@@ -6,25 +6,14 @@
|
|||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
#include <catch2/internal/catch_context.hpp>
|
#include <catch2/internal/catch_context.hpp>
|
||||||
#include <catch2/internal/catch_noncopyable.hpp>
|
|
||||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
#include <catch2/internal/catch_random_number_generator.hpp>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
Context* Context::currentContext = nullptr;
|
Context Context::currentContext;
|
||||||
|
|
||||||
void cleanUpContext() {
|
|
||||||
delete Context::currentContext;
|
|
||||||
Context::currentContext = nullptr;
|
|
||||||
}
|
|
||||||
void Context::createContext() {
|
|
||||||
currentContext = new Context();
|
|
||||||
}
|
|
||||||
|
|
||||||
Context& getCurrentMutableContext() {
|
Context& getCurrentMutableContext() {
|
||||||
if ( !Context::currentContext ) { Context::createContext(); }
|
return Context::currentContext;
|
||||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
|
||||||
return *Context::currentContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SimplePcg32& sharedRng() {
|
SimplePcg32& sharedRng() {
|
||||||
|
@@ -19,11 +19,9 @@ namespace Catch {
|
|||||||
IConfig const* m_config = nullptr;
|
IConfig const* m_config = nullptr;
|
||||||
IResultCapture* m_resultCapture = nullptr;
|
IResultCapture* m_resultCapture = nullptr;
|
||||||
|
|
||||||
CATCH_EXPORT static Context* currentContext;
|
CATCH_EXPORT static Context currentContext;
|
||||||
friend Context& getCurrentMutableContext();
|
friend Context& getCurrentMutableContext();
|
||||||
friend Context const& getCurrentContext();
|
friend Context const& getCurrentContext();
|
||||||
static void createContext();
|
|
||||||
friend void cleanUpContext();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr IResultCapture* getResultCapture() const {
|
constexpr IResultCapture* getResultCapture() const {
|
||||||
@@ -34,21 +32,14 @@ namespace Catch {
|
|||||||
m_resultCapture = resultCapture;
|
m_resultCapture = resultCapture;
|
||||||
}
|
}
|
||||||
constexpr void setConfig( IConfig const* config ) { m_config = config; }
|
constexpr void setConfig( IConfig const* config ) { m_config = config; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Context& getCurrentMutableContext();
|
Context& getCurrentMutableContext();
|
||||||
|
|
||||||
inline Context const& getCurrentContext() {
|
inline Context const& getCurrentContext() {
|
||||||
// We duplicate the logic from `getCurrentMutableContext` here,
|
return Context::currentContext;
|
||||||
// to avoid paying the call overhead in debug mode.
|
|
||||||
if ( !Context::currentContext ) { Context::createContext(); }
|
|
||||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
|
||||||
return *Context::currentContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanUpContext();
|
|
||||||
|
|
||||||
class SimplePcg32;
|
class SimplePcg32;
|
||||||
SimplePcg32& sharedRng();
|
SimplePcg32& sharedRng();
|
||||||
}
|
}
|
||||||
|
@@ -69,7 +69,7 @@
|
|||||||
#endif
|
#endif
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
#elif defined(CATCH_PLATFORM_LINUX)
|
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ namespace Catch {
|
|||||||
#define CATCH_TRAP() __asm__(".inst 0xde01")
|
#define CATCH_TRAP() __asm__(".inst 0xde01")
|
||||||
#endif
|
#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
|
// 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
|
// directly at the location of the failing check instead of breaking inside
|
||||||
// raise() called from it, i.e. one stack frame below.
|
// raise() called from it, i.e. one stack frame below.
|
||||||
|
@@ -86,23 +86,27 @@ namespace Catch {
|
|||||||
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
|
{ 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) {
|
static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||||
for (auto const& def : signalDefs) {
|
for (auto const& def : signalDefs) {
|
||||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
|
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
|
||||||
reportFatal(def.name);
|
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.
|
// This stops us from eating debugger breaks etc.
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
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
|
// For MSVC, we reserve part of the stack memory for handling
|
||||||
// memory overflow structured exception.
|
// memory overflow structured exception.
|
||||||
FatalConditionHandler::FatalConditionHandler() {
|
FatalConditionHandler::FatalConditionHandler() {
|
||||||
|
24
src/catch2/internal/catch_lifetimebound.hpp
Normal file
24
src/catch2/internal/catch_lifetimebound.hpp
Normal 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
|
@@ -26,6 +26,7 @@ namespace Catch {
|
|||||||
std::string message;
|
std::string message;
|
||||||
SourceLineInfo lineInfo;
|
SourceLineInfo lineInfo;
|
||||||
ResultWas::OfType type;
|
ResultWas::OfType type;
|
||||||
|
// The "ID" of the message, used to know when to remove it from reporter context.
|
||||||
unsigned int sequence;
|
unsigned int sequence;
|
||||||
|
|
||||||
DEPRECATED( "Explicitly use the 'sequence' member instead" )
|
DEPRECATED( "Explicitly use the 'sequence' member instead" )
|
||||||
|
@@ -25,6 +25,9 @@
|
|||||||
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
||||||
# define CATCH_PLATFORM_LINUX
|
# define CATCH_PLATFORM_LINUX
|
||||||
|
|
||||||
|
#elif defined(__QNX__)
|
||||||
|
# define CATCH_PLATFORM_QNX
|
||||||
|
|
||||||
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
|
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
# define CATCH_PLATFORM_WINDOWS
|
# define CATCH_PLATFORM_WINDOWS
|
||||||
|
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
@@ -23,16 +24,16 @@ namespace Catch {
|
|||||||
std::ostringstream m_referenceStream; // Used for copy state/ flags from
|
std::ostringstream m_referenceStream; // Used for copy state/ flags from
|
||||||
Detail::Mutex m_mutex;
|
Detail::Mutex m_mutex;
|
||||||
|
|
||||||
auto add() -> std::size_t {
|
auto add() -> std::pair<std::size_t, std::ostringstream*> {
|
||||||
Detail::LockGuard _( m_mutex );
|
Detail::LockGuard _( m_mutex );
|
||||||
if( m_unused.empty() ) {
|
if( m_unused.empty() ) {
|
||||||
m_streams.push_back( Detail::make_unique<std::ostringstream>() );
|
m_streams.push_back( Detail::make_unique<std::ostringstream>() );
|
||||||
return m_streams.size()-1;
|
return { m_streams.size()-1, m_streams.back().get() };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto index = m_unused.back();
|
auto index = m_unused.back();
|
||||||
m_unused.pop_back();
|
m_unused.pop_back();
|
||||||
return index;
|
return { index, m_streams[index].get() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,10 +47,10 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ReusableStringStream::ReusableStringStream()
|
ReusableStringStream::ReusableStringStream() {
|
||||||
: m_index( Singleton<StringStreams>::getMutable().add() ),
|
std::tie( m_index, m_oss ) =
|
||||||
m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
|
Singleton<StringStreams>::getMutable().add();
|
||||||
{}
|
}
|
||||||
|
|
||||||
ReusableStringStream::~ReusableStringStream() {
|
ReusableStringStream::~ReusableStringStream() {
|
||||||
static_cast<std::ostringstream*>( m_oss )->str("");
|
static_cast<std::ostringstream*>( m_oss )->str("");
|
||||||
|
@@ -483,28 +483,6 @@ namespace Catch {
|
|||||||
m_reporter->benchmarkFailed( error );
|
m_reporter->benchmarkFailed( error );
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunContext::pushScopedMessage( MessageInfo const& message ) {
|
|
||||||
Detail::g_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.
|
|
||||||
Detail::g_messages.erase(
|
|
||||||
std::find_if( Detail::g_messages.begin(),
|
|
||||||
Detail::g_messages.end(),
|
|
||||||
[id = message.sequence]( MessageInfo const& msg ) {
|
|
||||||
return msg.sequence == id;
|
|
||||||
} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
|
|
||||||
Detail::g_messageScopes.emplace_back( CATCH_MOVE(builder) );
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RunContext::getCurrentTestName() const {
|
std::string RunContext::getCurrentTestName() const {
|
||||||
return m_activeTestCase
|
return m_activeTestCase
|
||||||
? m_activeTestCase->getTestCaseInfo().name
|
? m_activeTestCase->getTestCaseInfo().name
|
||||||
@@ -831,11 +809,26 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IResultCapture& getResultCapture() {
|
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
|
||||||
if (auto* capture = getCurrentContext().getResultCapture())
|
Detail::g_messages.push_back( CATCH_MOVE( message ) );
|
||||||
return *capture;
|
}
|
||||||
else
|
|
||||||
CATCH_INTERNAL_ERROR("No result capture instance");
|
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) {
|
void seedRng(IConfig const& config) {
|
||||||
|
@@ -94,11 +94,6 @@ namespace Catch {
|
|||||||
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
|
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
|
||||||
void benchmarkFailed( StringRef error ) 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;
|
std::string getCurrentTestName() const override;
|
||||||
|
|
||||||
const AssertionResult* getLastResult() const override;
|
const AssertionResult* getLastResult() const override;
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
|
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
|
||||||
#define CATCH_STRING_MANIP_HPP_INCLUDED
|
#define CATCH_STRING_MANIP_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -28,10 +29,10 @@ namespace Catch {
|
|||||||
//! Returns a new string without whitespace at the start/end
|
//! Returns a new string without whitespace at the start/end
|
||||||
std::string trim( std::string const& str );
|
std::string trim( std::string const& str );
|
||||||
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
|
//! 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
|
// !!! 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 );
|
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +50,7 @@ namespace Catch {
|
|||||||
StringRef m_label;
|
StringRef m_label;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr pluralise(std::uint64_t count, StringRef label):
|
constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND):
|
||||||
m_count(count),
|
m_count(count),
|
||||||
m_label(label)
|
m_label(label)
|
||||||
{}
|
{}
|
||||||
|
@@ -8,11 +8,12 @@
|
|||||||
#ifndef CATCH_STRINGREF_HPP_INCLUDED
|
#ifndef CATCH_STRINGREF_HPP_INCLUDED
|
||||||
#define CATCH_STRINGREF_HPP_INCLUDED
|
#define CATCH_STRINGREF_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
@@ -36,14 +37,16 @@ namespace Catch {
|
|||||||
public: // construction
|
public: // construction
|
||||||
constexpr StringRef() noexcept = default;
|
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_start( rawChars ),
|
||||||
m_size( size )
|
m_size( size )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
StringRef( std::string const& stdString ) noexcept
|
StringRef(
|
||||||
|
std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept
|
||||||
: m_start( stdString.c_str() ),
|
: m_start( stdString.c_str() ),
|
||||||
m_size( stdString.size() )
|
m_size( stdString.size() )
|
||||||
{}
|
{}
|
||||||
@@ -89,7 +92,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the current start pointer. May not be null-terminated.
|
// 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;
|
return m_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
||||||
#define 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_source_line_info.hpp>
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
@@ -48,7 +49,7 @@ namespace TestCaseTracking {
|
|||||||
StringRef name;
|
StringRef name;
|
||||||
SourceLineInfo location;
|
SourceLineInfo location;
|
||||||
|
|
||||||
constexpr NameAndLocationRef( StringRef name_,
|
constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND,
|
||||||
SourceLineInfo location_ ):
|
SourceLineInfo location_ ):
|
||||||
name( name_ ), location( location_ ) {}
|
name( name_ ), location( location_ ) {}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <catch2/matchers/internal/catch_matchers_impl.hpp>
|
#include <catch2/matchers/internal/catch_matchers_impl.hpp>
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -79,11 +80,15 @@ namespace Matchers {
|
|||||||
return description;
|
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);
|
lhs.m_matchers.push_back(&rhs);
|
||||||
return CATCH_MOVE(lhs);
|
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);
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||||
return CATCH_MOVE(rhs);
|
return CATCH_MOVE(rhs);
|
||||||
}
|
}
|
||||||
@@ -131,11 +136,15 @@ namespace Matchers {
|
|||||||
return description;
|
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);
|
lhs.m_matchers.push_back(&rhs);
|
||||||
return CATCH_MOVE(lhs);
|
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);
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||||
return CATCH_MOVE(rhs);
|
return CATCH_MOVE(rhs);
|
||||||
}
|
}
|
||||||
@@ -155,7 +164,8 @@ namespace Matchers {
|
|||||||
MatcherBase<ArgT> const& m_underlyingMatcher;
|
MatcherBase<ArgT> const& m_underlyingMatcher;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
|
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher
|
||||||
|
CATCH_ATTR_LIFETIMEBOUND ):
|
||||||
m_underlyingMatcher( underlyingMatcher )
|
m_underlyingMatcher( underlyingMatcher )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -171,16 +181,22 @@ namespace Matchers {
|
|||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
template <typename T>
|
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;
|
return Detail::MatchAllOf<T>{} && lhs && rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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;
|
return Detail::MatchAnyOf<T>{} || lhs || rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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 };
|
return Detail::MatchNotOf<T>{ matcher };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include <catch2/matchers/catch_matchers.hpp>
|
#include <catch2/matchers/catch_matchers.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_logical_traits.hpp>
|
#include <catch2/internal/catch_logical_traits.hpp>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@@ -114,7 +115,8 @@ namespace Matchers {
|
|||||||
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
||||||
MatchAllOfGeneric& operator=(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} {}
|
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||||
|
|
||||||
template<typename Arg>
|
template<typename Arg>
|
||||||
@@ -136,8 +138,8 @@ namespace Matchers {
|
|||||||
template<typename... MatchersRHS>
|
template<typename... MatchersRHS>
|
||||||
friend
|
friend
|
||||||
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
|
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
|
||||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
|
MatchAllOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
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>
|
template<typename MatcherRHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||||
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
|
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
|
||||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatcherRHS const& rhs) {
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
|
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>
|
template<typename MatcherLHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||||
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
|
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
|
||||||
MatcherLHS const& lhs,
|
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAllOfGeneric<MatcherTs...>&& rhs) {
|
MatchAllOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
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(MatchAnyOfGeneric&&) = default;
|
||||||
MatchAnyOfGeneric& operator=(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} {}
|
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||||
|
|
||||||
template<typename Arg>
|
template<typename Arg>
|
||||||
@@ -190,8 +193,8 @@ namespace Matchers {
|
|||||||
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
||||||
template<typename... MatchersRHS>
|
template<typename... MatchersRHS>
|
||||||
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
|
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
|
||||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
|
MatchAnyOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
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>
|
template<typename MatcherRHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||||
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
|
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
|
||||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatcherRHS const& rhs) {
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
|
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>
|
template<typename MatcherLHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||||
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
|
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
|
||||||
MatcherLHS const& lhs,
|
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
|
MatchAnyOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND) {
|
||||||
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
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(MatchNotOfGeneric&&) = default;
|
||||||
MatchNotOfGeneric& operator=(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>
|
template<typename Arg>
|
||||||
bool match(Arg&& arg) const {
|
bool match(Arg&& arg) const {
|
||||||
@@ -237,7 +241,9 @@ namespace Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//! Negating negation can just unwrap and return underlying matcher
|
//! 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;
|
return matcher.m_matcher;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -247,20 +253,22 @@ namespace Matchers {
|
|||||||
// compose only generic matchers
|
// compose only generic matchers
|
||||||
template<typename MatcherLHS, typename MatcherRHS>
|
template<typename MatcherLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, 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 };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MatcherLHS, typename MatcherRHS>
|
template<typename MatcherLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, 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 };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Wrap provided generic matcher in generic negator
|
//! Wrap provided generic matcher in generic negator
|
||||||
template<typename MatcherT>
|
template<typename MatcherT>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<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};
|
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,25 +276,29 @@ namespace Matchers {
|
|||||||
// compose mixed generic and non-generic matchers
|
// compose mixed generic and non-generic matchers
|
||||||
template<typename MatcherLHS, typename ArgRHS>
|
template<typename MatcherLHS, typename ArgRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<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 };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ArgLHS, typename MatcherRHS>
|
template<typename ArgLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, 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 };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MatcherLHS, typename ArgRHS>
|
template<typename MatcherLHS, typename ArgRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<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 };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ArgLHS, typename MatcherRHS>
|
template<typename ArgLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, 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 };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -105,6 +105,7 @@ internal_headers = [
|
|||||||
'internal/catch_jsonwriter.hpp',
|
'internal/catch_jsonwriter.hpp',
|
||||||
'internal/catch_lazy_expr.hpp',
|
'internal/catch_lazy_expr.hpp',
|
||||||
'internal/catch_leak_detector.hpp',
|
'internal/catch_leak_detector.hpp',
|
||||||
|
'internal/catch_lifetimebound.hpp',
|
||||||
'internal/catch_list.hpp',
|
'internal/catch_list.hpp',
|
||||||
'internal/catch_logical_traits.hpp',
|
'internal/catch_logical_traits.hpp',
|
||||||
'internal/catch_message_info.hpp',
|
'internal/catch_message_info.hpp',
|
||||||
|
@@ -662,5 +662,38 @@ foreach(reporterName # "Automake" - the simple .trs format does not support any
|
|||||||
)
|
)
|
||||||
endforeach()
|
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)
|
list(APPEND CATCH_TEST_TARGETS SelfTest)
|
||||||
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
|
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
|
||||||
|
@@ -95,7 +95,7 @@ foreach(target DisabledExceptions-DefaultHandler DisabledExceptions-CustomHandle
|
|||||||
target_compile_options(${target}
|
target_compile_options(${target}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/EHs-c-;/D_HAS_EXCEPTIONS=0>
|
$<$<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)
|
target_link_libraries(${target} Catch2_buildall_interface)
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -109,18 +109,6 @@ set_tests_properties(CATCH_CONFIG_DISABLE_EXCEPTIONS-1
|
|||||||
FAIL_REGULAR_EXPRESSION "abort;terminate;fatal"
|
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.
|
# We must now test this works without the build flag.
|
||||||
add_executable(BazelReporterNoCatchConfig ${TESTS_DIR}/X30-BazelReporter.cpp)
|
add_executable(BazelReporterNoCatchConfig ${TESTS_DIR}/X30-BazelReporter.cpp)
|
||||||
target_link_libraries(BazelReporterNoCatchConfig Catch2WithMain)
|
target_link_libraries(BazelReporterNoCatchConfig Catch2WithMain)
|
||||||
@@ -429,6 +417,50 @@ set_tests_properties(Reporters::CrashInJunitReporter
|
|||||||
LABELS "uses-signals"
|
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
|
add_executable(AssertionStartingEventGoesBeforeAssertionIsEvaluated
|
||||||
X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp
|
X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp
|
||||||
)
|
)
|
||||||
|
28
tests/ExtraTests/X40-QuickExit.cpp
Normal file
28
tests/ExtraTests/X40-QuickExit.cpp
Normal 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") {}
|
@@ -6,11 +6,13 @@
|
|||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
// 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/internal/catch_enum_values_registry.hpp>
|
||||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||||
#include <catch2/matchers/catch_matchers_vector.hpp>
|
#include <catch2/matchers/catch_matchers_vector.hpp>
|
||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include <catch2/catch_template_test_macros.hpp>
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
@@ -139,3 +141,26 @@ TEST_CASE( "Exception thrown inside stringify does not fail the test", "[toStrin
|
|||||||
ThrowsOnStringification tos;
|
ThrowsOnStringification tos;
|
||||||
CHECK( tos == 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 );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
88
tests/TestScripts/testBazelExitGuardFile.py
Executable file
88
tests/TestScripts/testBazelExitGuardFile.py
Executable 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}')
|
||||||
|
|
Reference in New Issue
Block a user