home
readme
diff
tree
note
docs
0commit ed67eed87393f9e03cc3bdd9739ebe668c6220ab
1Author: Mason Wright <mason@lakefox.net>
2Date: Sun Nov 2 14:10:05 2025 -0700
3
4 Doc changes and catch2 fix
5
6 Catch2 had a weird utf-8 issue.
7 I am working on switching to groff style docs so they can be viewed anwhere
8
9diff --git a/docs/content/adding/adding-css-properties.3 b/docs/content/adding/adding-css-properties.3
10old mode 100644
11new mode 100755
12diff --git a/docs/content/features/supported-css-selectors.1 b/docs/content/features/supported-css-selectors.1
13old mode 100644
14new mode 100755
15diff --git a/docs/manpage/grim.3 b/docs/manpage/grim.3
16old mode 100644
17new mode 100755
18diff --git a/docs/manpage/grim.html b/docs/manpage/grim.html
19old mode 100644
20new mode 100755
21diff --git a/include/catch_amalgamated.cpp b/include/catch_amalgamated.cpp
22old mode 100755
23new mode 100644
24index 6aa6788..892d637
25--- a/include/catch_amalgamated.cpp
26+++ b/include/catch_amalgamated.cpp
27@@ -6,8 +6,8 @@
28
29 // SPDX-License-Identifier: BSL-1.0
30
31-// Catch v3.5.4
32-// Generated: 2024-04-10 12:03:46.281848
33+// Catch v3.11.0
34+// Generated: 2025-09-30 10:49:12.549018
35 // ----------------------------------------------------------
36 // This file is an amalgamation of multiple different files.
37 // You probably shouldn't edit it directly.
38@@ -128,7 +128,13 @@ namespace Catch {
39 namespace Catch {
40 namespace Benchmark {
41 namespace Detail {
42+ struct do_nothing {
43+ void operator()() const {}
44+ };
45+
46 BenchmarkFunction::callable::~callable() = default;
47+ BenchmarkFunction::BenchmarkFunction():
48+ f( new model<do_nothing>{ {} } ){}
49 } // namespace Detail
50 } // namespace Benchmark
51 } // namespace Catch
52@@ -326,7 +332,7 @@ namespace Catch {
53 double diff = b - m;
54 return a + diff * diff;
55 } ) /
56- ( last - first );
57+ static_cast<double>( last - first );
58 return std::sqrt( variance );
59 }
60
61@@ -361,7 +367,7 @@ namespace Catch {
62 double* first,
63 double* last ) {
64 auto count = last - first;
65- double idx = (count - 1) * k / static_cast<double>(q);
66+ double idx = static_cast<double>((count - 1) * k) / static_cast<double>(q);
67 int j = static_cast<int>(idx);
68 double g = idx - j;
69 std::nth_element(first, first + j, last);
70@@ -464,10 +470,10 @@ namespace Catch {
71
72 double accel = sum_cubes / ( 6 * std::pow( sum_squares, 1.5 ) );
73 long n = static_cast<long>( resample.size() );
74- double prob_n =
75+ double prob_n = static_cast<double>(
76 std::count_if( resample.begin(),
77 resample.end(),
78- [point]( double x ) { return x < point; } ) /
79+ [point]( double x ) { return x < point; } )) /
80 static_cast<double>( n );
81 // degenerate case with uniform samples
82 if ( Catch::Detail::directCompare( prob_n, 0. ) ) {
83@@ -621,7 +627,7 @@ std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) {
84
85 namespace Catch {
86
87- AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression):
88+ AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const& _lazyExpression):
89 lazyExpression(_lazyExpression),
90 resultType(_resultType) {}
91
92@@ -819,6 +825,8 @@ namespace Catch {
93 m_data.reporterSpecifications.push_back( std::move( *parsed ) );
94 }
95
96+ // Reading bazel env vars can change some parts of the config data,
97+ // so we have to process the bazel env before acting on the config.
98 if ( enableBazelEnvSupport() ) {
99 readBazelEnvVars();
100 }
101@@ -883,6 +891,8 @@ namespace Catch {
102
103 bool Config::showHelp() const { return m_data.showHelp; }
104
105+ std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
106+
107 // IConfig interface
108 bool Config::allowThrows() const { return !m_data.noThrow; }
109 StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
110@@ -944,6 +954,26 @@ namespace Catch {
111 m_data.shardCount = bazelShardOptions->shardCount;
112 }
113 }
114+
115+ const auto bazelExitGuardFile = Detail::getEnv( "TEST_PREMATURE_EXIT_FILE" );
116+ if (bazelExitGuardFile) {
117+ m_data.prematureExitGuardFilePath = bazelExitGuardFile;
118+ }
119+
120+ const auto bazelRandomSeed = Detail::getEnv( "TEST_RANDOM_SEED" );
121+ if ( bazelRandomSeed ) {
122+ auto parsedSeed = parseUInt( bazelRandomSeed, 0 );
123+ if ( !parsedSeed ) {
124+ // Currently we handle issues with parsing other Bazel Env
125+ // options by warning and ignoring the issue. So we do the
126+ // same for random seed option.
127+ Catch::cerr()
128+ << "Warning: could not parse 'TEST_RANDOM_SEED' ('"
129+ << bazelRandomSeed << "') as proper seed.\n";
130+ } else {
131+ m_data.rngSeed = *parsedSeed;
132+ }
133+ }
134 }
135
136 } // end namespace Catch
137@@ -969,28 +999,26 @@ namespace Catch {
138
139
140 ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
141- m_info( CATCH_MOVE(builder.m_info) ) {
142- m_info.message = builder.m_stream.str();
143- getResultCapture().pushScopedMessage( m_info );
144+ m_messageId( builder.m_info.sequence ) {
145+ MessageInfo info( CATCH_MOVE( builder.m_info ) );
146+ info.message = builder.m_stream.str();
147+ IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
148 }
149
150 ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
151- m_info( CATCH_MOVE( old.m_info ) ) {
152+ m_messageId( old.m_messageId ) {
153 old.m_moved = true;
154 }
155
156 ScopedMessage::~ScopedMessage() {
157- if ( !uncaught_exceptions() && !m_moved ){
158- getResultCapture().popScopedMessage(m_info);
159- }
160+ if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
161 }
162
163
164 Capturer::Capturer( StringRef macroName,
165 SourceLineInfo const& lineInfo,
166 ResultWas::OfType resultType,
167- StringRef names ):
168- m_resultCapture( getResultCapture() ) {
169+ StringRef names ) {
170 auto trimmed = [&] (size_t start, size_t end) {
171 while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
172 ++start;
173@@ -1036,30 +1064,30 @@ namespace Catch {
174 case ',':
175 if (start != pos && openings.empty()) {
176 m_messages.emplace_back(macroName, lineInfo, resultType);
177- m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
178- m_messages.back().message += " := ";
179+ m_messages.back().message += trimmed(start, pos);
180+ m_messages.back().message += " := "_sr;
181 start = pos;
182 }
183+ break;
184 default:; // noop
185 }
186 }
187 assert(openings.empty() && "Mismatched openings");
188 m_messages.emplace_back(macroName, lineInfo, resultType);
189- m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
190- m_messages.back().message += " := ";
191+ m_messages.back().message += trimmed(start, names.size() - 1);
192+ m_messages.back().message += " := "_sr;
193 }
194 Capturer::~Capturer() {
195- if ( !uncaught_exceptions() ){
196- assert( m_captured == m_messages.size() );
197- for( size_t i = 0; i < m_captured; ++i )
198- m_resultCapture.popScopedMessage( m_messages[i] );
199+ assert( m_captured == m_messages.size() );
200+ for (auto const& message : m_messages) {
201+ IResultCapture::popScopedMessage( message.sequence );
202 }
203 }
204
205 void Capturer::captureValue( size_t index, std::string const& value ) {
206 assert( index < m_messages.size() );
207 m_messages[index].message += value;
208- m_resultCapture.pushScopedMessage( m_messages[index] );
209+ IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
210 m_captured++;
211 }
212
213@@ -1143,7 +1171,6 @@ namespace Catch {
214 }
215 void cleanUp() {
216 cleanupSingletons();
217- cleanUpContext();
218 }
219 std::string translateActiveException() {
220 return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
221@@ -1154,8 +1181,9 @@ namespace Catch {
222
223
224
225-#include <algorithm>
226 #include <cassert>
227+#include <cstdio>
228+#include <cstdlib>
229 #include <exception>
230 #include <iomanip>
231 #include <set>
232@@ -1163,8 +1191,6 @@ namespace Catch {
233 namespace Catch {
234
235 namespace {
236- const int MaxExitCode = 255;
237-
238 IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) {
239 auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config));
240 CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\'');
241@@ -1272,6 +1298,50 @@ namespace Catch {
242 }
243 }
244
245+ // Creates empty file at path. The path must be writable, we do not
246+ // try to create directories in path because that's hard in C++14.
247+ void setUpGuardFile( std::string const& guardFilePath ) {
248+ if ( !guardFilePath.empty() ) {
249+#if defined( _MSC_VER )
250+ std::FILE* file = nullptr;
251+ if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) {
252+ char msgBuffer[100];
253+ const auto err = errno;
254+ std::string errMsg;
255+ if ( !strerror_s( msgBuffer, err ) ) {
256+ errMsg = msgBuffer;
257+ } else {
258+ errMsg = "Could not translate errno to a string";
259+ }
260+
261+#else
262+ std::FILE* file = std::fopen( guardFilePath.c_str(), "w" );
263+ if ( !file ) {
264+ const auto err = errno;
265+ const char* errMsg = std::strerror( err );
266+#endif
267+
268+ CATCH_RUNTIME_ERROR( "Could not open the exit guard file '"
269+ << guardFilePath << "' because '"
270+ << errMsg << "' (" << err << ')' );
271+ }
272+ const int ret = std::fclose( file );
273+ CATCH_ENFORCE(
274+ ret == 0,
275+ "Error when closing the exit guard file: " << ret );
276+ }
277+ }
278+
279+ // Removes file at path. Assuming we created it in setUpGuardFile.
280+ void tearDownGuardFile( std::string const& guardFilePath ) {
281+ if ( !guardFilePath.empty() ) {
282+ const int ret = std::remove( guardFilePath.c_str() );
283+ CATCH_ENFORCE(
284+ ret == 0,
285+ "Error when removing the exit guard file: " << ret );
286+ }
287+ }
288+
289 } // anon namespace
290
291 Session::Session() {
292@@ -1327,8 +1397,7 @@ namespace Catch {
293 }
294
295 int Session::applyCommandLine( int argc, char const * const * argv ) {
296- if( m_startupExceptions )
297- return 1;
298+ if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; }
299
300 auto result = m_cli.parse( Clara::Args( argc, argv ) );
301
302@@ -1344,7 +1413,7 @@ namespace Catch {
303 << TextFlow::Column( result.errorMessage() ).indent( 2 )
304 << "\n\n";
305 errStream->stream() << "Run with -? for usage\n\n" << std::flush;
306- return MaxExitCode;
307+ return UnspecifiedErrorExitCode;
308 }
309
310 if( m_configData.showHelp )
311@@ -1391,6 +1460,7 @@ namespace Catch {
312 static_cast<void>(std::getchar());
313 }
314 int exitCode = runInternal();
315+
316 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
317 Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
318 static_cast<void>(std::getchar());
319@@ -1414,8 +1484,7 @@ namespace Catch {
320 }
321
322 int Session::runInternal() {
323- if( m_startupExceptions )
324- return 1;
325+ if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; }
326
327 if (m_configData.showHelp || m_configData.libIdentify) {
328 return 0;
329@@ -1426,12 +1495,16 @@ namespace Catch {
330 << ") must be greater than the shard index ("
331 << m_configData.shardIndex << ")\n"
332 << std::flush;
333- return 1;
334+ return UnspecifiedErrorExitCode;
335 }
336
337 CATCH_TRY {
338 config(); // Force config to be constructed
339
340+ // We need to retrieve potential Bazel config with the full Config
341+ // constructor, so we have to create the guard file after it is created.
342+ setUpGuardFile( m_config->getExitGuardFilePath() );
343+
344 seedRng( *m_config );
345
346 if (m_configData.filenamesAsTags) {
347@@ -1449,7 +1522,7 @@ namespace Catch {
348 for ( auto const& spec : invalidSpecs ) {
349 reporter->reportInvalidTestSpec( spec );
350 }
351- return 1;
352+ return InvalidTestSpecExitCode;
353 }
354
355
356@@ -1461,31 +1534,34 @@ namespace Catch {
357 TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
358 auto const totals = tests.execute();
359
360+ // If we got here, running the tests finished normally-enough.
361+ // They might've failed, but that would've been reported elsewhere.
362+ tearDownGuardFile( m_config->getExitGuardFilePath() );
363+
364 if ( tests.hadUnmatchedTestSpecs()
365 && m_config->warnAboutUnmatchedTestSpecs() ) {
366- return 3;
367+ return UnmatchedTestSpecExitCode;
368 }
369
370 if ( totals.testCases.total() == 0
371 && !m_config->zeroTestsCountAsSuccess() ) {
372- return 2;
373+ return NoTestsRunExitCode;
374 }
375
376 if ( totals.testCases.total() > 0 &&
377 totals.testCases.total() == totals.testCases.skipped
378 && !m_config->zeroTestsCountAsSuccess() ) {
379- return 4;
380+ return AllTestsSkippedExitCode;
381 }
382
383- // Note that on unices only the lower 8 bits are usually used, clamping
384- // the return value to 255 prevents false negative when some multiple
385- // of 256 tests has failed
386- return (std::min) (MaxExitCode, static_cast<int>(totals.assertions.failed));
387+ if ( totals.assertions.failed ) { return TestFailureExitCode; }
388+ return 0;
389+
390 }
391 #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
392 catch( std::exception& ex ) {
393 Catch::cerr() << ex.what() << '\n' << std::flush;
394- return MaxExitCode;
395+ return UnspecifiedErrorExitCode;
396 }
397 #endif
398 }
399@@ -1521,26 +1597,26 @@ namespace Catch {
400 static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type),
401 "The size of the TestCaseProperties is different from the assumed size");
402
403- TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) {
404+ constexpr TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) {
405 return static_cast<TestCaseProperties>(
406 static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs)
407 );
408 }
409
410- TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) {
411+ constexpr TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) {
412 lhs = static_cast<TestCaseProperties>(
413 static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs)
414 );
415 return lhs;
416 }
417
418- TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) {
419+ constexpr TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) {
420 return static_cast<TestCaseProperties>(
421 static_cast<TCP_underlying_type>(lhs) & static_cast<TCP_underlying_type>(rhs)
422 );
423 }
424
425- bool applies(TestCaseProperties tcp) {
426+ constexpr bool applies(TestCaseProperties tcp) {
427 static_assert(static_cast<TCP_underlying_type>(TestCaseProperties::None) == 0,
428 "TestCaseProperties::None must be equal to 0");
429 return tcp != TestCaseProperties::None;
430@@ -1579,7 +1655,7 @@ namespace Catch {
431 return "Anonymous test case " + std::to_string(++counter);
432 }
433
434- StringRef extractFilenamePart(StringRef filename) {
435+ constexpr StringRef extractFilenamePart(StringRef filename) {
436 size_t lastDot = filename.size();
437 while (lastDot > 0 && filename[lastDot - 1] != '.') {
438 --lastDot;
439@@ -1597,7 +1673,7 @@ namespace Catch {
440 }
441
442 // Returns the upper bound on size of extra tags ([#file]+[.])
443- size_t sizeOfExtraTags(StringRef filepath) {
444+ constexpr size_t sizeOfExtraTags(StringRef filepath) {
445 // [.] is 3, [#] is another 3
446 const size_t extras = 3 + 3;
447 return extractFilenamePart(filepath).size() + extras;
448@@ -1758,10 +1834,6 @@ namespace Catch {
449 return lhs.tags < rhs.tags;
450 }
451
452- TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const {
453- return *m_info;
454- }
455-
456 } // end namespace Catch
457
458
459@@ -1902,7 +1974,7 @@ namespace Catch {
460
461 namespace {
462 static auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
463- return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
464+ return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
465 }
466 } // end unnamed namespace
467
468@@ -1919,7 +1991,7 @@ namespace Catch {
469 return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
470 }
471 auto Timer::getElapsedSeconds() const -> double {
472- return getElapsedMicroseconds()/1000000.0;
473+ return static_cast<double>(getElapsedMicroseconds())/1000000.0;
474 }
475
476
477@@ -1928,7 +2000,6 @@ namespace Catch {
478
479
480
481-#include <cmath>
482 #include <iomanip>
483
484 namespace Catch {
485@@ -1939,7 +2010,10 @@ namespace Detail {
486 const int hexThreshold = 255;
487
488 struct Endianness {
489- enum Arch { Big, Little };
490+ enum Arch : uint8_t {
491+ Big,
492+ Little
493+ };
494
495 static Arch which() {
496 int one = 1;
497@@ -1975,35 +2049,35 @@ namespace Detail {
498 std::string ret;
499 // This is enough for the "don't escape invisibles" case, and a good
500 // lower bound on the "escape invisibles" case.
501- ret.reserve(string.size() + 2);
502+ ret.reserve( string.size() + 2 );
503
504- if (!escapeInvisibles) {
505+ if ( !escapeInvisibles ) {
506 ret += '"';
507 ret += string;
508 ret += '"';
509 return ret;
510 }
511
512+ size_t last_start = 0;
513+ auto write_to = [&]( size_t idx ) {
514+ if ( last_start < idx ) {
515+ ret += string.substr( last_start, idx - last_start );
516+ }
517+ last_start = idx + 1;
518+ };
519+
520 ret += '"';
521- for (char c : string) {
522- switch (c) {
523- case '\r':
524- ret.append("\\r");
525- break;
526- case '\n':
527- ret.append("\\n");
528- break;
529- case '\t':
530- ret.append("\\t");
531- break;
532- case '\f':
533- ret.append("\\f");
534- break;
535- default:
536- ret.push_back(c);
537- break;
538+ for ( size_t i = 0; i < string.size(); ++i ) {
539+ const char c = string[i];
540+ if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
541+ write_to( i );
542+ if ( c == '\r' ) { ret.append( "\\r" ); }
543+ if ( c == '\n' ) { ret.append( "\\n" ); }
544+ if ( c == '\t' ) { ret.append( "\\t" ); }
545+ if ( c == '\f' ) { ret.append( "\\f" ); }
546 }
547 }
548+ write_to( string.size() );
549 ret += '"';
550
551 return ret;
552@@ -2028,6 +2102,13 @@ namespace Detail {
553 rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
554 return rss.str();
555 }
556+
557+ std::string makeExceptionHappenedString() {
558+ return "{ stringification failed with an exception: \"" +
559+ translateActiveException() + "\" }";
560+
561+ }
562+
563 } // end Detail namespace
564
565
566@@ -2156,13 +2237,13 @@ std::string StringMaker<unsigned char>::convert(unsigned char value) {
567 return ::Catch::Detail::stringify(static_cast<char>(value));
568 }
569
570-int StringMaker<float>::precision = 5;
571+int StringMaker<float>::precision = std::numeric_limits<float>::max_digits10;
572
573 std::string StringMaker<float>::convert(float value) {
574 return Detail::fpToString(value, precision) + 'f';
575 }
576
577-int StringMaker<double>::precision = 10;
578+int StringMaker<double>::precision = std::numeric_limits<double>::max_digits10;
579
580 std::string StringMaker<double>::convert(double value) {
581 return Detail::fpToString(value, precision);
582@@ -2273,7 +2354,7 @@ namespace Catch {
583 }
584
585 Version const& libraryVersion() {
586- static Version version( 3, 5, 4, "", 0 );
587+ static Version version( 3, 11, 0, "", 0 );
588 return version;
589 }
590
591@@ -2361,8 +2442,14 @@ namespace Catch {
592
593
594 namespace Catch {
595+ namespace Detail {
596+ void missingCaptureInstance() {
597+ CATCH_INTERNAL_ERROR( "No result capture instance" );
598+ }
599+ } // namespace Detail
600+
601 IResultCapture::~IResultCapture() = default;
602-}
603+} // namespace Catch
604
605
606
607@@ -2529,8 +2616,8 @@ namespace Catch {
608 void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
609 m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
610 }
611- void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) {
612- m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
613+ void AssertionHandler::handleMessage(ResultWas::OfType resultType, std::string&& message) {
614+ m_resultCapture.handleMessage( m_assertionInfo, resultType, CATCH_MOVE(message), m_reaction );
615 }
616
617 auto AssertionHandler::allowThrows() const -> bool {
618@@ -2676,7 +2763,7 @@ namespace Catch {
619 { TokenType::Argument,
620 next.substr( delimiterPos + 1, next.size() ) } );
621 } else {
622- if ( next[1] != '-' && next.size() > 2 ) {
623+ if ( next.size() > 1 && next[1] != '-' && next.size() > 2 ) {
624 // Combined short args, e.g. "-ab" for "-a -b"
625 for ( size_t i = 1; i < next.size(); ++i ) {
626 m_tokenBuffer.push_back(
627@@ -3355,6 +3442,9 @@ namespace Catch {
628 | Opt( config.allowZeroTests )
629 ["--allow-running-no-tests"]
630 ( "Treat 'No tests run' as a success" )
631+ | Opt( config.prematureExitGuardFilePath, "path" )
632+ ["--premature-exit-guard-file"]
633+ ( "create a file before running tests and delete it during clean exit" )
634 | Arg( config.testsOrTags, "test name|pattern|tags" )
635 ( "which test or tests to use" );
636
637@@ -3509,7 +3599,11 @@ namespace {
638 #endif // Windows/ ANSI/ None
639
640
641-#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC )
642+#if defined( CATCH_PLATFORM_LINUX ) \
643+ || defined( CATCH_PLATFORM_MAC ) \
644+ || defined( __GLIBC__ ) \
645+ || defined( __FreeBSD__ ) \
646+ || defined( CATCH_PLATFORM_QNX )
647 # define CATCH_INTERNAL_HAS_ISATTY
648 # include <unistd.h>
649 #endif
650@@ -3633,28 +3727,12 @@ namespace Catch {
651
652 namespace Catch {
653
654- Context* Context::currentContext = nullptr;
655-
656- void cleanUpContext() {
657- delete Context::currentContext;
658- Context::currentContext = nullptr;
659- }
660- void Context::createContext() {
661- currentContext = new Context();
662- }
663+ Context Context::currentContext;
664
665 Context& getCurrentMutableContext() {
666- if ( !Context::currentContext ) { Context::createContext(); }
667- // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
668- return *Context::currentContext;
669+ return Context::currentContext;
670 }
671
672- void Context::setResultCapture( IResultCapture* resultCapture ) {
673- m_resultCapture = resultCapture;
674- }
675-
676- void Context::setConfig( IConfig const* config ) { m_config = config; }
677-
678 SimplePcg32& sharedRng() {
679 static SimplePcg32 s_rng;
680 return s_rng;
681@@ -3757,7 +3835,7 @@ namespace Catch {
682 #endif
683 } // namespace Catch
684
685-#elif defined(CATCH_PLATFORM_LINUX)
686+#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
687 #include <fstream>
688 #include <string>
689
690@@ -3989,10 +4067,10 @@ namespace Catch {
691 // To avoid having to handle TFE explicitly everywhere, we just
692 // rethrow it so that it goes back up the caller.
693 catch( TestFailureException& ) {
694- std::rethrow_exception(std::current_exception());
695+ return "{ nested assertion failed }";
696 }
697 catch( TestSkipException& ) {
698- std::rethrow_exception(std::current_exception());
699+ return "{ nested SKIP() called }";
700 }
701 catch( std::exception const& ex ) {
702 return ex.what();
703@@ -4084,30 +4162,34 @@ namespace Catch {
704 // There is no 1-1 mapping between signals and windows exceptions.
705 // Windows can easily distinguish between SO and SigSegV,
706 // but SigInt, SigTerm, etc are handled differently.
707- static SignalDefs signalDefs[] = {
708+ static constexpr SignalDefs signalDefs[] = {
709 { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" },
710 { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
711 { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
712 { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
713 };
714
715+ // Since we do not support multiple instantiations, we put these
716+ // into global variables and rely on cleaning them up in outlined
717+ // constructors/destructors
718+ static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
719+
720+
721 static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
722 for (auto const& def : signalDefs) {
723 if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
724 reportFatal(def.name);
725 }
726 }
727- // If its not an exception we care about, pass it along.
728+ // If a filter was previously registered, invoke it
729+ if (previousTopLevelExceptionFilter) {
730+ return previousTopLevelExceptionFilter(ExceptionInfo);
731+ }
732+ // Otherwise, pass along all exceptions.
733 // This stops us from eating debugger breaks etc.
734 return EXCEPTION_CONTINUE_SEARCH;
735 }
736
737- // Since we do not support multiple instantiations, we put these
738- // into global variables and rely on cleaning them up in outlined
739- // constructors/destructors
740- static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
741-
742-
743 // For MSVC, we reserve part of the stack memory for handling
744 // memory overflow structured exception.
745 FatalConditionHandler::FatalConditionHandler() {
746@@ -4155,7 +4237,7 @@ namespace Catch {
747 const char* name;
748 };
749
750- static SignalDefs signalDefs[] = {
751+ static constexpr SignalDefs signalDefs[] = {
752 { SIGINT, "SIGINT - Terminal interrupt signal" },
753 { SIGILL, "SIGILL - Illegal instruction signal" },
754 { SIGFPE, "SIGFPE - Floating point error signal" },
755@@ -4319,8 +4401,6 @@ namespace Catch {
756
757 #include <cstdio>
758 #include <fstream>
759-#include <sstream>
760-#include <vector>
761
762 namespace Catch {
763
764@@ -4461,6 +4541,33 @@ namespace Detail {
765
766
767 namespace Catch {
768+
769+ namespace {
770+ static bool needsEscape( char c ) {
771+ return c == '"' || c == '\\' || c == '\b' || c == '\f' ||
772+ c == '\n' || c == '\r' || c == '\t';
773+ }
774+
775+ static Catch::StringRef makeEscapeStringRef( char c ) {
776+ if ( c == '"' ) {
777+ return "\\\""_sr;
778+ } else if ( c == '\\' ) {
779+ return "\\\\"_sr;
780+ } else if ( c == '\b' ) {
781+ return "\\b"_sr;
782+ } else if ( c == '\f' ) {
783+ return "\\f"_sr;
784+ } else if ( c == '\n' ) {
785+ return "\\n"_sr;
786+ } else if ( c == '\r' ) {
787+ return "\\r"_sr;
788+ } else if ( c == '\t' ) {
789+ return "\\t"_sr;
790+ }
791+ Catch::Detail::Unreachable();
792+ }
793+ } // namespace
794+
795 void JsonUtils::indent( std::ostream& os, std::uint64_t level ) {
796 for ( std::uint64_t i = 0; i < level; ++i ) {
797 os << " ";
798@@ -4570,30 +4677,19 @@ namespace Catch {
799
800 void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
801 if ( quote ) { m_os << '"'; }
802- for (char c : value) {
803- // Escape list taken from https://www.json.org/json-en.html,
804- // string definition.
805- // Note that while forward slash _can_ be escaped, it does
806- // not have to be, if JSON is not further embedded somewhere
807- // where forward slash is meaningful.
808- if ( c == '"' ) {
809- m_os << "\\\"";
810- } else if ( c == '\\' ) {
811- m_os << "\\\\";
812- } else if ( c == '\b' ) {
813- m_os << "\\b";
814- } else if ( c == '\f' ) {
815- m_os << "\\f";
816- } else if ( c == '\n' ) {
817- m_os << "\\n";
818- } else if ( c == '\r' ) {
819- m_os << "\\r";
820- } else if ( c == '\t' ) {
821- m_os << "\\t";
822- } else {
823- m_os << c;
824+ size_t current_start = 0;
825+ for ( size_t i = 0; i < value.size(); ++i ) {
826+ if ( needsEscape( value[i] ) ) {
827+ if ( current_start < i ) {
828+ m_os << value.substr( current_start, i - current_start );
829+ }
830+ m_os << makeEscapeStringRef( value[i] );
831+ current_start = i + 1;
832 }
833 }
834+ if ( current_start < value.size() ) {
835+ m_os << value.substr( current_start, value.size() - current_start );
836+ }
837 if ( quote ) { m_os << '"'; }
838 }
839
840@@ -4761,7 +4857,7 @@ namespace Catch {
841 namespace Catch {
842 CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
843 CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
844- static LeakDetector leakDetector;
845+ static const LeakDetector leakDetector;
846 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
847 }
848
849@@ -4790,17 +4886,18 @@ int main (int argc, char * argv[]) {
850
851 namespace Catch {
852
853- MessageInfo::MessageInfo( StringRef _macroName,
854- SourceLineInfo const& _lineInfo,
855- ResultWas::OfType _type )
856+ MessageInfo::MessageInfo( StringRef _macroName,
857+ SourceLineInfo const& _lineInfo,
858+ ResultWas::OfType _type )
859 : macroName( _macroName ),
860 lineInfo( _lineInfo ),
861 type( _type ),
862 sequence( ++globalCount )
863 {}
864
865- // This may need protecting if threading support is added
866- unsigned int MessageInfo::globalCount = 0;
867+ // Messages are owned by their individual threads, so the counter should be thread-local as well.
868+ // Alternative consideration: atomic, so threads don't share IDs and things are easier to debug.
869+ thread_local unsigned int MessageInfo::globalCount = 0;
870
871 } // end namespace Catch
872
873@@ -4808,138 +4905,328 @@ namespace Catch {
874
875 #include <cstdio>
876 #include <cstring>
877+#include <iosfwd>
878 #include <sstream>
879
880-#if defined(CATCH_CONFIG_NEW_CAPTURE)
881- #if defined(_MSC_VER)
882- #include <io.h> //_dup and _dup2
883- #define dup _dup
884- #define dup2 _dup2
885- #define fileno _fileno
886- #else
887- #include <unistd.h> // dup and dup2
888- #endif
889+#if defined( CATCH_CONFIG_NEW_CAPTURE )
890+# if defined( _MSC_VER )
891+# include <io.h> //_dup and _dup2
892+# define dup _dup
893+# define dup2 _dup2
894+# define fileno _fileno
895+# else
896+# include <unistd.h> // dup and dup2
897+# endif
898 #endif
899
900-
901 namespace Catch {
902
903- RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
904- : m_originalStream( originalStream ),
905- m_redirectionStream( redirectionStream ),
906- m_prevBuf( m_originalStream.rdbuf() )
907- {
908- m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
909- }
910+ namespace {
911+ //! A no-op implementation, used if no reporter wants output
912+ //! redirection.
913+ class NoopRedirect : public OutputRedirect {
914+ void activateImpl() override {}
915+ void deactivateImpl() override {}
916+ std::string getStdout() override { return {}; }
917+ std::string getStderr() override { return {}; }
918+ void clearBuffers() override {}
919+ };
920
921- RedirectedStream::~RedirectedStream() {
922- m_originalStream.rdbuf( m_prevBuf );
923- }
924+ /**
925+ * Redirects specific stream's rdbuf with another's.
926+ *
927+ * Redirection can be stopped and started on-demand, assumes
928+ * that the underlying stream's rdbuf aren't changed by other
929+ * users.
930+ */
931+ class RedirectedStreamNew {
932+ std::ostream& m_originalStream;
933+ std::ostream& m_redirectionStream;
934+ std::streambuf* m_prevBuf;
935
936- RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
937- auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
938+ public:
939+ RedirectedStreamNew( std::ostream& originalStream,
940+ std::ostream& redirectionStream ):
941+ m_originalStream( originalStream ),
942+ m_redirectionStream( redirectionStream ),
943+ m_prevBuf( m_originalStream.rdbuf() ) {}
944
945- RedirectedStdErr::RedirectedStdErr()
946- : m_cerr( Catch::cerr(), m_rss.get() ),
947- m_clog( Catch::clog(), m_rss.get() )
948- {}
949- auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
950+ void startRedirect() {
951+ m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
952+ }
953+ void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); }
954+ };
955
956- RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
957- : m_redirectedCout(redirectedCout),
958- m_redirectedCerr(redirectedCerr)
959- {}
960+ /**
961+ * Redirects the `std::cout`, `std::cerr`, `std::clog` streams,
962+ * but does not touch the actual `stdout`/`stderr` file descriptors.
963+ */
964+ class StreamRedirect : public OutputRedirect {
965+ ReusableStringStream m_redirectedOut, m_redirectedErr;
966+ RedirectedStreamNew m_cout, m_cerr, m_clog;
967
968- RedirectedStreams::~RedirectedStreams() {
969- m_redirectedCout += m_redirectedStdOut.str();
970- m_redirectedCerr += m_redirectedStdErr.str();
971- }
972+ public:
973+ StreamRedirect():
974+ m_cout( Catch::cout(), m_redirectedOut.get() ),
975+ m_cerr( Catch::cerr(), m_redirectedErr.get() ),
976+ m_clog( Catch::clog(), m_redirectedErr.get() ) {}
977+
978+ void activateImpl() override {
979+ m_cout.startRedirect();
980+ m_cerr.startRedirect();
981+ m_clog.startRedirect();
982+ }
983+ void deactivateImpl() override {
984+ m_cout.stopRedirect();
985+ m_cerr.stopRedirect();
986+ m_clog.stopRedirect();
987+ }
988+ std::string getStdout() override { return m_redirectedOut.str(); }
989+ std::string getStderr() override { return m_redirectedErr.str(); }
990+ void clearBuffers() override {
991+ m_redirectedOut.str( "" );
992+ m_redirectedErr.str( "" );
993+ }
994+ };
995
996-#if defined(CATCH_CONFIG_NEW_CAPTURE)
997+#if defined( CATCH_CONFIG_NEW_CAPTURE )
998
999-#if defined(_MSC_VER)
1000- TempFile::TempFile() {
1001- if (tmpnam_s(m_buffer)) {
1002- CATCH_RUNTIME_ERROR("Could not get a temp filename");
1003- }
1004- if (fopen_s(&m_file, m_buffer, "w+")) {
1005- char buffer[100];
1006- if (strerror_s(buffer, errno)) {
1007- CATCH_RUNTIME_ERROR("Could not translate errno to a string");
1008+ // Windows's implementation of std::tmpfile is terrible (it tries
1009+ // to create a file inside system folder, thus requiring elevated
1010+ // privileges for the binary), so we have to use tmpnam(_s) and
1011+ // create the file ourselves there.
1012+ class TempFile {
1013+ public:
1014+ TempFile( TempFile const& ) = delete;
1015+ TempFile& operator=( TempFile const& ) = delete;
1016+ TempFile( TempFile&& ) = delete;
1017+ TempFile& operator=( TempFile&& ) = delete;
1018+
1019+# if defined( _MSC_VER )
1020+ TempFile() {
1021+ if ( tmpnam_s( m_buffer ) ) {
1022+ CATCH_RUNTIME_ERROR( "Could not get a temp filename" );
1023+ }
1024+ if ( fopen_s( &m_file, m_buffer, "wb+" ) ) {
1025+ char buffer[100];
1026+ if ( strerror_s( buffer, errno ) ) {
1027+ CATCH_RUNTIME_ERROR(
1028+ "Could not translate errno to a string" );
1029+ }
1030+ CATCH_RUNTIME_ERROR( "Could not open the temp file: '"
1031+ << m_buffer
1032+ << "' because: " << buffer );
1033+ }
1034+ }
1035+# else
1036+ TempFile() {
1037+ m_file = std::tmpfile();
1038+ if ( !m_file ) {
1039+ CATCH_RUNTIME_ERROR( "Could not create a temp file." );
1040+ }
1041+ }
1042+# endif
1043+
1044+ ~TempFile() {
1045+ // TBD: What to do about errors here?
1046+ std::fclose( m_file );
1047+ // We manually create the file on Windows only, on Linux
1048+ // it will be autodeleted
1049+# if defined( _MSC_VER )
1050+ std::remove( m_buffer );
1051+# endif
1052+ }
1053+
1054+ std::FILE* getFile() { return m_file; }
1055+ std::string getContents() {
1056+ ReusableStringStream sstr;
1057+ constexpr long buffer_size = 100;
1058+ char buffer[buffer_size + 1] = {};
1059+ long current_pos = ftell( m_file );
1060+ CATCH_ENFORCE( current_pos >= 0,
1061+ "ftell failed, errno: " << errno );
1062+ std::rewind( m_file );
1063+ while ( current_pos > 0 ) {
1064+ auto read_characters =
1065+ std::fread( buffer,
1066+ 1,
1067+ std::min( buffer_size, current_pos ),
1068+ m_file );
1069+ buffer[read_characters] = '\0';
1070+ sstr << buffer;
1071+ current_pos -= static_cast<long>( read_characters );
1072+ }
1073+ return sstr.str();
1074+ }
1075+
1076+ void clear() { std::rewind( m_file ); }
1077+
1078+ private:
1079+ std::FILE* m_file = nullptr;
1080+ char m_buffer[L_tmpnam] = { 0 };
1081+ };
1082+
1083+ /**
1084+ * Redirects the actual `stdout`/`stderr` file descriptors.
1085+ *
1086+ * Works by replacing the file descriptors numbered 1 and 2
1087+ * with an open temporary file.
1088+ */
1089+ class FileRedirect : public OutputRedirect {
1090+ TempFile m_outFile, m_errFile;
1091+ int m_originalOut = -1;
1092+ int m_originalErr = -1;
1093+
1094+ // Flushes cout/cerr/clog streams and stdout/stderr FDs
1095+ void flushEverything() {
1096+ Catch::cout() << std::flush;
1097+ fflush( stdout );
1098+ // Since we support overriding these streams, we flush cerr
1099+ // even though std::cerr is unbuffered
1100+ Catch::cerr() << std::flush;
1101+ Catch::clog() << std::flush;
1102+ fflush( stderr );
1103 }
1104- CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
1105+
1106+ public:
1107+ FileRedirect():
1108+ m_originalOut( dup( fileno( stdout ) ) ),
1109+ m_originalErr( dup( fileno( stderr ) ) ) {
1110+ CATCH_ENFORCE( m_originalOut >= 0, "Could not dup stdout" );
1111+ CATCH_ENFORCE( m_originalErr >= 0, "Could not dup stderr" );
1112+ }
1113+
1114+ std::string getStdout() override { return m_outFile.getContents(); }
1115+ std::string getStderr() override { return m_errFile.getContents(); }
1116+ void clearBuffers() override {
1117+ m_outFile.clear();
1118+ m_errFile.clear();
1119+ }
1120+
1121+ void activateImpl() override {
1122+ // We flush before starting redirect, to ensure that we do
1123+ // not capture the end of message sent before activation.
1124+ flushEverything();
1125+
1126+ int ret;
1127+ ret = dup2( fileno( m_outFile.getFile() ), fileno( stdout ) );
1128+ CATCH_ENFORCE( ret >= 0,
1129+ "dup2 to stdout has failed, errno: " << errno );
1130+ ret = dup2( fileno( m_errFile.getFile() ), fileno( stderr ) );
1131+ CATCH_ENFORCE( ret >= 0,
1132+ "dup2 to stderr has failed, errno: " << errno );
1133+ }
1134+ void deactivateImpl() override {
1135+ // We flush before ending redirect, to ensure that we
1136+ // capture all messages sent while the redirect was active.
1137+ flushEverything();
1138+
1139+ int ret;
1140+ ret = dup2( m_originalOut, fileno( stdout ) );
1141+ CATCH_ENFORCE(
1142+ ret >= 0,
1143+ "dup2 of original stdout has failed, errno: " << errno );
1144+ ret = dup2( m_originalErr, fileno( stderr ) );
1145+ CATCH_ENFORCE(
1146+ ret >= 0,
1147+ "dup2 of original stderr has failed, errno: " << errno );
1148+ }
1149+ };
1150+
1151+#endif // CATCH_CONFIG_NEW_CAPTURE
1152+
1153+ } // end namespace
1154+
1155+ bool isRedirectAvailable( OutputRedirect::Kind kind ) {
1156+ switch ( kind ) {
1157+ // These two are always available
1158+ case OutputRedirect::None:
1159+ case OutputRedirect::Streams:
1160+ return true;
1161+#if defined( CATCH_CONFIG_NEW_CAPTURE )
1162+ case OutputRedirect::FileDescriptors:
1163+ return true;
1164+#endif
1165+ default:
1166+ return false;
1167 }
1168 }
1169+
1170+ Detail::unique_ptr<OutputRedirect> makeOutputRedirect( bool actual ) {
1171+ if ( actual ) {
1172+ // TODO: Clean this up later
1173+#if defined( CATCH_CONFIG_NEW_CAPTURE )
1174+ return Detail::make_unique<FileRedirect>();
1175 #else
1176- TempFile::TempFile() {
1177- m_file = std::tmpfile();
1178- if (!m_file) {
1179- CATCH_RUNTIME_ERROR("Could not create a temp file.");
1180+ return Detail::make_unique<StreamRedirect>();
1181+#endif
1182+ } else {
1183+ return Detail::make_unique<NoopRedirect>();
1184 }
1185 }
1186
1187-#endif
1188+ RedirectGuard scopedActivate( OutputRedirect& redirectImpl ) {
1189+ return RedirectGuard( true, redirectImpl );
1190+ }
1191
1192- TempFile::~TempFile() {
1193- // TBD: What to do about errors here?
1194- std::fclose(m_file);
1195- // We manually create the file on Windows only, on Linux
1196- // it will be autodeleted
1197-#if defined(_MSC_VER)
1198- std::remove(m_buffer);
1199-#endif
1200+ RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ) {
1201+ return RedirectGuard( false, redirectImpl );
1202 }
1203
1204+ OutputRedirect::~OutputRedirect() = default;
1205
1206- FILE* TempFile::getFile() {
1207- return m_file;
1208- }
1209+ RedirectGuard::RedirectGuard( bool activate, OutputRedirect& redirectImpl ):
1210+ m_redirect( &redirectImpl ),
1211+ m_activate( activate ),
1212+ m_previouslyActive( redirectImpl.isActive() ) {
1213
1214- std::string TempFile::getContents() {
1215- std::stringstream sstr;
1216- char buffer[100] = {};
1217- std::rewind(m_file);
1218- while (std::fgets(buffer, sizeof(buffer), m_file)) {
1219- sstr << buffer;
1220- }
1221- return sstr.str();
1222- }
1223+ // Skip cases where there is no actual state change.
1224+ if ( m_activate == m_previouslyActive ) { return; }
1225
1226- OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
1227- m_originalStdout(dup(1)),
1228- m_originalStderr(dup(2)),
1229- m_stdoutDest(stdout_dest),
1230- m_stderrDest(stderr_dest) {
1231- dup2(fileno(m_stdoutFile.getFile()), 1);
1232- dup2(fileno(m_stderrFile.getFile()), 2);
1233+ if ( m_activate ) {
1234+ m_redirect->activate();
1235+ } else {
1236+ m_redirect->deactivate();
1237+ }
1238 }
1239
1240- OutputRedirect::~OutputRedirect() {
1241- Catch::cout() << std::flush;
1242- fflush(stdout);
1243- // Since we support overriding these streams, we flush cerr
1244- // even though std::cerr is unbuffered
1245- Catch::cerr() << std::flush;
1246- Catch::clog() << std::flush;
1247- fflush(stderr);
1248+ RedirectGuard::~RedirectGuard() noexcept( false ) {
1249+ if ( m_moved ) { return; }
1250+ // Skip cases where there is no actual state change.
1251+ if ( m_activate == m_previouslyActive ) { return; }
1252
1253- dup2(m_originalStdout, 1);
1254- dup2(m_originalStderr, 2);
1255+ if ( m_activate ) {
1256+ m_redirect->deactivate();
1257+ } else {
1258+ m_redirect->activate();
1259+ }
1260+ }
1261
1262- m_stdoutDest += m_stdoutFile.getContents();
1263- m_stderrDest += m_stderrFile.getContents();
1264+ RedirectGuard::RedirectGuard( RedirectGuard&& rhs ) noexcept:
1265+ m_redirect( rhs.m_redirect ),
1266+ m_activate( rhs.m_activate ),
1267+ m_previouslyActive( rhs.m_previouslyActive ),
1268+ m_moved( false ) {
1269+ rhs.m_moved = true;
1270 }
1271
1272-#endif // CATCH_CONFIG_NEW_CAPTURE
1273+ RedirectGuard& RedirectGuard::operator=( RedirectGuard&& rhs ) noexcept {
1274+ m_redirect = rhs.m_redirect;
1275+ m_activate = rhs.m_activate;
1276+ m_previouslyActive = rhs.m_previouslyActive;
1277+ m_moved = false;
1278+ rhs.m_moved = true;
1279+ return *this;
1280+ }
1281
1282 } // namespace Catch
1283
1284-#if defined(CATCH_CONFIG_NEW_CAPTURE)
1285- #if defined(_MSC_VER)
1286- #undef dup
1287- #undef dup2
1288- #undef fileno
1289- #endif
1290+#if defined( CATCH_CONFIG_NEW_CAPTURE )
1291+# if defined( _MSC_VER )
1292+# undef dup
1293+# undef dup2
1294+# undef fileno
1295+# endif
1296 #endif
1297
1298
1299@@ -5022,6 +5309,12 @@ namespace Catch {
1300
1301
1302
1303+#if defined( __clang__ )
1304+# define CATCH2_CLANG_NO_SANITIZE_INTEGER \
1305+ __attribute__( ( no_sanitize( "unsigned-integer-overflow" ) ) )
1306+#else
1307+# define CATCH2_CLANG_NO_SANITIZE_INTEGER
1308+#endif
1309 namespace Catch {
1310
1311 namespace {
1312@@ -5031,6 +5324,7 @@ namespace {
1313 #pragma warning(disable:4146) // we negate uint32 during the rotate
1314 #endif
1315 // Safe rotr implementation thanks to John Regehr
1316+ CATCH2_CLANG_NO_SANITIZE_INTEGER
1317 uint32_t rotate_right(uint32_t val, uint32_t count) {
1318 const uint32_t mask = 31;
1319 count &= mask;
1320@@ -5064,10 +5358,11 @@ namespace {
1321 }
1322 }
1323
1324+ CATCH2_CLANG_NO_SANITIZE_INTEGER
1325 SimplePcg32::result_type SimplePcg32::operator()() {
1326 // prepare the output value
1327 const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
1328- const auto output = rotate_right(xorshifted, m_state >> 59u);
1329+ const auto output = rotate_right(xorshifted, static_cast<uint32_t>(m_state >> 59u));
1330
1331 // advance state
1332 m_state = m_state * 6364136223846793005ULL + s_inc;
1333@@ -5350,28 +5645,9 @@ ReporterSpec::ReporterSpec(
1334
1335
1336
1337-namespace Catch {
1338-
1339- bool isOk( ResultWas::OfType resultType ) {
1340- return ( resultType & ResultWas::FailureBit ) == 0;
1341- }
1342- bool isJustInfo( int flags ) {
1343- return flags == ResultWas::Info;
1344- }
1345-
1346- ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
1347- return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
1348- }
1349-
1350- bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
1351- bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
1352-
1353-} // end namespace Catch
1354-
1355-
1356-
1357 #include <cstdio>
1358 #include <sstream>
1359+#include <tuple>
1360 #include <vector>
1361
1362 namespace Catch {
1363@@ -5381,34 +5657,40 @@ namespace Catch {
1364 std::vector<Detail::unique_ptr<std::ostringstream>> m_streams;
1365 std::vector<std::size_t> m_unused;
1366 std::ostringstream m_referenceStream; // Used for copy state/ flags from
1367+ Detail::Mutex m_mutex;
1368
1369- auto add() -> std::size_t {
1370+ auto add() -> std::pair<std::size_t, std::ostringstream*> {
1371+ Detail::LockGuard _( m_mutex );
1372 if( m_unused.empty() ) {
1373 m_streams.push_back( Detail::make_unique<std::ostringstream>() );
1374- return m_streams.size()-1;
1375+ return { m_streams.size()-1, m_streams.back().get() };
1376 }
1377 else {
1378 auto index = m_unused.back();
1379 m_unused.pop_back();
1380- return index;
1381+ return { index, m_streams[index].get() };
1382 }
1383 }
1384
1385- void release( std::size_t index ) {
1386- m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
1387- m_unused.push_back(index);
1388+ void release( std::size_t index, std::ostream* originalPtr ) {
1389+ assert( originalPtr );
1390+ originalPtr->copyfmt( m_referenceStream ); // Restore initial flags and other state
1391+
1392+ Detail::LockGuard _( m_mutex );
1393+ assert( originalPtr == m_streams[index].get() && "Mismatch between release index and stream ptr" );
1394+ m_unused.push_back( index );
1395 }
1396 };
1397
1398- ReusableStringStream::ReusableStringStream()
1399- : m_index( Singleton<StringStreams>::getMutable().add() ),
1400- m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
1401- {}
1402+ ReusableStringStream::ReusableStringStream() {
1403+ std::tie( m_index, m_oss ) =
1404+ Singleton<StringStreams>::getMutable().add();
1405+ }
1406
1407 ReusableStringStream::~ReusableStringStream() {
1408 static_cast<std::ostringstream*>( m_oss )->str("");
1409 m_oss->clear();
1410- Singleton<StringStreams>::getMutable().release( m_index );
1411+ Singleton<StringStreams>::getMutable().release( m_index, m_oss );
1412 }
1413
1414 std::string ReusableStringStream::str() const {
1415@@ -5568,26 +5850,69 @@ namespace Catch {
1416 } // namespace
1417 }
1418
1419+ namespace Detail {
1420+ // Assertions are owned by the thread that is executing them.
1421+ // This allows for lock-free progress in common cases where we
1422+ // do not need to send the assertion events to the reporter.
1423+ // This also implies that messages are owned by their respective
1424+ // threads, and should not be shared across different threads.
1425+ //
1426+ // This implies that various pieces of metadata referring to last
1427+ // assertion result/source location/message handling, etc
1428+ // should also be thread local. For now we just use naked globals
1429+ // below, in the future we will want to allocate piece of memory
1430+ // from heap, to avoid consuming too much thread-local storage.
1431+
1432+ // This is used for the "if" part of CHECKED_IF/CHECKED_ELSE
1433+ static thread_local bool g_lastAssertionPassed = false;
1434+
1435+ // This is the source location for last encountered macro. It is
1436+ // used to provide the users with more precise location of error
1437+ // when an unexpected exception/fatal error happens.
1438+ static thread_local SourceLineInfo g_lastKnownLineInfo("DummyLocation", static_cast<size_t>(-1));
1439+
1440+ // Should we clear message scopes before sending off the messages to
1441+ // reporter? Set in `assertionPassedFastPath` to avoid doing the full
1442+ // clear there for performance reasons.
1443+ static thread_local bool g_clearMessageScopes = false;
1444+
1445+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
1446+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
1447+ // Actual messages to be provided to the reporter
1448+ static thread_local std::vector<MessageInfo> g_messages;
1449+
1450+ // Owners for the UNSCOPED_X information macro
1451+ static thread_local std::vector<ScopedMessage> g_messageScopes;
1452+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
1453+
1454+ } // namespace Detail
1455+
1456 RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
1457 : m_runInfo(_config->name()),
1458 m_config(_config),
1459 m_reporter(CATCH_MOVE(reporter)),
1460- m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
1461- m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
1462+ m_outputRedirect( makeOutputRedirect( m_reporter->getPreferences().shouldRedirectStdOut ) ),
1463+ m_abortAfterXFailedAssertions( m_config->abortAfter() ),
1464+ m_reportAssertionStarting( m_reporter->getPreferences().shouldReportAllAssertionStarts ),
1465+ m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ),
1466+ m_shouldDebugBreak( m_config->shouldDebugBreak() )
1467 {
1468 getCurrentMutableContext().setResultCapture( this );
1469 m_reporter->testRunStarting(m_runInfo);
1470 }
1471
1472 RunContext::~RunContext() {
1473+ updateTotalsFromAtomics();
1474 m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
1475 }
1476
1477 Totals RunContext::runTest(TestCaseHandle const& testCase) {
1478+ updateTotalsFromAtomics();
1479 const Totals prevTotals = m_totals;
1480
1481 auto const& testInfo = testCase.getTestCaseInfo();
1482 m_reporter->testCaseStarting(testInfo);
1483+ testCase.prepareTestCase();
1484 m_activeTestCase = &testCase;
1485
1486
1487@@ -5637,16 +5962,20 @@ namespace Catch {
1488
1489 m_reporter->testCasePartialStarting(testInfo, testRuns);
1490
1491+ updateTotalsFromAtomics();
1492 const auto beforeRunTotals = m_totals;
1493- std::string oneRunCout, oneRunCerr;
1494- runCurrentTest(oneRunCout, oneRunCerr);
1495+ runCurrentTest();
1496+ std::string oneRunCout = m_outputRedirect->getStdout();
1497+ std::string oneRunCerr = m_outputRedirect->getStderr();
1498+ m_outputRedirect->clearBuffers();
1499 redirectedCout += oneRunCout;
1500 redirectedCerr += oneRunCerr;
1501
1502+ updateTotalsFromAtomics();
1503 const auto singleRunTotals = m_totals.delta(beforeRunTotals);
1504 auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
1505-
1506 m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
1507+
1508 ++testRuns;
1509 } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
1510
1511@@ -5657,6 +5986,7 @@ namespace Catch {
1512 deltaTotals.testCases.failed++;
1513 }
1514 m_totals.testCases += deltaTotals.testCases;
1515+ testCase.tearDownTestCase();
1516 m_reporter->testCaseEnded(TestCaseStats(testInfo,
1517 deltaTotals,
1518 CATCH_MOVE(redirectedCout),
1519@@ -5671,43 +6001,54 @@ namespace Catch {
1520
1521
1522 void RunContext::assertionEnded(AssertionResult&& result) {
1523+ Detail::g_lastKnownLineInfo = result.m_info.lineInfo;
1524 if (result.getResultType() == ResultWas::Ok) {
1525- m_totals.assertions.passed++;
1526- m_lastAssertionPassed = true;
1527+ m_atomicAssertionCount.passed++;
1528+ Detail::g_lastAssertionPassed = true;
1529 } else if (result.getResultType() == ResultWas::ExplicitSkip) {
1530- m_totals.assertions.skipped++;
1531- m_lastAssertionPassed = true;
1532+ m_atomicAssertionCount.skipped++;
1533+ Detail::g_lastAssertionPassed = true;
1534 } else if (!result.succeeded()) {
1535- m_lastAssertionPassed = false;
1536+ Detail::g_lastAssertionPassed = false;
1537 if (result.isOk()) {
1538 }
1539- else if( m_activeTestCase->getTestCaseInfo().okToFail() )
1540- m_totals.assertions.failedButOk++;
1541+ else if( m_activeTestCase->getTestCaseInfo().okToFail() ) // Read from a shared state established before the threads could start, this is fine
1542+ m_atomicAssertionCount.failedButOk++;
1543 else
1544- m_totals.assertions.failed++;
1545+ m_atomicAssertionCount.failed++;
1546 }
1547 else {
1548- m_lastAssertionPassed = true;
1549+ Detail::g_lastAssertionPassed = true;
1550+ }
1551+
1552+ if ( Detail::g_clearMessageScopes ) {
1553+ Detail::g_messageScopes.clear();
1554+ Detail::g_clearMessageScopes = false;
1555 }
1556
1557- m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
1558+ // From here, we are touching shared state and need mutex.
1559+ Detail::LockGuard lock( m_assertionMutex );
1560+ {
1561+ auto _ = scopedDeactivate( *m_outputRedirect );
1562+ updateTotalsFromAtomics();
1563+ m_reporter->assertionEnded( AssertionStats( result, Detail::g_messages, m_totals ) );
1564+ }
1565
1566 if ( result.getResultType() != ResultWas::Warning ) {
1567- m_messageScopes.clear();
1568+ Detail::g_messageScopes.clear();
1569 }
1570
1571 // Reset working state. assertion info will be reset after
1572 // populateReaction is run if it is needed
1573 m_lastResult = CATCH_MOVE( result );
1574 }
1575- void RunContext::resetAssertionInfo() {
1576- m_lastAssertionInfo.macroName = StringRef();
1577- m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
1578- m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal;
1579- }
1580
1581 void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
1582- m_reporter->assertionStarting( info );
1583+ if (m_reportAssertionStarting) {
1584+ Detail::LockGuard lock( m_assertionMutex );
1585+ auto _ = scopedDeactivate( *m_outputRedirect );
1586+ m_reporter->assertionStarting( info );
1587+ }
1588 }
1589
1590 bool RunContext::sectionStarted( StringRef sectionName,
1591@@ -5723,10 +6064,14 @@ namespace Catch {
1592 m_activeSections.push_back(§ionTracker);
1593
1594 SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) );
1595- m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
1596+ Detail::g_lastKnownLineInfo = sectionLineInfo;
1597
1598- m_reporter->sectionStarting(sectionInfo);
1599+ {
1600+ auto _ = scopedDeactivate( *m_outputRedirect );
1601+ m_reporter->sectionStarting( sectionInfo );
1602+ }
1603
1604+ updateTotalsFromAtomics();
1605 assertions = m_totals.assertions;
1606
1607 return true;
1608@@ -5734,12 +6079,11 @@ namespace Catch {
1609 IGeneratorTracker*
1610 RunContext::acquireGeneratorTracker( StringRef generatorName,
1611 SourceLineInfo const& lineInfo ) {
1612- using namespace Generators;
1613- GeneratorTracker* tracker = GeneratorTracker::acquire(
1614+ auto* tracker = Generators::GeneratorTracker::acquire(
1615 m_trackerContext,
1616 TestCaseTracking::NameAndLocationRef(
1617 generatorName, lineInfo ) );
1618- m_lastAssertionInfo.lineInfo = lineInfo;
1619+ Detail::g_lastKnownLineInfo = lineInfo;
1620 return tracker;
1621 }
1622
1623@@ -5752,7 +6096,7 @@ namespace Catch {
1624 auto& currentTracker = m_trackerContext.currentTracker();
1625 assert(
1626 currentTracker.nameAndLocation() != nameAndLoc &&
1627- "Trying to create tracker for a genreator that already has one" );
1628+ "Trying to create tracker for a generator that already has one" );
1629
1630 auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>(
1631 CATCH_MOVE(nameAndLoc), m_trackerContext, ¤tTracker );
1632@@ -5771,12 +6115,13 @@ namespace Catch {
1633 return false;
1634 if (m_trackerContext.currentTracker().hasChildren())
1635 return false;
1636- m_totals.assertions.failed++;
1637+ m_atomicAssertionCount.failed++;
1638 assertions.failed++;
1639 return true;
1640 }
1641
1642 void RunContext::sectionEnded(SectionEndInfo&& endInfo) {
1643+ updateTotalsFromAtomics();
1644 Counts assertions = m_totals.assertions - endInfo.prevAssertions;
1645 bool missingAssertions = testForMissingAssertions(assertions);
1646
1647@@ -5785,9 +6130,14 @@ namespace Catch {
1648 m_activeSections.pop_back();
1649 }
1650
1651- m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions));
1652- m_messages.clear();
1653- m_messageScopes.clear();
1654+ {
1655+ auto _ = scopedDeactivate( *m_outputRedirect );
1656+ m_reporter->sectionEnded(
1657+ SectionStats( CATCH_MOVE( endInfo.sectionInfo ),
1658+ assertions,
1659+ endInfo.durationInSeconds,
1660+ missingAssertions ) );
1661+ }
1662 }
1663
1664 void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) {
1665@@ -5802,30 +6152,22 @@ namespace Catch {
1666 }
1667
1668 void RunContext::benchmarkPreparing( StringRef name ) {
1669- m_reporter->benchmarkPreparing(name);
1670+ auto _ = scopedDeactivate( *m_outputRedirect );
1671+ m_reporter->benchmarkPreparing( name );
1672 }
1673 void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
1674+ auto _ = scopedDeactivate( *m_outputRedirect );
1675 m_reporter->benchmarkStarting( info );
1676 }
1677 void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
1678+ auto _ = scopedDeactivate( *m_outputRedirect );
1679 m_reporter->benchmarkEnded( stats );
1680 }
1681 void RunContext::benchmarkFailed( StringRef error ) {
1682+ auto _ = scopedDeactivate( *m_outputRedirect );
1683 m_reporter->benchmarkFailed( error );
1684 }
1685
1686- void RunContext::pushScopedMessage(MessageInfo const & message) {
1687- m_messages.push_back(message);
1688- }
1689-
1690- void RunContext::popScopedMessage(MessageInfo const & message) {
1691- m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
1692- }
1693-
1694- void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
1695- m_messageScopes.emplace_back( CATCH_MOVE(builder) );
1696- }
1697-
1698 std::string RunContext::getCurrentTestName() const {
1699 return m_activeTestCase
1700 ? m_activeTestCase->getTestCaseInfo().name
1701@@ -5833,6 +6175,18 @@ namespace Catch {
1702 }
1703
1704 const AssertionResult * RunContext::getLastResult() const {
1705+ // m_lastResult is updated inside the assertion slow-path, under
1706+ // a mutex, so the read needs to happen under mutex as well.
1707+
1708+ // TBD: The last result only makes sense if it is a thread-local
1709+ // thing, because the answer is different per thread, like
1710+ // last line info, whether last assertion passed, and so on.
1711+ //
1712+ // However, the last result was also never updated in the
1713+ // assertion fast path, so it was always somewhat broken,
1714+ // and since IResultCapture::getLastResult is deprecated,
1715+ // we will leave it as is, until it is finally removed.
1716+ Detail::LockGuard _( m_assertionMutex );
1717 return &(*m_lastResult);
1718 }
1719
1720@@ -5841,18 +6195,46 @@ namespace Catch {
1721 }
1722
1723 void RunContext::handleFatalErrorCondition( StringRef message ) {
1724- // First notify reporter that bad things happened
1725- m_reporter->fatalErrorEncountered(message);
1726+ // We lock only when touching the reporters directly, to avoid
1727+ // deadlocks when we call into other functions that also want
1728+ // to lock the mutex before touching reporters.
1729+ //
1730+ // This does mean that we allow other threads to run while handling
1731+ // a fatal error, but this is all a best effort attempt anyway.
1732+ {
1733+ Detail::LockGuard lock( m_assertionMutex );
1734+ // TODO: scoped deactivate here? Just give up and do best effort?
1735+ // the deactivation can break things further, OTOH so can the
1736+ // capture
1737+ auto _ = scopedDeactivate( *m_outputRedirect );
1738+
1739+ // First notify reporter that bad things happened
1740+ m_reporter->fatalErrorEncountered( message );
1741+ }
1742
1743 // Don't rebuild the result -- the stringification itself can cause more fatal errors
1744 // Instead, fake a result data.
1745 AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
1746 tempResult.message = static_cast<std::string>(message);
1747- AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
1748+ AssertionResult result( makeDummyAssertionInfo(),
1749+ CATCH_MOVE( tempResult ) );
1750
1751 assertionEnded(CATCH_MOVE(result) );
1752- resetAssertionInfo();
1753
1754+
1755+ // At this point we touch sections/test cases from this thread
1756+ // to try and end them. Technically that is not supported when
1757+ // using multiple threads, but the worst thing that can happen
1758+ // is that the process aborts harder :-D
1759+ Detail::LockGuard lock( m_assertionMutex );
1760+
1761+ // Best effort cleanup for sections that have not been destructed yet
1762+ // Since this is a fatal error, we have not had and won't have the opportunity to destruct them properly
1763+ while (!m_activeSections.empty()) {
1764+ auto const& nl = m_activeSections.back()->nameAndLocation();
1765+ SectionEndInfo endInfo{ SectionInfo(nl.location, nl.name), {}, 0.0 };
1766+ sectionEndedEarly(CATCH_MOVE(endInfo));
1767+ }
1768 handleUnfinishedSections();
1769
1770 // Recreate section for test case (as we will lose the one that was in scope)
1771@@ -5862,7 +6244,7 @@ namespace Catch {
1772 Counts assertions;
1773 assertions.failed = 1;
1774 SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false);
1775- m_reporter->sectionEnded(testCaseSectionStats);
1776+ m_reporter->sectionEnded( testCaseSectionStats );
1777
1778 auto const& testInfo = m_activeTestCase->getTestCaseInfo();
1779
1780@@ -5875,47 +6257,49 @@ namespace Catch {
1781 std::string(),
1782 false));
1783 m_totals.testCases.failed++;
1784+ updateTotalsFromAtomics();
1785 m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
1786 }
1787
1788 bool RunContext::lastAssertionPassed() {
1789- return m_lastAssertionPassed;
1790+ return Detail::g_lastAssertionPassed;
1791 }
1792
1793- void RunContext::assertionPassed() {
1794- m_lastAssertionPassed = true;
1795- ++m_totals.assertions.passed;
1796- resetAssertionInfo();
1797- m_messageScopes.clear();
1798+ void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) {
1799+ // We want to save the line info for better experience with unexpected assertions
1800+ Detail::g_lastKnownLineInfo = lineInfo;
1801+ ++m_atomicAssertionCount.passed;
1802+ Detail::g_lastAssertionPassed = true;
1803+ Detail::g_clearMessageScopes = true;
1804+ }
1805+
1806+ void RunContext::updateTotalsFromAtomics() {
1807+ m_totals.assertions = Counts{
1808+ m_atomicAssertionCount.passed,
1809+ m_atomicAssertionCount.failed,
1810+ m_atomicAssertionCount.failedButOk,
1811+ m_atomicAssertionCount.skipped,
1812+ };
1813 }
1814
1815 bool RunContext::aborting() const {
1816- return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
1817+ return m_atomicAssertionCount.failed >= m_abortAfterXFailedAssertions;
1818 }
1819
1820- void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
1821+ void RunContext::runCurrentTest() {
1822 auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
1823 SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
1824 m_reporter->sectionStarting(testCaseSection);
1825+ updateTotalsFromAtomics();
1826 Counts prevAssertions = m_totals.assertions;
1827 double duration = 0;
1828 m_shouldReportUnexpected = true;
1829- m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
1830+ Detail::g_lastKnownLineInfo = testCaseInfo.lineInfo;
1831
1832 Timer timer;
1833 CATCH_TRY {
1834- if (m_reporter->getPreferences().shouldRedirectStdOut) {
1835-#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
1836- RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
1837-
1838- timer.start();
1839- invokeActiveTestCase();
1840-#else
1841- OutputRedirect r(redirectedCout, redirectedCerr);
1842- timer.start();
1843- invokeActiveTestCase();
1844-#endif
1845- } else {
1846+ {
1847+ auto _ = scopedActivate( *m_outputRedirect );
1848 timer.start();
1849 invokeActiveTestCase();
1850 }
1851@@ -5927,18 +6311,23 @@ namespace Catch {
1852 } CATCH_CATCH_ALL {
1853 // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
1854 // are reported without translation at the point of origin.
1855- if( m_shouldReportUnexpected ) {
1856+ if ( m_shouldReportUnexpected ) {
1857 AssertionReaction dummyReaction;
1858- handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
1859+ handleUnexpectedInflightException( makeDummyAssertionInfo(),
1860+ translateActiveException(),
1861+ dummyReaction );
1862 }
1863 }
1864+ updateTotalsFromAtomics();
1865 Counts assertions = m_totals.assertions - prevAssertions;
1866 bool missingAssertions = testForMissingAssertions(assertions);
1867
1868 m_testCaseTracker->close();
1869 handleUnfinishedSections();
1870- m_messages.clear();
1871- m_messageScopes.clear();
1872+ Detail::g_messageScopes.clear();
1873+ // TBD: At this point, m_messages should be empty. Do we want to
1874+ // assert that this is true, or keep the defensive clear call?
1875+ Detail::g_messages.clear();
1876
1877 SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
1878 m_reporter->sectionEnded(testCaseSectionStats);
1879@@ -5960,11 +6349,12 @@ namespace Catch {
1880 void RunContext::handleUnfinishedSections() {
1881 // If sections ended prematurely due to an exception we stored their
1882 // infos here so we can tear them down outside the unwind process.
1883- for (auto it = m_unfinishedSections.rbegin(),
1884- itEnd = m_unfinishedSections.rend();
1885- it != itEnd;
1886- ++it)
1887- sectionEnded(CATCH_MOVE(*it));
1888+ for ( auto it = m_unfinishedSections.rbegin(),
1889+ itEnd = m_unfinishedSections.rend();
1890+ it != itEnd;
1891+ ++it ) {
1892+ sectionEnded( CATCH_MOVE( *it ) );
1893+ }
1894 m_unfinishedSections.clear();
1895 }
1896
1897@@ -5978,7 +6368,7 @@ namespace Catch {
1898
1899 if( result ) {
1900 if (!m_includeSuccessfulResults) {
1901- assertionPassed();
1902+ assertionPassedFastPath(info.lineInfo);
1903 }
1904 else {
1905 reportExpr(info, ResultWas::Ok, &expr, negated);
1906@@ -5986,9 +6376,9 @@ namespace Catch {
1907 }
1908 else {
1909 reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
1910- populateReaction( reaction );
1911+ populateReaction(
1912+ reaction, info.resultDisposition & ResultDisposition::Normal );
1913 }
1914- resetAssertionInfo();
1915 }
1916 void RunContext::reportExpr(
1917 AssertionInfo const &info,
1918@@ -5996,7 +6386,7 @@ namespace Catch {
1919 ITransientExpression const *expr,
1920 bool negated ) {
1921
1922- m_lastAssertionInfo = info;
1923+ Detail::g_lastKnownLineInfo = info.lineInfo;
1924 AssertionResultData data( resultType, LazyExpression( negated ) );
1925
1926 AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
1927@@ -6008,27 +6398,28 @@ namespace Catch {
1928 void RunContext::handleMessage(
1929 AssertionInfo const& info,
1930 ResultWas::OfType resultType,
1931- StringRef message,
1932+ std::string&& message,
1933 AssertionReaction& reaction
1934 ) {
1935- m_lastAssertionInfo = info;
1936+ Detail::g_lastKnownLineInfo = info.lineInfo;
1937
1938 AssertionResultData data( resultType, LazyExpression( false ) );
1939- data.message = static_cast<std::string>(message);
1940- AssertionResult assertionResult{ m_lastAssertionInfo,
1941+ data.message = CATCH_MOVE( message );
1942+ AssertionResult assertionResult{ info,
1943 CATCH_MOVE( data ) };
1944
1945 const auto isOk = assertionResult.isOk();
1946 assertionEnded( CATCH_MOVE(assertionResult) );
1947 if ( !isOk ) {
1948- populateReaction( reaction );
1949+ populateReaction(
1950+ reaction, info.resultDisposition & ResultDisposition::Normal );
1951 } else if ( resultType == ResultWas::ExplicitSkip ) {
1952 // TODO: Need to handle this explicitly, as ExplicitSkip is
1953 // considered "OK"
1954 reaction.shouldSkip = true;
1955 }
1956- resetAssertionInfo();
1957 }
1958+
1959 void RunContext::handleUnexpectedExceptionNotThrown(
1960 AssertionInfo const& info,
1961 AssertionReaction& reaction
1962@@ -6041,55 +6432,88 @@ namespace Catch {
1963 std::string&& message,
1964 AssertionReaction& reaction
1965 ) {
1966- m_lastAssertionInfo = info;
1967+ Detail::g_lastKnownLineInfo = info.lineInfo;
1968
1969 AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
1970 data.message = CATCH_MOVE(message);
1971 AssertionResult assertionResult{ info, CATCH_MOVE(data) };
1972 assertionEnded( CATCH_MOVE(assertionResult) );
1973- populateReaction( reaction );
1974- resetAssertionInfo();
1975+ populateReaction( reaction,
1976+ info.resultDisposition & ResultDisposition::Normal );
1977 }
1978
1979- void RunContext::populateReaction( AssertionReaction& reaction ) {
1980- reaction.shouldDebugBreak = m_config->shouldDebugBreak();
1981- reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
1982+ void RunContext::populateReaction( AssertionReaction& reaction,
1983+ bool has_normal_disposition ) {
1984+ reaction.shouldDebugBreak = m_shouldDebugBreak;
1985+ reaction.shouldThrow = aborting() || has_normal_disposition;
1986+ }
1987+
1988+ AssertionInfo RunContext::makeDummyAssertionInfo() {
1989+ const bool testCaseJustStarted =
1990+ Detail::g_lastKnownLineInfo ==
1991+ m_activeTestCase->getTestCaseInfo().lineInfo;
1992+
1993+ return AssertionInfo{
1994+ testCaseJustStarted ? "TEST_CASE"_sr : StringRef(),
1995+ Detail::g_lastKnownLineInfo,
1996+ testCaseJustStarted ? StringRef() : "{Unknown expression after the reported line}"_sr,
1997+ ResultDisposition::Normal
1998+ };
1999 }
2000
2001 void RunContext::handleIncomplete(
2002 AssertionInfo const& info
2003 ) {
2004 using namespace std::string_literals;
2005- m_lastAssertionInfo = info;
2006+ Detail::g_lastKnownLineInfo = info.lineInfo;
2007
2008 AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
2009 data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
2010 AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
2011 assertionEnded( CATCH_MOVE(assertionResult) );
2012- resetAssertionInfo();
2013 }
2014+
2015 void RunContext::handleNonExpr(
2016 AssertionInfo const &info,
2017 ResultWas::OfType resultType,
2018 AssertionReaction &reaction
2019 ) {
2020- m_lastAssertionInfo = info;
2021-
2022 AssertionResultData data( resultType, LazyExpression( false ) );
2023 AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
2024
2025 const auto isOk = assertionResult.isOk();
2026+ if ( isOk && !m_includeSuccessfulResults ) {
2027+ assertionPassedFastPath( info.lineInfo );
2028+ return;
2029+ }
2030+
2031 assertionEnded( CATCH_MOVE(assertionResult) );
2032- if ( !isOk ) { populateReaction( reaction ); }
2033- resetAssertionInfo();
2034+ if ( !isOk ) {
2035+ populateReaction(
2036+ reaction, info.resultDisposition & ResultDisposition::Normal );
2037+ }
2038 }
2039
2040+ void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
2041+ Detail::g_messages.push_back( CATCH_MOVE( message ) );
2042+ }
2043
2044- IResultCapture& getResultCapture() {
2045- if (auto* capture = getCurrentContext().getResultCapture())
2046- return *capture;
2047- else
2048- CATCH_INTERNAL_ERROR("No result capture instance");
2049+ void IResultCapture::popScopedMessage( unsigned int messageId ) {
2050+ // Note: On average, it would probably be better to look for the message
2051+ // backwards. However, we do not expect to have to deal with more
2052+ // messages than low single digits, so the optimization is tiny,
2053+ // and we would have to hand-write the loop to avoid terrible
2054+ // codegen of reverse iterators in debug mode.
2055+ Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(),
2056+ Detail::g_messages.end(),
2057+ [=]( MessageInfo const& msg ) {
2058+ return msg.sequence ==
2059+ messageId;
2060+ } ) );
2061+ }
2062+
2063+ void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
2064+ Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) );
2065 }
2066
2067 void seedRng(IConfig const& config) {
2068@@ -6315,7 +6739,7 @@ namespace Catch {
2069 std::string origStr = CATCH_MOVE(str);
2070 str.clear();
2071 // There is at least one replacement, so reserve with the best guess
2072- // we can make without actually counting the number of occurences.
2073+ // we can make without actually counting the number of occurrences.
2074 str.reserve(origStr.size() - replaceThis.size() + withThis.size());
2075 do {
2076 str.append(origStr, copyBegin, i-copyBegin );
2077@@ -6361,7 +6785,6 @@ namespace Catch {
2078 #include <algorithm>
2079 #include <ostream>
2080 #include <cstring>
2081-#include <cstdint>
2082
2083 namespace Catch {
2084 StringRef::StringRef( char const* rawChars ) noexcept
2085@@ -6891,6 +7314,8 @@ namespace Catch {
2086 #include <iterator>
2087
2088 namespace Catch {
2089+ void ITestInvoker::prepareTestCase() {}
2090+ void ITestInvoker::tearDownTestCase() {}
2091 ITestInvoker::~ITestInvoker() = default;
2092
2093 namespace {
2094@@ -6927,7 +7352,7 @@ namespace Catch {
2095 TestType m_testAsFunction;
2096
2097 public:
2098- TestInvokerAsFunction( TestType testAsFunction ) noexcept:
2099+ constexpr TestInvokerAsFunction( TestType testAsFunction ) noexcept:
2100 m_testAsFunction( testAsFunction ) {}
2101
2102 void invoke() const override { m_testAsFunction(); }
2103@@ -7207,117 +7632,228 @@ namespace {
2104 return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr;
2105 }
2106
2107- bool isBoundary( std::string const& line, size_t at ) {
2108- assert( at > 0 );
2109- assert( at <= line.size() );
2110-
2111- return at == line.size() ||
2112- ( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) ||
2113- isBreakableBefore( line[at] ) ||
2114- isBreakableAfter( line[at - 1] );
2115- }
2116-
2117 } // namespace
2118
2119 namespace Catch {
2120 namespace TextFlow {
2121+ void AnsiSkippingString::preprocessString() {
2122+ for ( auto it = m_string.begin(); it != m_string.end(); ) {
2123+ // try to read through an ansi sequence
2124+ while ( it != m_string.end() && *it == '\033' &&
2125+ it + 1 != m_string.end() && *( it + 1 ) == '[' ) {
2126+ auto cursor = it + 2;
2127+ while ( cursor != m_string.end() &&
2128+ ( isdigit( *cursor ) || *cursor == ';' ) ) {
2129+ ++cursor;
2130+ }
2131+ if ( cursor == m_string.end() || *cursor != 'm' ) {
2132+ break;
2133+ }
2134+ // 'm' -> 0xff
2135+ *cursor = AnsiSkippingString::sentinel;
2136+ // if we've read an ansi sequence, set the iterator and
2137+ // return to the top of the loop
2138+ it = cursor + 1;
2139+ }
2140+ if ( it != m_string.end() ) {
2141+ ++m_size;
2142+ ++it;
2143+ }
2144+ }
2145+ }
2146+
2147+ AnsiSkippingString::AnsiSkippingString( std::string const& text ):
2148+ m_string( text ) {
2149+ preprocessString();
2150+ }
2151+
2152+ AnsiSkippingString::AnsiSkippingString( std::string&& text ):
2153+ m_string( CATCH_MOVE( text ) ) {
2154+ preprocessString();
2155+ }
2156+
2157+ AnsiSkippingString::const_iterator AnsiSkippingString::begin() const {
2158+ return const_iterator( m_string );
2159+ }
2160+
2161+ AnsiSkippingString::const_iterator AnsiSkippingString::end() const {
2162+ return const_iterator( m_string, const_iterator::EndTag{} );
2163+ }
2164+
2165+ std::string AnsiSkippingString::substring( const_iterator begin,
2166+ const_iterator end ) const {
2167+ // There's one caveat here to an otherwise simple substring: when
2168+ // making a begin iterator we might have skipped ansi sequences at
2169+ // the start. If `begin` here is a begin iterator, skipped over
2170+ // initial ansi sequences, we'll use the true beginning of the
2171+ // string. Lastly: We need to transform any chars we replaced with
2172+ // 0xff back to 'm'
2173+ auto str = std::string( begin == this->begin() ? m_string.begin()
2174+ : begin.m_it,
2175+ end.m_it );
2176+ std::transform( str.begin(), str.end(), str.begin(), []( char c ) {
2177+ return c == AnsiSkippingString::sentinel ? 'm' : c;
2178+ } );
2179+ return str;
2180+ }
2181+
2182+ void AnsiSkippingString::const_iterator::tryParseAnsiEscapes() {
2183+ // check if we've landed on an ansi sequence, and if so read through
2184+ // it
2185+ while ( m_it != m_string->end() && *m_it == '\033' &&
2186+ m_it + 1 != m_string->end() && *( m_it + 1 ) == '[' ) {
2187+ auto cursor = m_it + 2;
2188+ while ( cursor != m_string->end() &&
2189+ ( isdigit( *cursor ) || *cursor == ';' ) ) {
2190+ ++cursor;
2191+ }
2192+ if ( cursor == m_string->end() ||
2193+ *cursor != AnsiSkippingString::sentinel ) {
2194+ break;
2195+ }
2196+ // if we've read an ansi sequence, set the iterator and
2197+ // return to the top of the loop
2198+ m_it = cursor + 1;
2199+ }
2200+ }
2201+
2202+ void AnsiSkippingString::const_iterator::advance() {
2203+ assert( m_it != m_string->end() );
2204+ m_it++;
2205+ tryParseAnsiEscapes();
2206+ }
2207+
2208+ void AnsiSkippingString::const_iterator::unadvance() {
2209+ assert( m_it != m_string->begin() );
2210+ m_it--;
2211+ // if *m_it is 0xff, scan back to the \033 and then m_it-- once more
2212+ // (and repeat check)
2213+ while ( *m_it == AnsiSkippingString::sentinel ) {
2214+ while ( *m_it != '\033' ) {
2215+ assert( m_it != m_string->begin() );
2216+ m_it--;
2217+ }
2218+ // if this happens, we must have been a begin iterator that had
2219+ // skipped over ansi sequences at the start of a string
2220+ assert( m_it != m_string->begin() );
2221+ assert( *m_it == '\033' );
2222+ m_it--;
2223+ }
2224+ }
2225+
2226+ static bool isBoundary( AnsiSkippingString const& line,
2227+ AnsiSkippingString::const_iterator it ) {
2228+ return it == line.end() ||
2229+ ( isWhitespace( *it ) &&
2230+ !isWhitespace( *it.oneBefore() ) ) ||
2231+ isBreakableBefore( *it ) ||
2232+ isBreakableAfter( *it.oneBefore() );
2233+ }
2234
2235 void Column::const_iterator::calcLength() {
2236 m_addHyphen = false;
2237 m_parsedTo = m_lineStart;
2238+ AnsiSkippingString const& current_line = m_column.m_string;
2239
2240- std::string const& current_line = m_column.m_string;
2241- if ( current_line[m_lineStart] == '\n' ) {
2242- ++m_parsedTo;
2243+ if ( m_parsedTo == current_line.end() ) {
2244+ m_lineEnd = m_parsedTo;
2245+ return;
2246 }
2247
2248+ assert( m_lineStart != current_line.end() );
2249+ if ( *m_lineStart == '\n' ) { ++m_parsedTo; }
2250+
2251 const auto maxLineLength = m_column.m_width - indentSize();
2252- const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength);
2253- while ( m_parsedTo < maxParseTo &&
2254- current_line[m_parsedTo] != '\n' ) {
2255+ std::size_t lineLength = 0;
2256+ while ( m_parsedTo != current_line.end() &&
2257+ lineLength < maxLineLength && *m_parsedTo != '\n' ) {
2258 ++m_parsedTo;
2259+ ++lineLength;
2260 }
2261
2262 // If we encountered a newline before the column is filled,
2263 // then we linebreak at the newline and consider this line
2264 // finished.
2265- if ( m_parsedTo < m_lineStart + maxLineLength ) {
2266- m_lineLength = m_parsedTo - m_lineStart;
2267+ if ( lineLength < maxLineLength ) {
2268+ m_lineEnd = m_parsedTo;
2269 } else {
2270 // Look for a natural linebreak boundary in the column
2271 // (We look from the end, so that the first found boundary is
2272 // the right one)
2273- size_t newLineLength = maxLineLength;
2274- while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) {
2275- --newLineLength;
2276+ m_lineEnd = m_parsedTo;
2277+ while ( lineLength > 0 &&
2278+ !isBoundary( current_line, m_lineEnd ) ) {
2279+ --lineLength;
2280+ --m_lineEnd;
2281 }
2282- while ( newLineLength > 0 &&
2283- isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) {
2284- --newLineLength;
2285+ while ( lineLength > 0 &&
2286+ isWhitespace( *m_lineEnd.oneBefore() ) ) {
2287+ --lineLength;
2288+ --m_lineEnd;
2289 }
2290
2291- // If we found one, then that is where we linebreak
2292- if ( newLineLength > 0 ) {
2293- m_lineLength = newLineLength;
2294- } else {
2295- // Otherwise we have to split text with a hyphen
2296+ // If we found one, then that is where we linebreak, otherwise
2297+ // we have to split text with a hyphen
2298+ if ( lineLength == 0 ) {
2299 m_addHyphen = true;
2300- m_lineLength = maxLineLength - 1;
2301+ m_lineEnd = m_parsedTo.oneBefore();
2302 }
2303 }
2304 }
2305
2306 size_t Column::const_iterator::indentSize() const {
2307- auto initial =
2308- m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos;
2309+ auto initial = m_lineStart == m_column.m_string.begin()
2310+ ? m_column.m_initialIndent
2311+ : std::string::npos;
2312 return initial == std::string::npos ? m_column.m_indent : initial;
2313 }
2314
2315- std::string
2316- Column::const_iterator::addIndentAndSuffix( size_t position,
2317- size_t length ) const {
2318+ std::string Column::const_iterator::addIndentAndSuffix(
2319+ AnsiSkippingString::const_iterator start,
2320+ AnsiSkippingString::const_iterator end ) const {
2321 std::string ret;
2322 const auto desired_indent = indentSize();
2323- ret.reserve( desired_indent + length + m_addHyphen );
2324+ // ret.reserve( desired_indent + (end - start) + m_addHyphen );
2325 ret.append( desired_indent, ' ' );
2326- ret.append( m_column.m_string, position, length );
2327- if ( m_addHyphen ) {
2328- ret.push_back( '-' );
2329- }
2330+ // ret.append( start, end );
2331+ ret += m_column.m_string.substring( start, end );
2332+ if ( m_addHyphen ) { ret.push_back( '-' ); }
2333
2334 return ret;
2335 }
2336
2337- Column::const_iterator::const_iterator( Column const& column ): m_column( column ) {
2338+ Column::const_iterator::const_iterator( Column const& column ):
2339+ m_column( column ),
2340+ m_lineStart( column.m_string.begin() ),
2341+ m_lineEnd( column.m_string.begin() ),
2342+ m_parsedTo( column.m_string.begin() ) {
2343 assert( m_column.m_width > m_column.m_indent );
2344 assert( m_column.m_initialIndent == std::string::npos ||
2345 m_column.m_width > m_column.m_initialIndent );
2346 calcLength();
2347- if ( m_lineLength == 0 ) {
2348- m_lineStart = m_column.m_string.size();
2349+ if ( m_lineStart == m_lineEnd ) {
2350+ m_lineStart = m_column.m_string.end();
2351 }
2352 }
2353
2354 std::string Column::const_iterator::operator*() const {
2355 assert( m_lineStart <= m_parsedTo );
2356- return addIndentAndSuffix( m_lineStart, m_lineLength );
2357+ return addIndentAndSuffix( m_lineStart, m_lineEnd );
2358 }
2359
2360 Column::const_iterator& Column::const_iterator::operator++() {
2361- m_lineStart += m_lineLength;
2362- std::string const& current_line = m_column.m_string;
2363- if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) {
2364- m_lineStart += 1;
2365+ m_lineStart = m_lineEnd;
2366+ AnsiSkippingString const& current_line = m_column.m_string;
2367+ if ( m_lineStart != current_line.end() && *m_lineStart == '\n' ) {
2368+ m_lineStart++;
2369 } else {
2370- while ( m_lineStart < current_line.size() &&
2371- isWhitespace( current_line[m_lineStart] ) ) {
2372+ while ( m_lineStart != current_line.end() &&
2373+ isWhitespace( *m_lineStart ) ) {
2374 ++m_lineStart;
2375 }
2376 }
2377
2378- if ( m_lineStart != current_line.size() ) {
2379- calcLength();
2380- }
2381+ if ( m_lineStart != current_line.end() ) { calcLength(); }
2382 return *this;
2383 }
2384
2385@@ -7414,25 +7950,25 @@ namespace Catch {
2386 return os;
2387 }
2388
2389- Columns operator+(Column const& lhs, Column const& rhs) {
2390+ Columns operator+( Column const& lhs, Column const& rhs ) {
2391 Columns cols;
2392 cols += lhs;
2393 cols += rhs;
2394 return cols;
2395 }
2396- Columns operator+(Column&& lhs, Column&& rhs) {
2397+ Columns operator+( Column&& lhs, Column&& rhs ) {
2398 Columns cols;
2399 cols += CATCH_MOVE( lhs );
2400 cols += CATCH_MOVE( rhs );
2401 return cols;
2402 }
2403
2404- Columns& operator+=(Columns& lhs, Column const& rhs) {
2405+ Columns& operator+=( Columns& lhs, Column const& rhs ) {
2406 lhs.m_columns.push_back( rhs );
2407 return lhs;
2408 }
2409- Columns& operator+=(Columns& lhs, Column&& rhs) {
2410- lhs.m_columns.push_back( CATCH_MOVE(rhs) );
2411+ Columns& operator+=( Columns& lhs, Column&& rhs ) {
2412+ lhs.m_columns.push_back( CATCH_MOVE( rhs ) );
2413 return lhs;
2414 }
2415 Columns operator+( Columns const& lhs, Column const& rhs ) {
2416@@ -7545,134 +8081,130 @@ namespace {
2417
2418 void hexEscapeChar(std::ostream& os, unsigned char c) {
2419 std::ios_base::fmtflags f(os.flags());
2420- os << "\\x"
2421+ os << "\\x"_sr
2422 << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
2423 << static_cast<int>(c);
2424 os.flags(f);
2425 }
2426
2427- bool shouldNewline(XmlFormatting fmt) {
2428+ constexpr bool shouldNewline(XmlFormatting fmt) {
2429 return !!(static_cast<std::underlying_type_t<XmlFormatting>>(fmt & XmlFormatting::Newline));
2430 }
2431
2432- bool shouldIndent(XmlFormatting fmt) {
2433+ constexpr bool shouldIndent(XmlFormatting fmt) {
2434 return !!(static_cast<std::underlying_type_t<XmlFormatting>>(fmt & XmlFormatting::Indent));
2435 }
2436
2437 } // anonymous namespace
2438
2439- XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
2440- return static_cast<XmlFormatting>(
2441- static_cast<std::underlying_type_t<XmlFormatting>>(lhs) |
2442- static_cast<std::underlying_type_t<XmlFormatting>>(rhs)
2443- );
2444- }
2445-
2446- XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
2447- return static_cast<XmlFormatting>(
2448- static_cast<std::underlying_type_t<XmlFormatting>>(lhs) &
2449- static_cast<std::underlying_type_t<XmlFormatting>>(rhs)
2450- );
2451- }
2452-
2453-
2454- XmlEncode::XmlEncode( StringRef str, ForWhat forWhat )
2455- : m_str( str ),
2456- m_forWhat( forWhat )
2457- {}
2458-
2459 void XmlEncode::encodeTo( std::ostream& os ) const {
2460 // Apostrophe escaping not necessary if we always use " to write attributes
2461 // (see: http://www.w3.org/TR/xml/#syntax)
2462+ size_t last_start = 0;
2463+ auto write_to = [&]( size_t idx ) {
2464+ if ( last_start < idx ) {
2465+ os << m_str.substr( last_start, idx - last_start );
2466+ }
2467+ last_start = idx + 1;
2468+ };
2469
2470- for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
2471- unsigned char c = static_cast<unsigned char>(m_str[idx]);
2472- switch (c) {
2473- case '<': os << "<"; break;
2474- case '&': os << "&"; break;
2475+ for ( std::size_t idx = 0; idx < m_str.size(); ++idx ) {
2476+ unsigned char c = static_cast<unsigned char>( m_str[idx] );
2477+ switch ( c ) {
2478+ case '<':
2479+ write_to( idx );
2480+ os << "<"_sr;
2481+ break;
2482+ case '&':
2483+ write_to( idx );
2484+ os << "&"_sr;
2485+ break;
2486
2487 case '>':
2488 // See: http://www.w3.org/TR/xml/#syntax
2489- if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
2490- os << ">";
2491- else
2492- os << c;
2493+ if ( idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']' ) {
2494+ write_to( idx );
2495+ os << ">"_sr;
2496+ }
2497 break;
2498
2499 case '\"':
2500- if (m_forWhat == ForAttributes)
2501- os << """;
2502- else
2503- os << c;
2504+ if ( m_forWhat == ForAttributes ) {
2505+ write_to( idx );
2506+ os << """_sr;
2507+ }
2508 break;
2509
2510 default:
2511 // Check for control characters and invalid utf-8
2512
2513 // Escape control characters in standard ascii
2514- // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
2515- if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
2516- hexEscapeChar(os, c);
2517+ // see
2518+ // http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
2519+ if ( c < 0x09 || ( c > 0x0D && c < 0x20 ) || c == 0x7F ) {
2520+ write_to( idx );
2521+ hexEscapeChar( os, c );
2522 break;
2523 }
2524
2525 // Plain ASCII: Write it to stream
2526- if (c < 0x7F) {
2527- os << c;
2528+ if ( c < 0x7F ) {
2529 break;
2530 }
2531
2532 // UTF-8 territory
2533- // Check if the encoding is valid and if it is not, hex escape bytes.
2534- // Important: We do not check the exact decoded values for validity, only the encoding format
2535- // First check that this bytes is a valid lead byte:
2536- // This means that it is not encoded as 1111 1XXX
2537+ // Check if the encoding is valid and if it is not, hex escape
2538+ // bytes. Important: We do not check the exact decoded values for
2539+ // validity, only the encoding format First check that this bytes is
2540+ // a valid lead byte: This means that it is not encoded as 1111 1XXX
2541 // Or as 10XX XXXX
2542- if (c < 0xC0 ||
2543- c >= 0xF8) {
2544- hexEscapeChar(os, c);
2545+ if ( c < 0xC0 || c >= 0xF8 ) {
2546+ write_to( idx );
2547+ hexEscapeChar( os, c );
2548 break;
2549 }
2550
2551- auto encBytes = trailingBytes(c);
2552- // Are there enough bytes left to avoid accessing out-of-bounds memory?
2553- if (idx + encBytes - 1 >= m_str.size()) {
2554- hexEscapeChar(os, c);
2555+ auto encBytes = trailingBytes( c );
2556+ // Are there enough bytes left to avoid accessing out-of-bounds
2557+ // memory?
2558+ if ( idx + encBytes - 1 >= m_str.size() ) {
2559+ write_to( idx );
2560+ hexEscapeChar( os, c );
2561 break;
2562 }
2563 // The header is valid, check data
2564 // The next encBytes bytes must together be a valid utf-8
2565- // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
2566+ // This means: bitpattern 10XX XXXX and the extracted value is sane
2567+ // (ish)
2568 bool valid = true;
2569- uint32_t value = headerValue(c);
2570- for (std::size_t n = 1; n < encBytes; ++n) {
2571- unsigned char nc = static_cast<unsigned char>(m_str[idx + n]);
2572- valid &= ((nc & 0xC0) == 0x80);
2573- value = (value << 6) | (nc & 0x3F);
2574+ uint32_t value = headerValue( c );
2575+ for ( std::size_t n = 1; n < encBytes; ++n ) {
2576+ unsigned char nc = static_cast<unsigned char>( m_str[idx + n] );
2577+ valid &= ( ( nc & 0xC0 ) == 0x80 );
2578+ value = ( value << 6 ) | ( nc & 0x3F );
2579 }
2580
2581 if (
2582 // Wrong bit pattern of following bytes
2583- (!valid) ||
2584+ ( !valid ) ||
2585 // Overlong encodings
2586- (value < 0x80) ||
2587- (0x80 <= value && value < 0x800 && encBytes > 2) ||
2588- (0x800 < value && value < 0x10000 && encBytes > 3) ||
2589+ ( value < 0x80 ) ||
2590+ ( 0x80 <= value && value < 0x800 && encBytes > 2 ) ||
2591+ ( 0x800 < value && value < 0x10000 && encBytes > 3 ) ||
2592 // Encoded value out of range
2593- (value >= 0x110000)
2594- ) {
2595- hexEscapeChar(os, c);
2596+ ( value >= 0x110000 ) ) {
2597+ write_to( idx );
2598+ hexEscapeChar( os, c );
2599 break;
2600 }
2601
2602 // If we got here, this is in fact a valid(ish) utf-8 sequence
2603- for (std::size_t n = 0; n < encBytes; ++n) {
2604- os << m_str[idx + n];
2605- }
2606 idx += encBytes - 1;
2607 break;
2608 }
2609 }
2610+
2611+ write_to( m_str.size() );
2612 }
2613
2614 std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
2615@@ -8077,7 +8609,7 @@ namespace Detail {
2616
2617 std::string WithinRelMatcher::describe() const {
2618 Catch::ReusableStringStream sstr;
2619- sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
2620+ sstr << "and " << ::Catch::Detail::stringify(m_target) << " are within " << m_epsilon * 100. << "% of each other";
2621 return sstr.str();
2622 }
2623
2624@@ -8583,7 +9115,8 @@ private:
2625 << m_config->testSpec()
2626 << '\n';
2627 }
2628- m_stream << "RNG seed: " << getSeed() << '\n';
2629+ m_stream << "RNG seed: " << getSeed() << '\n'
2630+ << std::flush;
2631 }
2632
2633 void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
2634@@ -8817,7 +9350,7 @@ struct RowBreak {};
2635 struct OutputFlush {};
2636
2637 class Duration {
2638- enum class Unit {
2639+ enum class Unit : uint8_t {
2640 Auto,
2641 Nanoseconds,
2642 Microseconds,
2643@@ -8889,7 +9422,10 @@ public:
2644 };
2645 } // end anon namespace
2646
2647-enum class Justification { Left, Right };
2648+enum class Justification : uint8_t {
2649+ Left,
2650+ Right
2651+};
2652
2653 struct ColumnInfo {
2654 std::string name;
2655@@ -9001,7 +9537,10 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config):
2656 { "est run time high mean high std dev", 14, Justification::Right }
2657 };
2658 }
2659- }())) {}
2660+ }())) {
2661+ m_preferences.shouldReportAllAssertionStarts = false;
2662+}
2663+
2664 ConsoleReporter::~ConsoleReporter() = default;
2665
2666 std::string ConsoleReporter::getDescription() {
2667@@ -9016,8 +9555,6 @@ void ConsoleReporter::reportInvalidTestSpec( StringRef arg ) {
2668 m_stream << "Invalid Filter: " << arg << '\n';
2669 }
2670
2671-void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
2672-
2673 void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
2674 AssertionResult const& result = _assertionStats.assertionResult;
2675
2676@@ -9129,7 +9666,8 @@ void ConsoleReporter::testRunStarting(TestRunInfo const& _testRunInfo) {
2677 m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: "
2678 << m_config->testSpec() << '\n';
2679 }
2680- m_stream << "Randomness seeded to: " << getSeed() << '\n';
2681+ m_stream << "Randomness seeded to: " << getSeed() << '\n'
2682+ << std::flush;
2683 }
2684
2685 void ConsoleReporter::lazyPrint() {
2686@@ -9457,6 +9995,7 @@ namespace Catch {
2687
2688 #include <algorithm>
2689 #include <cfloat>
2690+#include <cmath>
2691 #include <cstdio>
2692 #include <ostream>
2693 #include <iomanip>
2694@@ -9620,9 +10159,29 @@ namespace Catch {
2695 out << "All available tags:\n";
2696 }
2697
2698+ // minimum whitespace to pad tag counts, possibly overwritten below
2699+ size_t maxTagCountLen = 2;
2700+
2701+ // determine necessary padding for tag count column
2702+ if ( ! tags.empty() ) {
2703+ const auto maxTagCount =
2704+ std::max_element( tags.begin(),
2705+ tags.end(),
2706+ []( auto const& lhs, auto const& rhs ) {
2707+ return lhs.count < rhs.count;
2708+ } )
2709+ ->count;
2710+
2711+ // more padding necessary for 3+ digits
2712+ if (maxTagCount >= 100) {
2713+ auto numDigits = 1 + std::floor( std::log10( maxTagCount ) );
2714+ maxTagCountLen = static_cast<size_t>( numDigits );
2715+ }
2716+ }
2717+
2718 for ( auto const& tagCount : tags ) {
2719 ReusableStringStream rss;
2720- rss << " " << std::setw( 2 ) << tagCount.count << " ";
2721+ rss << " " << std::setw( maxTagCountLen ) << tagCount.count << " ";
2722 auto str = rss.str();
2723 auto wrapper = TextFlow::Column( tagCount.all() )
2724 .initialIndent( 0 )
2725@@ -9820,6 +10379,8 @@ namespace Catch {
2726 // not, but for machine-parseable reporters I think the answer
2727 // should be yes.
2728 m_preferences.shouldReportAllAssertions = true;
2729+ // We only handle assertions when they end
2730+ m_preferences.shouldReportAllAssertionStarts = false;
2731
2732 m_objectWriters.emplace( m_stream );
2733 m_writers.emplace( Writer::Object );
2734@@ -10049,7 +10610,6 @@ namespace Catch {
2735 endObject();
2736 }
2737
2738- void JsonReporter::assertionStarting( AssertionInfo const& /*assertionInfo*/ ) {}
2739 void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) {
2740 // TODO: There is lot of different things to handle here, but
2741 // we can fill it in later, after we show that the basic
2742@@ -10215,7 +10775,8 @@ namespace Catch {
2743 xml( m_stream )
2744 {
2745 m_preferences.shouldRedirectStdOut = true;
2746- m_preferences.shouldReportAllAssertions = true;
2747+ m_preferences.shouldReportAllAssertions = false;
2748+ m_preferences.shouldReportAllAssertionStarts = false;
2749 m_shouldStoreSuccesfulAssertions = false;
2750 }
2751
2752@@ -10325,7 +10886,7 @@ namespace Catch {
2753 if( !rootName.empty() )
2754 name = rootName + '/' + name;
2755
2756- if( sectionNode.hasAnyAssertions()
2757+ if ( sectionNode.stats.assertions.total() > 0
2758 || !sectionNode.stdOut.empty()
2759 || !sectionNode.stdErr.empty() ) {
2760 XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
2761@@ -10446,6 +11007,8 @@ namespace Catch {
2762 reporterish.getPreferences().shouldRedirectStdOut;
2763 m_preferences.shouldReportAllAssertions |=
2764 reporterish.getPreferences().shouldReportAllAssertions;
2765+ m_preferences.shouldReportAllAssertionStarts |=
2766+ reporterish.getPreferences().shouldReportAllAssertionStarts;
2767 }
2768
2769 void MultiReporter::addListener( IEventListenerPtr&& listener ) {
2770@@ -10713,9 +11276,9 @@ namespace Catch {
2771 if (!rootName.empty())
2772 name = rootName + '/' + name;
2773
2774- if ( sectionNode.hasAnyAssertions()
2775+ if ( sectionNode.stats.assertions.total() > 0
2776 || !sectionNode.stdOut.empty()
2777- || !sectionNode.stdErr.empty() ) {
2778+ || !sectionNode.stdErr.empty() ) {
2779 XmlWriter::ScopedElement e = xml.scopedElement("testCase");
2780 xml.writeAttribute("name"_sr, name);
2781 xml.writeAttribute("duration"_sr, static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
2782@@ -11003,7 +11566,8 @@ namespace Catch {
2783 if ( m_config->testSpec().hasFilters() ) {
2784 m_stream << "# filters: " << m_config->testSpec() << '\n';
2785 }
2786- m_stream << "# rng-seed: " << m_config->rngSeed() << '\n';
2787+ m_stream << "# rng-seed: " << m_config->rngSeed() << '\n'
2788+ << std::flush;
2789 }
2790
2791 void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
2792@@ -11217,6 +11781,7 @@ namespace Catch {
2793 {
2794 m_preferences.shouldRedirectStdOut = true;
2795 m_preferences.shouldReportAllAssertions = true;
2796+ m_preferences.shouldReportAllAssertionStarts = false;
2797 }
2798
2799 XmlReporter::~XmlReporter() = default;
2800@@ -11273,8 +11838,6 @@ namespace Catch {
2801 }
2802 }
2803
2804- void XmlReporter::assertionStarting( AssertionInfo const& ) { }
2805-
2806 void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
2807
2808 AssertionResult const& result = assertionStats.assertionResult;
2809diff --git a/include/catch_amalgamated.hpp b/include/catch_amalgamated.hpp
2810old mode 100755
2811new mode 100644
2812index 7e75a5d..d5eb1bb
2813--- a/include/catch_amalgamated.hpp
2814+++ b/include/catch_amalgamated.hpp
2815@@ -6,8 +6,8 @@
2816
2817 // SPDX-License-Identifier: BSL-1.0
2818
2819-// Catch v3.5.4
2820-// Generated: 2024-04-10 12:03:45.785902
2821+// Catch v3.11.0
2822+// Generated: 2025-09-30 10:49:11.225746
2823 // ----------------------------------------------------------
2824 // This file is an amalgamation of multiple different files.
2825 // You probably shouldn't edit it directly.
2826@@ -101,6 +101,9 @@
2827 #elif defined(linux) || defined(__linux) || defined(__linux__)
2828 # define CATCH_PLATFORM_LINUX
2829
2830+#elif defined(__QNX__)
2831+# define CATCH_PLATFORM_QNX
2832+
2833 #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
2834 # define CATCH_PLATFORM_WINDOWS
2835
2836@@ -150,7 +153,7 @@
2837 # define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
2838 _Pragma( "GCC diagnostic ignored \"-Wshadow\"" )
2839
2840-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
2841+# define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P
2842
2843 #endif
2844
2845@@ -174,35 +177,13 @@
2846 // clang-cl defines _MSC_VER as well as __clang__, which could cause the
2847 // start/stop internal suppression macros to be double defined.
2848 #if defined(__clang__) && !defined(_MSC_VER)
2849-
2850+# define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P
2851 # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
2852 # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
2853-
2854 #endif // __clang__ && !_MSC_VER
2855
2856 #if defined(__clang__)
2857
2858-// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
2859-// which results in calls to destructors being emitted for each temporary,
2860-// without a matching initialization. In practice, this can result in something
2861-// like `std::string::~string` being called on an uninitialized value.
2862-//
2863-// For example, this code will likely segfault under IBM XL:
2864-// ```
2865-// REQUIRE(std::string("12") + "34" == "1234")
2866-// ```
2867-//
2868-// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which
2869-// results in calls to the immediately evaluated lambda expressions to be
2870-// reported as unevaluated lambdas.
2871-// https://developer.nvidia.com/nvidia_bug/3321845.
2872-//
2873-// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
2874-# if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER )
2875-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
2876-# endif
2877-
2878-
2879 # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
2880 _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
2881 _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
2882@@ -213,8 +194,16 @@
2883 # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
2884 _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
2885
2886-# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
2887- _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
2888+# if (__clang_major__ >= 20)
2889+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
2890+ _Pragma( "clang diagnostic ignored \"-Wvariadic-macro-arguments-omitted\"" )
2891+# elif (__clang_major__ == 19)
2892+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
2893+ _Pragma( "clang diagnostic ignored \"-Wc++20-extensions\"" )
2894+# else
2895+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
2896+ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
2897+# endif
2898
2899 # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
2900 _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
2901@@ -227,6 +216,27 @@
2902
2903 #endif // __clang__
2904
2905+// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
2906+// which results in calls to destructors being emitted for each temporary,
2907+// without a matching initialization. In practice, this can result in something
2908+// like `std::string::~string` being called on an uninitialized value.
2909+//
2910+// For example, this code will likely segfault under IBM XL:
2911+// ```
2912+// REQUIRE(std::string("12") + "34" == "1234")
2913+// ```
2914+//
2915+// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which
2916+// results in calls to the immediately evaluated lambda expressions to be
2917+// reported as unevaluated lambdas.
2918+// https://developer.nvidia.com/nvidia_bug/3321845.
2919+//
2920+// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
2921+#if defined( __ibmxl__ ) || defined( __CUDACC__ ) || defined( __NVCOMPILER )
2922+# define CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P
2923+#endif
2924+
2925+
2926
2927 ////////////////////////////////////////////////////////////////////////////////
2928 // We know some environments not to support full POSIX signals
2929@@ -301,13 +311,17 @@
2930 # endif
2931
2932 // Universal Windows platform does not support SEH
2933-// Or console colours (or console at all...)
2934-# if defined(CATCH_PLATFORM_WINDOWS_UWP)
2935-# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
2936-# else
2937+# if !defined(CATCH_PLATFORM_WINDOWS_UWP)
2938 # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
2939 # endif
2940
2941+// Only some Windows platform families support the console
2942+# if defined(WINAPI_FAMILY_PARTITION)
2943+# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
2944+# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
2945+# endif
2946+# endif
2947+
2948 // MSVC traditional preprocessor needs some workaround for __VA_ARGS__
2949 // _MSVC_TRADITIONAL == 0 means new conformant preprocessor
2950 // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
2951@@ -450,6 +464,22 @@
2952 #endif
2953
2954
2955+// The goal of this macro is to avoid evaluation of the arguments, but
2956+// still have the compiler warn on problems inside...
2957+#if defined( CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P ) && \
2958+ !defined( CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P ) && !defined(CATCH_CONFIG_USE_BUILTIN_CONSTANT_P)
2959+#define CATCH_CONFIG_USE_BUILTIN_CONSTANT_P
2960+#endif
2961+
2962+#if defined( CATCH_CONFIG_USE_BUILTIN_CONSTANT_P ) && \
2963+ !defined( CATCH_CONFIG_NO_USE_BUILTIN_CONSTANT_P )
2964+# define CATCH_INTERNAL_IGNORE_BUT_WARN( ... ) \
2965+ (void)__builtin_constant_p( __VA_ARGS__ ) /* NOLINT(cppcoreguidelines-pro-type-vararg, \
2966+ hicpp-vararg) */
2967+#else
2968+# define CATCH_INTERNAL_IGNORE_BUT_WARN( ... )
2969+#endif
2970+
2971 // Even if we do not think the compiler has that warning, we still have
2972 // to provide a macro that can be used by the code.
2973 #if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
2974@@ -486,13 +516,6 @@
2975 # define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS
2976 #endif
2977
2978-
2979-// The goal of this macro is to avoid evaluation of the arguments, but
2980-// still have the compiler warn on problems inside...
2981-#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
2982-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
2983-#endif
2984-
2985 #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
2986 # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
2987 #elif defined(__clang__) && (__clang_major__ < 5)
2988@@ -548,31 +571,27 @@ namespace Catch {
2989 IConfig const* m_config = nullptr;
2990 IResultCapture* m_resultCapture = nullptr;
2991
2992- CATCH_EXPORT static Context* currentContext;
2993+ CATCH_EXPORT static Context currentContext;
2994 friend Context& getCurrentMutableContext();
2995 friend Context const& getCurrentContext();
2996- static void createContext();
2997- friend void cleanUpContext();
2998
2999 public:
3000- IResultCapture* getResultCapture() const { return m_resultCapture; }
3001- IConfig const* getConfig() const { return m_config; }
3002- void setResultCapture( IResultCapture* resultCapture );
3003- void setConfig( IConfig const* config );
3004+ constexpr IResultCapture* getResultCapture() const {
3005+ return m_resultCapture;
3006+ }
3007+ constexpr IConfig const* getConfig() const { return m_config; }
3008+ constexpr void setResultCapture( IResultCapture* resultCapture ) {
3009+ m_resultCapture = resultCapture;
3010+ }
3011+ constexpr void setConfig( IConfig const* config ) { m_config = config; }
3012 };
3013
3014 Context& getCurrentMutableContext();
3015
3016 inline Context const& getCurrentContext() {
3017- // We duplicate the logic from `getCurrentMutableContext` here,
3018- // to avoid paying the call overhead in debug mode.
3019- if ( !Context::currentContext ) { Context::createContext(); }
3020- // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
3021- return *Context::currentContext;
3022+ return Context::currentContext;
3023 }
3024
3025- void cleanUpContext();
3026-
3027 class SimplePcg32;
3028 SimplePcg32& sharedRng();
3029 }
3030@@ -669,7 +688,6 @@ namespace Catch {
3031 #define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
3032
3033 #include <string>
3034-#include <chrono>
3035
3036
3037
3038@@ -819,8 +837,10 @@ namespace Catch {
3039
3040 }; };
3041
3042- bool isOk( ResultWas::OfType resultType );
3043- bool isJustInfo( int flags );
3044+ constexpr bool isOk( ResultWas::OfType resultType ) {
3045+ return ( resultType & ResultWas::FailureBit ) == 0;
3046+ }
3047+ constexpr bool isJustInfo( int flags ) { return flags == ResultWas::Info; }
3048
3049
3050 // ResultDisposition::Flags enum
3051@@ -832,11 +852,18 @@ namespace Catch {
3052 SuppressFail = 0x08 // Failures are reported but do not fail the test
3053 }; };
3054
3055- ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
3056+ constexpr ResultDisposition::Flags operator|( ResultDisposition::Flags lhs,
3057+ ResultDisposition::Flags rhs ) {
3058+ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) |
3059+ static_cast<int>( rhs ) );
3060+ }
3061
3062- bool shouldContinueOnFailure( int flags );
3063- inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
3064- bool shouldSuppressFailure( int flags );
3065+ constexpr bool isFalseTest( int flags ) {
3066+ return ( flags & ResultDisposition::FalseTest ) != 0;
3067+ }
3068+ constexpr bool shouldSuppressFailure( int flags ) {
3069+ return ( flags & ResultDisposition::SuppressFail ) != 0;
3070+ }
3071
3072 } // end namespace Catch
3073
3074@@ -929,7 +956,7 @@ namespace Detail {
3075 }
3076
3077 explicit operator bool() const {
3078- return m_ptr;
3079+ return m_ptr != nullptr;
3080 }
3081
3082 friend void swap(unique_ptr& lhs, unique_ptr& rhs) {
3083@@ -1040,10 +1067,9 @@ namespace Catch {
3084 virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
3085 virtual void benchmarkFailed( StringRef error ) = 0;
3086
3087- virtual void pushScopedMessage( MessageInfo const& message ) = 0;
3088- virtual void popScopedMessage( MessageInfo const& message ) = 0;
3089-
3090- virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
3091+ static void pushScopedMessage( MessageInfo&& message );
3092+ static void popScopedMessage( unsigned int messageId );
3093+ static void emplaceUnscopedMessage( MessageBuilder&& builder );
3094
3095 virtual void handleFatalErrorCondition( StringRef message ) = 0;
3096
3097@@ -1054,7 +1080,7 @@ namespace Catch {
3098 virtual void handleMessage
3099 ( AssertionInfo const& info,
3100 ResultWas::OfType resultType,
3101- StringRef message,
3102+ std::string&& message,
3103 AssertionReaction& reaction ) = 0;
3104 virtual void handleUnexpectedExceptionNotThrown
3105 ( AssertionInfo const& info,
3106@@ -1071,9 +1097,7 @@ namespace Catch {
3107 AssertionReaction &reaction ) = 0;
3108
3109
3110-
3111 virtual bool lastAssertionPassed() = 0;
3112- virtual void assertionPassed() = 0;
3113
3114 // Deprecated, do not use:
3115 virtual std::string getCurrentTestName() const = 0;
3116@@ -1081,7 +1105,18 @@ namespace Catch {
3117 virtual void exceptionEarlyReported() = 0;
3118 };
3119
3120- IResultCapture& getResultCapture();
3121+ namespace Detail {
3122+ [[noreturn]]
3123+ void missingCaptureInstance();
3124+ }
3125+ inline IResultCapture& getResultCapture() {
3126+ if (auto* capture = getCurrentContext().getResultCapture()) {
3127+ return *capture;
3128+ } else {
3129+ Detail::missingCaptureInstance();
3130+ }
3131+ }
3132+
3133 }
3134
3135 #endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
3136@@ -1100,6 +1135,7 @@ namespace Catch {
3137
3138 //! Deriving classes become noncopyable and nonmovable
3139 class NonCopyable {
3140+ public:
3141 NonCopyable( NonCopyable const& ) = delete;
3142 NonCopyable( NonCopyable&& ) = delete;
3143 NonCopyable& operator=( NonCopyable const& ) = delete;
3144@@ -1115,7 +1151,6 @@ namespace Catch {
3145 #endif // CATCH_NONCOPYABLE_HPP_INCLUDED
3146
3147 #include <chrono>
3148-#include <iosfwd>
3149 #include <string>
3150 #include <vector>
3151
3152@@ -1302,7 +1337,7 @@ namespace Catch {
3153 int high_mild = 0; // 1.5 to 3 times IQR above Q3
3154 int high_severe = 0; // more than 3 times IQR above Q3
3155
3156- int total() const {
3157+ constexpr int total() const {
3158 return low_severe + low_mild + high_mild + high_severe;
3159 }
3160 };
3161@@ -1570,8 +1605,7 @@ namespace Catch {
3162 namespace Benchmark {
3163 namespace Detail {
3164 template <typename T, typename U>
3165- struct is_related
3166- : std::is_same<std::decay_t<T>, std::decay_t<U>> {};
3167+ static constexpr bool is_related_v = std::is_same<std::decay_t<T>, std::decay_t<U>>::value;
3168
3169 /// We need to reinvent std::function because every piece of code that might add overhead
3170 /// in a measurement context needs to have consistent performance characteristics so that we
3171@@ -1584,22 +1618,17 @@ namespace Catch {
3172 private:
3173 struct callable {
3174 virtual void call(Chronometer meter) const = 0;
3175- virtual Catch::Detail::unique_ptr<callable> clone() const = 0;
3176 virtual ~callable(); // = default;
3177
3178 callable() = default;
3179- callable(callable const&) = default;
3180- callable& operator=(callable const&) = default;
3181+ callable(callable&&) = default;
3182+ callable& operator=(callable&&) = default;
3183 };
3184 template <typename Fun>
3185 struct model : public callable {
3186 model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
3187 model(Fun const& fun_) : fun(fun_) {}
3188
3189- Catch::Detail::unique_ptr<callable> clone() const override {
3190- return Catch::Detail::make_unique<model<Fun>>( *this );
3191- }
3192-
3193 void call(Chronometer meter) const override {
3194 call(meter, is_callable<Fun(Chronometer)>());
3195 }
3196@@ -1613,37 +1642,23 @@ namespace Catch {
3197 Fun fun;
3198 };
3199
3200- struct do_nothing { void operator()() const {} };
3201-
3202- template <typename T>
3203- BenchmarkFunction(model<T>* c) : f(c) {}
3204-
3205 public:
3206- BenchmarkFunction()
3207- : f(new model<do_nothing>{ {} }) {}
3208+ BenchmarkFunction();
3209
3210 template <typename Fun,
3211- std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0>
3212+ std::enable_if_t<!is_related_v<Fun, BenchmarkFunction>, int> = 0>
3213 BenchmarkFunction(Fun&& fun)
3214 : f(new model<std::decay_t<Fun>>(CATCH_FORWARD(fun))) {}
3215
3216 BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
3217 f( CATCH_MOVE( that.f ) ) {}
3218
3219- BenchmarkFunction(BenchmarkFunction const& that)
3220- : f(that.f->clone()) {}
3221-
3222 BenchmarkFunction&
3223 operator=( BenchmarkFunction&& that ) noexcept {
3224 f = CATCH_MOVE( that.f );
3225 return *this;
3226 }
3227
3228- BenchmarkFunction& operator=(BenchmarkFunction const& that) {
3229- f = that.f->clone();
3230- return *this;
3231- }
3232-
3233 void operator()(Chronometer meter) const { f->call(meter); }
3234
3235 private:
3236@@ -1757,8 +1772,6 @@ namespace Catch {
3237 #define CATCH_TIMING_HPP_INCLUDED
3238
3239
3240-#include <type_traits>
3241-
3242 namespace Catch {
3243 namespace Benchmark {
3244 template <typename Result>
3245@@ -1780,7 +1793,7 @@ namespace Catch {
3246 template <typename Clock, typename Fun, typename... Args>
3247 TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) {
3248 auto start = Clock::now();
3249- auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
3250+ auto&& r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...);
3251 auto end = Clock::now();
3252 auto delta = end - start;
3253 return { delta, CATCH_FORWARD(r), 1 };
3254@@ -1946,15 +1959,17 @@ namespace Catch {
3255 namespace Detail {
3256 template <typename Clock>
3257 std::vector<double> resolution(int k) {
3258- std::vector<TimePoint<Clock>> times;
3259- times.reserve(static_cast<size_t>(k + 1));
3260- for ( int i = 0; i < k + 1; ++i ) {
3261- times.push_back( Clock::now() );
3262+ const size_t points = static_cast<size_t>( k + 1 );
3263+ // To avoid overhead from the branch inside vector::push_back,
3264+ // we allocate them all and then overwrite.
3265+ std::vector<TimePoint<Clock>> times(points);
3266+ for ( auto& time : times ) {
3267+ time = Clock::now();
3268 }
3269
3270 std::vector<double> deltas;
3271 deltas.reserve(static_cast<size_t>(k));
3272- for ( size_t idx = 1; idx < times.size(); ++idx ) {
3273+ for ( size_t idx = 1; idx < points; ++idx ) {
3274 deltas.push_back( static_cast<double>(
3275 ( times[idx] - times[idx - 1] ).count() ) );
3276 }
3277@@ -2103,12 +2118,12 @@ namespace Catch {
3278 : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
3279
3280 template <typename Clock>
3281- ExecutionPlan prepare(const IConfig &cfg, Environment env) const {
3282+ ExecutionPlan prepare(const IConfig &cfg, Environment env) {
3283 auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
3284 auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
3285 auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
3286 int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
3287- return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
3288+ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
3289 }
3290
3291 template <typename Clock = default_clock>
3292@@ -2144,9 +2159,7 @@ namespace Catch {
3293 auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
3294 BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
3295 getResultCapture().benchmarkEnded(stats);
3296- } CATCH_CATCH_ANON (TestFailureException const&) {
3297- getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
3298- } CATCH_CATCH_ALL{
3299+ } CATCH_CATCH_ALL {
3300 getResultCapture().benchmarkFailed(translateActiveException());
3301 // We let the exception go further up so that the
3302 // test case is marked as failed.
3303@@ -2155,7 +2168,7 @@ namespace Catch {
3304 }
3305
3306 // sets lambda to be used in fun *and* executes benchmark!
3307- template <typename Fun, std::enable_if_t<!Detail::is_related<Fun, Benchmark>::value, int> = 0>
3308+ template <typename Fun, std::enable_if_t<!Detail::is_related_v<Fun, Benchmark>, int> = 0>
3309 Benchmark & operator=(Fun func) {
3310 auto const* cfg = getCurrentContext().getConfig();
3311 if (!cfg->skipBenchmarks()) {
3312@@ -2484,18 +2497,14 @@ namespace Catch {
3313 return rawMemoryToString( &object, sizeof(object) );
3314 }
3315
3316- template<typename T>
3317- class IsStreamInsertable {
3318- template<typename Stream, typename U>
3319- static auto test(int)
3320- -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
3321-
3322- template<typename, typename>
3323- static auto test(...)->std::false_type;
3324+ template<typename T,typename = void>
3325+ static constexpr bool IsStreamInsertable_v = false;
3326
3327- public:
3328- static const bool value = decltype(test<std::ostream, const T&>(0))::value;
3329- };
3330+ template <typename T>
3331+ static constexpr bool IsStreamInsertable_v<
3332+ T,
3333+ decltype( void( std::declval<std::ostream&>() << std::declval<T>() ) )> =
3334+ true;
3335
3336 template<typename E>
3337 std::string convertUnknownEnumToString( E e );
3338@@ -2540,7 +2549,7 @@ namespace Catch {
3339 struct StringMaker {
3340 template <typename Fake = T>
3341 static
3342- std::enable_if_t<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
3343+ std::enable_if_t<::Catch::Detail::IsStreamInsertable_v<Fake>, std::string>
3344 convert(const Fake& value) {
3345 ReusableStringStream rss;
3346 // NB: call using the function-like syntax to avoid ambiguity with
3347@@ -2551,7 +2560,7 @@ namespace Catch {
3348
3349 template <typename Fake = T>
3350 static
3351- std::enable_if_t<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
3352+ std::enable_if_t<!::Catch::Detail::IsStreamInsertable_v<Fake>, std::string>
3353 convert( const Fake& value ) {
3354 #if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
3355 return Detail::convertUnstreamable(value);
3356@@ -2563,11 +2572,17 @@ namespace Catch {
3357
3358 namespace Detail {
3359
3360+ std::string makeExceptionHappenedString();
3361+
3362 // This function dispatches all stringification requests inside of Catch.
3363 // Should be preferably called fully qualified, like ::Catch::Detail::stringify
3364 template <typename T>
3365- std::string stringify(const T& e) {
3366- return ::Catch::StringMaker<std::remove_cv_t<std::remove_reference_t<T>>>::convert(e);
3367+ std::string stringify( const T& e ) {
3368+ CATCH_TRY {
3369+ return ::Catch::StringMaker<
3370+ std::remove_cv_t<std::remove_reference_t<T>>>::convert( e );
3371+ }
3372+ CATCH_CATCH_ALL { return makeExceptionHappenedString(); }
3373 }
3374
3375 template<typename E>
3376@@ -2943,7 +2958,7 @@ namespace Catch {
3377 }
3378
3379 template<typename R>
3380- struct StringMaker<R, std::enable_if_t<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>> {
3381+ struct StringMaker<R, std::enable_if_t<is_range<R>::value && !::Catch::Detail::IsStreamInsertable_v<R>>> {
3382 static std::string convert( R const& range ) {
3383 return rangeToString( range );
3384 }
3385@@ -3050,13 +3065,19 @@ struct ratio_string<std::milli> {
3386 template<typename Duration>
3387 struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
3388 static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
3389- auto converted = std::chrono::system_clock::to_time_t(time_point);
3390+ const auto systemish = std::chrono::time_point_cast<
3391+ std::chrono::system_clock::duration>( time_point );
3392+ const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
3393
3394 #ifdef _MSC_VER
3395 std::tm timeInfo = {};
3396- gmtime_s(&timeInfo, &converted);
3397+ const auto err = gmtime_s( &timeInfo, &as_time_t );
3398+ if ( err ) {
3399+ return "gmtime from provided timepoint has failed. This "
3400+ "happens e.g. with pre-1970 dates using Microsoft libc";
3401+ }
3402 #else
3403- std::tm* timeInfo = std::gmtime(&converted);
3404+ std::tm* timeInfo = std::gmtime( &as_time_t );
3405 #endif
3406
3407 auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
3408@@ -3284,13 +3305,13 @@ namespace Catch {
3409 ITransientExpression const* m_transientExpression = nullptr;
3410 bool m_isNegated;
3411 public:
3412- LazyExpression( bool isNegated ):
3413+ constexpr LazyExpression( bool isNegated ):
3414 m_isNegated(isNegated)
3415 {}
3416- LazyExpression(LazyExpression const& other) = default;
3417+ constexpr LazyExpression(LazyExpression const& other) = default;
3418 LazyExpression& operator = ( LazyExpression const& ) = delete;
3419
3420- explicit operator bool() const {
3421+ constexpr explicit operator bool() const {
3422 return m_transientExpression != nullptr;
3423 }
3424
3425@@ -3347,6 +3368,18 @@ namespace Catch {
3426 #endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED
3427
3428
3429+#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED
3430+#define CATCH_CASE_SENSITIVE_HPP_INCLUDED
3431+
3432+namespace Catch {
3433+
3434+ enum class CaseSensitive { Yes, No };
3435+
3436+} // namespace Catch
3437+
3438+#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED
3439+
3440+
3441 #ifndef CATCH_CONFIG_HPP_INCLUDED
3442 #define CATCH_CONFIG_HPP_INCLUDED
3443
3444@@ -3366,18 +3399,6 @@ namespace Catch {
3445 #define CATCH_WILDCARD_PATTERN_HPP_INCLUDED
3446
3447
3448-
3449-#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED
3450-#define CATCH_CASE_SENSITIVE_HPP_INCLUDED
3451-
3452-namespace Catch {
3453-
3454- enum class CaseSensitive { Yes, No };
3455-
3456-} // namespace Catch
3457-
3458-#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED
3459-
3460 #include <string>
3461
3462 namespace Catch
3463@@ -3776,7 +3797,7 @@ namespace Catch {
3464 WarnAbout::What warnings = WarnAbout::Nothing;
3465 ShowDurations showDurations = ShowDurations::DefaultForReporter;
3466 double minDuration = -1;
3467- TestRunOrder runOrder = TestRunOrder::Declared;
3468+ TestRunOrder runOrder = TestRunOrder::Randomized;
3469 ColourMode defaultColourMode = ColourMode::PlatformDefault;
3470 WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
3471
3472@@ -3787,6 +3808,8 @@ namespace Catch {
3473
3474 std::vector<std::string> testsOrTags;
3475 std::vector<std::string> sectionsToRun;
3476+
3477+ std::string prematureExitGuardFilePath;
3478 };
3479
3480
3481@@ -3814,6 +3837,8 @@ namespace Catch {
3482
3483 bool showHelp() const;
3484
3485+ std::string const& getExitGuardFilePath() const;
3486+
3487 // IConfig interface
3488 bool allowThrows() const override;
3489 StringRef name() const override;
3490@@ -3922,6 +3947,19 @@ namespace Catch {
3491 #define CATCH_MESSAGE_INFO_HPP_INCLUDED
3492
3493
3494+
3495+#ifndef CATCH_DEPRECATION_MACRO_HPP_INCLUDED
3496+#define CATCH_DEPRECATION_MACRO_HPP_INCLUDED
3497+
3498+
3499+#if !defined( CATCH_CONFIG_NO_DEPRECATION_ANNOTATIONS )
3500+# define DEPRECATED( msg ) [[deprecated( msg )]]
3501+#else
3502+# define DEPRECATED( msg )
3503+#endif
3504+
3505+#endif // CATCH_DEPRECATION_MACRO_HPP_INCLUDED
3506+
3507 #include <string>
3508
3509 namespace Catch {
3510@@ -3935,16 +3973,19 @@ namespace Catch {
3511 std::string message;
3512 SourceLineInfo lineInfo;
3513 ResultWas::OfType type;
3514+ // The "ID" of the message, used to know when to remove it from reporter context.
3515 unsigned int sequence;
3516
3517+ DEPRECATED( "Explicitly use the 'sequence' member instead" )
3518 bool operator == (MessageInfo const& other) const {
3519 return sequence == other.sequence;
3520 }
3521+ DEPRECATED( "Explicitly use the 'sequence' member instead" )
3522 bool operator < (MessageInfo const& other) const {
3523 return sequence < other.sequence;
3524 }
3525 private:
3526- static unsigned int globalCount;
3527+ static thread_local unsigned int globalCount;
3528 };
3529
3530 } // end namespace Catch
3531@@ -3992,13 +4033,12 @@ namespace Catch {
3532 ScopedMessage( ScopedMessage&& old ) noexcept;
3533 ~ScopedMessage();
3534
3535- MessageInfo m_info;
3536+ unsigned int m_messageId;
3537 bool m_moved = false;
3538 };
3539
3540 class Capturer {
3541 std::vector<MessageInfo> m_messages;
3542- IResultCapture& m_resultCapture;
3543 size_t m_captured = 0;
3544 public:
3545 Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
3546@@ -4029,7 +4069,7 @@ namespace Catch {
3547 do { \
3548 Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
3549 catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
3550- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
3551+ catchAssertionHandler.complete(); \
3552 } while( false )
3553
3554 ///////////////////////////////////////////////////////////////////////////////
3555@@ -4046,7 +4086,7 @@ namespace Catch {
3556
3557 ///////////////////////////////////////////////////////////////////////////////
3558 #define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
3559- Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
3560+ Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
3561
3562
3563 #if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
3564@@ -4221,15 +4261,13 @@ namespace Catch {
3565 };
3566
3567 template <typename F, typename = void>
3568- struct is_unary_function : std::false_type {};
3569+ static constexpr bool is_unary_function_v = false;
3570
3571 template <typename F>
3572- struct is_unary_function<
3573+ static constexpr bool is_unary_function_v<
3574 F,
3575- Catch::Detail::void_t<decltype(
3576- std::declval<F>()( fake_arg() ) )
3577- >
3578- > : std::true_type {};
3579+ Catch::Detail::void_t<decltype( std::declval<F>()(
3580+ fake_arg() ) )>> = true;
3581
3582 // Traits for extracting arg and return type of lambdas (for single
3583 // argument lambdas)
3584@@ -4662,14 +4700,14 @@ namespace Catch {
3585
3586 template <typename T,
3587 typename = typename std::enable_if_t<
3588- !Detail::is_unary_function<T>::value>>
3589+ !Detail::is_unary_function_v<T>>>
3590 ParserRefImpl( T& ref, StringRef hint ):
3591 m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
3592 m_hint( hint ) {}
3593
3594 template <typename LambdaT,
3595 typename = typename std::enable_if_t<
3596- Detail::is_unary_function<LambdaT>::value>>
3597+ Detail::is_unary_function_v<LambdaT>>>
3598 ParserRefImpl( LambdaT const& ref, StringRef hint ):
3599 m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
3600 m_hint( hint ) {}
3601@@ -4736,7 +4774,7 @@ namespace Catch {
3602
3603 template <typename LambdaT,
3604 typename = typename std::enable_if_t<
3605- Detail::is_unary_function<LambdaT>::value>>
3606+ Detail::is_unary_function_v<LambdaT>>>
3607 Opt( LambdaT const& ref, StringRef hint ):
3608 ParserRefImpl( ref, hint ) {}
3609
3610@@ -4746,7 +4784,7 @@ namespace Catch {
3611
3612 template <typename T,
3613 typename = typename std::enable_if_t<
3614- !Detail::is_unary_function<T>::value>>
3615+ !Detail::is_unary_function_v<T>>>
3616 Opt( T& ref, StringRef hint ):
3617 ParserRefImpl( ref, hint ) {}
3618
3619@@ -4916,6 +4954,14 @@ namespace Catch {
3620
3621 namespace Catch {
3622
3623+ // TODO: Use C++17 `inline` variables
3624+ constexpr int UnspecifiedErrorExitCode = 1;
3625+ constexpr int NoTestsRunExitCode = 2;
3626+ constexpr int UnmatchedTestSpecExitCode = 3;
3627+ constexpr int AllTestsSkippedExitCode = 4;
3628+ constexpr int InvalidTestSpecExitCode = 5;
3629+ constexpr int TestFailureExitCode = 42;
3630+
3631 class Session : Detail::NonCopyable {
3632 public:
3633
3634@@ -5000,7 +5046,7 @@ namespace Catch {
3635 #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
3636 CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
3637 CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
3638- namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
3639+ namespace{ const Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
3640 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
3641
3642 #endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
3643@@ -5177,7 +5223,7 @@ namespace Detail {
3644 * when the compiler handles `ExprLhs<T> == b`, it also tries to resolve
3645 * the overload set for `b == ExprLhs<T>`.
3646 *
3647- * To accomodate these use cases, decomposer ended up rather complex.
3648+ * To accommodate these use cases, decomposer ended up rather complex.
3649 *
3650 * 1) These types are handled by adding SFINAE overloads to our comparison
3651 * operators, checking whether `T == U` are comparable with the given
3652@@ -5210,10 +5256,11 @@ namespace Detail {
3653 *
3654 * 3) If a type has no linkage, we also cannot capture it by reference.
3655 * The solution is once again to capture them by value. We handle
3656- * the common cases by using `std::is_arithmetic` as the default
3657- * for `Catch::capture_by_value`, but that is only a some-effort
3658- * heuristic. But as with 2), users can specialize `capture_by_value`
3659- * for their own types as needed.
3660+ * the common cases by using `std::is_arithmetic` and `std::is_enum`
3661+ * as the default for `Catch::capture_by_value`, but that is only a
3662+ * some-effort heuristic. These combine to capture all possible bitfield
3663+ * bases, and also some trait-like types. As with 2), users can
3664+ * specialize `capture_by_value` for their own types as needed.
3665 *
3666 * 4) To support C++20 and make the SFINAE on our decomposing operators
3667 * work, the SFINAE has to happen in return type, rather than in
3668@@ -5242,9 +5289,11 @@ namespace Detail {
3669 #ifdef __clang__
3670 # pragma clang diagnostic push
3671 # pragma clang diagnostic ignored "-Wsign-compare"
3672+# pragma clang diagnostic ignored "-Wnon-virtual-dtor"
3673 #elif defined __GNUC__
3674 # pragma GCC diagnostic push
3675 # pragma GCC diagnostic ignored "-Wsign-compare"
3676+# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
3677 #endif
3678
3679 #if defined(CATCH_CPP20_OR_GREATER) && __has_include(<compare>)
3680@@ -5263,13 +5312,22 @@ namespace Catch {
3681 using RemoveCVRef_t = std::remove_cv_t<std::remove_reference_t<T>>;
3682 }
3683
3684- // Note: There is nothing that stops us from extending this,
3685- // e.g. to `std::is_scalar`, but the more encompassing
3686- // traits are usually also more expensive. For now we
3687- // keep this as it used to be and it can be changed later.
3688+ // Note: This is about as much as we can currently reasonably support.
3689+ // In an ideal world, we could capture by value small trivially
3690+ // copyable types, but the actual `std::is_trivially_copyable`
3691+ // trait is a huge mess with standard-violating results on
3692+ // GCC and Clang, which are unlikely to be fixed soon due to ABI
3693+ // concerns.
3694+ // `std::is_scalar` also causes issues due to the `is_pointer`
3695+ // component, which causes ambiguity issues with (references-to)
3696+ // function pointer. If those are resolved, we still need to
3697+ // disambiguate the overload set for arrays, through explicit
3698+ // overload for references to sized arrays.
3699 template <typename T>
3700 struct capture_by_value
3701- : std::integral_constant<bool, std::is_arithmetic<T>{}> {};
3702+ : std::integral_constant<bool,
3703+ std::is_arithmetic<T>::value ||
3704+ std::is_enum<T>::value> {};
3705
3706 #if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )
3707 template <>
3708@@ -5287,10 +5345,13 @@ namespace Catch {
3709 bool m_isBinaryExpression;
3710 bool m_result;
3711
3712+ protected:
3713+ ~ITransientExpression() = default;
3714+
3715 public:
3716 constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
3717 constexpr auto getResult() const -> bool { return m_result; }
3718- //! This function **has** to be overriden by the derived class.
3719+ //! This function **has** to be overridden by the derived class.
3720 virtual void streamReconstructedExpression( std::ostream& os ) const;
3721
3722 constexpr ITransientExpression( bool isBinaryExpression, bool result )
3723@@ -5298,17 +5359,13 @@ namespace Catch {
3724 m_result( result )
3725 {}
3726
3727- ITransientExpression() = default;
3728- ITransientExpression(ITransientExpression const&) = default;
3729- ITransientExpression& operator=(ITransientExpression const&) = default;
3730+ constexpr ITransientExpression( ITransientExpression const& ) = default;
3731+ constexpr ITransientExpression& operator=( ITransientExpression const& ) = default;
3732
3733 friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {
3734 expr.streamReconstructedExpression(out);
3735 return out;
3736 }
3737-
3738- protected:
3739- ~ITransientExpression() = default;
3740 };
3741
3742 void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
3743@@ -5617,12 +5674,12 @@ namespace Catch {
3744
3745
3746 template<typename T>
3747- void handleExpr( ExprLhs<T> const& expr ) {
3748+ constexpr void handleExpr( ExprLhs<T> const& expr ) {
3749 handleExpr( expr.makeUnaryExpr() );
3750 }
3751 void handleExpr( ITransientExpression const& expr );
3752
3753- void handleMessage(ResultWas::OfType resultType, StringRef message);
3754+ void handleMessage(ResultWas::OfType resultType, std::string&& message);
3755
3756 void handleExceptionThrownAsExpected();
3757 void handleUnexpectedExceptionNotThrown();
3758@@ -5678,8 +5735,6 @@ namespace Catch {
3759
3760 #endif
3761
3762-#define INTERNAL_CATCH_REACT( handler ) handler.complete();
3763-
3764 ///////////////////////////////////////////////////////////////////////////////
3765 #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
3766 do { /* NOLINT(bugprone-infinite-loop) */ \
3767@@ -5692,7 +5747,7 @@ namespace Catch {
3768 catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \
3769 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
3770 } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
3771- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
3772+ catchAssertionHandler.complete(); \
3773 } while( (void)0, (false) && static_cast<const bool&>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
3774 // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
3775
3776@@ -5720,7 +5775,7 @@ namespace Catch {
3777 catch( ... ) { \
3778 catchAssertionHandler.handleUnexpectedInflightException(); \
3779 } \
3780- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
3781+ catchAssertionHandler.complete(); \
3782 } while( false )
3783
3784 ///////////////////////////////////////////////////////////////////////////////
3785@@ -5741,7 +5796,7 @@ namespace Catch {
3786 } \
3787 else \
3788 catchAssertionHandler.handleThrowingCallSkipped(); \
3789- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
3790+ catchAssertionHandler.complete(); \
3791 } while( false )
3792
3793 ///////////////////////////////////////////////////////////////////////////////
3794@@ -5765,7 +5820,7 @@ namespace Catch {
3795 } \
3796 else \
3797 catchAssertionHandler.handleThrowingCallSkipped(); \
3798- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
3799+ catchAssertionHandler.complete(); \
3800 } while( false )
3801
3802
3803@@ -5789,7 +5844,7 @@ namespace Catch {
3804 } \
3805 else \
3806 catchAssertionHandler.handleThrowingCallSkipped(); \
3807- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
3808+ catchAssertionHandler.complete(); \
3809 } while( false )
3810
3811 #endif // CATCH_CONFIG_DISABLE
3812@@ -5897,7 +5952,7 @@ namespace Catch {
3813 #else
3814
3815 // These section definitions imply that at most one section at one level
3816-// will be intered (because only one section's __LINE__ can be equal to
3817+// will be entered (because only one section's __LINE__ can be equal to
3818 // the dummy `catchInternalSectionHint` variable from `TEST_CASE`).
3819
3820 namespace Catch {
3821@@ -5951,6 +6006,8 @@ namespace Catch {
3822
3823 class ITestInvoker {
3824 public:
3825+ virtual void prepareTestCase();
3826+ virtual void tearDownTestCase();
3827 virtual void invoke() const = 0;
3828 virtual ~ITestInvoker(); // = default
3829 };
3830@@ -5988,7 +6045,8 @@ template<typename C>
3831 class TestInvokerAsMethod : public ITestInvoker {
3832 void (C::*m_testAsMethod)();
3833 public:
3834- TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
3835+ constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept:
3836+ m_testAsMethod( testAsMethod ) {}
3837
3838 void invoke() const override {
3839 C obj;
3840@@ -6003,6 +6061,34 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
3841 return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod );
3842 }
3843
3844+template <typename C>
3845+class TestInvokerFixture : public ITestInvoker {
3846+ void ( C::*m_testAsMethod )() const;
3847+ Detail::unique_ptr<C> m_fixture = nullptr;
3848+
3849+public:
3850+ constexpr TestInvokerFixture( void ( C::*testAsMethod )() const ) noexcept:
3851+ m_testAsMethod( testAsMethod ) {}
3852+
3853+ void prepareTestCase() override {
3854+ m_fixture = Detail::make_unique<C>();
3855+ }
3856+
3857+ void tearDownTestCase() override {
3858+ m_fixture.reset();
3859+ }
3860+
3861+ void invoke() const override {
3862+ auto* f = m_fixture.get();
3863+ ( f->*m_testAsMethod )();
3864+ }
3865+};
3866+
3867+template<typename C>
3868+Detail::unique_ptr<ITestInvoker> makeTestInvokerFixture( void ( C::*testAsMethod )() const ) {
3869+ return Detail::make_unique<TestInvokerFixture<C>>( testAsMethod );
3870+}
3871+
3872 struct NameAndTags {
3873 constexpr NameAndTags( StringRef name_ = StringRef(),
3874 StringRef tags_ = StringRef() ) noexcept:
3875@@ -6099,6 +6185,26 @@ static int catchInternalSectionHint = 0;
3876 #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
3877 INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
3878
3879+ ///////////////////////////////////////////////////////////////////////////////
3880+ #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... ) \
3881+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
3882+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
3883+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
3884+ namespace { \
3885+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) { \
3886+ void test() const; \
3887+ }; \
3888+ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
3889+ Catch::makeTestInvokerFixture( &TestName::test ), \
3890+ CATCH_INTERNAL_LINEINFO, \
3891+ #ClassName##_catch_sr, \
3892+ Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
3893+ } \
3894+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
3895+ void TestName::test() const
3896+ #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... ) \
3897+ INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
3898+
3899
3900 ///////////////////////////////////////////////////////////////////////////////
3901 #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
3902@@ -6129,6 +6235,57 @@ static int catchInternalSectionHint = 0;
3903 #endif // CATCH_TEST_REGISTRY_HPP_INCLUDED
3904
3905
3906+#ifndef CATCH_UNREACHABLE_HPP_INCLUDED
3907+#define CATCH_UNREACHABLE_HPP_INCLUDED
3908+
3909+/**\file
3910+ * Polyfill `std::unreachable`
3911+ *
3912+ * We need something like `std::unreachable` to tell the compiler that
3913+ * some macros, e.g. `FAIL` or `SKIP`, do not continue execution in normal
3914+ * manner, and should handle it as such, e.g. not warn if there is no return
3915+ * from non-void function after a `FAIL` or `SKIP`.
3916+ */
3917+
3918+#include <exception>
3919+
3920+#if defined( __cpp_lib_unreachable ) && __cpp_lib_unreachable > 202202L
3921+# include <utility>
3922+namespace Catch {
3923+ namespace Detail {
3924+ using Unreachable = std::unreachable;
3925+ }
3926+} // namespace Catch
3927+
3928+#else // vv If we do not have std::unreachable, we implement something similar
3929+
3930+namespace Catch {
3931+ namespace Detail {
3932+
3933+ [[noreturn]]
3934+ inline void Unreachable() noexcept {
3935+# if defined( NDEBUG )
3936+# if defined( _MSC_VER ) && !defined( __clang__ )
3937+ __assume( false );
3938+# elif defined( __GNUC__ )
3939+ __builtin_unreachable();
3940+# else // vv platform without known optimization hint
3941+ std::terminate();
3942+# endif
3943+# else // ^^ NDEBUG
3944+ // For non-release builds, we prefer termination on bug over UB
3945+ std::terminate();
3946+# endif //
3947+ }
3948+
3949+ } // namespace Detail
3950+} // end namespace Catch
3951+
3952+#endif
3953+
3954+#endif // CATCH_UNREACHABLE_HPP_INCLUDED
3955+
3956+
3957 // All of our user-facing macros support configuration toggle, that
3958 // forces them to be defined prefixed with CATCH_. We also like to
3959 // support another toggle that can minimize (disable) their implementation.
3960@@ -6156,13 +6313,20 @@ static int catchInternalSectionHint = 0;
3961 #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
3962 #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
3963 #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
3964+ #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ )
3965 #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
3966 #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
3967 #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
3968- #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
3969+ #define CATCH_FAIL( ... ) do { \
3970+ INTERNAL_CATCH_MSG("CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \
3971+ Catch::Detail::Unreachable(); \
3972+ } while ( false )
3973 #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
3974 #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
3975- #define CATCH_SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
3976+ #define CATCH_SKIP( ... ) do { \
3977+ INTERNAL_CATCH_MSG( "CATCH_SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \
3978+ Catch::Detail::Unreachable(); \
3979+ } while (false)
3980
3981
3982 #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
3983@@ -6210,6 +6374,7 @@ static int catchInternalSectionHint = 0;
3984 #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
3985 #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
3986 #define CATCH_METHOD_AS_TEST_CASE( method, ... )
3987+ #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
3988 #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
3989 #define CATCH_SECTION( ... )
3990 #define CATCH_DYNAMIC_SECTION( ... )
3991@@ -6255,13 +6420,20 @@ static int catchInternalSectionHint = 0;
3992 #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
3993 #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
3994 #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
3995+ #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ )
3996 #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
3997 #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
3998 #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
3999- #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
4000+ #define FAIL( ... ) do { \
4001+ INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \
4002+ Catch::Detail::Unreachable(); \
4003+ } while (false)
4004 #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
4005 #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
4006- #define SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
4007+ #define SKIP( ... ) do { \
4008+ INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \
4009+ Catch::Detail::Unreachable(); \
4010+ } while (false)
4011
4012
4013 #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
4014@@ -6308,6 +6480,7 @@ static int catchInternalSectionHint = 0;
4015 #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
4016 #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
4017 #define METHOD_AS_TEST_CASE( method, ... )
4018+ #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
4019 #define REGISTER_TEST_CASE( Function, ... ) (void)(0)
4020 #define SECTION( ... )
4021 #define DYNAMIC_SECTION( ... )
4022@@ -6354,6 +6527,15 @@ static int catchInternalSectionHint = 0;
4023 #endif
4024
4025
4026+namespace Catch {
4027+ namespace Detail {
4028+ template <int N>
4029+ struct priority_tag : priority_tag<N - 1> {};
4030+ template <>
4031+ struct priority_tag<0> {};
4032+ }
4033+}
4034+
4035 #define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
4036 #define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
4037 #define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
4038@@ -6413,10 +6595,10 @@ static int catchInternalSectionHint = 0;
4039 #define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
4040
4041 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
4042-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
4043+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>(Catch::Detail::priority_tag<1>{}))
4044 #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
4045 #else
4046-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
4047+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>(Catch::Detail::priority_tag<1>{})))
4048 #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
4049 #endif
4050
4051@@ -6439,11 +6621,11 @@ static int catchInternalSectionHint = 0;
4052
4053 #define INTERNAL_CATCH_TYPE_GEN\
4054 template<typename...> struct TypeList {};\
4055- template<typename...Ts>\
4056- constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
4057+ template<typename... Ts>\
4058+ constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TypeList<Ts...> { return {}; }\
4059 template<template<typename...> class...> struct TemplateTypeList{};\
4060 template<template<typename...> class...Cs>\
4061- constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
4062+ constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TemplateTypeList<Cs...> { return {}; }\
4063 template<typename...>\
4064 struct append;\
4065 template<typename...>\
4066@@ -6473,10 +6655,10 @@ static int catchInternalSectionHint = 0;
4067 #define INTERNAL_CATCH_NTTP_1(signature, ...)\
4068 template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
4069 template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
4070- constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
4071+ constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> Nttp<__VA_ARGS__> { return {}; } \
4072 template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
4073 template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
4074- constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
4075+ constexpr auto get_wrapper(Catch::Detail::priority_tag<0>) noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
4076 \
4077 template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
4078 struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
4079@@ -6501,13 +6683,14 @@ static int catchInternalSectionHint = 0;
4080 template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
4081 static void TestName()
4082
4083-#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
4084- template<typename Type>\
4085- void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
4086+#define INTERNAL_CATCH_TYPES_REGISTER(TestFunc)\
4087+ template<typename... Ts>\
4088+ void reg_test(TypeList<Ts...>, Catch::NameAndTags nameAndTags)\
4089 {\
4090- Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
4091+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Ts...>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
4092 }
4093
4094+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature, ...)
4095 #define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
4096 template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
4097 void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
4098@@ -6556,7 +6739,7 @@ static int catchInternalSectionHint = 0;
4099 #define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
4100 #define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
4101 #define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
4102-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
4103+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_TYPES_REGISTER(TestFunc) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
4104 #define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
4105 #define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
4106 #define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
4107@@ -6566,7 +6749,7 @@ static int catchInternalSectionHint = 0;
4108 #define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
4109 #define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
4110 #define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
4111-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
4112+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_TYPES_REGISTER(TestFunc) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
4113 #define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
4114 #define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
4115 #define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
4116@@ -6695,11 +6878,11 @@ static int catchInternalSectionHint = 0;
4117 constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
4118 constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
4119 constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
4120- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + std::string(types_list[index % num_types]) + '>', Tags } ), index++)... };/* NOLINT */\
4121+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */\
4122 } \
4123 }; \
4124- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
4125- using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
4126+ static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
4127+ using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(Catch::Detail::priority_tag<1>{})), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
4128 TestInit t; \
4129 t.reg_tests(); \
4130 return 0; \
4131@@ -6744,7 +6927,7 @@ static int catchInternalSectionHint = 0;
4132 (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
4133 } \
4134 };\
4135- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
4136+ static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
4137 using TestInit = typename convert<TestName, TmplList>::type; \
4138 TestInit t; \
4139 t.reg_tests(); \
4140@@ -6780,7 +6963,7 @@ static int catchInternalSectionHint = 0;
4141 (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
4142 }\
4143 };\
4144- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
4145+ static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
4146 TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
4147 return 0;\
4148 }();\
4149@@ -6827,11 +7010,11 @@ static int catchInternalSectionHint = 0;
4150 constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
4151 constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
4152 constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
4153- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + std::string(types_list[index % num_types]) + '>', Tags } ), index++)... };/* NOLINT */ \
4154+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + '<' + types_list[index % num_types] + '>', Tags } ), index++)... };/* NOLINT */ \
4155 }\
4156 };\
4157- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
4158- using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
4159+ static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
4160+ using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(Catch::Detail::priority_tag<1>{})), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
4161 TestInit t;\
4162 t.reg_tests();\
4163 return 0;\
4164@@ -6879,7 +7062,7 @@ static int catchInternalSectionHint = 0;
4165 (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
4166 }\
4167 };\
4168- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
4169+ static const int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
4170 using TestInit = typename convert<TestNameClass, TmplList>::type;\
4171 TestInit t;\
4172 t.reg_tests();\
4173@@ -7098,14 +7281,24 @@ namespace Catch {
4174 TestCaseInfo* m_info;
4175 ITestInvoker* m_invoker;
4176 public:
4177- TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
4178+ constexpr TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
4179 m_info(info), m_invoker(invoker) {}
4180
4181+ void prepareTestCase() const {
4182+ m_invoker->prepareTestCase();
4183+ }
4184+
4185+ void tearDownTestCase() const {
4186+ m_invoker->tearDownTestCase();
4187+ }
4188+
4189 void invoke() const {
4190 m_invoker->invoke();
4191 }
4192
4193- TestCaseInfo const& getTestCaseInfo() const;
4194+ constexpr TestCaseInfo const& getTestCaseInfo() const {
4195+ return *m_info;
4196+ }
4197 };
4198
4199 Detail::unique_ptr<TestCaseInfo>
4200@@ -7121,6 +7314,22 @@ namespace Catch {
4201 #endif // CATCH_TEST_CASE_INFO_HPP_INCLUDED
4202
4203
4204+#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED
4205+#define CATCH_TEST_RUN_INFO_HPP_INCLUDED
4206+
4207+
4208+namespace Catch {
4209+
4210+ struct TestRunInfo {
4211+ constexpr TestRunInfo(StringRef _name) : name(_name) {}
4212+ StringRef name;
4213+ };
4214+
4215+} // end namespace Catch
4216+
4217+#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED
4218+
4219+
4220 #ifndef CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
4221 #define CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
4222
4223@@ -7168,7 +7377,7 @@ namespace Catch {
4224 class ExceptionTranslator : public IExceptionTranslator {
4225 public:
4226
4227- ExceptionTranslator( std::string(*translateFunction)( T const& ) )
4228+ constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) )
4229 : m_translateFunction( translateFunction )
4230 {}
4231
4232@@ -7208,7 +7417,7 @@ namespace Catch {
4233 static std::string translatorName( signature ); \
4234 CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
4235 CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
4236- namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
4237+ namespace{ const Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
4238 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
4239 static std::string translatorName( signature )
4240
4241@@ -7269,8 +7478,8 @@ namespace Catch {
4242 #define CATCH_VERSION_MACROS_HPP_INCLUDED
4243
4244 #define CATCH_VERSION_MAJOR 3
4245-#define CATCH_VERSION_MINOR 5
4246-#define CATCH_VERSION_PATCH 4
4247+#define CATCH_VERSION_MINOR 11
4248+#define CATCH_VERSION_PATCH 0
4249
4250 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED
4251
4252@@ -7311,7 +7520,7 @@ namespace Catch {
4253 m_msg(msg)
4254 {}
4255
4256- const char* what() const noexcept override final;
4257+ const char* what() const noexcept final;
4258 };
4259
4260 } // end namespace Catch
4261@@ -7685,8 +7894,9 @@ namespace Generators {
4262 class FilterGenerator final : public IGenerator<T> {
4263 GeneratorWrapper<T> m_generator;
4264 Predicate m_predicate;
4265+ static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
4266 public:
4267- template <typename P = Predicate>
4268+ template <typename P>
4269 FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
4270 m_generator(CATCH_MOVE(generator)),
4271 m_predicate(CATCH_FORWARD(pred))
4272@@ -7718,7 +7928,7 @@ namespace Generators {
4273
4274 template <typename T, typename Predicate>
4275 GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
4276- return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
4277+ return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, typename std::remove_reference<Predicate>::type>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
4278 }
4279
4280 template <typename T>
4281@@ -7949,7 +8159,10 @@ namespace Catch {
4282 // it, and it provides an escape hatch to the users who need it.
4283 #if defined( __SIZEOF_INT128__ )
4284 # define CATCH_CONFIG_INTERNAL_UINT128
4285-#elif defined( _MSC_VER ) && ( defined( _WIN64 ) || defined( _M_ARM64 ) )
4286+// Unlike GCC, MSVC does not polyfill umul as mulh + mul pair on ARM machines.
4287+// Currently we do not bother doing this ourselves, but we could if it became
4288+// important for perf.
4289+#elif defined( _MSC_VER ) && defined( _M_X64 )
4290 # define CATCH_CONFIG_INTERNAL_MSVC_UMUL128
4291 #endif
4292
4293@@ -7964,7 +8177,6 @@ namespace Catch {
4294 !defined( CATCH_CONFIG_MSVC_UMUL128 )
4295 # define CATCH_CONFIG_MSVC_UMUL128
4296 # include <intrin.h>
4297-# pragma intrinsic( _umul128 )
4298 #endif
4299
4300
4301@@ -7995,7 +8207,7 @@ namespace Catch {
4302 struct ExtendedMultResult {
4303 T upper;
4304 T lower;
4305- bool operator==( ExtendedMultResult const& rhs ) const {
4306+ constexpr bool operator==( ExtendedMultResult const& rhs ) const {
4307 return upper == rhs.upper && lower == rhs.lower;
4308 }
4309 };
4310@@ -8113,6 +8325,7 @@ namespace Catch {
4311 * get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1])
4312 */
4313 template <typename OriginalType, typename UnsignedType>
4314+ constexpr
4315 std::enable_if_t<std::is_signed<OriginalType>::value, UnsignedType>
4316 transposeToNaturalOrder( UnsignedType in ) {
4317 static_assert(
4318@@ -8133,6 +8346,7 @@ namespace Catch {
4319
4320 template <typename OriginalType,
4321 typename UnsignedType>
4322+ constexpr
4323 std::enable_if_t<std::is_unsigned<OriginalType>::value, UnsignedType>
4324 transposeToNaturalOrder(UnsignedType in) {
4325 static_assert(
4326@@ -8184,24 +8398,24 @@ class uniform_integer_distribution {
4327 // distribution will be reused many times and this is an optimization.
4328 UnsignedIntegerType m_rejection_threshold = 0;
4329
4330- UnsignedIntegerType computeDistance(IntegerType a, IntegerType b) const {
4331+ static constexpr UnsignedIntegerType computeDistance(IntegerType a, IntegerType b) {
4332 // This overflows and returns 0 if a == 0 and b == TYPE_MAX.
4333 // We handle that later when generating the number.
4334 return transposeTo(b) - transposeTo(a) + 1;
4335 }
4336
4337- static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) {
4338+ static constexpr UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) {
4339 // distance == 0 means that we will return all possible values from
4340 // the type's range, and that we shouldn't reject anything.
4341 if ( ab_distance == 0 ) { return 0; }
4342 return ( ~ab_distance + 1 ) % ab_distance;
4343 }
4344
4345- static UnsignedIntegerType transposeTo(IntegerType in) {
4346+ static constexpr UnsignedIntegerType transposeTo(IntegerType in) {
4347 return Detail::transposeToNaturalOrder<IntegerType>(
4348 static_cast<UnsignedIntegerType>( in ) );
4349 }
4350- static IntegerType transposeBack(UnsignedIntegerType in) {
4351+ static constexpr IntegerType transposeBack(UnsignedIntegerType in) {
4352 return static_cast<IntegerType>(
4353 Detail::transposeToNaturalOrder<IntegerType>(in) );
4354 }
4355@@ -8209,7 +8423,7 @@ class uniform_integer_distribution {
4356 public:
4357 using result_type = IntegerType;
4358
4359- uniform_integer_distribution( IntegerType a, IntegerType b ):
4360+ constexpr uniform_integer_distribution( IntegerType a, IntegerType b ):
4361 m_a( transposeTo(a) ),
4362 m_ab_distance( computeDistance(a, b) ),
4363 m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) {
4364@@ -8217,7 +8431,7 @@ public:
4365 }
4366
4367 template <typename Generator>
4368- result_type operator()( Generator& g ) {
4369+ constexpr result_type operator()( Generator& g ) {
4370 // All possible values of result_type are valid.
4371 if ( m_ab_distance == 0 ) {
4372 return transposeBack( Detail::fillBitsFrom<UnsignedIntegerType>( g ) );
4373@@ -8235,8 +8449,8 @@ public:
4374 return transposeBack(m_a + emul.upper);
4375 }
4376
4377- result_type a() const { return transposeBack(m_a); }
4378- result_type b() const { return transposeBack(m_ab_distance + m_a - 1); }
4379+ constexpr result_type a() const { return transposeBack(m_a); }
4380+ constexpr result_type b() const { return transposeBack(m_ab_distance + m_a - 1); }
4381 };
4382
4383 } // end namespace Catch
4384@@ -8649,7 +8863,7 @@ public:
4385
4386 template <typename InputIterator,
4387 typename InputSentinel,
4388- typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
4389+ typename ResultType = std::remove_const_t<typename std::iterator_traits<InputIterator>::value_type>>
4390 GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
4391 return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(from, to));
4392 }
4393@@ -8694,26 +8908,9 @@ auto from_range(Container const& cnt) {
4394 #define CATCH_INTERFACES_REPORTER_HPP_INCLUDED
4395
4396
4397-
4398-#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED
4399-#define CATCH_TEST_RUN_INFO_HPP_INCLUDED
4400-
4401-
4402-namespace Catch {
4403-
4404- struct TestRunInfo {
4405- constexpr TestRunInfo(StringRef _name) : name(_name) {}
4406- StringRef name;
4407- };
4408-
4409-} // end namespace Catch
4410-
4411-#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED
4412-
4413 #include <map>
4414 #include <string>
4415 #include <vector>
4416-#include <iosfwd>
4417
4418 namespace Catch {
4419
4420@@ -8809,6 +9006,11 @@ namespace Catch {
4421 //! Catch2 should call `Reporter::assertionEnded` even for passing
4422 //! assertions
4423 bool shouldReportAllAssertions = false;
4424+ //! Catch2 should call `Reporter::assertionStarting` for all assertions
4425+ // Defaults to true for backwards compatibility, but none of our current
4426+ // reporters actually want this, and it enables a fast path in assertion
4427+ // handling.
4428+ bool shouldReportAllAssertionStarts = true;
4429 };
4430
4431 /**
4432@@ -9331,6 +9533,17 @@ namespace Catch {
4433 bool isDebuggerActive();
4434 }
4435
4436+#if !defined( CATCH_TRAP ) && defined( __clang__ ) && defined( __has_builtin )
4437+# if __has_builtin( __builtin_debugtrap )
4438+# define CATCH_TRAP() __builtin_debugtrap()
4439+# endif
4440+#endif
4441+
4442+#if !defined( CATCH_TRAP ) && defined( _MSC_VER )
4443+# define CATCH_TRAP() __debugbreak()
4444+#endif
4445+
4446+#if !defined(CATCH_TRAP) // If we couldn't use compiler-specific impl from above, we get into platform-specific options
4447 #ifdef CATCH_PLATFORM_MAC
4448
4449 #if defined(__i386__) || defined(__x86_64__)
4450@@ -9355,7 +9568,7 @@ namespace Catch {
4451 #define CATCH_TRAP() __asm__(".inst 0xde01")
4452 #endif
4453
4454-#elif defined(CATCH_PLATFORM_LINUX)
4455+#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
4456 // If we can use inline assembler, do it because this allows us to break
4457 // directly at the location of the failing check instead of breaking inside
4458 // raise() called from it, i.e. one stack frame below.
4459@@ -9366,15 +9579,15 @@ namespace Catch {
4460
4461 #define CATCH_TRAP() raise(SIGTRAP)
4462 #endif
4463-#elif defined(_MSC_VER)
4464- #define CATCH_TRAP() __debugbreak()
4465 #elif defined(__MINGW32__)
4466 extern "C" __declspec(dllimport) void __stdcall DebugBreak();
4467 #define CATCH_TRAP() DebugBreak()
4468 #endif
4469+#endif // ^^ CATCH_TRAP is not defined yet, so we define it
4470+
4471
4472-#ifndef CATCH_BREAK_INTO_DEBUGGER
4473- #ifdef CATCH_TRAP
4474+#if !defined(CATCH_BREAK_INTO_DEBUGGER)
4475+ #if defined(CATCH_TRAP)
4476 #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
4477 #else
4478 #define CATCH_BREAK_INTO_DEBUGGER() []{}()
4479@@ -9388,7 +9601,7 @@ namespace Catch {
4480 #define CATCH_ENFORCE_HPP_INCLUDED
4481
4482
4483-#include <exception>
4484+#include <exception> // for `std::exception` in no-exception configuration
4485
4486 namespace Catch {
4487 #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
4488@@ -9484,7 +9697,6 @@ namespace Catch {
4489 #define CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
4490
4491
4492-#include <vector>
4493 #include <string>
4494
4495 namespace Catch {
4496@@ -9684,8 +9896,8 @@ namespace Detail {
4497 #ifndef CATCH_IS_PERMUTATION_HPP_INCLUDED
4498 #define CATCH_IS_PERMUTATION_HPP_INCLUDED
4499
4500-#include <algorithm>
4501 #include <iterator>
4502+#include <type_traits>
4503
4504 namespace Catch {
4505 namespace Detail {
4506@@ -9694,6 +9906,7 @@ namespace Catch {
4507 typename Sentinel,
4508 typename T,
4509 typename Comparator>
4510+ constexpr
4511 ForwardIter find_sentinel( ForwardIter start,
4512 Sentinel sentinel,
4513 T const& value,
4514@@ -9709,6 +9922,7 @@ namespace Catch {
4515 typename Sentinel,
4516 typename T,
4517 typename Comparator>
4518+ constexpr
4519 std::ptrdiff_t count_sentinel( ForwardIter start,
4520 Sentinel sentinel,
4521 T const& value,
4522@@ -9722,6 +9936,7 @@ namespace Catch {
4523 }
4524
4525 template <typename ForwardIter, typename Sentinel>
4526+ constexpr
4527 std::enable_if_t<!std::is_same<ForwardIter, Sentinel>::value,
4528 std::ptrdiff_t>
4529 sentinel_distance( ForwardIter iter, const Sentinel sentinel ) {
4530@@ -9734,8 +9949,8 @@ namespace Catch {
4531 }
4532
4533 template <typename ForwardIter>
4534- std::ptrdiff_t sentinel_distance( ForwardIter first,
4535- ForwardIter last ) {
4536+ constexpr std::ptrdiff_t sentinel_distance( ForwardIter first,
4537+ ForwardIter last ) {
4538 return std::distance( first, last );
4539 }
4540
4541@@ -9744,11 +9959,11 @@ namespace Catch {
4542 typename ForwardIter2,
4543 typename Sentinel2,
4544 typename Comparator>
4545- bool check_element_counts( ForwardIter1 first_1,
4546- const Sentinel1 end_1,
4547- ForwardIter2 first_2,
4548- const Sentinel2 end_2,
4549- Comparator cmp ) {
4550+ constexpr bool check_element_counts( ForwardIter1 first_1,
4551+ const Sentinel1 end_1,
4552+ ForwardIter2 first_2,
4553+ const Sentinel2 end_2,
4554+ Comparator cmp ) {
4555 auto cursor = first_1;
4556 while ( cursor != end_1 ) {
4557 if ( find_sentinel( first_1, cursor, *cursor, cmp ) ==
4558@@ -9778,11 +9993,11 @@ namespace Catch {
4559 typename ForwardIter2,
4560 typename Sentinel2,
4561 typename Comparator>
4562- bool is_permutation( ForwardIter1 first_1,
4563- const Sentinel1 end_1,
4564- ForwardIter2 first_2,
4565- const Sentinel2 end_2,
4566- Comparator cmp ) {
4567+ constexpr bool is_permutation( ForwardIter1 first_1,
4568+ const Sentinel1 end_1,
4569+ ForwardIter2 first_2,
4570+ const Sentinel2 end_2,
4571+ Comparator cmp ) {
4572 // TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types
4573 // TODO: Comparator has to be "both sides", e.g. a == b => b == a
4574 // This skips shared prefix of the two ranges
4575@@ -9819,8 +10034,6 @@ namespace Catch {
4576
4577
4578 #include <iosfwd>
4579-#include <cstddef>
4580-#include <ostream>
4581 #include <string>
4582
4583 namespace Catch {
4584@@ -10029,106 +10242,67 @@ namespace Catch {
4585 #define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
4586
4587
4588-#include <cstdio>
4589-#include <iosfwd>
4590+#include <cassert>
4591 #include <string>
4592
4593 namespace Catch {
4594
4595- class RedirectedStream {
4596- std::ostream& m_originalStream;
4597- std::ostream& m_redirectionStream;
4598- std::streambuf* m_prevBuf;
4599-
4600- public:
4601- RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
4602- ~RedirectedStream();
4603- };
4604-
4605- class RedirectedStdOut {
4606- ReusableStringStream m_rss;
4607- RedirectedStream m_cout;
4608- public:
4609- RedirectedStdOut();
4610- auto str() const -> std::string;
4611- };
4612-
4613- // StdErr has two constituent streams in C++, std::cerr and std::clog
4614- // This means that we need to redirect 2 streams into 1 to keep proper
4615- // order of writes
4616- class RedirectedStdErr {
4617- ReusableStringStream m_rss;
4618- RedirectedStream m_cerr;
4619- RedirectedStream m_clog;
4620+ class OutputRedirect {
4621+ bool m_redirectActive = false;
4622+ virtual void activateImpl() = 0;
4623+ virtual void deactivateImpl() = 0;
4624 public:
4625- RedirectedStdErr();
4626- auto str() const -> std::string;
4627- };
4628+ enum Kind {
4629+ //! No redirect (noop implementation)
4630+ None,
4631+ //! Redirect std::cout/std::cerr/std::clog streams internally
4632+ Streams,
4633+ //! Redirect the stdout/stderr file descriptors into files
4634+ FileDescriptors,
4635+ };
4636
4637- class RedirectedStreams {
4638- public:
4639- RedirectedStreams(RedirectedStreams const&) = delete;
4640- RedirectedStreams& operator=(RedirectedStreams const&) = delete;
4641- RedirectedStreams(RedirectedStreams&&) = delete;
4642- RedirectedStreams& operator=(RedirectedStreams&&) = delete;
4643+ virtual ~OutputRedirect(); // = default;
4644
4645- RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
4646- ~RedirectedStreams();
4647- private:
4648- std::string& m_redirectedCout;
4649- std::string& m_redirectedCerr;
4650- RedirectedStdOut m_redirectedStdOut;
4651- RedirectedStdErr m_redirectedStdErr;
4652+ // TODO: Do we want to check that redirect is not active before retrieving the output?
4653+ virtual std::string getStdout() = 0;
4654+ virtual std::string getStderr() = 0;
4655+ virtual void clearBuffers() = 0;
4656+ bool isActive() const { return m_redirectActive; }
4657+ void activate() {
4658+ assert( !m_redirectActive && "redirect is already active" );
4659+ activateImpl();
4660+ m_redirectActive = true;
4661+ }
4662+ void deactivate() {
4663+ assert( m_redirectActive && "redirect is not active" );
4664+ deactivateImpl();
4665+ m_redirectActive = false;
4666+ }
4667 };
4668
4669-#if defined(CATCH_CONFIG_NEW_CAPTURE)
4670-
4671- // Windows's implementation of std::tmpfile is terrible (it tries
4672- // to create a file inside system folder, thus requiring elevated
4673- // privileges for the binary), so we have to use tmpnam(_s) and
4674- // create the file ourselves there.
4675- class TempFile {
4676- public:
4677- TempFile(TempFile const&) = delete;
4678- TempFile& operator=(TempFile const&) = delete;
4679- TempFile(TempFile&&) = delete;
4680- TempFile& operator=(TempFile&&) = delete;
4681-
4682- TempFile();
4683- ~TempFile();
4684-
4685- std::FILE* getFile();
4686- std::string getContents();
4687-
4688- private:
4689- std::FILE* m_file = nullptr;
4690- #if defined(_MSC_VER)
4691- char m_buffer[L_tmpnam] = { 0 };
4692- #endif
4693- };
4694+ bool isRedirectAvailable( OutputRedirect::Kind kind);
4695+ Detail::unique_ptr<OutputRedirect> makeOutputRedirect( bool actual );
4696
4697+ class RedirectGuard {
4698+ OutputRedirect* m_redirect;
4699+ bool m_activate;
4700+ bool m_previouslyActive;
4701+ bool m_moved = false;
4702
4703- class OutputRedirect {
4704 public:
4705- OutputRedirect(OutputRedirect const&) = delete;
4706- OutputRedirect& operator=(OutputRedirect const&) = delete;
4707- OutputRedirect(OutputRedirect&&) = delete;
4708- OutputRedirect& operator=(OutputRedirect&&) = delete;
4709+ RedirectGuard( bool activate, OutputRedirect& redirectImpl );
4710+ ~RedirectGuard() noexcept( false );
4711
4712+ RedirectGuard( RedirectGuard const& ) = delete;
4713+ RedirectGuard& operator=( RedirectGuard const& ) = delete;
4714
4715- OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
4716- ~OutputRedirect();
4717-
4718- private:
4719- int m_originalStdout = -1;
4720- int m_originalStderr = -1;
4721- TempFile m_stdoutFile;
4722- TempFile m_stderrFile;
4723- std::string& m_stdoutDest;
4724- std::string& m_stderrDest;
4725+ // C++14 needs move-able guards to return them from functions
4726+ RedirectGuard( RedirectGuard&& rhs ) noexcept;
4727+ RedirectGuard& operator=( RedirectGuard&& rhs ) noexcept;
4728 };
4729
4730-#endif
4731+ RedirectGuard scopedActivate( OutputRedirect& redirectImpl );
4732+ RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl );
4733
4734 } // end namespace Catch
4735
4736@@ -10443,6 +10617,48 @@ using TestCaseTracking::SectionTracker;
4737
4738 #endif // CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
4739
4740+
4741+#ifndef CATCH_THREAD_SUPPORT_HPP_INCLUDED
4742+#define CATCH_THREAD_SUPPORT_HPP_INCLUDED
4743+
4744+
4745+#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
4746+# include <atomic>
4747+# include <mutex>
4748+#endif
4749+
4750+
4751+namespace Catch {
4752+ namespace Detail {
4753+#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
4754+ using Mutex = std::mutex;
4755+ using LockGuard = std::lock_guard<std::mutex>;
4756+ struct AtomicCounts {
4757+ std::atomic<std::uint64_t> passed = 0;
4758+ std::atomic<std::uint64_t> failed = 0;
4759+ std::atomic<std::uint64_t> failedButOk = 0;
4760+ std::atomic<std::uint64_t> skipped = 0;
4761+ };
4762+#else // ^^ Use actual mutex, lock and atomics
4763+ // vv Dummy implementations for single-thread performance
4764+
4765+ struct Mutex {
4766+ void lock() {}
4767+ void unlock() {}
4768+ };
4769+
4770+ struct LockGuard {
4771+ LockGuard( Mutex ) {}
4772+ };
4773+
4774+ using AtomicCounts = Counts;
4775+#endif
4776+
4777+ } // namespace Detail
4778+} // namespace Catch
4779+
4780+#endif // CATCH_THREAD_SUPPORT_HPP_INCLUDED
4781+
4782 #include <string>
4783
4784 namespace Catch {
4785@@ -10451,6 +10667,7 @@ namespace Catch {
4786 class IConfig;
4787 class IEventListener;
4788 using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
4789+ class OutputRedirect;
4790
4791 ///////////////////////////////////////////////////////////////////////////
4792
4793@@ -10476,7 +10693,7 @@ namespace Catch {
4794 void handleMessage
4795 ( AssertionInfo const& info,
4796 ResultWas::OfType resultType,
4797- StringRef message,
4798+ std::string&& message,
4799 AssertionReaction& reaction ) override;
4800 void handleUnexpectedExceptionNotThrown
4801 ( AssertionInfo const& info,
4802@@ -10514,11 +10731,6 @@ namespace Catch {
4803 void benchmarkEnded( BenchmarkStats<> const& stats ) override;
4804 void benchmarkFailed( StringRef error ) override;
4805
4806- void pushScopedMessage( MessageInfo const& message ) override;
4807- void popScopedMessage( MessageInfo const& message ) override;
4808-
4809- void emplaceUnscopedMessage( MessageBuilder&& builder ) override;
4810-
4811 std::string getCurrentTestName() const override;
4812
4813 const AssertionResult* getLastResult() const override;
4814@@ -10529,18 +10741,18 @@ namespace Catch {
4815
4816 bool lastAssertionPassed() override;
4817
4818- void assertionPassed() override;
4819-
4820 public:
4821 // !TBD We need to do this another way!
4822 bool aborting() const;
4823
4824 private:
4825+ void assertionPassedFastPath( SourceLineInfo lineInfo );
4826+ // Update the non-thread-safe m_totals from the atomic assertion counts.
4827+ void updateTotalsFromAtomics();
4828
4829- void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
4830+ void runCurrentTest();
4831 void invokeActiveTestCase();
4832
4833- void resetAssertionInfo();
4834 bool testForMissingAssertions( Counts& assertions );
4835
4836 void assertionEnded( AssertionResult&& result );
4837@@ -10550,30 +10762,39 @@ namespace Catch {
4838 ITransientExpression const *expr,
4839 bool negated );
4840
4841- void populateReaction( AssertionReaction& reaction );
4842+ void populateReaction( AssertionReaction& reaction, bool has_normal_disposition );
4843+
4844+ // Creates dummy info for unexpected exceptions/fatal errors,
4845+ // where we do not have the access to one, but we still need
4846+ // to send one to the reporters.
4847+ AssertionInfo makeDummyAssertionInfo();
4848
4849 private:
4850
4851 void handleUnfinishedSections();
4852-
4853+ mutable Detail::Mutex m_assertionMutex;
4854 TestRunInfo m_runInfo;
4855 TestCaseHandle const* m_activeTestCase = nullptr;
4856 ITracker* m_testCaseTracker = nullptr;
4857 Optional<AssertionResult> m_lastResult;
4858-
4859 IConfig const* m_config;
4860 Totals m_totals;
4861+ Detail::AtomicCounts m_atomicAssertionCount;
4862 IEventListenerPtr m_reporter;
4863- std::vector<MessageInfo> m_messages;
4864- std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
4865- AssertionInfo m_lastAssertionInfo;
4866 std::vector<SectionEndInfo> m_unfinishedSections;
4867 std::vector<ITracker*> m_activeSections;
4868 TrackerContext m_trackerContext;
4869+ Detail::unique_ptr<OutputRedirect> m_outputRedirect;
4870 FatalConditionHandler m_fatalConditionhandler;
4871- bool m_lastAssertionPassed = false;
4872+ // Caches m_config->abortAfter() to avoid vptr calls/allow inlining
4873+ size_t m_abortAfterXFailedAssertions;
4874 bool m_shouldReportUnexpected = true;
4875+ // Caches whether `assertionStarting` events should be sent to the reporter.
4876+ bool m_reportAssertionStarting;
4877+ // Caches whether `assertionEnded` events for successful assertions should be sent to the reporter
4878 bool m_includeSuccessfulResults;
4879+ // Caches m_config->shouldDebugBreak() to avoid vptr calls/allow inlining
4880+ bool m_shouldDebugBreak;
4881 };
4882
4883 void seedRng(IConfig const& config);
4884@@ -10587,7 +10808,6 @@ namespace Catch {
4885 #define CATCH_SHARDING_HPP_INCLUDED
4886
4887 #include <cassert>
4888-#include <cmath>
4889 #include <algorithm>
4890
4891 namespace Catch {
4892@@ -10944,6 +11164,107 @@ namespace Catch {
4893
4894 class Columns;
4895
4896+ /**
4897+ * Abstraction for a string with ansi escape sequences that
4898+ * automatically skips over escapes when iterating. Only graphical
4899+ * escape sequences are considered.
4900+ *
4901+ * Internal representation:
4902+ * An escape sequence looks like \033[39;49m
4903+ * We need bidirectional iteration and the unbound length of escape
4904+ * sequences poses a problem for operator-- To make this work we'll
4905+ * replace the last `m` with a 0xff (this is a codepoint that won't have
4906+ * any utf-8 meaning).
4907+ */
4908+ class AnsiSkippingString {
4909+ std::string m_string;
4910+ std::size_t m_size = 0;
4911+
4912+ // perform 0xff replacement and calculate m_size
4913+ void preprocessString();
4914+
4915+ public:
4916+ class const_iterator;
4917+ using iterator = const_iterator;
4918+ // note: must be u-suffixed or this will cause a "truncation of
4919+ // constant value" warning on MSVC
4920+ static constexpr char sentinel = static_cast<char>( 0xffu );
4921+
4922+ explicit AnsiSkippingString( std::string const& text );
4923+ explicit AnsiSkippingString( std::string&& text );
4924+
4925+ const_iterator begin() const;
4926+ const_iterator end() const;
4927+
4928+ size_t size() const { return m_size; }
4929+
4930+ std::string substring( const_iterator begin,
4931+ const_iterator end ) const;
4932+ };
4933+
4934+ class AnsiSkippingString::const_iterator {
4935+ friend AnsiSkippingString;
4936+ struct EndTag {};
4937+
4938+ const std::string* m_string;
4939+ std::string::const_iterator m_it;
4940+
4941+ explicit const_iterator( const std::string& string, EndTag ):
4942+ m_string( &string ), m_it( string.end() ) {}
4943+
4944+ void tryParseAnsiEscapes();
4945+ void advance();
4946+ void unadvance();
4947+
4948+ public:
4949+ using difference_type = std::ptrdiff_t;
4950+ using value_type = char;
4951+ using pointer = value_type*;
4952+ using reference = value_type&;
4953+ using iterator_category = std::bidirectional_iterator_tag;
4954+
4955+ explicit const_iterator( const std::string& string ):
4956+ m_string( &string ), m_it( string.begin() ) {
4957+ tryParseAnsiEscapes();
4958+ }
4959+
4960+ char operator*() const { return *m_it; }
4961+
4962+ const_iterator& operator++() {
4963+ advance();
4964+ return *this;
4965+ }
4966+ const_iterator operator++( int ) {
4967+ iterator prev( *this );
4968+ operator++();
4969+ return prev;
4970+ }
4971+ const_iterator& operator--() {
4972+ unadvance();
4973+ return *this;
4974+ }
4975+ const_iterator operator--( int ) {
4976+ iterator prev( *this );
4977+ operator--();
4978+ return prev;
4979+ }
4980+
4981+ bool operator==( const_iterator const& other ) const {
4982+ return m_it == other.m_it;
4983+ }
4984+ bool operator!=( const_iterator const& other ) const {
4985+ return !operator==( other );
4986+ }
4987+ bool operator<=( const_iterator const& other ) const {
4988+ return m_it <= other.m_it;
4989+ }
4990+
4991+ const_iterator oneBefore() const {
4992+ auto it = *this;
4993+ return --it;
4994+ }
4995+ };
4996+
4997 /**
4998 * Represents a column of text with specific width and indentation
4999 *
5000@@ -10953,10 +11274,11 @@ namespace Catch {
5001 */
5002 class Column {
5003 // String to be written out
5004- std::string m_string;
5005+ AnsiSkippingString m_string;
5006 // Width of the column for linebreaking
5007 size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1;
5008- // Indentation of other lines (including first if initial indent is unset)
5009+ // Indentation of other lines (including first if initial indent is
5010+ // unset)
5011 size_t m_indent = 0;
5012 // Indentation of the first line
5013 size_t m_initialIndent = std::string::npos;
5014@@ -10971,16 +11293,19 @@ namespace Catch {
5015
5016 Column const& m_column;
5017 // Where does the current line start?
5018- size_t m_lineStart = 0;
5019+ AnsiSkippingString::const_iterator m_lineStart;
5020 // How long should the current line be?
5021- size_t m_lineLength = 0;
5022+ AnsiSkippingString::const_iterator m_lineEnd;
5023 // How far have we checked the string to iterate?
5024- size_t m_parsedTo = 0;
5025+ AnsiSkippingString::const_iterator m_parsedTo;
5026 // Should a '-' be appended to the line?
5027 bool m_addHyphen = false;
5028
5029 const_iterator( Column const& column, EndTag ):
5030- m_column( column ), m_lineStart( m_column.m_string.size() ) {}
5031+ m_column( column ),
5032+ m_lineStart( m_column.m_string.end() ),
5033+ m_lineEnd( column.m_string.end() ),
5034+ m_parsedTo( column.m_string.end() ) {}
5035
5036 // Calculates the length of the current line
5037 void calcLength();
5038@@ -10990,8 +11315,9 @@ namespace Catch {
5039
5040 // Creates an indented and (optionally) suffixed string from
5041 // current iterator position, indentation and length.
5042- std::string addIndentAndSuffix( size_t position,
5043- size_t length ) const;
5044+ std::string addIndentAndSuffix(
5045+ AnsiSkippingString::const_iterator start,
5046+ AnsiSkippingString::const_iterator end ) const;
5047
5048 public:
5049 using difference_type = std::ptrdiff_t;
5050@@ -11008,7 +11334,8 @@ namespace Catch {
5051 const_iterator operator++( int );
5052
5053 bool operator==( const_iterator const& other ) const {
5054- return m_lineStart == other.m_lineStart && &m_column == &other.m_column;
5055+ return m_lineStart == other.m_lineStart &&
5056+ &m_column == &other.m_column;
5057 }
5058 bool operator!=( const_iterator const& other ) const {
5059 return !operator==( other );
5060@@ -11018,7 +11345,7 @@ namespace Catch {
5061
5062 explicit Column( std::string const& text ): m_string( text ) {}
5063 explicit Column( std::string&& text ):
5064- m_string( CATCH_MOVE(text)) {}
5065+ m_string( CATCH_MOVE( text ) ) {}
5066
5067 Column& width( size_t newWidth ) & {
5068 assert( newWidth > 0 );
5069@@ -11049,7 +11376,9 @@ namespace Catch {
5070
5071 size_t width() const { return m_width; }
5072 const_iterator begin() const { return const_iterator( *this ); }
5073- const_iterator end() const { return { *this, const_iterator::EndTag{} }; }
5074+ const_iterator end() const {
5075+ return { *this, const_iterator::EndTag{} };
5076+ }
5077
5078 friend std::ostream& operator<<( std::ostream& os,
5079 Column const& col );
5080@@ -11151,16 +11480,25 @@ namespace Catch {
5081
5082 #include <iosfwd>
5083 #include <vector>
5084+#include <cstdint>
5085
5086 namespace Catch {
5087- enum class XmlFormatting {
5088+ enum class XmlFormatting : std::uint8_t {
5089 None = 0x00,
5090 Indent = 0x01,
5091 Newline = 0x02,
5092 };
5093
5094- XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
5095- XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
5096+ constexpr XmlFormatting operator|( XmlFormatting lhs, XmlFormatting rhs ) {
5097+ return static_cast<XmlFormatting>( static_cast<std::uint8_t>( lhs ) |
5098+ static_cast<std::uint8_t>( rhs ) );
5099+ }
5100+
5101+ constexpr XmlFormatting operator&( XmlFormatting lhs, XmlFormatting rhs ) {
5102+ return static_cast<XmlFormatting>( static_cast<std::uint8_t>( lhs ) &
5103+ static_cast<std::uint8_t>( rhs ) );
5104+ }
5105+
5106
5107 /**
5108 * Helper for XML-encoding text (escaping angle brackets, quotes, etc)
5109@@ -11172,7 +11510,9 @@ namespace Catch {
5110 public:
5111 enum ForWhat { ForTextNodes, ForAttributes };
5112
5113- XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes );
5114+ constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ):
5115+ m_str( str ), m_forWhat( forWhat ) {}
5116+
5117
5118 void encodeTo( std::ostream& os ) const;
5119
5120@@ -11320,12 +11660,22 @@ namespace Catch {
5121
5122 namespace Catch {
5123
5124+#ifdef __clang__
5125+# pragma clang diagnostic push
5126+# pragma clang diagnostic ignored "-Wsign-compare"
5127+# pragma clang diagnostic ignored "-Wnon-virtual-dtor"
5128+#elif defined __GNUC__
5129+# pragma GCC diagnostic push
5130+# pragma GCC diagnostic ignored "-Wsign-compare"
5131+# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
5132+#endif
5133+
5134 template<typename ArgT, typename MatcherT>
5135 class MatchExpr : public ITransientExpression {
5136 ArgT && m_arg;
5137 MatcherT const& m_matcher;
5138 public:
5139- MatchExpr( ArgT && arg, MatcherT const& matcher )
5140+ constexpr MatchExpr( ArgT && arg, MatcherT const& matcher )
5141 : ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose
5142 m_arg( CATCH_FORWARD(arg) ),
5143 m_matcher( matcher )
5144@@ -11338,6 +11688,13 @@ namespace Catch {
5145 }
5146 };
5147
5148+#ifdef __clang__
5149+# pragma clang diagnostic pop
5150+#elif defined __GNUC__
5151+# pragma GCC diagnostic pop
5152+#endif
5153+
5154+
5155 namespace Matchers {
5156 template <typename ArgT>
5157 class MatcherBase;
5158@@ -11348,7 +11705,8 @@ namespace Catch {
5159 void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher );
5160
5161 template<typename ArgT, typename MatcherT>
5162- auto makeMatchExpr( ArgT && arg, MatcherT const& matcher ) -> MatchExpr<ArgT, MatcherT> {
5163+ constexpr MatchExpr<ArgT, MatcherT>
5164+ makeMatchExpr( ArgT&& arg, MatcherT const& matcher ) {
5165 return MatchExpr<ArgT, MatcherT>( CATCH_FORWARD(arg), matcher );
5166 }
5167
5168@@ -11362,7 +11720,7 @@ namespace Catch {
5169 INTERNAL_CATCH_TRY { \
5170 catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher ) ); \
5171 } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
5172- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
5173+ catchAssertionHandler.complete(); \
5174 } while( false )
5175
5176
5177@@ -11372,7 +11730,10 @@ namespace Catch {
5178 Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
5179 if( catchAssertionHandler.allowThrows() ) \
5180 try { \
5181+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
5182+ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
5183 static_cast<void>(__VA_ARGS__ ); \
5184+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
5185 catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
5186 } \
5187 catch( exceptionType const& ex ) { \
5188@@ -11383,7 +11744,7 @@ namespace Catch {
5189 } \
5190 else \
5191 catchAssertionHandler.handleThrowingCallSkipped(); \
5192- INTERNAL_CATCH_REACT( catchAssertionHandler ) \
5193+ catchAssertionHandler.complete(); \
5194 } while( false )
5195
5196
5197@@ -11669,19 +12030,19 @@ namespace Matchers {
5198 }
5199
5200 template<typename T>
5201- using is_generic_matcher = std::is_base_of<
5202+ static constexpr bool is_generic_matcher_v = std::is_base_of<
5203 Catch::Matchers::MatcherGenericBase,
5204 std::remove_cv_t<std::remove_reference_t<T>>
5205- >;
5206+ >::value;
5207
5208 template<typename... Ts>
5209- using are_generic_matchers = Catch::Detail::conjunction<is_generic_matcher<Ts>...>;
5210+ static constexpr bool are_generic_matchers_v = Catch::Detail::conjunction<std::integral_constant<bool,is_generic_matcher_v<Ts>>...>::value;
5211
5212 template<typename T>
5213- using is_matcher = std::is_base_of<
5214+ static constexpr bool is_matcher_v = std::is_base_of<
5215 Catch::Matchers::MatcherUntypedBase,
5216 std::remove_cv_t<std::remove_reference_t<T>>
5217- >;
5218+ >::value;
5219
5220
5221 template<std::size_t N, typename Arg>
5222@@ -11754,7 +12115,7 @@ namespace Matchers {
5223
5224 //! Avoids type nesting for `GenericAllOf && some matcher` case
5225 template<typename MatcherRHS>
5226- friend std::enable_if_t<is_matcher<MatcherRHS>::value,
5227+ friend std::enable_if_t<is_matcher_v<MatcherRHS>,
5228 MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
5229 MatchAllOfGeneric<MatcherTs...>&& lhs,
5230 MatcherRHS const& rhs) {
5231@@ -11763,7 +12124,7 @@ namespace Matchers {
5232
5233 //! Avoids type nesting for `some matcher && GenericAllOf` case
5234 template<typename MatcherLHS>
5235- friend std::enable_if_t<is_matcher<MatcherLHS>::value,
5236+ friend std::enable_if_t<is_matcher_v<MatcherLHS>,
5237 MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
5238 MatcherLHS const& lhs,
5239 MatchAllOfGeneric<MatcherTs...>&& rhs) {
5240@@ -11808,7 +12169,7 @@ namespace Matchers {
5241
5242 //! Avoids type nesting for `GenericAnyOf || some matcher` case
5243 template<typename MatcherRHS>
5244- friend std::enable_if_t<is_matcher<MatcherRHS>::value,
5245+ friend std::enable_if_t<is_matcher_v<MatcherRHS>,
5246 MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
5247 MatchAnyOfGeneric<MatcherTs...>&& lhs,
5248 MatcherRHS const& rhs) {
5249@@ -11817,7 +12178,7 @@ namespace Matchers {
5250
5251 //! Avoids type nesting for `some matcher || GenericAnyOf` case
5252 template<typename MatcherLHS>
5253- friend std::enable_if_t<is_matcher<MatcherLHS>::value,
5254+ friend std::enable_if_t<is_matcher_v<MatcherLHS>,
5255 MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
5256 MatcherLHS const& lhs,
5257 MatchAnyOfGeneric<MatcherTs...>&& rhs) {
5258@@ -11857,20 +12218,20 @@ namespace Matchers {
5259
5260 // compose only generic matchers
5261 template<typename MatcherLHS, typename MatcherRHS>
5262- std::enable_if_t<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
5263+ std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
5264 operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
5265 return { lhs, rhs };
5266 }
5267
5268 template<typename MatcherLHS, typename MatcherRHS>
5269- std::enable_if_t<Detail::are_generic_matchers<MatcherLHS, MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
5270+ std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
5271 operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
5272 return { lhs, rhs };
5273 }
5274
5275 //! Wrap provided generic matcher in generic negator
5276 template<typename MatcherT>
5277- std::enable_if_t<Detail::is_generic_matcher<MatcherT>::value, Detail::MatchNotOfGeneric<MatcherT>>
5278+ std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
5279 operator ! (MatcherT const& matcher) {
5280 return Detail::MatchNotOfGeneric<MatcherT>{matcher};
5281 }
5282@@ -11878,25 +12239,25 @@ namespace Matchers {
5283
5284 // compose mixed generic and non-generic matchers
5285 template<typename MatcherLHS, typename ArgRHS>
5286- std::enable_if_t<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
5287+ std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
5288 operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
5289 return { lhs, rhs };
5290 }
5291
5292 template<typename ArgLHS, typename MatcherRHS>
5293- std::enable_if_t<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
5294+ std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
5295 operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
5296 return { lhs, rhs };
5297 }
5298
5299 template<typename MatcherLHS, typename ArgRHS>
5300- std::enable_if_t<Detail::is_generic_matcher<MatcherLHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
5301+ std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
5302 operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
5303 return { lhs, rhs };
5304 }
5305
5306 template<typename ArgLHS, typename MatcherRHS>
5307- std::enable_if_t<Detail::is_generic_matcher<MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
5308+ std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
5309 operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
5310 return { lhs, rhs };
5311 }
5312@@ -11973,7 +12334,7 @@ namespace Catch {
5313 //! Creates a matcher that accepts ranges/containers with specific size
5314 HasSizeMatcher SizeIs(std::size_t sz);
5315 template <typename Matcher>
5316- std::enable_if_t<Detail::is_matcher<Matcher>::value,
5317+ std::enable_if_t<Detail::is_matcher_v<Matcher>,
5318 SizeMatchesMatcher<Matcher>> SizeIs(Matcher&& m) {
5319 return SizeMatchesMatcher<Matcher>{CATCH_FORWARD(m)};
5320 }
5321@@ -11988,8 +12349,8 @@ namespace Catch {
5322 #define CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
5323
5324
5325-#include <algorithm>
5326 #include <functional>
5327+#include <type_traits>
5328
5329 namespace Catch {
5330 namespace Matchers {
5331@@ -12051,14 +12412,14 @@ namespace Catch {
5332 * Uses `std::equal_to` to do the comparison
5333 */
5334 template <typename T>
5335- std::enable_if_t<!Detail::is_matcher<T>::value,
5336+ std::enable_if_t<!Detail::is_matcher_v<T>,
5337 ContainsElementMatcher<T, std::equal_to<>>> Contains(T&& elem) {
5338 return { CATCH_FORWARD(elem), std::equal_to<>{} };
5339 }
5340
5341 //! Creates a matcher that checks whether a range contains element matching a matcher
5342 template <typename Matcher>
5343- std::enable_if_t<Detail::is_matcher<Matcher>::value,
5344+ std::enable_if_t<Detail::is_matcher_v<Matcher>,
5345 ContainsMatcherMatcher<Matcher>> Contains(Matcher&& matcher) {
5346 return { CATCH_FORWARD(matcher) };
5347 }
5348@@ -12435,8 +12796,7 @@ namespace Catch {
5349 #define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
5350
5351
5352-#include <algorithm>
5353-#include <utility>
5354+#include <functional>
5355
5356 namespace Catch {
5357 namespace Matchers {
5358@@ -12452,12 +12812,14 @@ namespace Catch {
5359
5360 public:
5361 template <typename TargetRangeLike2, typename Equality2>
5362+ constexpr
5363 RangeEqualsMatcher( TargetRangeLike2&& range,
5364 Equality2&& predicate ):
5365 m_desired( CATCH_FORWARD( range ) ),
5366 m_predicate( CATCH_FORWARD( predicate ) ) {}
5367
5368 template <typename RangeLike>
5369+ constexpr
5370 bool match( RangeLike&& rng ) const {
5371 auto rng_start = begin( rng );
5372 const auto rng_end = end( rng );
5373@@ -12490,12 +12852,14 @@ namespace Catch {
5374
5375 public:
5376 template <typename TargetRangeLike2, typename Equality2>
5377+ constexpr
5378 UnorderedRangeEqualsMatcher( TargetRangeLike2&& range,
5379 Equality2&& predicate ):
5380 m_desired( CATCH_FORWARD( range ) ),
5381 m_predicate( CATCH_FORWARD( predicate ) ) {}
5382
5383 template <typename RangeLike>
5384+ constexpr
5385 bool match( RangeLike&& rng ) const {
5386 using std::begin;
5387 using std::end;
5388@@ -12516,51 +12880,64 @@ namespace Catch {
5389 * Creates a matcher that checks if all elements in a range are equal
5390 * to all elements in another range.
5391 *
5392- * Uses `std::equal_to` to do the comparison
5393+ * Uses the provided predicate `predicate` to do the comparisons
5394+ * (defaulting to `std::equal_to`)
5395 */
5396- template <typename RangeLike>
5397- std::enable_if_t<!Detail::is_matcher<RangeLike>::value,
5398- RangeEqualsMatcher<RangeLike, std::equal_to<>>>
5399- RangeEquals( RangeLike&& range ) {
5400- return { CATCH_FORWARD( range ), std::equal_to<>{} };
5401+ template <typename RangeLike,
5402+ typename Equality = decltype( std::equal_to<>{} )>
5403+ constexpr
5404+ RangeEqualsMatcher<RangeLike, Equality>
5405+ RangeEquals( RangeLike&& range,
5406+ Equality&& predicate = std::equal_to<>{} ) {
5407+ return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
5408 }
5409
5410 /**
5411 * Creates a matcher that checks if all elements in a range are equal
5412- * to all elements in another range.
5413+ * to all elements in an initializer list.
5414 *
5415- * Uses to provided predicate `predicate` to do the comparisons
5416+ * Uses the provided predicate `predicate` to do the comparisons
5417+ * (defaulting to `std::equal_to`)
5418 */
5419- template <typename RangeLike, typename Equality>
5420- RangeEqualsMatcher<RangeLike, Equality>
5421- RangeEquals( RangeLike&& range, Equality&& predicate ) {
5422- return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
5423+ template <typename T,
5424+ typename Equality = decltype( std::equal_to<>{} )>
5425+ constexpr
5426+ RangeEqualsMatcher<std::initializer_list<T>, Equality>
5427+ RangeEquals( std::initializer_list<T> range,
5428+ Equality&& predicate = std::equal_to<>{} ) {
5429+ return { range, CATCH_FORWARD( predicate ) };
5430 }
5431
5432 /**
5433 * Creates a matcher that checks if all elements in a range are equal
5434- * to all elements in another range, in some permutation
5435+ * to all elements in another range, in some permutation.
5436 *
5437- * Uses `std::equal_to` to do the comparison
5438+ * Uses the provided predicate `predicate` to do the comparisons
5439+ * (defaulting to `std::equal_to`)
5440 */
5441- template <typename RangeLike>
5442- std::enable_if_t<
5443- !Detail::is_matcher<RangeLike>::value,
5444- UnorderedRangeEqualsMatcher<RangeLike, std::equal_to<>>>
5445- UnorderedRangeEquals( RangeLike&& range ) {
5446- return { CATCH_FORWARD( range ), std::equal_to<>{} };
5447+ template <typename RangeLike,
5448+ typename Equality = decltype( std::equal_to<>{} )>
5449+ constexpr
5450+ UnorderedRangeEqualsMatcher<RangeLike, Equality>
5451+ UnorderedRangeEquals( RangeLike&& range,
5452+ Equality&& predicate = std::equal_to<>{} ) {
5453+ return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
5454 }
5455
5456 /**
5457 * Creates a matcher that checks if all elements in a range are equal
5458- * to all elements in another range, in some permutation.
5459+ * to all elements in an initializer list, in some permutation.
5460 *
5461- * Uses to provided predicate `predicate` to do the comparisons
5462+ * Uses the provided predicate `predicate` to do the comparisons
5463+ * (defaulting to `std::equal_to`)
5464 */
5465- template <typename RangeLike, typename Equality>
5466- UnorderedRangeEqualsMatcher<RangeLike, Equality>
5467- UnorderedRangeEquals( RangeLike&& range, Equality&& predicate ) {
5468- return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
5469+ template <typename T,
5470+ typename Equality = decltype( std::equal_to<>{} )>
5471+ constexpr
5472+ UnorderedRangeEqualsMatcher<std::initializer_list<T>, Equality>
5473+ UnorderedRangeEquals( std::initializer_list<T> range,
5474+ Equality&& predicate = std::equal_to<>{} ) {
5475+ return { range, CATCH_FORWARD( predicate ) };
5476 }
5477 } // namespace Matchers
5478 } // namespace Catch
5479@@ -13003,9 +13380,11 @@ namespace Catch {
5480 public:
5481 // GCC5 compat: we cannot use inherited constructor, because it
5482 // doesn't implement backport of P0136
5483- AutomakeReporter(ReporterConfig&& _config):
5484- StreamingReporterBase(CATCH_MOVE(_config))
5485- {}
5486+ AutomakeReporter( ReporterConfig&& _config ):
5487+ StreamingReporterBase( CATCH_MOVE( _config ) ) {
5488+ m_preferences.shouldReportAllAssertionStarts = false;
5489+ }
5490+
5491 ~AutomakeReporter() override;
5492
5493 static std::string getDescription() {
5494@@ -13032,7 +13411,10 @@ namespace Catch {
5495
5496 class CompactReporter final : public StreamingReporterBase {
5497 public:
5498- using StreamingReporterBase::StreamingReporterBase;
5499+ CompactReporter( ReporterConfig&& _config ):
5500+ StreamingReporterBase( CATCH_MOVE( _config ) ) {
5501+ m_preferences.shouldReportAllAssertionStarts = false;
5502+ }
5503
5504 ~CompactReporter() override;
5505
5506@@ -13074,8 +13456,6 @@ namespace Catch {
5507 void noMatchingTestCases( StringRef unmatchedSpec ) override;
5508 void reportInvalidTestSpec( StringRef arg ) override;
5509
5510- void assertionStarting(AssertionInfo const&) override;
5511-
5512 void assertionEnded(AssertionStats const& _assertionStats) override;
5513
5514 void sectionStarting(SectionInfo const& _sectionInfo) override;
5515@@ -13429,7 +13809,6 @@ namespace Catch {
5516 void sectionStarting( SectionInfo const& sectionInfo ) override;
5517 void sectionEnded( SectionStats const& sectionStats ) override;
5518
5519- void assertionStarting( AssertionInfo const& assertionInfo ) override;
5520 void assertionEnded( AssertionStats const& assertionStats ) override;
5521
5522 //void testRunEndedCumulative() override;
5523@@ -13556,6 +13935,11 @@ namespace Catch {
5524 void updatePreferences(IEventListener const& reporterish);
5525
5526 public:
5527+ MultiReporter( IConfig const* config ):
5528+ IEventListener( config ) {
5529+ m_preferences.shouldReportAllAssertionStarts = false;
5530+ }
5531+
5532 using IEventListener::IEventListener;
5533
5534 void addListener( IEventListenerPtr&& listener );
5535@@ -13691,22 +14075,24 @@ namespace Catch {
5536
5537 #if !defined(CATCH_CONFIG_DISABLE)
5538
5539-# define CATCH_REGISTER_REPORTER( name, reporterType ) \
5540- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
5541- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
5542- namespace { \
5543- Catch::ReporterRegistrar<reporterType> INTERNAL_CATCH_UNIQUE_NAME( \
5544- catch_internal_RegistrarFor )( name ); \
5545- } \
5546+# define CATCH_REGISTER_REPORTER( name, reporterType ) \
5547+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
5548+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
5549+ namespace { \
5550+ const Catch::ReporterRegistrar<reporterType> \
5551+ INTERNAL_CATCH_UNIQUE_NAME( catch_internal_RegistrarFor )( \
5552+ name ); \
5553+ } \
5554 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
5555
5556-# define CATCH_REGISTER_LISTENER( listenerType ) \
5557- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
5558- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
5559- namespace { \
5560- Catch::ListenerRegistrar<listenerType> INTERNAL_CATCH_UNIQUE_NAME( \
5561- catch_internal_RegistrarFor )( #listenerType##_catch_sr ); \
5562- } \
5563+# define CATCH_REGISTER_LISTENER( listenerType ) \
5564+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
5565+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
5566+ namespace { \
5567+ const Catch::ListenerRegistrar<listenerType> \
5568+ INTERNAL_CATCH_UNIQUE_NAME( catch_internal_RegistrarFor )( \
5569+ #listenerType##_catch_sr ); \
5570+ } \
5571 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
5572
5573 #else // CATCH_CONFIG_DISABLE
5574@@ -13732,7 +14118,8 @@ namespace Catch {
5575 : CumulativeReporterBase(CATCH_MOVE(config))
5576 , xml(m_stream) {
5577 m_preferences.shouldRedirectStdOut = true;
5578- m_preferences.shouldReportAllAssertions = true;
5579+ m_preferences.shouldReportAllAssertions = false;
5580+ m_preferences.shouldReportAllAssertionStarts = false;
5581 m_shouldStoreSuccesfulAssertions = false;
5582 }
5583
5584@@ -13781,6 +14168,7 @@ namespace Catch {
5585 TAPReporter( ReporterConfig&& config ):
5586 StreamingReporterBase( CATCH_MOVE(config) ) {
5587 m_preferences.shouldReportAllAssertions = true;
5588+ m_preferences.shouldReportAllAssertionStarts = false;
5589 }
5590
5591 static std::string getDescription() {
5592@@ -13824,6 +14212,7 @@ namespace Catch {
5593 : StreamingReporterBase( CATCH_MOVE(_config) )
5594 {
5595 m_preferences.shouldRedirectStdOut = true;
5596+ m_preferences.shouldReportAllAssertionStarts = false;
5597 }
5598
5599 ~TeamCityReporter() override;
5600@@ -13891,8 +14280,6 @@ namespace Catch {
5601
5602 void sectionStarting(SectionInfo const& sectionInfo) override;
5603
5604- void assertionStarting(AssertionInfo const&) override;
5605-
5606 void assertionEnded(AssertionStats const& assertionStats) override;
5607
5608 void sectionEnded(SectionStats const& sectionStats) override;