diff options
| author | Martin Hořeňovský | 2025-12-28 22:31:54 +0100 |
|---|---|---|
| committer | Martin Hořeňovský | 2025-12-28 22:31:54 +0100 |
| commit | 88abf9bf325c798c33f54f6b9220ef885b267f4f (patch) | |
| tree | 4b1a63974a61836ff064cd28c7ac9d8a7cb9efdb /extras/catch_amalgamated.cpp | |
| parent | 970ec144f56d78703c51d08a855844e91c8560a8 (diff) | |
| download | catch3-3.12.0.tar.gz | |
v3.12.0v3.12.0
Diffstat (limited to 'extras/catch_amalgamated.cpp')
| -rw-r--r-- | extras/catch_amalgamated.cpp | 195 |
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; |