This commit is contained in:
Martin Hořeňovský
2025-09-30 10:54:31 +02:00
parent 6500dc8149
commit b3fb4b9fea
9 changed files with 243 additions and 136 deletions

View File

@@ -35,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."

View File

@@ -73,7 +73,7 @@ test execution. Specifically it understands
> 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 X.Y.Z > 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

View File

@@ -653,7 +653,7 @@ Verbosity defaults to _normal_.
## Create file to guard against silent early termination ## Create file to guard against silent early termination
<pre>--premature-exit-guard-file &lt;path&gt;</pre> <pre>--premature-exit-guard-file &lt;path&gt;</pre>
> Introduced in Catch2 X.Y.Z > Introduced in Catch2 3.11.0
Tells Catch2 to create an empty file at specified path before the tests 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 start, and delete it after the tests finish. If the file is present after

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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