66
77// SPDX-License-Identifier: BSL-1.0
88
9- // Catch v3.11 .0
10- // Generated: 2025-09-30 10:49:12.549018
9+ // Catch v3.12 .0
10+ // Generated: 2025-12-28 22:27:25.828797
1111// ----------------------------------------------------------
1212// This file is an amalgamation of multiple different files.
1313// You probably shouldn't edit it directly.
@@ -2045,6 +2045,36 @@ namespace Detail {
20452045 }
20462046 } // end unnamed namespace
20472047
2048+ std::size_t catch_strnlen ( const char * str, std::size_t n ) {
2049+ auto ret = std::char_traits<char >::find ( str, n, ' \0 ' );
2050+ if ( ret != nullptr ) { return static_cast <std::size_t >( ret - str ); }
2051+ return n;
2052+ }
2053+
2054+ std::string formatTimeT (std::time_t time) {
2055+ #ifdef _MSC_VER
2056+ std::tm timeInfo = {};
2057+ const auto err = gmtime_s ( &timeInfo, &time );
2058+ if ( err ) {
2059+ return " gmtime from provided timepoint has failed. This "
2060+ " happens e.g. with pre-1970 dates using Microsoft libc" ;
2061+ }
2062+ #else
2063+ std::tm* timeInfo = std::gmtime ( &time );
2064+ #endif
2065+
2066+ auto const timeStampSize = sizeof ( " 2017-01-16T17:06:45Z" );
2067+ char timeStamp[timeStampSize];
2068+ const char * const fmt = " %Y-%m-%dT%H:%M:%SZ" ;
2069+
2070+ #ifdef _MSC_VER
2071+ std::strftime ( timeStamp, timeStampSize, fmt, &timeInfo );
2072+ #else
2073+ std::strftime ( timeStamp, timeStampSize, fmt, timeInfo );
2074+ #endif
2075+ return std::string ( timeStamp, timeStampSize - 1 );
2076+ }
2077+
20482078 std::string convertIntoString (StringRef string, bool escapeInvisibles) {
20492079 std::string ret;
20502080 // This is enough for the "don't escape invisibles" case, and a good
@@ -2354,7 +2384,7 @@ namespace Catch {
23542384 }
23552385
23562386 Version const & libraryVersion () {
2357- static Version version ( 3 , 11 , 0 , " " , 0 );
2387+ static Version version ( 3 , 12 , 0 , " " , 0 );
23582388 return version;
23592389 }
23602390
@@ -3402,7 +3432,7 @@ namespace Catch {
34023432 ( " list all listeners" )
34033433 | Opt ( setTestOrder, " decl|lex|rand" )
34043434 [" --order" ]
3405- ( " test case order (defaults to decl )" )
3435+ ( " test case order (defaults to rand )" )
34063436 | Opt ( setRngSeed, " 'time'|'random-device'|number" )
34073437 [" --rng-seed" ]
34083438 ( " set a specific seed for random numbers" )
@@ -3602,7 +3632,9 @@ namespace {
36023632#if defined( CATCH_PLATFORM_LINUX ) \
36033633 || defined ( CATCH_PLATFORM_MAC ) \
36043634 || defined ( __GLIBC__ ) \
3605- || defined ( __FreeBSD__ ) \
3635+ || (defined ( __FreeBSD__ ) \
3636+ /* PlayStation platform does not have `isatty()` */ \
3637+ && !defined (CATCH_PLATFORM_PLAYSTATION)) \
36063638 || defined ( CATCH_PLATFORM_QNX )
36073639# define CATCH_INTERNAL_HAS_ISATTY
36083640# include < unistd.h>
@@ -4886,19 +4918,22 @@ int main (int argc, char * argv[]) {
48864918
48874919namespace Catch {
48884920
4921+ namespace {
4922+ // Messages are owned by their individual threads, so the counter should
4923+ // be thread-local as well. Alternative consideration: atomic counter,
4924+ // so threads don't share IDs and things are easier to debug.
4925+ static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0 ;
4926+ }
4927+
48894928 MessageInfo::MessageInfo ( StringRef _macroName,
48904929 SourceLineInfo const & _lineInfo,
48914930 ResultWas::OfType _type )
48924931 : macroName( _macroName ),
48934932 lineInfo ( _lineInfo ),
48944933 type( _type ),
4895- sequence( ++globalCount )
4934+ sequence( ++messageIDCounter )
48964935 {}
48974936
4898- // Messages are owned by their individual threads, so the counter should be thread-local as well.
4899- // Alternative consideration: atomic, so threads don't share IDs and things are easier to debug.
4900- thread_local unsigned int MessageInfo::globalCount = 0 ;
4901-
49024937} // end namespace Catch
49034938
49044939
@@ -5814,12 +5849,8 @@ namespace Catch {
58145849
58155850 for ( auto const & child : m_children ) {
58165851 if ( child->isSectionTracker () &&
5817- std::find ( filters.begin (),
5818- filters.end (),
5819- static_cast <SectionTracker const &>(
5820- *child )
5821- .trimmedName () ) !=
5822- filters.end () ) {
5852+ static_cast <SectionTracker const &>( *child )
5853+ .trimmedName () == filters[0 ] ) {
58235854 return true ;
58245855 }
58255856 }
@@ -5862,27 +5893,98 @@ namespace Catch {
58625893 // should also be thread local. For now we just use naked globals
58635894 // below, in the future we will want to allocate piece of memory
58645895 // from heap, to avoid consuming too much thread-local storage.
5896+ //
5897+ // Note that we also don't want non-trivial the thread-local variables
5898+ // below be initialized for every thread, only for those that touch
5899+ // Catch2. To make this work with both GCC/Clang and MSVC, we have to
5900+ // make them thread-local magic statics. (Class-level statics have the
5901+ // desired semantics on GCC, but not on MSVC).
58655902
58665903 // This is used for the "if" part of CHECKED_IF/CHECKED_ELSE
5867- static thread_local bool g_lastAssertionPassed = false ;
5904+ static CATCH_INTERNAL_THREAD_LOCAL bool g_lastAssertionPassed = false ;
58685905
58695906 // This is the source location for last encountered macro. It is
58705907 // used to provide the users with more precise location of error
58715908 // when an unexpected exception/fatal error happens.
5872- static thread_local SourceLineInfo g_lastKnownLineInfo (" DummyLocation" , static_cast <size_t >(-1 ));
5909+ static CATCH_INTERNAL_THREAD_LOCAL SourceLineInfo
5910+ g_lastKnownLineInfo ( " DummyLocation" , static_cast <size_t >( -1 ) );
58735911
58745912 // Should we clear message scopes before sending off the messages to
58755913 // reporter? Set in `assertionPassedFastPath` to avoid doing the full
58765914 // clear there for performance reasons.
5877- static thread_local bool g_clearMessageScopes = false ;
5915+ static CATCH_INTERNAL_THREAD_LOCAL bool g_clearMessageScopes = false ;
5916+
5917+
5918+ // Holds the data for both scoped and unscoped messages together,
5919+ // to avoid issues where their lifetimes start in wrong order,
5920+ // and then are destroyed in wrong order.
5921+ class MessageHolder {
5922+ // The actual message vector passed to the reporters
5923+ std::vector<MessageInfo> messages;
5924+ // IDs of messages from UNSCOPED_X macros, which we have to
5925+ // remove manually.
5926+ std::vector<unsigned int > unscoped_ids;
5927+
5928+ public:
5929+ // We do not need to special-case the unscoped messages when
5930+ // we only keep around the raw msg ids.
5931+ ~MessageHolder () = default ;
5932+
5933+
5934+ void addUnscopedMessage (MessageBuilder&& builder) {
5935+ repairUnscopedMessageInvariant ();
5936+ MessageInfo info ( CATCH_MOVE ( builder.m_info ) );
5937+ info.message = builder.m_stream .str ();
5938+ unscoped_ids.push_back ( info.sequence );
5939+ messages.push_back ( CATCH_MOVE ( info ) );
5940+ }
5941+
5942+ void addScopedMessage (MessageInfo&& info) {
5943+ messages.push_back ( CATCH_MOVE ( info ) );
5944+ }
5945+
5946+ std::vector<MessageInfo> const & getMessages () const {
5947+ return messages;
5948+ }
5949+
5950+ void removeMessage ( unsigned int messageId ) {
5951+ // Note: On average, it would probably be better to look for
5952+ // the message backwards. However, we do not expect to have
5953+ // to deal with more messages than low single digits, so
5954+ // the improvement is tiny, and we would have to hand-write
5955+ // the loop to avoid terrible codegen of reverse iterators
5956+ // in debug mode.
5957+ auto iter =
5958+ std::find_if ( messages.begin (),
5959+ messages.end (),
5960+ [messageId]( MessageInfo const & msg ) {
5961+ return msg.sequence == messageId;
5962+ } );
5963+ assert ( iter != messages.end () &&
5964+ " Trying to remove non-existent message." );
5965+ messages.erase ( iter );
5966+ }
5967+
5968+ void removeUnscopedMessages () {
5969+ for ( const auto messageId : unscoped_ids ) {
5970+ removeMessage ( messageId );
5971+ }
5972+ unscoped_ids.clear ();
5973+ g_clearMessageScopes = false ;
5974+ }
5975+
5976+ void repairUnscopedMessageInvariant () {
5977+ if ( g_clearMessageScopes ) { removeUnscopedMessages (); }
5978+ g_clearMessageScopes = false ;
5979+ }
5980+ };
58785981
58795982 CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
58805983 CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
5881- // Actual messages to be provided to the reporter
5882- static thread_local std::vector<MessageInfo> g_messages;
5883-
5884- // Owners for the UNSCOPED_X information macro
5885- static thread_local std::vector<ScopedMessage> g_messageScopes;
5984+ static MessageHolder& g_messageHolder () {
5985+ static CATCH_INTERNAL_THREAD_LOCAL MessageHolder value;
5986+ return value;
5987+ }
58865988 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
58875989
58885990 } // namespace Detail
@@ -5899,6 +6001,13 @@ namespace Catch {
58996001 {
59006002 getCurrentMutableContext ().setResultCapture ( this );
59016003 m_reporter->testRunStarting (m_runInfo);
6004+
6005+ // TODO: HACK!
6006+ // We need to make sure the underlying cache is initialized
6007+ // while we are guaranteed to be running in a single thread,
6008+ // because the initialization is not thread-safe.
6009+ ReusableStringStream rss;
6010+ (void )rss;
59026011 }
59036012
59046013 RunContext::~RunContext () {
@@ -6021,21 +6130,19 @@ namespace Catch {
60216130 Detail::g_lastAssertionPassed = true ;
60226131 }
60236132
6024- if ( Detail::g_clearMessageScopes ) {
6025- Detail::g_messageScopes.clear ();
6026- Detail::g_clearMessageScopes = false ;
6027- }
6133+ auto & msgHolder = Detail::g_messageHolder ();
6134+ msgHolder.repairUnscopedMessageInvariant ();
60286135
60296136 // From here, we are touching shared state and need mutex.
60306137 Detail::LockGuard lock ( m_assertionMutex );
60316138 {
60326139 auto _ = scopedDeactivate ( *m_outputRedirect );
60336140 updateTotalsFromAtomics ();
6034- m_reporter->assertionEnded ( AssertionStats ( result, Detail::g_messages , m_totals ) );
6141+ m_reporter->assertionEnded ( AssertionStats ( result, msgHolder. getMessages () , m_totals ) );
60356142 }
60366143
60376144 if ( result.getResultType () != ResultWas::Warning ) {
6038- Detail::g_messageScopes. clear ();
6145+ msgHolder. removeUnscopedMessages ();
60396146 }
60406147
60416148 // Reset working state. assertion info will be reset after
@@ -6324,10 +6431,10 @@ namespace Catch {
63246431
63256432 m_testCaseTracker->close ();
63266433 handleUnfinishedSections ();
6327- Detail::g_messageScopes. clear ();
6328- // TBD: At this point, m_messages should be empty. Do we want to
6329- // assert that this is true, or keep the defensive clear call?
6330- Detail::g_messages. clear ( );
6434+ auto & msgHolder = Detail::g_messageHolder ();
6435+ msgHolder. removeUnscopedMessages ();
6436+ assert ( msgHolder. getMessages (). empty () &&
6437+ " There should be no leftover messages after the test ends " );
63316438
63326439 SectionStats testCaseSectionStats (CATCH_MOVE (testCaseSection), assertions, duration, missingAssertions);
63336440 m_reporter->sectionEnded (testCaseSectionStats);
@@ -6495,25 +6602,15 @@ namespace Catch {
64956602 }
64966603
64976604 void IResultCapture::pushScopedMessage ( MessageInfo&& message ) {
6498- Detail::g_messages. push_back ( CATCH_MOVE ( message ) );
6605+ Detail::g_messageHolder (). addScopedMessage ( CATCH_MOVE ( message ) );
64996606 }
65006607
65016608 void IResultCapture::popScopedMessage ( unsigned int messageId ) {
6502- // Note: On average, it would probably be better to look for the message
6503- // backwards. However, we do not expect to have to deal with more
6504- // messages than low single digits, so the optimization is tiny,
6505- // and we would have to hand-write the loop to avoid terrible
6506- // codegen of reverse iterators in debug mode.
6507- Detail::g_messages.erase ( std::find_if ( Detail::g_messages.begin (),
6508- Detail::g_messages.end (),
6509- [=]( MessageInfo const & msg ) {
6510- return msg.sequence ==
6511- messageId;
6512- } ) );
6609+ Detail::g_messageHolder ().removeMessage ( messageId );
65136610 }
65146611
65156612 void IResultCapture::emplaceUnscopedMessage ( MessageBuilder&& builder ) {
6516- Detail::g_messageScopes. emplace_back ( CATCH_MOVE ( builder ) );
6613+ Detail::g_messageHolder (). addUnscopedMessage ( CATCH_MOVE ( builder ) );
65176614 }
65186615
65196616 void seedRng (IConfig const & config) {
@@ -7219,9 +7316,9 @@ namespace TestCaseTracking {
72197316 bool SectionTracker::isComplete () const {
72207317 bool complete = true ;
72217318
7222- if (m_filters.empty ()
7319+ if ( m_filters.empty ()
72237320 || m_filters[0 ].empty ()
7224- || std::find ( m_filters. begin (), m_filters. end (), m_trimmed_name) != m_filters. end () ) {
7321+ || m_filters[ 0 ] == m_trimmed_name ) {
72257322 complete = TrackerBase::isComplete ();
72267323 }
72277324 return complete;
0 commit comments