mirror of
https://github.com/catchorg/Catch2.git
synced 2026-05-27 02:54:46 +02:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88abf9bf32 | |||
| 970ec144f5 | |||
| eb3811c555 | |||
| 343cc059fe | |||
| 97091636d0 | |||
| f80956a43a | |||
| 32eac2d1bb | |||
| e849735e11 | |||
| d26f763180 | |||
| 5e44382423 | |||
| 985a3f4460 | |||
| a1faad9315 | |||
| 31ee3beb0a | |||
| 3b853aa9fb | |||
| 49d79e9e9c | |||
| 33e6fd217a | |||
| a58df2d7c5 | |||
| a9223b2bb3 | |||
| 363ca5af18 | |||
| cb6d713774 | |||
| 8e4ab5dd8f | |||
| 8219ed79f2 |
@@ -4,13 +4,11 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# From macos-14 forward, the baseline "macos-X" image is Arm based,
|
||||
# and not Intel based.
|
||||
runs-on: ${{matrix.image}}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [macos-13, macos-14, macos-15]
|
||||
image: [macos-14, macos-15, macos-15-intel]
|
||||
build_type: [Debug, Release]
|
||||
std: [14, 17]
|
||||
|
||||
|
||||
+2
-2
@@ -76,8 +76,8 @@ expand_template(
|
||||
"#cmakedefine CATCH_CONFIG_WINDOWS_SEH": "",
|
||||
"#cmakedefine CATCH_CONFIG_USE_BUILTIN_CONSTANT_P": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_USE_BUILTIN_CONSTANT_P": "",
|
||||
"#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "",
|
||||
"#cmakedefine CATCH_CONFIG_THREAD_SAFE_ASSERTIONS": "",
|
||||
"#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS": "",
|
||||
},
|
||||
template = "src/catch2/catch_user_config.hpp.in",
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@ set(_OverridableOptions
|
||||
"EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT"
|
||||
"USE_BUILTIN_CONSTANT_P"
|
||||
"DEPRECATION_ANNOTATIONS"
|
||||
"EXPERIMENTAL_THREAD_SAFE_ASSERTIONS"
|
||||
"THREAD_SAFE_ASSERTIONS"
|
||||
)
|
||||
|
||||
foreach(OptionName ${_OverridableOptions})
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
endif()
|
||||
|
||||
project(Catch2
|
||||
VERSION 3.11.0 # CML version placeholder, don't delete
|
||||
VERSION 3.12.0 # CML version placeholder, don't delete
|
||||
LANGUAGES CXX
|
||||
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
|
||||
DESCRIPTION "A modern, C++-native, unit test framework."
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[Disabling deprecation warnings](#disabling-deprecation-warnings)<br>
|
||||
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
|
||||
[Static analysis support](#static-analysis-support)<br>
|
||||
[Experimental thread safety](#experimental-thread-safety)<br>
|
||||
[Thread safety in assertions (and messages)](#thread-safety-in-assertions-and-messages)<br>
|
||||
|
||||
Catch2 is designed to "just work" as much as possible, and most of the
|
||||
configuration options below are changed automatically during compilation,
|
||||
@@ -316,17 +316,21 @@ no backwards compatibility guarantees._
|
||||
are not meant to be runnable, only "scannable".
|
||||
|
||||
|
||||
## Experimental thread safety
|
||||
|
||||
<a id="experimental-thread-safety"></a>
|
||||
## Thread safety in assertions (and messages)
|
||||
|
||||
> Introduced in Catch2 3.9.0
|
||||
|
||||
> Made non-experimental in Catch2 3.12.0
|
||||
|
||||
Catch2 can optionally support thread-safe assertions, that means, multiple
|
||||
user-spawned threads can use the assertion macros at the same time. Due
|
||||
to the performance cost this imposes even on single-threaded usage, Catch2
|
||||
defaults to non-thread-safe assertions.
|
||||
|
||||
CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // enables thread safe assertions
|
||||
CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // force-disables thread safe assertions
|
||||
CATCH_CONFIG_THREAD_SAFE_ASSERTIONS // enables thread safe assertions
|
||||
CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS // force-disables thread safe assertions
|
||||
|
||||
See [the documentation on thread safety in Catch2](thread-safety.md#top)
|
||||
for details on which macros are safe and other notes.
|
||||
|
||||
@@ -10,7 +10,7 @@ in-memory logs if they are not needed (the test case passed).
|
||||
Unlike reporters, each registered event listener is always active. Event
|
||||
listeners are always notified before reporter(s).
|
||||
|
||||
To write your own event listener, you should derive from `Catch::TestEventListenerBase`,
|
||||
To write your own event listener, you should derive from `Catch::EventListenerBase`,
|
||||
as it provides empty stubs for all reporter events, allowing you to
|
||||
only override events you care for. Afterwards you have to register it
|
||||
with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows
|
||||
|
||||
+1
-1
@@ -275,7 +275,7 @@ There are two ways to handle this, depending on whether you want this
|
||||
to be an error or not.
|
||||
|
||||
* If empty generator **is** an error, throw an exception in constructor.
|
||||
* If empty generator **is not** an error, use the [`SKIP`](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
|
||||
* If empty generator **is not** an error, use the [`SKIP` macro](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
# Release notes
|
||||
**Contents**<br>
|
||||
[3.12.0](#3120)<br>
|
||||
[3.11.0](#3110)<br>
|
||||
[3.10.0](#3100)<br>
|
||||
[3.9.1](#391)<br>
|
||||
@@ -71,6 +72,31 @@
|
||||
[Even Older versions](#even-older-versions)<br>
|
||||
|
||||
|
||||
## 3.12.0
|
||||
|
||||
### Fixes
|
||||
* Fixed unscoped messages after a passing fast-pathed assertion being lost.
|
||||
* Fixed the help string for `--order` to mention random order as the default. (#3045)
|
||||
* Fixed small documentation typos. (#3039)
|
||||
* Fixed compilation with `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS` for older C++ standards.
|
||||
* Fixed a thread-safety issue with message macros being used too early after the process starts.
|
||||
* Fixed automatic configuration to properly handle PlayStation platform. (#3054)
|
||||
* **Fixed the _weird_ behaviour of section filtering when specifying multiple filters.** (#3038)
|
||||
* See #3038 for more details.
|
||||
|
||||
### Improvements
|
||||
* Added `lifetimebound` attribute to various places.
|
||||
* As an example, compiler that supports lifetime analysis will now diagnose invalid use of Matcher combinators.
|
||||
* Minor compile-time improvements to stringification. (#3028)
|
||||
* `std::tuple` printer does not recurse.
|
||||
* Some implementation details were outlined into the cpp file.
|
||||
* Global variables will only be marked with `thread_local` in thread-safe builds. (#3044)
|
||||
|
||||
### Miscellaneous
|
||||
* The thread safety support is no longer experimental.
|
||||
* The new CMake option and C++ define is now `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS`.
|
||||
|
||||
|
||||
## 3.11.0
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -87,7 +87,7 @@ TEST_CASE("complex test case") {
|
||||
```
|
||||
|
||||
This test case will report 5 passing assertions; one for each of the three
|
||||
values in section `a1`, and then two in section `a2`, from values 2 and 4.
|
||||
values in section `a1`, and then two in section `a2`, from values 2 and 6.
|
||||
|
||||
Note that as soon as one section is skipped, the entire test case will
|
||||
be reported as _skipped_ (unless there is a failing assertion, in which
|
||||
|
||||
@@ -20,7 +20,7 @@ test case macros is not thread-safe. The way sections define paths through
|
||||
the test is incompatible with user spawning threads arbitrarily, so this
|
||||
limitation is here to stay.
|
||||
|
||||
**Important: thread safety in Catch2 is [opt-in](configuration.md#experimental-thread-safety)**
|
||||
**Important: thread safety in Catch2 is [opt-in](configuration.md#thread-safety)**
|
||||
|
||||
|
||||
## Using assertion macros from spawned threads
|
||||
|
||||
+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;
|
||||
|
||||
+144
-117
@@ -6,8 +6,8 @@
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
// Catch v3.11.0
|
||||
// Generated: 2025-09-30 10:49:11.225746
|
||||
// Catch v3.12.0
|
||||
// Generated: 2025-12-28 22:27:25.408132
|
||||
// ----------------------------------------------------------
|
||||
// This file is an amalgamation of multiple different files.
|
||||
// You probably shouldn't edit it directly.
|
||||
@@ -694,11 +694,30 @@ namespace Catch {
|
||||
#ifndef CATCH_STRINGREF_HPP_INCLUDED
|
||||
#define CATCH_STRINGREF_HPP_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||
#define CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||
|
||||
#if !defined( __has_cpp_attribute )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND
|
||||
#elif __has_cpp_attribute( msvc::lifetimebound )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]]
|
||||
#elif __has_cpp_attribute( clang::lifetimebound )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
|
||||
#elif __has_cpp_attribute( lifetimebound )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]]
|
||||
#else
|
||||
# define CATCH_ATTR_LIFETIMEBOUND
|
||||
#endif
|
||||
|
||||
#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <cassert>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Catch {
|
||||
@@ -722,14 +741,16 @@ namespace Catch {
|
||||
public: // construction
|
||||
constexpr StringRef() noexcept = default;
|
||||
|
||||
StringRef( char const* rawChars ) noexcept;
|
||||
StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept;
|
||||
|
||||
constexpr StringRef( char const* rawChars, size_type size ) noexcept
|
||||
constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND,
|
||||
size_type size ) noexcept
|
||||
: m_start( rawChars ),
|
||||
m_size( size )
|
||||
{}
|
||||
|
||||
StringRef( std::string const& stdString ) noexcept
|
||||
StringRef(
|
||||
std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept
|
||||
: m_start( stdString.c_str() ),
|
||||
m_size( stdString.size() )
|
||||
{}
|
||||
@@ -775,7 +796,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
// Returns the current start pointer. May not be null-terminated.
|
||||
constexpr char const* data() const noexcept {
|
||||
constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND {
|
||||
return m_start;
|
||||
}
|
||||
|
||||
@@ -2305,7 +2326,7 @@ namespace Catch {
|
||||
#ifndef CATCH_TOSTRING_HPP_INCLUDED
|
||||
#define CATCH_TOSTRING_HPP_INCLUDED
|
||||
|
||||
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
@@ -2473,13 +2494,9 @@ namespace Catch {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
inline 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::size_t catch_strnlen(const char *str, std::size_t n);
|
||||
|
||||
std::string formatTimeT( std::time_t time );
|
||||
|
||||
constexpr StringRef unprintableString = "{?}"_sr;
|
||||
|
||||
@@ -2844,44 +2861,38 @@ namespace Catch {
|
||||
|
||||
// Separate std::tuple specialization
|
||||
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
||||
#include <tuple>
|
||||
# include <tuple>
|
||||
# include <utility>
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
template<
|
||||
typename Tuple,
|
||||
std::size_t N = 0,
|
||||
bool = (N < std::tuple_size<Tuple>::value)
|
||||
>
|
||||
struct TupleElementPrinter {
|
||||
static void print(const Tuple& tuple, std::ostream& os) {
|
||||
os << (N ? ", " : " ")
|
||||
<< ::Catch::Detail::stringify(std::get<N>(tuple));
|
||||
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
|
||||
}
|
||||
};
|
||||
template <typename Tuple, std::size_t... Is>
|
||||
void PrintTuple( const Tuple& tuple,
|
||||
std::ostream& os,
|
||||
std::index_sequence<Is...> ) {
|
||||
// 1 + Account for when the tuple is empty
|
||||
char a[1 + sizeof...( Is )] = {
|
||||
( ( os << ( Is ? ", " : " " )
|
||||
<< ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
|
||||
'\0' )... };
|
||||
(void)a;
|
||||
}
|
||||
|
||||
template<
|
||||
typename Tuple,
|
||||
std::size_t N
|
||||
>
|
||||
struct TupleElementPrinter<Tuple, N, false> {
|
||||
static void print(const Tuple&, std::ostream&) {}
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<typename ...Types>
|
||||
template <typename... Types>
|
||||
struct StringMaker<std::tuple<Types...>> {
|
||||
static std::string convert(const std::tuple<Types...>& tuple) {
|
||||
static std::string convert( const std::tuple<Types...>& tuple ) {
|
||||
ReusableStringStream rss;
|
||||
rss << '{';
|
||||
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
|
||||
Detail::PrintTuple(
|
||||
tuple,
|
||||
rss.get(),
|
||||
std::make_index_sequence<sizeof...( Types )>{} );
|
||||
rss << " }";
|
||||
return rss.str();
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace Catch
|
||||
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
|
||||
@@ -3068,28 +3079,7 @@ struct ratio_string<std::milli> {
|
||||
const auto systemish = std::chrono::time_point_cast<
|
||||
std::chrono::system_clock::duration>( time_point );
|
||||
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
|
||||
|
||||
#ifdef _MSC_VER
|
||||
std::tm timeInfo = {};
|
||||
const auto err = gmtime_s( &timeInfo, &as_time_t );
|
||||
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( &as_time_t );
|
||||
#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);
|
||||
return ::Catch::Detail::formatTimeT( as_time_t );
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -3984,8 +3974,6 @@ namespace Catch {
|
||||
bool operator < (MessageInfo const& other) const {
|
||||
return sequence < other.sequence;
|
||||
}
|
||||
private:
|
||||
static thread_local unsigned int globalCount;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
@@ -7478,7 +7466,7 @@ namespace Catch {
|
||||
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||
|
||||
#define CATCH_VERSION_MAJOR 3
|
||||
#define CATCH_VERSION_MINOR 11
|
||||
#define CATCH_VERSION_MINOR 12
|
||||
#define CATCH_VERSION_PATCH 0
|
||||
|
||||
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||
@@ -10094,8 +10082,8 @@ namespace Catch {
|
||||
|
||||
class JsonValueWriter {
|
||||
public:
|
||||
JsonValueWriter( std::ostream& os );
|
||||
JsonValueWriter( std::ostream& os, std::uint64_t indent_level );
|
||||
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||
|
||||
JsonObjectWriter writeObject() &&;
|
||||
JsonArrayWriter writeArray() &&;
|
||||
@@ -10129,8 +10117,8 @@ namespace Catch {
|
||||
|
||||
class JsonObjectWriter {
|
||||
public:
|
||||
JsonObjectWriter( std::ostream& os );
|
||||
JsonObjectWriter( std::ostream& os, std::uint64_t indent_level );
|
||||
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||
|
||||
JsonObjectWriter( JsonObjectWriter&& source ) noexcept;
|
||||
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
|
||||
@@ -10148,8 +10136,8 @@ namespace Catch {
|
||||
|
||||
class JsonArrayWriter {
|
||||
public:
|
||||
JsonArrayWriter( std::ostream& os );
|
||||
JsonArrayWriter( std::ostream& os, std::uint64_t indent_level );
|
||||
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||
|
||||
JsonArrayWriter( JsonArrayWriter&& source ) noexcept;
|
||||
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
|
||||
@@ -10422,7 +10410,7 @@ namespace TestCaseTracking {
|
||||
StringRef name;
|
||||
SourceLineInfo location;
|
||||
|
||||
constexpr NameAndLocationRef( StringRef name_,
|
||||
constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND,
|
||||
SourceLineInfo location_ ):
|
||||
name( name_ ), location( location_ ) {}
|
||||
|
||||
@@ -10622,7 +10610,7 @@ using TestCaseTracking::SectionTracker;
|
||||
#define CATCH_THREAD_SUPPORT_HPP_INCLUDED
|
||||
|
||||
|
||||
#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
|
||||
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS )
|
||||
# include <atomic>
|
||||
# include <mutex>
|
||||
#endif
|
||||
@@ -10630,14 +10618,14 @@ using TestCaseTracking::SectionTracker;
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
|
||||
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS )
|
||||
using Mutex = std::mutex;
|
||||
using LockGuard = std::lock_guard<std::mutex>;
|
||||
struct AtomicCounts {
|
||||
std::atomic<std::uint64_t> passed = 0;
|
||||
std::atomic<std::uint64_t> failed = 0;
|
||||
std::atomic<std::uint64_t> failedButOk = 0;
|
||||
std::atomic<std::uint64_t> skipped = 0;
|
||||
std::atomic<std::uint64_t> passed{ 0 };
|
||||
std::atomic<std::uint64_t> failed{ 0 };
|
||||
std::atomic<std::uint64_t> failedButOk{ 0 };
|
||||
std::atomic<std::uint64_t> skipped{ 0 };
|
||||
};
|
||||
#else // ^^ Use actual mutex, lock and atomics
|
||||
// vv Dummy implementations for single-thread performance
|
||||
@@ -10942,10 +10930,10 @@ namespace Catch {
|
||||
//! Returns a new string without whitespace at the start/end
|
||||
std::string trim( std::string const& str );
|
||||
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
|
||||
StringRef trim(StringRef ref);
|
||||
StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND );
|
||||
|
||||
// !!! Be aware, returns refs into original string - make sure original string outlives them
|
||||
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
|
||||
std::vector<StringRef> splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter );
|
||||
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
|
||||
|
||||
/**
|
||||
@@ -10963,7 +10951,7 @@ namespace Catch {
|
||||
StringRef m_label;
|
||||
|
||||
public:
|
||||
constexpr pluralise(std::uint64_t count, StringRef label):
|
||||
constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND):
|
||||
m_count(count),
|
||||
m_label(label)
|
||||
{}
|
||||
@@ -11442,6 +11430,19 @@ namespace Catch {
|
||||
#endif // CATCH_TEXTFLOW_HPP_INCLUDED
|
||||
|
||||
|
||||
#ifndef CATCH_THREAD_LOCAL_HPP_INCLUDED
|
||||
#define CATCH_THREAD_LOCAL_HPP_INCLUDED
|
||||
|
||||
|
||||
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS )
|
||||
#define CATCH_INTERNAL_THREAD_LOCAL thread_local
|
||||
#else
|
||||
#define CATCH_INTERNAL_THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
#endif // CATCH_THREAD_LOCAL_HPP_INCLUDED
|
||||
|
||||
|
||||
#ifndef CATCH_TO_STRING_HPP_INCLUDED
|
||||
#define CATCH_TO_STRING_HPP_INCLUDED
|
||||
|
||||
@@ -11510,7 +11511,7 @@ namespace Catch {
|
||||
public:
|
||||
enum ForWhat { ForTextNodes, ForAttributes };
|
||||
|
||||
constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ):
|
||||
constexpr XmlEncode( StringRef str CATCH_ATTR_LIFETIMEBOUND, ForWhat forWhat = ForTextNodes ):
|
||||
m_str( str ), m_forWhat( forWhat ) {}
|
||||
|
||||
|
||||
@@ -11528,7 +11529,7 @@ namespace Catch {
|
||||
|
||||
class ScopedElement {
|
||||
public:
|
||||
ScopedElement( XmlWriter* writer, XmlFormatting fmt );
|
||||
ScopedElement( XmlWriter* writer CATCH_ATTR_LIFETIMEBOUND, XmlFormatting fmt );
|
||||
|
||||
ScopedElement( ScopedElement&& other ) noexcept;
|
||||
ScopedElement& operator=( ScopedElement&& other ) noexcept;
|
||||
@@ -11560,7 +11561,7 @@ namespace Catch {
|
||||
XmlFormatting m_fmt;
|
||||
};
|
||||
|
||||
XmlWriter( std::ostream& os );
|
||||
XmlWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
~XmlWriter();
|
||||
|
||||
XmlWriter( XmlWriter const& ) = delete;
|
||||
@@ -11818,11 +11819,15 @@ namespace Matchers {
|
||||
return description;
|
||||
}
|
||||
|
||||
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
||||
friend MatchAllOf operator&&( MatchAllOf&& lhs,
|
||||
MatcherBase<ArgT> const& rhs
|
||||
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
lhs.m_matchers.push_back(&rhs);
|
||||
return CATCH_MOVE(lhs);
|
||||
}
|
||||
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
|
||||
friend MatchAllOf
|
||||
operator&&( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAllOf&& rhs ) {
|
||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||
return CATCH_MOVE(rhs);
|
||||
}
|
||||
@@ -11870,11 +11875,15 @@ namespace Matchers {
|
||||
return description;
|
||||
}
|
||||
|
||||
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
||||
friend MatchAnyOf operator||( MatchAnyOf&& lhs,
|
||||
MatcherBase<ArgT> const& rhs
|
||||
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
lhs.m_matchers.push_back(&rhs);
|
||||
return CATCH_MOVE(lhs);
|
||||
}
|
||||
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
|
||||
friend MatchAnyOf
|
||||
operator||( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAnyOf&& rhs ) {
|
||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||
return CATCH_MOVE(rhs);
|
||||
}
|
||||
@@ -11894,7 +11903,8 @@ namespace Matchers {
|
||||
MatcherBase<ArgT> const& m_underlyingMatcher;
|
||||
|
||||
public:
|
||||
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
|
||||
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher
|
||||
CATCH_ATTR_LIFETIMEBOUND ):
|
||||
m_underlyingMatcher( underlyingMatcher )
|
||||
{}
|
||||
|
||||
@@ -11910,16 +11920,22 @@ namespace Matchers {
|
||||
} // namespace Detail
|
||||
|
||||
template <typename T>
|
||||
Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
||||
Detail::MatchAllOf<T>
|
||||
operator&&( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchAllOf<T>{} && lhs && rhs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
||||
Detail::MatchAnyOf<T>
|
||||
operator||( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchAnyOf<T>{} || lhs || rhs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) {
|
||||
Detail::MatchNotOf<T>
|
||||
operator!( MatcherBase<T> const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchNotOf<T>{ matcher };
|
||||
}
|
||||
|
||||
@@ -12086,7 +12102,8 @@ namespace Matchers {
|
||||
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
||||
MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
|
||||
|
||||
MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
||||
MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
|
||||
: m_matchers{ {std::addressof(matchers)...} } {}
|
||||
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||
|
||||
template<typename Arg>
|
||||
@@ -12108,8 +12125,8 @@ namespace Matchers {
|
||||
template<typename... MatchersRHS>
|
||||
friend
|
||||
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
||||
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAllOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
|
||||
@@ -12117,8 +12134,8 @@ namespace Matchers {
|
||||
template<typename MatcherRHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
||||
MatcherRHS const& rhs) {
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
|
||||
}
|
||||
|
||||
@@ -12126,8 +12143,8 @@ namespace Matchers {
|
||||
template<typename MatcherLHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
|
||||
MatcherLHS const& lhs,
|
||||
MatchAllOfGeneric<MatcherTs...>&& rhs) {
|
||||
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAllOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
};
|
||||
@@ -12141,7 +12158,8 @@ namespace Matchers {
|
||||
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
|
||||
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
|
||||
|
||||
MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
||||
MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
|
||||
: m_matchers{ {std::addressof(matchers)...} } {}
|
||||
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||
|
||||
template<typename Arg>
|
||||
@@ -12162,8 +12180,8 @@ namespace Matchers {
|
||||
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
||||
template<typename... MatchersRHS>
|
||||
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
||||
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAnyOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
|
||||
@@ -12171,8 +12189,8 @@ namespace Matchers {
|
||||
template<typename MatcherRHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
||||
MatcherRHS const& rhs) {
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
|
||||
}
|
||||
|
||||
@@ -12180,8 +12198,8 @@ namespace Matchers {
|
||||
template<typename MatcherLHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
|
||||
MatcherLHS const& lhs,
|
||||
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
|
||||
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAnyOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND) {
|
||||
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
};
|
||||
@@ -12197,7 +12215,8 @@ namespace Matchers {
|
||||
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
|
||||
MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
|
||||
|
||||
explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {}
|
||||
explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND)
|
||||
: m_matcher{matcher} {}
|
||||
|
||||
template<typename Arg>
|
||||
bool match(Arg&& arg) const {
|
||||
@@ -12209,7 +12228,9 @@ namespace Matchers {
|
||||
}
|
||||
|
||||
//! Negating negation can just unwrap and return underlying matcher
|
||||
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
|
||||
friend MatcherT const&
|
||||
operator!( MatchNotOfGeneric<MatcherT> const& matcher
|
||||
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return matcher.m_matcher;
|
||||
}
|
||||
};
|
||||
@@ -12219,20 +12240,22 @@ namespace Matchers {
|
||||
// compose only generic matchers
|
||||
template<typename MatcherLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
|
||||
operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
||||
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename MatcherLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
|
||||
operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
||||
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
//! Wrap provided generic matcher in generic negator
|
||||
template<typename MatcherT>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
|
||||
operator ! (MatcherT const& matcher) {
|
||||
operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
|
||||
}
|
||||
|
||||
@@ -12240,25 +12263,29 @@ namespace Matchers {
|
||||
// compose mixed generic and non-generic matchers
|
||||
template<typename MatcherLHS, typename ArgRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
||||
operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
||||
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename ArgLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
||||
operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
||||
operator&&( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename MatcherLHS, typename ArgRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
||||
operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
||||
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename ArgLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
||||
operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
||||
operator||( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
project(
|
||||
'catch2',
|
||||
'cpp',
|
||||
version: '3.11.0', # CML version placeholder, don't delete
|
||||
version: '3.12.0', # CML version placeholder, don't delete
|
||||
license: 'BSL-1.0',
|
||||
meson_version: '>=0.54.1',
|
||||
)
|
||||
|
||||
@@ -98,6 +98,7 @@ set(IMPL_HEADERS
|
||||
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
|
||||
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
|
||||
${SOURCES_DIR}/internal/catch_leak_detector.hpp
|
||||
${SOURCES_DIR}/internal/catch_lifetimebound.hpp
|
||||
${SOURCES_DIR}/internal/catch_list.hpp
|
||||
${SOURCES_DIR}/internal/catch_logical_traits.hpp
|
||||
${SOURCES_DIR}/internal/catch_message_info.hpp
|
||||
@@ -139,6 +140,7 @@ set(IMPL_HEADERS
|
||||
${SOURCES_DIR}/internal/catch_test_registry.hpp
|
||||
${SOURCES_DIR}/internal/catch_test_spec_parser.hpp
|
||||
${SOURCES_DIR}/internal/catch_textflow.hpp
|
||||
${SOURCES_DIR}/internal/catch_thread_local.hpp
|
||||
${SOURCES_DIR}/internal/catch_thread_support.hpp
|
||||
${SOURCES_DIR}/internal/catch_to_string.hpp
|
||||
${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
#include <catch2/internal/catch_jsonwriter.hpp>
|
||||
#include <catch2/internal/catch_lazy_expr.hpp>
|
||||
#include <catch2/internal/catch_leak_detector.hpp>
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
#include <catch2/internal/catch_list.hpp>
|
||||
#include <catch2/internal/catch_logical_traits.hpp>
|
||||
#include <catch2/internal/catch_message_info.hpp>
|
||||
@@ -121,6 +122,7 @@
|
||||
#include <catch2/internal/catch_test_registry.hpp>
|
||||
#include <catch2/internal/catch_test_spec_parser.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/internal/catch_thread_local.hpp>
|
||||
#include <catch2/internal/catch_thread_support.hpp>
|
||||
#include <catch2/internal/catch_to_string.hpp>
|
||||
#include <catch2/internal/catch_uncaught_exceptions.hpp>
|
||||
|
||||
@@ -57,6 +57,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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#ifndef CATCH_TOSTRING_HPP_INCLUDED
|
||||
#define CATCH_TOSTRING_HPP_INCLUDED
|
||||
|
||||
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
@@ -40,13 +40,9 @@ namespace Catch {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
inline 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::size_t catch_strnlen(const char *str, std::size_t n);
|
||||
|
||||
std::string formatTimeT( std::time_t time );
|
||||
|
||||
constexpr StringRef unprintableString = "{?}"_sr;
|
||||
|
||||
@@ -411,44 +407,38 @@ namespace Catch {
|
||||
|
||||
// Separate std::tuple specialization
|
||||
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
||||
#include <tuple>
|
||||
# include <tuple>
|
||||
# include <utility>
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
template<
|
||||
typename Tuple,
|
||||
std::size_t N = 0,
|
||||
bool = (N < std::tuple_size<Tuple>::value)
|
||||
>
|
||||
struct TupleElementPrinter {
|
||||
static void print(const Tuple& tuple, std::ostream& os) {
|
||||
os << (N ? ", " : " ")
|
||||
<< ::Catch::Detail::stringify(std::get<N>(tuple));
|
||||
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
|
||||
}
|
||||
};
|
||||
template <typename Tuple, std::size_t... Is>
|
||||
void PrintTuple( const Tuple& tuple,
|
||||
std::ostream& os,
|
||||
std::index_sequence<Is...> ) {
|
||||
// 1 + Account for when the tuple is empty
|
||||
char a[1 + sizeof...( Is )] = {
|
||||
( ( os << ( Is ? ", " : " " )
|
||||
<< ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
|
||||
'\0' )... };
|
||||
(void)a;
|
||||
}
|
||||
|
||||
template<
|
||||
typename Tuple,
|
||||
std::size_t N
|
||||
>
|
||||
struct TupleElementPrinter<Tuple, N, false> {
|
||||
static void print(const Tuple&, std::ostream&) {}
|
||||
};
|
||||
} // namespace Detail
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<typename ...Types>
|
||||
template <typename... Types>
|
||||
struct StringMaker<std::tuple<Types...>> {
|
||||
static std::string convert(const std::tuple<Types...>& tuple) {
|
||||
static std::string convert( const std::tuple<Types...>& tuple ) {
|
||||
ReusableStringStream rss;
|
||||
rss << '{';
|
||||
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
|
||||
Detail::PrintTuple(
|
||||
tuple,
|
||||
rss.get(),
|
||||
std::make_index_sequence<sizeof...( Types )>{} );
|
||||
rss << " }";
|
||||
return rss.str();
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace Catch
|
||||
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
|
||||
@@ -635,28 +625,7 @@ struct ratio_string<std::milli> {
|
||||
const auto systemish = std::chrono::time_point_cast<
|
||||
std::chrono::system_clock::duration>( time_point );
|
||||
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
|
||||
|
||||
#ifdef _MSC_VER
|
||||
std::tm timeInfo = {};
|
||||
const auto err = gmtime_s( &timeInfo, &as_time_t );
|
||||
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( &as_time_t );
|
||||
#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);
|
||||
return ::Catch::Detail::formatTimeT( as_time_t );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -196,12 +196,12 @@
|
||||
#endif
|
||||
|
||||
|
||||
#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS
|
||||
#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS
|
||||
#cmakedefine CATCH_CONFIG_THREAD_SAFE_ASSERTIONS
|
||||
#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS
|
||||
|
||||
#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) && \
|
||||
defined( CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
|
||||
# error Cannot force EXPERIMENTAL_THREAD_SAFE_ASSERTIONS to both ON and OFF
|
||||
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) && \
|
||||
defined( CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS )
|
||||
# error Cannot force THREAD_SAFE_ASSERTIONS to both ON and OFF
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
Version const& libraryVersion() {
|
||||
static Version version( 3, 11, 0, "", 0 );
|
||||
static Version version( 3, 12, 0, "", 0 );
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||
|
||||
#define CATCH_VERSION_MAJOR 3
|
||||
#define CATCH_VERSION_MINOR 11
|
||||
#define CATCH_VERSION_MINOR 12
|
||||
#define CATCH_VERSION_PATCH 0
|
||||
|
||||
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
|
||||
|
||||
@@ -265,7 +265,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" )
|
||||
|
||||
@@ -164,7 +164,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>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef CATCH_JSONWRITER_HPP_INCLUDED
|
||||
#define CATCH_JSONWRITER_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
|
||||
@@ -27,8 +28,8 @@ namespace Catch {
|
||||
|
||||
class JsonValueWriter {
|
||||
public:
|
||||
JsonValueWriter( std::ostream& os );
|
||||
JsonValueWriter( std::ostream& os, std::uint64_t indent_level );
|
||||
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||
|
||||
JsonObjectWriter writeObject() &&;
|
||||
JsonArrayWriter writeArray() &&;
|
||||
@@ -62,8 +63,8 @@ namespace Catch {
|
||||
|
||||
class JsonObjectWriter {
|
||||
public:
|
||||
JsonObjectWriter( std::ostream& os );
|
||||
JsonObjectWriter( std::ostream& os, std::uint64_t indent_level );
|
||||
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||
|
||||
JsonObjectWriter( JsonObjectWriter&& source ) noexcept;
|
||||
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
|
||||
@@ -81,8 +82,8 @@ namespace Catch {
|
||||
|
||||
class JsonArrayWriter {
|
||||
public:
|
||||
JsonArrayWriter( std::ostream& os );
|
||||
JsonArrayWriter( std::ostream& os, std::uint64_t indent_level );
|
||||
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||
|
||||
JsonArrayWriter( JsonArrayWriter&& source ) noexcept;
|
||||
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#ifndef CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||
#define CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||
|
||||
#if !defined( __has_cpp_attribute )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND
|
||||
#elif __has_cpp_attribute( msvc::lifetimebound )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]]
|
||||
#elif __has_cpp_attribute( clang::lifetimebound )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
|
||||
#elif __has_cpp_attribute( lifetimebound )
|
||||
# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]]
|
||||
#else
|
||||
# define CATCH_ATTR_LIFETIMEBOUND
|
||||
#endif
|
||||
|
||||
#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||
@@ -7,20 +7,24 @@
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <catch2/internal/catch_message_info.hpp>
|
||||
#include <catch2/internal/catch_thread_local.hpp>
|
||||
|
||||
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
|
||||
|
||||
@@ -37,8 +37,6 @@ namespace Catch {
|
||||
bool operator < (MessageInfo const& other) const {
|
||||
return sequence < other.sequence;
|
||||
}
|
||||
private:
|
||||
static thread_local unsigned int globalCount;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <catch2/internal/catch_output_redirect.hpp>
|
||||
#include <catch2/internal/catch_assertion_handler.hpp>
|
||||
#include <catch2/internal/catch_test_failure_exception.hpp>
|
||||
#include <catch2/internal/catch_thread_local.hpp>
|
||||
#include <catch2/internal/catch_result_type.hpp>
|
||||
|
||||
#include <cassert>
|
||||
@@ -129,12 +130,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;
|
||||
}
|
||||
}
|
||||
@@ -177,27 +174,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
|
||||
@@ -214,6 +282,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() {
|
||||
@@ -336,21 +411,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
|
||||
@@ -639,10 +712,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);
|
||||
@@ -810,25 +883,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) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
|
||||
#define CATCH_STRING_MANIP_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -28,10 +29,10 @@ namespace Catch {
|
||||
//! Returns a new string without whitespace at the start/end
|
||||
std::string trim( std::string const& str );
|
||||
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
|
||||
StringRef trim(StringRef ref);
|
||||
StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND );
|
||||
|
||||
// !!! Be aware, returns refs into original string - make sure original string outlives them
|
||||
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
|
||||
std::vector<StringRef> splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter );
|
||||
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
|
||||
|
||||
/**
|
||||
@@ -49,7 +50,7 @@ namespace Catch {
|
||||
StringRef m_label;
|
||||
|
||||
public:
|
||||
constexpr pluralise(std::uint64_t count, StringRef label):
|
||||
constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND):
|
||||
m_count(count),
|
||||
m_label(label)
|
||||
{}
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
#ifndef CATCH_STRINGREF_HPP_INCLUDED
|
||||
#define CATCH_STRINGREF_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <cassert>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Catch {
|
||||
@@ -36,14 +37,16 @@ namespace Catch {
|
||||
public: // construction
|
||||
constexpr StringRef() noexcept = default;
|
||||
|
||||
StringRef( char const* rawChars ) noexcept;
|
||||
StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept;
|
||||
|
||||
constexpr StringRef( char const* rawChars, size_type size ) noexcept
|
||||
constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND,
|
||||
size_type size ) noexcept
|
||||
: m_start( rawChars ),
|
||||
m_size( size )
|
||||
{}
|
||||
|
||||
StringRef( std::string const& stdString ) noexcept
|
||||
StringRef(
|
||||
std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept
|
||||
: m_start( stdString.c_str() ),
|
||||
m_size( stdString.size() )
|
||||
{}
|
||||
@@ -89,7 +92,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
// Returns the current start pointer. May not be null-terminated.
|
||||
constexpr char const* data() const noexcept {
|
||||
constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND {
|
||||
return m_start;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,9 +172,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;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
||||
#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
#include <catch2/internal/catch_source_line_info.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
@@ -48,7 +49,7 @@ namespace TestCaseTracking {
|
||||
StringRef name;
|
||||
SourceLineInfo location;
|
||||
|
||||
constexpr NameAndLocationRef( StringRef name_,
|
||||
constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND,
|
||||
SourceLineInfo location_ ):
|
||||
name( name_ ), location( location_ ) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
#ifndef CATCH_THREAD_LOCAL_HPP_INCLUDED
|
||||
#define CATCH_THREAD_LOCAL_HPP_INCLUDED
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
|
||||
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS )
|
||||
#define CATCH_INTERNAL_THREAD_LOCAL thread_local
|
||||
#else
|
||||
#define CATCH_INTERNAL_THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
#endif // CATCH_THREAD_LOCAL_HPP_INCLUDED
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <catch2/catch_user_config.hpp>
|
||||
|
||||
#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
|
||||
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS )
|
||||
# include <atomic>
|
||||
# include <mutex>
|
||||
#endif
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
|
||||
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS )
|
||||
using Mutex = std::mutex;
|
||||
using LockGuard = std::lock_guard<std::mutex>;
|
||||
struct AtomicCounts {
|
||||
std::atomic<std::uint64_t> passed = 0;
|
||||
std::atomic<std::uint64_t> failed = 0;
|
||||
std::atomic<std::uint64_t> failedButOk = 0;
|
||||
std::atomic<std::uint64_t> skipped = 0;
|
||||
std::atomic<std::uint64_t> passed{ 0 };
|
||||
std::atomic<std::uint64_t> failed{ 0 };
|
||||
std::atomic<std::uint64_t> failedButOk{ 0 };
|
||||
std::atomic<std::uint64_t> skipped{ 0 };
|
||||
};
|
||||
#else // ^^ Use actual mutex, lock and atomics
|
||||
// vv Dummy implementations for single-thread performance
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef CATCH_XMLWRITER_HPP_INCLUDED
|
||||
#define CATCH_XMLWRITER_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
|
||||
@@ -43,7 +44,7 @@ namespace Catch {
|
||||
public:
|
||||
enum ForWhat { ForTextNodes, ForAttributes };
|
||||
|
||||
constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ):
|
||||
constexpr XmlEncode( StringRef str CATCH_ATTR_LIFETIMEBOUND, ForWhat forWhat = ForTextNodes ):
|
||||
m_str( str ), m_forWhat( forWhat ) {}
|
||||
|
||||
|
||||
@@ -61,7 +62,7 @@ namespace Catch {
|
||||
|
||||
class ScopedElement {
|
||||
public:
|
||||
ScopedElement( XmlWriter* writer, XmlFormatting fmt );
|
||||
ScopedElement( XmlWriter* writer CATCH_ATTR_LIFETIMEBOUND, XmlFormatting fmt );
|
||||
|
||||
ScopedElement( ScopedElement&& other ) noexcept;
|
||||
ScopedElement& operator=( ScopedElement&& other ) noexcept;
|
||||
@@ -93,7 +94,7 @@ namespace Catch {
|
||||
XmlFormatting m_fmt;
|
||||
};
|
||||
|
||||
XmlWriter( std::ostream& os );
|
||||
XmlWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||
~XmlWriter();
|
||||
|
||||
XmlWriter( XmlWriter const& ) = delete;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <catch2/matchers/internal/catch_matchers_impl.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -79,11 +80,15 @@ namespace Matchers {
|
||||
return description;
|
||||
}
|
||||
|
||||
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
||||
friend MatchAllOf operator&&( MatchAllOf&& lhs,
|
||||
MatcherBase<ArgT> const& rhs
|
||||
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
lhs.m_matchers.push_back(&rhs);
|
||||
return CATCH_MOVE(lhs);
|
||||
}
|
||||
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
|
||||
friend MatchAllOf
|
||||
operator&&( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAllOf&& rhs ) {
|
||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||
return CATCH_MOVE(rhs);
|
||||
}
|
||||
@@ -131,11 +136,15 @@ namespace Matchers {
|
||||
return description;
|
||||
}
|
||||
|
||||
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
||||
friend MatchAnyOf operator||( MatchAnyOf&& lhs,
|
||||
MatcherBase<ArgT> const& rhs
|
||||
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
lhs.m_matchers.push_back(&rhs);
|
||||
return CATCH_MOVE(lhs);
|
||||
}
|
||||
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
|
||||
friend MatchAnyOf
|
||||
operator||( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAnyOf&& rhs ) {
|
||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||
return CATCH_MOVE(rhs);
|
||||
}
|
||||
@@ -155,7 +164,8 @@ namespace Matchers {
|
||||
MatcherBase<ArgT> const& m_underlyingMatcher;
|
||||
|
||||
public:
|
||||
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
|
||||
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher
|
||||
CATCH_ATTR_LIFETIMEBOUND ):
|
||||
m_underlyingMatcher( underlyingMatcher )
|
||||
{}
|
||||
|
||||
@@ -171,16 +181,22 @@ namespace Matchers {
|
||||
} // namespace Detail
|
||||
|
||||
template <typename T>
|
||||
Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
||||
Detail::MatchAllOf<T>
|
||||
operator&&( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchAllOf<T>{} && lhs && rhs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
||||
Detail::MatchAnyOf<T>
|
||||
operator||( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchAnyOf<T>{} || lhs || rhs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) {
|
||||
Detail::MatchNotOf<T>
|
||||
operator!( MatcherBase<T> const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchNotOf<T>{ matcher };
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||
#include <catch2/internal/catch_logical_traits.hpp>
|
||||
|
||||
#include <array>
|
||||
@@ -114,7 +115,8 @@ namespace Matchers {
|
||||
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
||||
MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
|
||||
|
||||
MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
||||
MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
|
||||
: m_matchers{ {std::addressof(matchers)...} } {}
|
||||
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||
|
||||
template<typename Arg>
|
||||
@@ -136,8 +138,8 @@ namespace Matchers {
|
||||
template<typename... MatchersRHS>
|
||||
friend
|
||||
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
||||
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAllOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
|
||||
@@ -145,8 +147,8 @@ namespace Matchers {
|
||||
template<typename MatcherRHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
||||
MatcherRHS const& rhs) {
|
||||
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
|
||||
}
|
||||
|
||||
@@ -154,8 +156,8 @@ namespace Matchers {
|
||||
template<typename MatcherLHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
|
||||
MatcherLHS const& lhs,
|
||||
MatchAllOfGeneric<MatcherTs...>&& rhs) {
|
||||
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAllOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
};
|
||||
@@ -169,7 +171,8 @@ namespace Matchers {
|
||||
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
|
||||
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
|
||||
|
||||
MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
||||
MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
|
||||
: m_matchers{ {std::addressof(matchers)...} } {}
|
||||
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||
|
||||
template<typename Arg>
|
||||
@@ -190,8 +193,8 @@ namespace Matchers {
|
||||
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
||||
template<typename... MatchersRHS>
|
||||
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
||||
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAnyOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
|
||||
@@ -199,8 +202,8 @@ namespace Matchers {
|
||||
template<typename MatcherRHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
||||
MatcherRHS const& rhs) {
|
||||
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
|
||||
}
|
||||
|
||||
@@ -208,8 +211,8 @@ namespace Matchers {
|
||||
template<typename MatcherLHS>
|
||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
|
||||
MatcherLHS const& lhs,
|
||||
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
|
||||
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatchAnyOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND) {
|
||||
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
||||
}
|
||||
};
|
||||
@@ -225,7 +228,8 @@ namespace Matchers {
|
||||
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
|
||||
MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
|
||||
|
||||
explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {}
|
||||
explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND)
|
||||
: m_matcher{matcher} {}
|
||||
|
||||
template<typename Arg>
|
||||
bool match(Arg&& arg) const {
|
||||
@@ -237,7 +241,9 @@ namespace Matchers {
|
||||
}
|
||||
|
||||
//! Negating negation can just unwrap and return underlying matcher
|
||||
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
|
||||
friend MatcherT const&
|
||||
operator!( MatchNotOfGeneric<MatcherT> const& matcher
|
||||
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return matcher.m_matcher;
|
||||
}
|
||||
};
|
||||
@@ -247,20 +253,22 @@ namespace Matchers {
|
||||
// compose only generic matchers
|
||||
template<typename MatcherLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
|
||||
operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
||||
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename MatcherLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
|
||||
operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
||||
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
//! Wrap provided generic matcher in generic negator
|
||||
template<typename MatcherT>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
|
||||
operator ! (MatcherT const& matcher) {
|
||||
operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
|
||||
}
|
||||
|
||||
@@ -268,25 +276,29 @@ namespace Matchers {
|
||||
// compose mixed generic and non-generic matchers
|
||||
template<typename MatcherLHS, typename ArgRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
||||
operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
||||
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename ArgLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
||||
operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
||||
operator&&( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename MatcherLHS, typename ArgRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
||||
operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
||||
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
template<typename ArgLHS, typename MatcherRHS>
|
||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
||||
operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
||||
operator||( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||
return { lhs, rhs };
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ internal_headers = [
|
||||
'internal/catch_jsonwriter.hpp',
|
||||
'internal/catch_lazy_expr.hpp',
|
||||
'internal/catch_leak_detector.hpp',
|
||||
'internal/catch_lifetimebound.hpp',
|
||||
'internal/catch_list.hpp',
|
||||
'internal/catch_logical_traits.hpp',
|
||||
'internal/catch_message_info.hpp',
|
||||
@@ -147,6 +148,7 @@ internal_headers = [
|
||||
'internal/catch_test_registry.hpp',
|
||||
'internal/catch_test_spec_parser.hpp',
|
||||
'internal/catch_textflow.hpp',
|
||||
'internal/catch_thread_local.hpp',
|
||||
'internal/catch_thread_support.hpp',
|
||||
'internal/catch_to_string.hpp',
|
||||
'internal/catch_uncaught_exceptions.hpp',
|
||||
|
||||
+14
-21
@@ -310,38 +310,23 @@ set_tests_properties(UnmatchedOutputFilter
|
||||
PASS_REGULAR_EXPRESSION "No test cases matched '\\[this-tag-does-not-exist\\]'"
|
||||
)
|
||||
|
||||
add_test(NAME FilteredSection-1 COMMAND $<TARGET_FILE:SelfTest> \#1394 -c RunSection)
|
||||
set_tests_properties(FilteredSection-1 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
||||
add_test(NAME FilteredSection-2 COMMAND $<TARGET_FILE:SelfTest> \#1394\ nested -c NestedRunSection -c s1)
|
||||
set_tests_properties(FilteredSection-2 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
||||
add_test(NAME FilteredSections::SimpleExample::1 COMMAND $<TARGET_FILE:SelfTest> \#1394 -c RunSection)
|
||||
set_tests_properties(FilteredSections::SimpleExample::1 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
||||
add_test(NAME FilteredSections::SimpleExample::2 COMMAND $<TARGET_FILE:SelfTest> \#1394\ nested -c NestedRunSection -c s1)
|
||||
set_tests_properties(FilteredSections::SimpleExample::2 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
FilteredSection::GeneratorsDontCauseInfiniteLoop-1
|
||||
FilteredSections::GeneratorsDontCauseInfiniteLoop
|
||||
COMMAND
|
||||
$<TARGET_FILE:SelfTest> "#2025: original repro" -c "fov_0"
|
||||
)
|
||||
set_tests_properties(FilteredSection::GeneratorsDontCauseInfiniteLoop-1
|
||||
set_tests_properties(FilteredSections::GeneratorsDontCauseInfiniteLoop
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "inside with fov: 0" # This should happen
|
||||
FAIL_REGULAR_EXPRESSION "inside with fov: 1" # This would mean there was no filtering
|
||||
)
|
||||
|
||||
# GENERATE between filtered sections (both are selected)
|
||||
add_test(
|
||||
NAME
|
||||
FilteredSection::GeneratorsDontCauseInfiniteLoop-2
|
||||
COMMAND
|
||||
$<TARGET_FILE:SelfTest> "#2025: same-level sections"
|
||||
-c "A"
|
||||
-c "B"
|
||||
--colour-mode none
|
||||
)
|
||||
set_tests_properties(FilteredSection::GeneratorsDontCauseInfiniteLoop-2
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "All tests passed \\(4 assertions in 1 test case\\)"
|
||||
)
|
||||
|
||||
add_test(NAME ApprovalTests
|
||||
COMMAND
|
||||
Python3::Interpreter
|
||||
@@ -694,6 +679,14 @@ set_tests_properties("Bazel::RngSeedEnvVar::MalformedValueIsIgnored"
|
||||
PASS_REGULAR_EXPRESSION "Randomness seeded to: 17171717"
|
||||
)
|
||||
|
||||
add_test(NAME "FilteredSections::DifferentSimpleFilters"
|
||||
COMMAND
|
||||
Python3::Interpreter "${CMAKE_CURRENT_LIST_DIR}/TestScripts/testSectionFiltering.py" $<TARGET_FILE:SelfTest>
|
||||
)
|
||||
set_tests_properties("FilteredSections::DifferentSimpleFilters"
|
||||
PROPERTIES
|
||||
LABELS "uses-python"
|
||||
)
|
||||
|
||||
list(APPEND CATCH_TEST_TARGETS SelfTest)
|
||||
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
|
||||
|
||||
@@ -177,6 +177,18 @@ set_tests_properties(DeferredStaticChecks
|
||||
PASS_REGULAR_EXPRESSION "test cases: 1 \\| 1 failed\nassertions: 3 \\| 3 failed"
|
||||
)
|
||||
|
||||
add_executable(MixingClearedAndUnclearedMessages ${TESTS_DIR}/X06-MixingClearedAndUnclearedMessages.cpp)
|
||||
target_link_libraries(MixingClearedAndUnclearedMessages PRIVATE Catch2WithMain)
|
||||
|
||||
add_test(NAME MixingClearedAndUnclearedMessages
|
||||
COMMAND
|
||||
MixingClearedAndUnclearedMessages
|
||||
-r compact)
|
||||
set_tests_properties(MixingClearedAndUnclearedMessages
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION ": false with 1 message: 'b'"
|
||||
)
|
||||
|
||||
add_executable(FallbackStringifier ${TESTS_DIR}/X10-FallbackStringifier.cpp)
|
||||
target_compile_definitions(FallbackStringifier PRIVATE CATCH_CONFIG_FALLBACK_STRINGIFIER=fallbackStringifier)
|
||||
target_link_libraries(FallbackStringifier Catch2WithMain)
|
||||
@@ -528,6 +540,7 @@ set(EXTRA_TEST_BINARIES
|
||||
DuplicatedTestCases-DuplicatedTestCaseMethods
|
||||
NoTests
|
||||
ListenersGetEventsBeforeReporters
|
||||
MixingClearedAndUnclearedMessages
|
||||
# DebugBreakMacros
|
||||
)
|
||||
|
||||
@@ -553,3 +566,26 @@ set_tests_properties(AmalgamatedFileTest
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "All tests passed \\(14 assertions in 3 test cases\\)"
|
||||
)
|
||||
|
||||
add_executable(ThreadSafetyTests
|
||||
${TESTS_DIR}/X94-ThreadSafetyTests.cpp
|
||||
)
|
||||
target_link_libraries(ThreadSafetyTests Catch2_buildall_interface)
|
||||
target_compile_definitions(ThreadSafetyTests PUBLIC CATCH_CONFIG_THREAD_SAFE_ASSERTIONS)
|
||||
|
||||
add_test(NAME ThreadSafetyTests::ScopedMessagesAndAssertions
|
||||
COMMAND ThreadSafetyTests -r compact "Failed REQUIRE in the main thread is fine"
|
||||
)
|
||||
set_tests_properties(ThreadSafetyTests::ScopedMessagesAndAssertions
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "assertions: 801 \\| 400 passed \\| 401 failed as expected"
|
||||
RUN_SERIAL ON
|
||||
)
|
||||
add_test(NAME ThreadSafetyTests::UnscopedMessagesAndAssertions
|
||||
COMMAND ThreadSafetyTests -r compact "Using unscoped messages in sibling threads"
|
||||
)
|
||||
set_tests_properties(ThreadSafetyTests::UnscopedMessagesAndAssertions
|
||||
PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "assertions: 401 \\| 401 failed as expected"
|
||||
RUN_SERIAL ON
|
||||
)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
/**\file
|
||||
* Checks that when we use up an unscoped message (e.g. `UNSCOPED_INFO`),
|
||||
* with an assertion, and then add another message later, it will be
|
||||
* properly reported with later failing assertion.
|
||||
*
|
||||
* This needs separate binary to avoid the main test binary's validating
|
||||
* listener, which disables the assertion fast path.
|
||||
*/
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
TEST_CASE(
|
||||
"Delayed unscoped message clearing does not catch newly inserted messages",
|
||||
"[messages][unscoped][!shouldfail]" ) {
|
||||
UNSCOPED_INFO( "a" );
|
||||
REQUIRE( true );
|
||||
UNSCOPED_INFO( "b" );
|
||||
REQUIRE( false );
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
// Copyright Catch2 Authors
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
/**\file
|
||||
* Test that assertions and messages are thread-safe.
|
||||
*
|
||||
* This is done by spamming assertions and messages on multiple subthreads.
|
||||
* In manual, this reliably causes segfaults if the test is linked against
|
||||
* a non-thread-safe version of Catch2.
|
||||
*
|
||||
* The CTest test definition should also verify that the final assertion
|
||||
* count is correct.
|
||||
*/
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
TEST_CASE( "Failed REQUIRE in the main thread is fine", "[!shouldfail]" ) {
|
||||
std::vector<std::thread> threads;
|
||||
for ( size_t t = 0; t < 4; ++t) {
|
||||
threads.emplace_back( [t]() {
|
||||
CAPTURE(t);
|
||||
for (size_t i = 0; i < 100; ++i) {
|
||||
CAPTURE(i);
|
||||
CHECK( false );
|
||||
CHECK( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
REQUIRE( false );
|
||||
}
|
||||
|
||||
TEST_CASE( "Using unscoped messages in sibling threads", "[!shouldfail]" ) {
|
||||
std::vector<std::thread> threads;
|
||||
for ( size_t t = 0; t < 4; ++t) {
|
||||
threads.emplace_back( [t]() {
|
||||
UNSCOPED_INFO("thread " << t << " start");
|
||||
for (size_t i = 0; i < 100; ++i) {
|
||||
for (size_t j = 0; j < 4; ++j) {
|
||||
UNSCOPED_INFO("t=" << i << ", " << j);
|
||||
}
|
||||
CHECK( false );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
REQUIRE( false );
|
||||
}
|
||||
@@ -138,6 +138,7 @@ Nor would this
|
||||
:test-result: FAIL Custom exceptions can be translated when testing for throwing as something else
|
||||
:test-result: FAIL Custom std-exceptions can be custom translated
|
||||
:test-result: PASS Default scale is invisible to comparison
|
||||
:test-result: XFAIL Delayed unscoped message clearing does not catch newly inserted messages
|
||||
:test-result: PASS Directly creating an EnumInfo
|
||||
:test-result: SKIP Empty generators can SKIP in constructor
|
||||
:test-result: PASS Empty stream name opens cout stream
|
||||
|
||||
@@ -136,6 +136,7 @@
|
||||
:test-result: FAIL Custom exceptions can be translated when testing for throwing as something else
|
||||
:test-result: FAIL Custom std-exceptions can be custom translated
|
||||
:test-result: PASS Default scale is invisible to comparison
|
||||
:test-result: XFAIL Delayed unscoped message clearing does not catch newly inserted messages
|
||||
:test-result: PASS Directly creating an EnumInfo
|
||||
:test-result: SKIP Empty generators can SKIP in constructor
|
||||
:test-result: PASS Empty stream name opens cout stream
|
||||
|
||||
@@ -562,6 +562,8 @@ Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'c
|
||||
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'custom std exception'
|
||||
Approx.tests.cpp:<line number>: passed: 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 )
|
||||
Approx.tests.cpp:<line number>: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 )
|
||||
Message.tests.cpp:<line number>: passed: true with 1 message: 'a'
|
||||
Message.tests.cpp:<line number>: failed: false with 1 message: 'b'
|
||||
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(0) == "Value1" for: Value1 == "Value1"
|
||||
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(1) == "Value2" for: Value2 == "Value2"
|
||||
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **}
|
||||
@@ -2888,7 +2890,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
|
||||
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
|
||||
Misc.tests.cpp:<line number>: passed:
|
||||
Misc.tests.cpp:<line number>: passed:
|
||||
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
|
||||
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
|
||||
test cases: 436 | 317 passed | 95 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2305 | 2106 passed | 157 failed | 42 failed as expected
|
||||
|
||||
|
||||
|
||||
@@ -560,6 +560,8 @@ Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'c
|
||||
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'custom std exception'
|
||||
Approx.tests.cpp:<line number>: passed: 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 )
|
||||
Approx.tests.cpp:<line number>: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 )
|
||||
Message.tests.cpp:<line number>: passed: true with 1 message: 'a'
|
||||
Message.tests.cpp:<line number>: failed: false with 1 message: 'b'
|
||||
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(0) == "Value1" for: Value1 == "Value1"
|
||||
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(1) == "Value2" for: Value2 == "Value2"
|
||||
ToString.tests.cpp:<line number>: passed: enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **}
|
||||
@@ -2877,7 +2879,7 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
|
||||
InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
|
||||
Misc.tests.cpp:<line number>: passed:
|
||||
Misc.tests.cpp:<line number>: passed:
|
||||
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
|
||||
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
|
||||
test cases: 436 | 317 passed | 95 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2305 | 2106 passed | 157 failed | 42 failed as expected
|
||||
|
||||
|
||||
|
||||
@@ -450,6 +450,17 @@ Exception.tests.cpp:<line number>: FAILED:
|
||||
due to unexpected exception with message:
|
||||
custom std exception
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Delayed unscoped message clearing does not catch newly inserted messages
|
||||
-------------------------------------------------------------------------------
|
||||
Message.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Message.tests.cpp:<line number>: FAILED:
|
||||
REQUIRE( false )
|
||||
with message:
|
||||
b
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Empty generators can SKIP in constructor
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -1719,6 +1730,6 @@ due to unexpected exception with message:
|
||||
Why would you throw a std::string?
|
||||
|
||||
===============================================================================
|
||||
test cases: 435 | 335 passed | 76 failed | 7 skipped | 17 failed as expected
|
||||
assertions: 2282 | 2105 passed | 136 failed | 41 failed as expected
|
||||
test cases: 436 | 335 passed | 76 failed | 7 skipped | 18 failed as expected
|
||||
assertions: 2284 | 2106 passed | 136 failed | 42 failed as expected
|
||||
|
||||
|
||||
@@ -4148,6 +4148,22 @@ Approx.tests.cpp:<line number>: PASSED:
|
||||
with expansion:
|
||||
0.00001 != Approx( 0.0000001 )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Delayed unscoped message clearing does not catch newly inserted messages
|
||||
-------------------------------------------------------------------------------
|
||||
Message.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Message.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( true )
|
||||
with message:
|
||||
a
|
||||
|
||||
Message.tests.cpp:<line number>: FAILED:
|
||||
REQUIRE( false )
|
||||
with message:
|
||||
b
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Directly creating an EnumInfo
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -19295,6 +19311,6 @@ Misc.tests.cpp:<line number>
|
||||
Misc.tests.cpp:<line number>: PASSED:
|
||||
|
||||
===============================================================================
|
||||
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
|
||||
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
|
||||
test cases: 436 | 317 passed | 95 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2305 | 2106 passed | 157 failed | 42 failed as expected
|
||||
|
||||
|
||||
@@ -4146,6 +4146,22 @@ Approx.tests.cpp:<line number>: PASSED:
|
||||
with expansion:
|
||||
0.00001 != Approx( 0.0000001 )
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Delayed unscoped message clearing does not catch newly inserted messages
|
||||
-------------------------------------------------------------------------------
|
||||
Message.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Message.tests.cpp:<line number>: PASSED:
|
||||
REQUIRE( true )
|
||||
with message:
|
||||
a
|
||||
|
||||
Message.tests.cpp:<line number>: FAILED:
|
||||
REQUIRE( false )
|
||||
with message:
|
||||
b
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Directly creating an EnumInfo
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -19284,6 +19300,6 @@ Misc.tests.cpp:<line number>
|
||||
Misc.tests.cpp:<line number>: PASSED:
|
||||
|
||||
===============================================================================
|
||||
test cases: 435 | 317 passed | 95 failed | 6 skipped | 17 failed as expected
|
||||
assertions: 2303 | 2105 passed | 157 failed | 41 failed as expected
|
||||
test cases: 436 | 317 passed | 95 failed | 6 skipped | 18 failed as expected
|
||||
assertions: 2305 | 2106 passed | 157 failed | 42 failed as expected
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuitesloose text artifact
|
||||
>
|
||||
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2315" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2317" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<properties>
|
||||
<property name="random-seed" value="1"/>
|
||||
<property name="filters" value=""*" ~[!nonportable] ~[!benchmark] ~[approvals]"/>
|
||||
@@ -547,6 +547,15 @@ at Exception.tests.cpp:<line number>
|
||||
</error>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Default scale is invisible to comparison" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="Delayed unscoped message clearing does not catch newly inserted messages" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure message="false" type="REQUIRE">
|
||||
FAILED:
|
||||
REQUIRE( false )
|
||||
b
|
||||
at Message.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Directly creating an EnumInfo" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="Empty generators can SKIP in constructor" time="{duration}" status="run">
|
||||
<skipped type="SKIP">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites>
|
||||
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2315" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<testsuite name="<exe-name>" errors="17" failures="140" skipped="12" tests="2317" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<properties>
|
||||
<property name="random-seed" value="1"/>
|
||||
<property name="filters" value=""*" ~[!nonportable] ~[!benchmark] ~[approvals]"/>
|
||||
@@ -546,6 +546,15 @@ at Exception.tests.cpp:<line number>
|
||||
</error>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Default scale is invisible to comparison" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="Delayed unscoped message clearing does not catch newly inserted messages" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure message="false" type="REQUIRE">
|
||||
FAILED:
|
||||
REQUIRE( false )
|
||||
b
|
||||
at Message.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Directly creating an EnumInfo" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="Empty generators can SKIP in constructor" time="{duration}" status="run">
|
||||
<skipped type="SKIP">
|
||||
|
||||
@@ -1699,6 +1699,14 @@ at Message.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testCase>
|
||||
<testCase name="Captures outlive section end/Dummy section" duration="{duration}"/>
|
||||
<testCase name="Delayed unscoped message clearing does not catch newly inserted messages" duration="{duration}">
|
||||
<skipped message="REQUIRE(false)">
|
||||
FAILED:
|
||||
REQUIRE( false )
|
||||
b
|
||||
at Message.tests.cpp:<line number>
|
||||
</skipped>
|
||||
</testCase>
|
||||
<testCase name="FAIL aborts the test" duration="{duration}">
|
||||
<failure message="FAIL()">
|
||||
FAILED:
|
||||
|
||||
@@ -1698,6 +1698,14 @@ at Message.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testCase>
|
||||
<testCase name="Captures outlive section end/Dummy section" duration="{duration}"/>
|
||||
<testCase name="Delayed unscoped message clearing does not catch newly inserted messages" duration="{duration}">
|
||||
<skipped message="REQUIRE(false)">
|
||||
FAILED:
|
||||
REQUIRE( false )
|
||||
b
|
||||
at Message.tests.cpp:<line number>
|
||||
</skipped>
|
||||
</testCase>
|
||||
<testCase name="FAIL aborts the test" duration="{duration}">
|
||||
<failure message="FAIL()">
|
||||
FAILED:
|
||||
|
||||
@@ -1024,6 +1024,10 @@ not ok {test-number} - unexpected exception with message: 'custom std exception'
|
||||
ok {test-number} - 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 )
|
||||
# Default scale is invisible to comparison
|
||||
ok {test-number} - std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 )
|
||||
# Delayed unscoped message clearing does not catch newly inserted messages
|
||||
ok {test-number} - true with 1 message: 'a'
|
||||
# Delayed unscoped message clearing does not catch newly inserted messages
|
||||
not ok {test-number} - false with 1 message: 'b'
|
||||
# Directly creating an EnumInfo
|
||||
ok {test-number} - enumInfo->lookup(0) == "Value1" for: Value1 == "Value1"
|
||||
# Directly creating an EnumInfo
|
||||
@@ -4627,5 +4631,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
|
||||
ok {test-number} -
|
||||
# xmlentitycheck
|
||||
ok {test-number} -
|
||||
1..2315
|
||||
1..2317
|
||||
|
||||
|
||||
@@ -1022,6 +1022,10 @@ not ok {test-number} - unexpected exception with message: 'custom std exception'
|
||||
ok {test-number} - 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 )
|
||||
# Default scale is invisible to comparison
|
||||
ok {test-number} - std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 )
|
||||
# Delayed unscoped message clearing does not catch newly inserted messages
|
||||
ok {test-number} - true with 1 message: 'a'
|
||||
# Delayed unscoped message clearing does not catch newly inserted messages
|
||||
not ok {test-number} - false with 1 message: 'b'
|
||||
# Directly creating an EnumInfo
|
||||
ok {test-number} - enumInfo->lookup(0) == "Value1" for: Value1 == "Value1"
|
||||
# Directly creating an EnumInfo
|
||||
@@ -4616,5 +4620,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
|
||||
ok {test-number} -
|
||||
# xmlentitycheck
|
||||
ok {test-number} -
|
||||
1..2315
|
||||
1..2317
|
||||
|
||||
|
||||
@@ -322,6 +322,9 @@
|
||||
##teamcity[testFinished name='Custom std-exceptions can be custom translated' duration="{duration}"]
|
||||
##teamcity[testStarted name='Default scale is invisible to comparison']
|
||||
##teamcity[testFinished name='Default scale is invisible to comparison' duration="{duration}"]
|
||||
##teamcity[testStarted name='Delayed unscoped message clearing does not catch newly inserted messages']
|
||||
##teamcity[testIgnored name='Delayed unscoped message clearing does not catch newly inserted messages' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexpression failed with message:|n "b"|n REQUIRE( false )|nwith expansion:|n false|n- failure ignore as test marked as |'ok to fail|'|n']
|
||||
##teamcity[testFinished name='Delayed unscoped message clearing does not catch newly inserted messages' duration="{duration}"]
|
||||
##teamcity[testStarted name='Directly creating an EnumInfo']
|
||||
##teamcity[testFinished name='Directly creating an EnumInfo' duration="{duration}"]
|
||||
##teamcity[testStarted name='Empty generators can SKIP in constructor']
|
||||
|
||||
@@ -322,6 +322,9 @@
|
||||
##teamcity[testFinished name='Custom std-exceptions can be custom translated' duration="{duration}"]
|
||||
##teamcity[testStarted name='Default scale is invisible to comparison']
|
||||
##teamcity[testFinished name='Default scale is invisible to comparison' duration="{duration}"]
|
||||
##teamcity[testStarted name='Delayed unscoped message clearing does not catch newly inserted messages']
|
||||
##teamcity[testIgnored name='Delayed unscoped message clearing does not catch newly inserted messages' message='Message.tests.cpp:<line number>|n...............................................................................|n|nMessage.tests.cpp:<line number>|nexpression failed with message:|n "b"|n REQUIRE( false )|nwith expansion:|n false|n- failure ignore as test marked as |'ok to fail|'|n']
|
||||
##teamcity[testFinished name='Delayed unscoped message clearing does not catch newly inserted messages' duration="{duration}"]
|
||||
##teamcity[testStarted name='Directly creating an EnumInfo']
|
||||
##teamcity[testFinished name='Directly creating an EnumInfo' duration="{duration}"]
|
||||
##teamcity[testStarted name='Empty generators can SKIP in constructor']
|
||||
|
||||
@@ -4613,6 +4613,31 @@ C
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="Delayed unscoped message clearing does not catch newly inserted messages" tags="[!shouldfail][messages][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
<Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
a
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
<Original>
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
b
|
||||
</Info>
|
||||
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
<Original>
|
||||
false
|
||||
</Original>
|
||||
<Expanded>
|
||||
false
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="Directly creating an EnumInfo" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
|
||||
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
|
||||
<Original>
|
||||
@@ -22324,6 +22349,6 @@ Approx( -1.95996398454005449 )
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<OverallResults successes="2105" failures="157" expectedFailures="41" skips="12"/>
|
||||
<OverallResultsCases successes="317" failures="95" expectedFailures="17" skips="6"/>
|
||||
<OverallResults successes="2106" failures="157" expectedFailures="42" skips="12"/>
|
||||
<OverallResultsCases successes="317" failures="95" expectedFailures="18" skips="6"/>
|
||||
</Catch2TestRun>
|
||||
|
||||
@@ -4613,6 +4613,31 @@ C
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="Delayed unscoped message clearing does not catch newly inserted messages" tags="[!shouldfail][messages][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
<Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
a
|
||||
</Info>
|
||||
<Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
<Original>
|
||||
true
|
||||
</Original>
|
||||
<Expanded>
|
||||
true
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Info filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
b
|
||||
</Info>
|
||||
<Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
|
||||
<Original>
|
||||
false
|
||||
</Original>
|
||||
<Expanded>
|
||||
false
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<TestCase name="Directly creating an EnumInfo" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
|
||||
<Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
|
||||
<Original>
|
||||
@@ -22323,6 +22348,6 @@ Approx( -1.95996398454005449 )
|
||||
</Section>
|
||||
<OverallResult success="true" skips="0"/>
|
||||
</TestCase>
|
||||
<OverallResults successes="2105" failures="157" expectedFailures="41" skips="12"/>
|
||||
<OverallResultsCases successes="317" failures="95" expectedFailures="17" skips="6"/>
|
||||
<OverallResults successes="2106" failures="157" expectedFailures="42" skips="12"/>
|
||||
<OverallResultsCases successes="317" failures="95" expectedFailures="18" skips="6"/>
|
||||
</Catch2TestRun>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/internal/catch_enforce.hpp>
|
||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
||||
#include <catch2/internal/catch_optional.hpp>
|
||||
@@ -170,3 +171,64 @@ TEST_CASE( "Decomposer checks that the argument is 0 when handling "
|
||||
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE( "foo", "[approvals]" ) {
|
||||
SECTION( "A" ) {
|
||||
SECTION( "B1" ) { REQUIRE( true ); }
|
||||
SECTION( "B2" ) { REQUIRE( true ); }
|
||||
SECTION( "B3" ) { REQUIRE( true ); }
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE( "bar", "[approvals]" ) {
|
||||
REQUIRE( true );
|
||||
SECTION( "A" ) {
|
||||
SECTION( "B1" ) { REQUIRE( true ); }
|
||||
SECTION( "B2" ) { REQUIRE( true ); }
|
||||
SECTION( "B3" ) { REQUIRE( true ); }
|
||||
}
|
||||
REQUIRE( true );
|
||||
}
|
||||
|
||||
TEST_CASE( "baz", "[approvals]" ) {
|
||||
SECTION( "A" ) { REQUIRE( true ); }
|
||||
auto _ = GENERATE( 1, 2, 3 );
|
||||
(void)_;
|
||||
SECTION( "B" ) { REQUIRE( true ); }
|
||||
}
|
||||
|
||||
TEST_CASE( "qux", "[approvals]" ) {
|
||||
REQUIRE( true );
|
||||
SECTION( "A" ) { REQUIRE( true ); }
|
||||
auto _ = GENERATE( 1, 2, 3 );
|
||||
(void)_;
|
||||
SECTION( "B" ) { REQUIRE( true ); }
|
||||
REQUIRE( true );
|
||||
}
|
||||
|
||||
TEST_CASE( "corge", "[approvals]" ) {
|
||||
REQUIRE( true );
|
||||
SECTION( "A" ) {
|
||||
REQUIRE( true );
|
||||
}
|
||||
auto i = GENERATE( 1, 2, 3 );
|
||||
DYNAMIC_SECTION( "i=" << i ) {
|
||||
REQUIRE( true );
|
||||
}
|
||||
REQUIRE( true );
|
||||
}
|
||||
|
||||
TEST_CASE("grault", "[approvals]") {
|
||||
REQUIRE( true );
|
||||
SECTION( "A" ) {
|
||||
REQUIRE( true );
|
||||
}
|
||||
SECTION("B") {
|
||||
auto i = GENERATE( 1, 2, 3 );
|
||||
DYNAMIC_SECTION( "i=" << i ) {
|
||||
REQUIRE( true );
|
||||
}
|
||||
}
|
||||
REQUIRE( true );
|
||||
}
|
||||
|
||||
@@ -360,3 +360,12 @@ TEST_CASE( "Scoped message applies to all assertions in scope",
|
||||
CHECK( false );
|
||||
CHECK( false );
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Delayed unscoped message clearing does not catch newly inserted messages",
|
||||
"[messages][unscoped][!shouldfail]" ) {
|
||||
UNSCOPED_INFO( "a" );
|
||||
REQUIRE( true );
|
||||
UNSCOPED_INFO( "b" );
|
||||
REQUIRE( false );
|
||||
}
|
||||
|
||||
@@ -354,27 +354,9 @@ TEST_CASE("#1514: stderr/stdout is not captured in tests aborted by an exception
|
||||
FAIL("1514");
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE( "#2025: -c shouldn't cause infinite loop", "[sections][generators][regression][.approvals]" ) {
|
||||
SECTION( "Check cursor from buffer offset" ) {
|
||||
auto bufPos = GENERATE_REF( range( 0, 44 ) );
|
||||
WHEN( "Buffer position is " << bufPos ) { REQUIRE( 1 == 1 ); }
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("#2025: original repro", "[sections][generators][regression][.approvals]") {
|
||||
auto fov = GENERATE(true, false);
|
||||
DYNAMIC_SECTION("fov_" << fov) {
|
||||
std::cout << "inside with fov: " << fov << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("#2025: same-level sections", "[sections][generators][regression][.approvals]") {
|
||||
SECTION("A") {
|
||||
SUCCEED();
|
||||
}
|
||||
auto i = GENERATE(1, 2, 3);
|
||||
SECTION("B") {
|
||||
REQUIRE(i < 4);
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright Catch2 Authors
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at
|
||||
# https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
"""
|
||||
This test script verifies the behaviour of the legacy section filtering
|
||||
using `-c`, `--section` CLI parameters.
|
||||
|
||||
This is done by having a hardcoded set of test filter + section filter
|
||||
combinations, together with the expected number of assertions that will
|
||||
be run inside the test for given filter combo.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Tuple, List
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
def make_cli_filter(section_names: Tuple[str, ...]) -> List[str]:
|
||||
final = []
|
||||
for name in section_names:
|
||||
final.append('--section')
|
||||
final.append(name)
|
||||
return final
|
||||
|
||||
def run_one_test(binary_path: str, test_name: str, section_names: Tuple[str, ...], expected_assertions: int):
|
||||
cmd = [
|
||||
binary_path,
|
||||
'--reporter', 'xml',
|
||||
test_name
|
||||
]
|
||||
cmd.extend(make_cli_filter(section_names))
|
||||
try:
|
||||
ret = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
universal_newlines=True,
|
||||
)
|
||||
stdout = ret.stdout
|
||||
except subprocess.SubprocessError as ex:
|
||||
print('Could not run "{}"'.format(cmd))
|
||||
print("Return code: {}".format(ex.returncode))
|
||||
print("stdout: {}".format(ex.stdout))
|
||||
print("stderr: {}".format(ex.stderr))
|
||||
raise
|
||||
|
||||
try:
|
||||
tree = ET.fromstring(stdout)
|
||||
except ET.ParseError as ex:
|
||||
print("Invalid XML: '{}'".format(ex))
|
||||
raise
|
||||
|
||||
# Validate that we ran exactly 1 test case, and it passed
|
||||
test_case_stats = tree.find('OverallResultsCases')
|
||||
expected_testcases = {'successes' : '1', 'failures' : '0', 'expectedFailures': '0', 'skips': '0'}
|
||||
assert test_case_stats.attrib == expected_testcases, f'We did not run single passing test case as expected. {test_name}: {test_case_stats.attrib}'
|
||||
|
||||
# Validate that we got exactly the expected number of passing assertions
|
||||
expected_assertions = {'successes' : str(expected_assertions), 'failures' : '0', 'expectedFailures': '0', 'skips': '0'}
|
||||
assertion_stats = tree.find('OverallResults')
|
||||
assert assertion_stats.attrib == expected_assertions, f'"{test_name}": {assertion_stats.attrib} vs {expected_assertions}'
|
||||
|
||||
|
||||
# Inputs taken from issue #3038
|
||||
tests = {
|
||||
'foo': (
|
||||
((), 3),
|
||||
(('A',), 3),
|
||||
(('A', 'B'), 0),
|
||||
(('A', 'B1'), 1),
|
||||
(('A', 'B2'), 1),
|
||||
(('A', 'B1', 'B2'), 1),
|
||||
(('A', 'B2', 'XXXX'), 1),
|
||||
),
|
||||
'bar': (
|
||||
((), 9),
|
||||
(('A',), 9),
|
||||
(('A', 'B1'), 3),
|
||||
(('XXXX',), 2),
|
||||
(('B1',), 2),
|
||||
(('A', 'B1', 'B2'), 3),
|
||||
),
|
||||
'baz': (
|
||||
((), 4),
|
||||
(('A',), 1),
|
||||
(('A', 'B'), 1),
|
||||
(('A', 'XXXX'), 1),
|
||||
(('B',), 3),
|
||||
(('XXXX',), 0),
|
||||
),
|
||||
'qux': (
|
||||
((), 12),
|
||||
(('A',), 7),
|
||||
(('B',), 9),
|
||||
(('B', 'XXXX'), 9),
|
||||
(('XXXX',), 6),
|
||||
),
|
||||
'corge': (
|
||||
((), 12),
|
||||
(('i=2',), 7),
|
||||
(('i=3',), 7),
|
||||
),
|
||||
'grault': (
|
||||
((), 12),
|
||||
(('A',), 3),
|
||||
(('B',), 9),
|
||||
(('B', 'i=1'), 7),
|
||||
(('B', 'XXXX'), 6),
|
||||
),
|
||||
}
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Wrong number of arguments, expected just the path to Catch2 SelfTest binary")
|
||||
exit(1)
|
||||
|
||||
bin_path = os.path.abspath(sys.argv[1])
|
||||
|
||||
for test_filter, specs in tests.items():
|
||||
for section_path, expected_assertions in specs:
|
||||
run_one_test(bin_path, test_filter, section_path, expected_assertions)
|
||||
Reference in New Issue
Block a user