v3.12.0
This commit is contained in:
8 files changed
+321
-171
No files matched your search
+146
-49
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user