Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/extras/catch_amalgamated.cpp
diff options
context:
space:
mode:
authorMartin Hořeňovský2025-12-28 22:31:54 +0100
committerMartin Hořeňovský2025-12-28 22:31:54 +0100
commit88abf9bf325c798c33f54f6b9220ef885b267f4f (patch)
tree4b1a63974a61836ff064cd28c7ac9d8a7cb9efdb /extras/catch_amalgamated.cpp
parent970ec144f56d78703c51d08a855844e91c8560a8 (diff)
downloadcatch3-3.12.0.tar.gz
v3.12.0v3.12.0
Diffstat (limited to 'extras/catch_amalgamated.cpp')
-rw-r--r--extras/catch_amalgamated.cpp195
1 files changed, 146 insertions, 49 deletions
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp
index 892d637d..a4406d0c 100644
--- a/extras/catch_amalgamated.cpp
+++ b/extras/catch_amalgamated.cpp
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.11.0
-// Generated: 2025-09-30 10:49:12.549018
+// Catch v3.12.0
+// Generated: 2025-12-28 22:27:25.828797
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -2045,6 +2045,36 @@ namespace Detail {
}
} // end unnamed namespace
+ std::size_t catch_strnlen( const char* str, std::size_t n ) {
+ auto ret = std::char_traits<char>::find( str, n, '\0' );
+ if ( ret != nullptr ) { return static_cast<std::size_t>( ret - str ); }
+ return n;
+ }
+
+ std::string formatTimeT(std::time_t time) {
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ const auto err = gmtime_s( &timeInfo, &time );
+ if ( err ) {
+ return "gmtime from provided timepoint has failed. This "
+ "happens e.g. with pre-1970 dates using Microsoft libc";
+ }
+#else
+ std::tm* timeInfo = std::gmtime( &time );
+#endif
+
+ auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" );
+ char timeStamp[timeStampSize];
+ const char* const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime( timeStamp, timeStampSize, fmt, &timeInfo );
+#else
+ std::strftime( timeStamp, timeStampSize, fmt, timeInfo );
+#endif
+ return std::string( timeStamp, timeStampSize - 1 );
+ }
+
std::string convertIntoString(StringRef string, bool escapeInvisibles) {
std::string ret;
// This is enough for the "don't escape invisibles" case, and a good
@@ -2354,7 +2384,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 11, 0, "", 0 );
+ static Version version( 3, 12, 0, "", 0 );
return version;
}
@@ -3402,7 +3432,7 @@ namespace Catch {
( "list all listeners" )
| Opt( setTestOrder, "decl|lex|rand" )
["--order"]
- ( "test case order (defaults to decl)" )
+ ( "test case order (defaults to rand)" )
| Opt( setRngSeed, "'time'|'random-device'|number" )
["--rng-seed"]
( "set a specific seed for random numbers" )
@@ -3602,7 +3632,9 @@ namespace {
#if defined( CATCH_PLATFORM_LINUX ) \
|| defined( CATCH_PLATFORM_MAC ) \
|| defined( __GLIBC__ ) \
- || defined( __FreeBSD__ ) \
+ || (defined( __FreeBSD__ ) \
+ /* PlayStation platform does not have `isatty()` */ \
+ && !defined(CATCH_PLATFORM_PLAYSTATION)) \
|| defined( CATCH_PLATFORM_QNX )
# define CATCH_INTERNAL_HAS_ISATTY
# include <unistd.h>
@@ -4886,19 +4918,22 @@ int main (int argc, char * argv[]) {
namespace Catch {
+ namespace {
+ // Messages are owned by their individual threads, so the counter should
+ // be thread-local as well. Alternative consideration: atomic counter,
+ // so threads don't share IDs and things are easier to debug.
+ static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0;
+ }
+
MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
: macroName( _macroName ),
lineInfo( _lineInfo ),
type( _type ),
- sequence( ++globalCount )
+ sequence( ++messageIDCounter )
{}
- // Messages are owned by their individual threads, so the counter should be thread-local as well.
- // Alternative consideration: atomic, so threads don't share IDs and things are easier to debug.
- thread_local unsigned int MessageInfo::globalCount = 0;
-
} // end namespace Catch
@@ -5814,12 +5849,8 @@ namespace Catch {
for ( auto const& child : m_children ) {
if ( child->isSectionTracker() &&
- std::find( filters.begin(),
- filters.end(),
- static_cast<SectionTracker const&>(
- *child )
- .trimmedName() ) !=
- filters.end() ) {
+ static_cast<SectionTracker const&>( *child )
+ .trimmedName() == filters[0] ) {
return true;
}
}
@@ -5862,27 +5893,98 @@ namespace Catch {
// should also be thread local. For now we just use naked globals
// below, in the future we will want to allocate piece of memory
// from heap, to avoid consuming too much thread-local storage.
+ //
+ // Note that we also don't want non-trivial the thread-local variables
+ // below be initialized for every thread, only for those that touch
+ // Catch2. To make this work with both GCC/Clang and MSVC, we have to
+ // make them thread-local magic statics. (Class-level statics have the
+ // desired semantics on GCC, but not on MSVC).
// This is used for the "if" part of CHECKED_IF/CHECKED_ELSE
- static thread_local bool g_lastAssertionPassed = false;
+ static CATCH_INTERNAL_THREAD_LOCAL bool g_lastAssertionPassed = false;
// This is the source location for last encountered macro. It is
// used to provide the users with more precise location of error
// when an unexpected exception/fatal error happens.
- static thread_local SourceLineInfo g_lastKnownLineInfo("DummyLocation", static_cast<size_t>(-1));
+ static CATCH_INTERNAL_THREAD_LOCAL SourceLineInfo
+ g_lastKnownLineInfo( "DummyLocation", static_cast<size_t>( -1 ) );
// Should we clear message scopes before sending off the messages to
// reporter? Set in `assertionPassedFastPath` to avoid doing the full
// clear there for performance reasons.
- static thread_local bool g_clearMessageScopes = false;
+ static CATCH_INTERNAL_THREAD_LOCAL bool g_clearMessageScopes = false;
+
+
+ // Holds the data for both scoped and unscoped messages together,
+ // to avoid issues where their lifetimes start in wrong order,
+ // and then are destroyed in wrong order.
+ class MessageHolder {
+ // The actual message vector passed to the reporters
+ std::vector<MessageInfo> messages;
+ // IDs of messages from UNSCOPED_X macros, which we have to
+ // remove manually.
+ std::vector<unsigned int> unscoped_ids;
+
+ public:
+ // We do not need to special-case the unscoped messages when
+ // we only keep around the raw msg ids.
+ ~MessageHolder() = default;
+
+
+ void addUnscopedMessage(MessageBuilder&& builder) {
+ repairUnscopedMessageInvariant();
+ MessageInfo info( CATCH_MOVE( builder.m_info ) );
+ info.message = builder.m_stream.str();
+ unscoped_ids.push_back( info.sequence );
+ messages.push_back( CATCH_MOVE( info ) );
+ }
+
+ void addScopedMessage(MessageInfo&& info) {
+ messages.push_back( CATCH_MOVE( info ) );
+ }
+
+ std::vector<MessageInfo> const& getMessages() const {
+ return messages;
+ }
+
+ void removeMessage( 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 improvement is tiny, and we would have to hand-write
+ // the loop to avoid terrible codegen of reverse iterators
+ // in debug mode.
+ auto iter =
+ std::find_if( messages.begin(),
+ messages.end(),
+ [messageId]( MessageInfo const& msg ) {
+ return msg.sequence == messageId;
+ } );
+ assert( iter != messages.end() &&
+ "Trying to remove non-existent message." );
+ messages.erase( iter );
+ }
+
+ void removeUnscopedMessages() {
+ for ( const auto messageId : unscoped_ids ) {
+ removeMessage( messageId );
+ }
+ unscoped_ids.clear();
+ g_clearMessageScopes = false;
+ }
+
+ void repairUnscopedMessageInvariant() {
+ if ( g_clearMessageScopes ) { removeUnscopedMessages(); }
+ g_clearMessageScopes = false;
+ }
+ };
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
- // Actual messages to be provided to the reporter
- static thread_local std::vector<MessageInfo> g_messages;
-
- // Owners for the UNSCOPED_X information macro
- static thread_local std::vector<ScopedMessage> g_messageScopes;
+ static MessageHolder& g_messageHolder() {
+ static CATCH_INTERNAL_THREAD_LOCAL MessageHolder value;
+ return value;
+ }
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
} // namespace Detail
@@ -5899,6 +6001,13 @@ namespace Catch {
{
getCurrentMutableContext().setResultCapture( this );
m_reporter->testRunStarting(m_runInfo);
+
+ // TODO: HACK!
+ // We need to make sure the underlying cache is initialized
+ // while we are guaranteed to be running in a single thread,
+ // because the initialization is not thread-safe.
+ ReusableStringStream rss;
+ (void)rss;
}
RunContext::~RunContext() {
@@ -6021,21 +6130,19 @@ namespace Catch {
Detail::g_lastAssertionPassed = true;
}
- if ( Detail::g_clearMessageScopes ) {
- Detail::g_messageScopes.clear();
- Detail::g_clearMessageScopes = false;
- }
+ auto& msgHolder = Detail::g_messageHolder();
+ msgHolder.repairUnscopedMessageInvariant();
// From here, we are touching shared state and need mutex.
Detail::LockGuard lock( m_assertionMutex );
{
auto _ = scopedDeactivate( *m_outputRedirect );
updateTotalsFromAtomics();
- m_reporter->assertionEnded( AssertionStats( result, Detail::g_messages, m_totals ) );
+ m_reporter->assertionEnded( AssertionStats( result, msgHolder.getMessages(), m_totals ) );
}
if ( result.getResultType() != ResultWas::Warning ) {
- Detail::g_messageScopes.clear();
+ msgHolder.removeUnscopedMessages();
}
// Reset working state. assertion info will be reset after
@@ -6324,10 +6431,10 @@ namespace Catch {
m_testCaseTracker->close();
handleUnfinishedSections();
- Detail::g_messageScopes.clear();
- // TBD: At this point, m_messages should be empty. Do we want to
- // assert that this is true, or keep the defensive clear call?
- Detail::g_messages.clear();
+ auto& msgHolder = Detail::g_messageHolder();
+ msgHolder.removeUnscopedMessages();
+ assert( msgHolder.getMessages().empty() &&
+ "There should be no leftover messages after the test ends" );
SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats);
@@ -6495,25 +6602,15 @@ namespace Catch {
}
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
- Detail::g_messages.push_back( CATCH_MOVE( message ) );
+ Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) );
}
void IResultCapture::popScopedMessage( unsigned int messageId ) {
- // Note: On average, it would probably be better to look for the message
- // backwards. However, we do not expect to have to deal with more
- // messages than low single digits, so the optimization is tiny,
- // and we would have to hand-write the loop to avoid terrible
- // codegen of reverse iterators in debug mode.
- Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(),
- Detail::g_messages.end(),
- [=]( MessageInfo const& msg ) {
- return msg.sequence ==
- messageId;
- } ) );
+ Detail::g_messageHolder().removeMessage( messageId );
}
void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
- Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) );
+ Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) );
}
void seedRng(IConfig const& config) {
@@ -7219,9 +7316,9 @@ namespace TestCaseTracking {
bool SectionTracker::isComplete() const {
bool complete = true;
- if (m_filters.empty()
+ if ( m_filters.empty()
|| m_filters[0].empty()
- || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
+ || m_filters[0] == m_trimmed_name ) {
complete = TrackerBase::isComplete();
}
return complete;