diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6f0590f..9bd7e01d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,7 +33,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
- VERSION 3.6.0 # CML version placeholder, don't delete
+ VERSION 3.7.0 # CML version placeholder, don't delete
LANGUAGES CXX
# HOMEPAGE_URL is not supported until CMake version 3.12, which
# we do not target yet.
diff --git a/docs/release-notes.md b/docs/release-notes.md
index db40f598..9116a3d4 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -2,6 +2,7 @@
# Release notes
**Contents**
+[3.7.0](#370)
[3.6.0](#360)
[3.5.4](#354)
[3.5.3](#353)
@@ -63,6 +64,26 @@
[Even Older versions](#even-older-versions)
+## 3.7.0
+
+### improvements
+* Slightly improved compile times of benchmarks
+* Made the resolution estimation in benchmarks slightly more precise
+* Added new test case macro, `TEST_CASE_PERSISTENT_FIXTURE` (#2885, #1602)
+ * Unlike `TEST_CASE_METHOD`, the same underlying instance is used for all partial runs of that test case
+* **MASSIVELY** improved performance of the JUnit reporter when handling successful assertions (#2897)
+ * For 1 test case and 10M assertions, the new reporter runs 3x faster and uses up only 8 MB of memory, while the old one needs 7 GB of memory.
+* Reworked how output redirects works.
+ * Combining a reporter writing to stdout with capturing reporter no longer leads to the capturing reporter seeing all of the other reporter's output.
+ * The file based redirect no longer opens up a new temporary file for each partial test case run, so it will not run out of temporary files when running many tests in single process.
+
+### Miscellaneous
+* Better documentation for matchers on thrown exceptions (`REQUIRE_THROWS_MATCHES`)
+* Improved `catch_discover_tests`'s handling of environment paths (#2878)
+ * It won't reorder paths in `DL_PATHS` or `DYLD_FRAMEWORK_PATHS` args
+ * It won't overwrite the environment paths for test discovery
+
+
## 3.6.0
### Fixes
diff --git a/docs/test-fixtures.md b/docs/test-fixtures.md
index 6bc115e2..653b50e0 100644
--- a/docs/test-fixtures.md
+++ b/docs/test-fixtures.md
@@ -88,7 +88,7 @@ class.
### 3. `TEST_CASE_PERSISTENT_FIXTURE`
-> [Introduced](https://github.com/catchorg/Catch2/pull/2885) in Catch2 X.Y.Z
+> [Introduced](https://github.com/catchorg/Catch2/pull/2885) in Catch2 3.7.0
`TEST_CASE_PERSISTENT_FIXTURE` behaves in the same way as
[TEST_CASE_METHOD](#1-test_case_method) except that there will only be
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp
index c080ad19..4178e328 100644
--- a/extras/catch_amalgamated.cpp
+++ b/extras/catch_amalgamated.cpp
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.6.0
-// Generated: 2024-05-05 20:53:27.562886
+// Catch v3.7.0
+// Generated: 2024-08-14 12:04:53.604337
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -128,7 +128,13 @@ namespace Catch {
namespace Catch {
namespace Benchmark {
namespace Detail {
+ struct do_nothing {
+ void operator()() const {}
+ };
+
BenchmarkFunction::callable::~callable() = default;
+ BenchmarkFunction::BenchmarkFunction():
+ f( new model{ {} } ){}
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
@@ -1040,6 +1046,7 @@ namespace Catch {
m_messages.back().message += " := ";
start = pos;
}
+ break;
default:; // noop
}
}
@@ -2273,7 +2280,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 6, 0, "", 0 );
+ static Version version( 3, 7, 0, "", 0 );
return version;
}
@@ -4808,138 +4815,328 @@ namespace Catch {
#include
#include
+#include
#include
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
- #if defined(_MSC_VER)
- #include //_dup and _dup2
- #define dup _dup
- #define dup2 _dup2
- #define fileno _fileno
- #else
- #include // dup and dup2
- #endif
+#if defined( CATCH_CONFIG_NEW_CAPTURE )
+# if defined( _MSC_VER )
+# include //_dup and _dup2
+# define dup _dup
+# define dup2 _dup2
+# define fileno _fileno
+# else
+# include // dup and dup2
+# endif
#endif
-
namespace Catch {
- RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
- : m_originalStream( originalStream ),
- m_redirectionStream( redirectionStream ),
- m_prevBuf( m_originalStream.rdbuf() )
- {
- m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
- }
+ namespace {
+ //! A no-op implementation, used if no reporter wants output
+ //! redirection.
+ class NoopRedirect : public OutputRedirect {
+ void activateImpl() override {}
+ void deactivateImpl() override {}
+ std::string getStdout() override { return {}; }
+ std::string getStderr() override { return {}; }
+ void clearBuffers() override {}
+ };
- RedirectedStream::~RedirectedStream() {
- m_originalStream.rdbuf( m_prevBuf );
- }
+ /**
+ * Redirects specific stream's rdbuf with another's.
+ *
+ * Redirection can be stopped and started on-demand, assumes
+ * that the underlying stream's rdbuf aren't changed by other
+ * users.
+ */
+ class RedirectedStreamNew {
+ std::ostream& m_originalStream;
+ std::ostream& m_redirectionStream;
+ std::streambuf* m_prevBuf;
- RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
- auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
+ public:
+ RedirectedStreamNew( std::ostream& originalStream,
+ std::ostream& redirectionStream ):
+ m_originalStream( originalStream ),
+ m_redirectionStream( redirectionStream ),
+ m_prevBuf( m_originalStream.rdbuf() ) {}
- RedirectedStdErr::RedirectedStdErr()
- : m_cerr( Catch::cerr(), m_rss.get() ),
- m_clog( Catch::clog(), m_rss.get() )
- {}
- auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
-
- RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
- : m_redirectedCout(redirectedCout),
- m_redirectedCerr(redirectedCerr)
- {}
-
- RedirectedStreams::~RedirectedStreams() {
- m_redirectedCout += m_redirectedStdOut.str();
- m_redirectedCerr += m_redirectedStdErr.str();
- }
-
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
-
-#if defined(_MSC_VER)
- TempFile::TempFile() {
- if (tmpnam_s(m_buffer)) {
- CATCH_RUNTIME_ERROR("Could not get a temp filename");
- }
- if (fopen_s(&m_file, m_buffer, "w+")) {
- char buffer[100];
- if (strerror_s(buffer, errno)) {
- CATCH_RUNTIME_ERROR("Could not translate errno to a string");
+ void startRedirect() {
+ m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
}
- CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
- }
- }
-#else
- TempFile::TempFile() {
- m_file = std::tmpfile();
- if (!m_file) {
- CATCH_RUNTIME_ERROR("Could not create a temp file.");
- }
- }
+ void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); }
+ };
-#endif
+ /**
+ * Redirects the `std::cout`, `std::cerr`, `std::clog` streams,
+ * but does not touch the actual `stdout`/`stderr` file descriptors.
+ */
+ class StreamRedirect : public OutputRedirect {
+ ReusableStringStream m_redirectedOut, m_redirectedErr;
+ RedirectedStreamNew m_cout, m_cerr, m_clog;
- TempFile::~TempFile() {
- // TBD: What to do about errors here?
- std::fclose(m_file);
- // We manually create the file on Windows only, on Linux
- // it will be autodeleted
-#if defined(_MSC_VER)
- std::remove(m_buffer);
-#endif
- }
+ public:
+ StreamRedirect():
+ m_cout( Catch::cout(), m_redirectedOut.get() ),
+ m_cerr( Catch::cerr(), m_redirectedErr.get() ),
+ m_clog( Catch::clog(), m_redirectedErr.get() ) {}
+ void activateImpl() override {
+ m_cout.startRedirect();
+ m_cerr.startRedirect();
+ m_clog.startRedirect();
+ }
+ void deactivateImpl() override {
+ m_cout.stopRedirect();
+ m_cerr.stopRedirect();
+ m_clog.stopRedirect();
+ }
+ std::string getStdout() override { return m_redirectedOut.str(); }
+ std::string getStderr() override { return m_redirectedErr.str(); }
+ void clearBuffers() override {
+ m_redirectedOut.str( "" );
+ m_redirectedErr.str( "" );
+ }
+ };
- FILE* TempFile::getFile() {
- return m_file;
- }
+#if defined( CATCH_CONFIG_NEW_CAPTURE )
- std::string TempFile::getContents() {
- std::stringstream sstr;
- char buffer[100] = {};
- std::rewind(m_file);
- while (std::fgets(buffer, sizeof(buffer), m_file)) {
- sstr << buffer;
- }
- return sstr.str();
- }
+ // Windows's implementation of std::tmpfile is terrible (it tries
+ // to create a file inside system folder, thus requiring elevated
+ // privileges for the binary), so we have to use tmpnam(_s) and
+ // create the file ourselves there.
+ class TempFile {
+ public:
+ TempFile( TempFile const& ) = delete;
+ TempFile& operator=( TempFile const& ) = delete;
+ TempFile( TempFile&& ) = delete;
+ TempFile& operator=( TempFile&& ) = delete;
- OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
- m_originalStdout(dup(1)),
- m_originalStderr(dup(2)),
- m_stdoutDest(stdout_dest),
- m_stderrDest(stderr_dest) {
- dup2(fileno(m_stdoutFile.getFile()), 1);
- dup2(fileno(m_stderrFile.getFile()), 2);
- }
+# if defined( _MSC_VER )
+ TempFile() {
+ if ( tmpnam_s( m_buffer ) ) {
+ CATCH_RUNTIME_ERROR( "Could not get a temp filename" );
+ }
+ if ( fopen_s( &m_file, m_buffer, "wb+" ) ) {
+ char buffer[100];
+ if ( strerror_s( buffer, errno ) ) {
+ CATCH_RUNTIME_ERROR(
+ "Could not translate errno to a string" );
+ }
+ CATCH_RUNTIME_ERROR( "Could not open the temp file: '"
+ << m_buffer
+ << "' because: " << buffer );
+ }
+ }
+# else
+ TempFile() {
+ m_file = std::tmpfile();
+ if ( !m_file ) {
+ CATCH_RUNTIME_ERROR( "Could not create a temp file." );
+ }
+ }
+# endif
- OutputRedirect::~OutputRedirect() {
- Catch::cout() << std::flush;
- fflush(stdout);
- // Since we support overriding these streams, we flush cerr
- // even though std::cerr is unbuffered
- Catch::cerr() << std::flush;
- Catch::clog() << std::flush;
- fflush(stderr);
+ ~TempFile() {
+ // TBD: What to do about errors here?
+ std::fclose( m_file );
+ // We manually create the file on Windows only, on Linux
+ // it will be autodeleted
+# if defined( _MSC_VER )
+ std::remove( m_buffer );
+# endif
+ }
- dup2(m_originalStdout, 1);
- dup2(m_originalStderr, 2);
+ std::FILE* getFile() { return m_file; }
+ std::string getContents() {
+ ReusableStringStream sstr;
+ constexpr long buffer_size = 100;
+ char buffer[buffer_size + 1] = {};
+ long current_pos = ftell( m_file );
+ CATCH_ENFORCE( current_pos >= 0,
+ "ftell failed, errno: " << errno );
+ std::rewind( m_file );
+ while ( current_pos > 0 ) {
+ auto read_characters =
+ std::fread( buffer,
+ 1,
+ std::min( buffer_size, current_pos ),
+ m_file );
+ buffer[read_characters] = '\0';
+ sstr << buffer;
+ current_pos -= static_cast( read_characters );
+ }
+ return sstr.str();
+ }
- m_stdoutDest += m_stdoutFile.getContents();
- m_stderrDest += m_stderrFile.getContents();
- }
+ void clear() { std::rewind( m_file ); }
+
+ private:
+ std::FILE* m_file = nullptr;
+ char m_buffer[L_tmpnam] = { 0 };
+ };
+
+ /**
+ * Redirects the actual `stdout`/`stderr` file descriptors.
+ *
+ * Works by replacing the file descriptors numbered 1 and 2
+ * with an open temporary file.
+ */
+ class FileRedirect : public OutputRedirect {
+ TempFile m_outFile, m_errFile;
+ int m_originalOut = -1;
+ int m_originalErr = -1;
+
+ // Flushes cout/cerr/clog streams and stdout/stderr FDs
+ void flushEverything() {
+ Catch::cout() << std::flush;
+ fflush( stdout );
+ // Since we support overriding these streams, we flush cerr
+ // even though std::cerr is unbuffered
+ Catch::cerr() << std::flush;
+ Catch::clog() << std::flush;
+ fflush( stderr );
+ }
+
+ public:
+ FileRedirect():
+ m_originalOut( dup( fileno( stdout ) ) ),
+ m_originalErr( dup( fileno( stderr ) ) ) {
+ CATCH_ENFORCE( m_originalOut >= 0, "Could not dup stdout" );
+ CATCH_ENFORCE( m_originalErr >= 0, "Could not dup stderr" );
+ }
+
+ std::string getStdout() override { return m_outFile.getContents(); }
+ std::string getStderr() override { return m_errFile.getContents(); }
+ void clearBuffers() override {
+ m_outFile.clear();
+ m_errFile.clear();
+ }
+
+ void activateImpl() override {
+ // We flush before starting redirect, to ensure that we do
+ // not capture the end of message sent before activation.
+ flushEverything();
+
+ int ret;
+ ret = dup2( fileno( m_outFile.getFile() ), fileno( stdout ) );
+ CATCH_ENFORCE( ret >= 0,
+ "dup2 to stdout has failed, errno: " << errno );
+ ret = dup2( fileno( m_errFile.getFile() ), fileno( stderr ) );
+ CATCH_ENFORCE( ret >= 0,
+ "dup2 to stderr has failed, errno: " << errno );
+ }
+ void deactivateImpl() override {
+ // We flush before ending redirect, to ensure that we
+ // capture all messages sent while the redirect was active.
+ flushEverything();
+
+ int ret;
+ ret = dup2( m_originalOut, fileno( stdout ) );
+ CATCH_ENFORCE(
+ ret >= 0,
+ "dup2 of original stdout has failed, errno: " << errno );
+ ret = dup2( m_originalErr, fileno( stderr ) );
+ CATCH_ENFORCE(
+ ret >= 0,
+ "dup2 of original stderr has failed, errno: " << errno );
+ }
+ };
#endif // CATCH_CONFIG_NEW_CAPTURE
+ } // end namespace
+
+ bool isRedirectAvailable( OutputRedirect::Kind kind ) {
+ switch ( kind ) {
+ // These two are always available
+ case OutputRedirect::None:
+ case OutputRedirect::Streams:
+ return true;
+#if defined( CATCH_CONFIG_NEW_CAPTURE )
+ case OutputRedirect::FileDescriptors:
+ return true;
+#endif
+ default:
+ return false;
+ }
+ }
+
+ Detail::unique_ptr makeOutputRedirect( bool actual ) {
+ if ( actual ) {
+ // TODO: Clean this up later
+#if defined( CATCH_CONFIG_NEW_CAPTURE )
+ return Detail::make_unique();
+#else
+ return Detail::make_unique();
+#endif
+ } else {
+ return Detail::make_unique();
+ }
+ }
+
+ RedirectGuard scopedActivate( OutputRedirect& redirectImpl ) {
+ return RedirectGuard( true, redirectImpl );
+ }
+
+ RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ) {
+ return RedirectGuard( false, redirectImpl );
+ }
+
+ OutputRedirect::~OutputRedirect() = default;
+
+ RedirectGuard::RedirectGuard( bool activate, OutputRedirect& redirectImpl ):
+ m_redirect( &redirectImpl ),
+ m_activate( activate ),
+ m_previouslyActive( redirectImpl.isActive() ) {
+
+ // Skip cases where there is no actual state change.
+ if ( m_activate == m_previouslyActive ) { return; }
+
+ if ( m_activate ) {
+ m_redirect->activate();
+ } else {
+ m_redirect->deactivate();
+ }
+ }
+
+ RedirectGuard::~RedirectGuard() noexcept( false ) {
+ if ( m_moved ) { return; }
+ // Skip cases where there is no actual state change.
+ if ( m_activate == m_previouslyActive ) { return; }
+
+ if ( m_activate ) {
+ m_redirect->deactivate();
+ } else {
+ m_redirect->activate();
+ }
+ }
+
+ RedirectGuard::RedirectGuard( RedirectGuard&& rhs ) noexcept:
+ m_redirect( rhs.m_redirect ),
+ m_activate( rhs.m_activate ),
+ m_previouslyActive( rhs.m_previouslyActive ),
+ m_moved( false ) {
+ rhs.m_moved = true;
+ }
+
+ RedirectGuard& RedirectGuard::operator=( RedirectGuard&& rhs ) noexcept {
+ m_redirect = rhs.m_redirect;
+ m_activate = rhs.m_activate;
+ m_previouslyActive = rhs.m_previouslyActive;
+ m_moved = false;
+ rhs.m_moved = true;
+ return *this;
+ }
+
} // namespace Catch
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
- #if defined(_MSC_VER)
- #undef dup
- #undef dup2
- #undef fileno
- #endif
+#if defined( CATCH_CONFIG_NEW_CAPTURE )
+# if defined( _MSC_VER )
+# undef dup
+# undef dup2
+# undef fileno
+# endif
#endif
@@ -5573,6 +5770,7 @@ namespace Catch {
m_config(_config),
m_reporter(CATCH_MOVE(reporter)),
m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
+ m_outputRedirect( makeOutputRedirect( m_reporter->getPreferences().shouldRedirectStdOut ) ),
m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
{
getCurrentMutableContext().setResultCapture( this );
@@ -5588,6 +5786,7 @@ namespace Catch {
auto const& testInfo = testCase.getTestCaseInfo();
m_reporter->testCaseStarting(testInfo);
+ testCase.prepareTestCase();
m_activeTestCase = &testCase;
@@ -5638,15 +5837,17 @@ namespace Catch {
m_reporter->testCasePartialStarting(testInfo, testRuns);
const auto beforeRunTotals = m_totals;
- std::string oneRunCout, oneRunCerr;
- runCurrentTest(oneRunCout, oneRunCerr);
+ runCurrentTest();
+ std::string oneRunCout = m_outputRedirect->getStdout();
+ std::string oneRunCerr = m_outputRedirect->getStderr();
+ m_outputRedirect->clearBuffers();
redirectedCout += oneRunCout;
redirectedCerr += oneRunCerr;
const auto singleRunTotals = m_totals.delta(beforeRunTotals);
auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
-
m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
+
++testRuns;
} while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
@@ -5657,6 +5858,7 @@ namespace Catch {
deltaTotals.testCases.failed++;
}
m_totals.testCases += deltaTotals.testCases;
+ testCase.tearDownTestCase();
m_reporter->testCaseEnded(TestCaseStats(testInfo,
deltaTotals,
CATCH_MOVE(redirectedCout),
@@ -5690,7 +5892,10 @@ namespace Catch {
m_lastAssertionPassed = true;
}
- m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
+ {
+ auto _ = scopedDeactivate( *m_outputRedirect );
+ m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) );
+ }
if ( result.getResultType() != ResultWas::Warning ) {
m_messageScopes.clear();
@@ -5707,6 +5912,7 @@ namespace Catch {
}
void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
+ auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->assertionStarting( info );
}
@@ -5725,7 +5931,10 @@ namespace Catch {
SectionInfo sectionInfo( sectionLineInfo, static_cast(sectionName) );
m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
- m_reporter->sectionStarting(sectionInfo);
+ {
+ auto _ = scopedDeactivate( *m_outputRedirect );
+ m_reporter->sectionStarting( sectionInfo );
+ }
assertions = m_totals.assertions;
@@ -5785,7 +5994,15 @@ namespace Catch {
m_activeSections.pop_back();
}
- m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions));
+ {
+ auto _ = scopedDeactivate( *m_outputRedirect );
+ m_reporter->sectionEnded(
+ SectionStats( CATCH_MOVE( endInfo.sectionInfo ),
+ assertions,
+ endInfo.durationInSeconds,
+ missingAssertions ) );
+ }
+
m_messages.clear();
m_messageScopes.clear();
}
@@ -5802,15 +6019,19 @@ namespace Catch {
}
void RunContext::benchmarkPreparing( StringRef name ) {
- m_reporter->benchmarkPreparing(name);
+ auto _ = scopedDeactivate( *m_outputRedirect );
+ m_reporter->benchmarkPreparing( name );
}
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
+ auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->benchmarkStarting( info );
}
void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
+ auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->benchmarkEnded( stats );
}
void RunContext::benchmarkFailed( StringRef error ) {
+ auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->benchmarkFailed( error );
}
@@ -5841,8 +6062,13 @@ namespace Catch {
}
void RunContext::handleFatalErrorCondition( StringRef message ) {
+ // TODO: scoped deactivate here? Just give up and do best effort?
+ // the deactivation can break things further, OTOH so can the
+ // capture
+ auto _ = scopedDeactivate( *m_outputRedirect );
+
// First notify reporter that bad things happened
- m_reporter->fatalErrorEncountered(message);
+ m_reporter->fatalErrorEncountered( message );
// Don't rebuild the result -- the stringification itself can cause more fatal errors
// Instead, fake a result data.
@@ -5869,7 +6095,7 @@ namespace Catch {
Counts assertions;
assertions.failed = 1;
SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false);
- m_reporter->sectionEnded(testCaseSectionStats);
+ m_reporter->sectionEnded( testCaseSectionStats );
auto const& testInfo = m_activeTestCase->getTestCaseInfo();
@@ -5900,7 +6126,7 @@ namespace Catch {
return m_totals.assertions.failed >= static_cast(m_config->abortAfter());
}
- void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
+ void RunContext::runCurrentTest() {
auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
m_reporter->sectionStarting(testCaseSection);
@@ -5911,18 +6137,8 @@ namespace Catch {
Timer timer;
CATCH_TRY {
- if (m_reporter->getPreferences().shouldRedirectStdOut) {
-#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
- RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
-
- timer.start();
- invokeActiveTestCase();
-#else
- OutputRedirect r(redirectedCout, redirectedCerr);
- timer.start();
- invokeActiveTestCase();
-#endif
- } else {
+ {
+ auto _ = scopedActivate( *m_outputRedirect );
timer.start();
invokeActiveTestCase();
}
@@ -5967,11 +6183,12 @@ namespace Catch {
void RunContext::handleUnfinishedSections() {
// If sections ended prematurely due to an exception we stored their
// infos here so we can tear them down outside the unwind process.
- for (auto it = m_unfinishedSections.rbegin(),
- itEnd = m_unfinishedSections.rend();
- it != itEnd;
- ++it)
- sectionEnded(CATCH_MOVE(*it));
+ for ( auto it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it ) {
+ sectionEnded( CATCH_MOVE( *it ) );
+ }
m_unfinishedSections.clear();
}
@@ -6898,6 +7115,8 @@ namespace Catch {
#include
namespace Catch {
+ void ITestInvoker::prepareTestCase() {}
+ void ITestInvoker::tearDownTestCase() {}
ITestInvoker::~ITestInvoker() = default;
namespace {
@@ -10333,7 +10552,7 @@ namespace Catch {
xml( m_stream )
{
m_preferences.shouldRedirectStdOut = true;
- m_preferences.shouldReportAllAssertions = true;
+ m_preferences.shouldReportAllAssertions = false;
m_shouldStoreSuccesfulAssertions = false;
}
@@ -10443,7 +10662,7 @@ namespace Catch {
if( !rootName.empty() )
name = rootName + '/' + name;
- if( sectionNode.hasAnyAssertions()
+ if ( sectionNode.stats.assertions.total() > 0
|| !sectionNode.stdOut.empty()
|| !sectionNode.stdErr.empty() ) {
XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp
index 6cc67e76..968aeb8d 100644
--- a/extras/catch_amalgamated.hpp
+++ b/extras/catch_amalgamated.hpp
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.6.0
-// Generated: 2024-05-05 20:53:27.071502
+// Catch v3.7.0
+// Generated: 2024-08-14 12:04:53.220567
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -1584,22 +1584,17 @@ namespace Catch {
private:
struct callable {
virtual void call(Chronometer meter) const = 0;
- virtual Catch::Detail::unique_ptr clone() const = 0;
virtual ~callable(); // = default;
callable() = default;
- callable(callable const&) = default;
- callable& operator=(callable const&) = default;
+ callable(callable&&) = default;
+ callable& operator=(callable&&) = default;
};
template
struct model : public callable {
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(fun_) {}
- Catch::Detail::unique_ptr clone() const override {
- return Catch::Detail::make_unique>( *this );
- }
-
void call(Chronometer meter) const override {
call(meter, is_callable());
}
@@ -1613,14 +1608,8 @@ namespace Catch {
Fun fun;
};
- struct do_nothing { void operator()() const {} };
-
- template
- BenchmarkFunction(model* c) : f(c) {}
-
public:
- BenchmarkFunction()
- : f(new model{ {} }) {}
+ BenchmarkFunction();
template ::value, int> = 0>
@@ -1630,20 +1619,12 @@ namespace Catch {
BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
f( CATCH_MOVE( that.f ) ) {}
- BenchmarkFunction(BenchmarkFunction const& that)
- : f(that.f->clone()) {}
-
BenchmarkFunction&
operator=( BenchmarkFunction&& that ) noexcept {
f = CATCH_MOVE( that.f );
return *this;
}
- BenchmarkFunction& operator=(BenchmarkFunction const& that) {
- f = that.f->clone();
- return *this;
- }
-
void operator()(Chronometer meter) const { f->call(meter); }
private:
@@ -1780,7 +1761,7 @@ namespace Catch {
template
TimingOf measure(Fun&& fun, Args&&... args) {
auto start = Clock::now();
- auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
+ auto&& r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...);
auto end = Clock::now();
auto delta = end - start;
return { delta, CATCH_FORWARD(r), 1 };
@@ -1946,15 +1927,17 @@ namespace Catch {
namespace Detail {
template
std::vector resolution(int k) {
- std::vector> times;
- times.reserve(static_cast(k + 1));
- for ( int i = 0; i < k + 1; ++i ) {
- times.push_back( Clock::now() );
+ const size_t points = static_cast( k + 1 );
+ // To avoid overhead from the branch inside vector::push_back,
+ // we allocate them all and then overwrite.
+ std::vector> times(points);
+ for ( auto& time : times ) {
+ time = Clock::now();
}
std::vector deltas;
deltas.reserve(static_cast(k));
- for ( size_t idx = 1; idx < times.size(); ++idx ) {
+ for ( size_t idx = 1; idx < points; ++idx ) {
deltas.push_back( static_cast(
( times[idx] - times[idx - 1] ).count() ) );
}
@@ -2103,12 +2086,12 @@ namespace Catch {
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
template
- ExecutionPlan prepare(const IConfig &cfg, Environment env) const {
+ ExecutionPlan prepare(const IConfig &cfg, Environment env) {
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime()));
auto&& test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun);
int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed));
- return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
+ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
}
template
@@ -3347,6 +3330,18 @@ namespace Catch {
#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED
+#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED
+#define CATCH_CASE_SENSITIVE_HPP_INCLUDED
+
+namespace Catch {
+
+ enum class CaseSensitive { Yes, No };
+
+} // namespace Catch
+
+#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED
+
+
#ifndef CATCH_CONFIG_HPP_INCLUDED
#define CATCH_CONFIG_HPP_INCLUDED
@@ -3366,18 +3361,6 @@ namespace Catch {
#define CATCH_WILDCARD_PATTERN_HPP_INCLUDED
-
-#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED
-#define CATCH_CASE_SENSITIVE_HPP_INCLUDED
-
-namespace Catch {
-
- enum class CaseSensitive { Yes, No };
-
-} // namespace Catch
-
-#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED
-
#include
namespace Catch
@@ -5953,6 +5936,8 @@ namespace Catch {
class ITestInvoker {
public:
+ virtual void prepareTestCase();
+ virtual void tearDownTestCase();
virtual void invoke() const = 0;
virtual ~ITestInvoker(); // = default
};
@@ -6005,6 +5990,33 @@ Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) {
return Detail::make_unique>( testAsMethod );
}
+template
+class TestInvokerFixture : public ITestInvoker {
+ void ( C::*m_testAsMethod )() const;
+ Detail::unique_ptr m_fixture = nullptr;
+
+public:
+ TestInvokerFixture( void ( C::*testAsMethod )() const) noexcept : m_testAsMethod( testAsMethod ) {}
+
+ void prepareTestCase() override {
+ m_fixture = Detail::make_unique();
+ }
+
+ void tearDownTestCase() override {
+ m_fixture.reset();
+ }
+
+ void invoke() const override {
+ auto* f = m_fixture.get();
+ ( f->*m_testAsMethod )();
+ }
+};
+
+template
+Detail::unique_ptr makeTestInvokerFixture( void ( C::*testAsMethod )() const ) {
+ return Detail::make_unique>( testAsMethod );
+}
+
struct NameAndTags {
constexpr NameAndTags( StringRef name_ = StringRef(),
StringRef tags_ = StringRef() ) noexcept:
@@ -6101,6 +6113,26 @@ static int catchInternalSectionHint = 0;
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+ namespace { \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) { \
+ void test() const; \
+ }; \
+ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
+ Catch::makeTestInvokerFixture( &TestName::test ), \
+ CATCH_INTERNAL_LINEINFO, \
+ #ClassName##_catch_sr, \
+ Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ void TestName::test() const
+ #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
+
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
@@ -6158,6 +6190,7 @@ static int catchInternalSectionHint = 0;
#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ )
#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
@@ -6212,6 +6245,7 @@ static int catchInternalSectionHint = 0;
#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
#define CATCH_METHOD_AS_TEST_CASE( method, ... )
+ #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define CATCH_SECTION( ... )
#define CATCH_DYNAMIC_SECTION( ... )
@@ -6257,6 +6291,7 @@ static int catchInternalSectionHint = 0;
#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ )
#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
@@ -6310,6 +6345,7 @@ static int catchInternalSectionHint = 0;
#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
#define METHOD_AS_TEST_CASE( method, ... )
+ #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
#define REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define SECTION( ... )
#define DYNAMIC_SECTION( ... )
@@ -7103,6 +7139,14 @@ namespace Catch {
TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
m_info(info), m_invoker(invoker) {}
+ void prepareTestCase() const {
+ m_invoker->prepareTestCase();
+ }
+
+ void tearDownTestCase() const {
+ m_invoker->tearDownTestCase();
+ }
+
void invoke() const {
m_invoker->invoke();
}
@@ -7271,7 +7315,7 @@ namespace Catch {
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
-#define CATCH_VERSION_MINOR 6
+#define CATCH_VERSION_MINOR 7
#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -10033,106 +10077,67 @@ namespace Catch {
#define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
-#include
-#include
+#include
#include
namespace Catch {
- class RedirectedStream {
- std::ostream& m_originalStream;
- std::ostream& m_redirectionStream;
- std::streambuf* m_prevBuf;
-
- public:
- RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
- ~RedirectedStream();
- };
-
- class RedirectedStdOut {
- ReusableStringStream m_rss;
- RedirectedStream m_cout;
- public:
- RedirectedStdOut();
- auto str() const -> std::string;
- };
-
- // StdErr has two constituent streams in C++, std::cerr and std::clog
- // This means that we need to redirect 2 streams into 1 to keep proper
- // order of writes
- class RedirectedStdErr {
- ReusableStringStream m_rss;
- RedirectedStream m_cerr;
- RedirectedStream m_clog;
- public:
- RedirectedStdErr();
- auto str() const -> std::string;
- };
-
- class RedirectedStreams {
- public:
- RedirectedStreams(RedirectedStreams const&) = delete;
- RedirectedStreams& operator=(RedirectedStreams const&) = delete;
- RedirectedStreams(RedirectedStreams&&) = delete;
- RedirectedStreams& operator=(RedirectedStreams&&) = delete;
-
- RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
- ~RedirectedStreams();
- private:
- std::string& m_redirectedCout;
- std::string& m_redirectedCerr;
- RedirectedStdOut m_redirectedStdOut;
- RedirectedStdErr m_redirectedStdErr;
- };
-
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
-
- // Windows's implementation of std::tmpfile is terrible (it tries
- // to create a file inside system folder, thus requiring elevated
- // privileges for the binary), so we have to use tmpnam(_s) and
- // create the file ourselves there.
- class TempFile {
- public:
- TempFile(TempFile const&) = delete;
- TempFile& operator=(TempFile const&) = delete;
- TempFile(TempFile&&) = delete;
- TempFile& operator=(TempFile&&) = delete;
-
- TempFile();
- ~TempFile();
-
- std::FILE* getFile();
- std::string getContents();
-
- private:
- std::FILE* m_file = nullptr;
- #if defined(_MSC_VER)
- char m_buffer[L_tmpnam] = { 0 };
- #endif
- };
-
-
class OutputRedirect {
+ bool m_redirectActive = false;
+ virtual void activateImpl() = 0;
+ virtual void deactivateImpl() = 0;
public:
- OutputRedirect(OutputRedirect const&) = delete;
- OutputRedirect& operator=(OutputRedirect const&) = delete;
- OutputRedirect(OutputRedirect&&) = delete;
- OutputRedirect& operator=(OutputRedirect&&) = delete;
+ enum Kind {
+ //! No redirect (noop implementation)
+ None,
+ //! Redirect std::cout/std::cerr/std::clog streams internally
+ Streams,
+ //! Redirect the stdout/stderr file descriptors into files
+ FileDescriptors,
+ };
+ virtual ~OutputRedirect(); // = default;
- OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
- ~OutputRedirect();
-
- private:
- int m_originalStdout = -1;
- int m_originalStderr = -1;
- TempFile m_stdoutFile;
- TempFile m_stderrFile;
- std::string& m_stdoutDest;
- std::string& m_stderrDest;
+ // TODO: Do we want to check that redirect is not active before retrieving the output?
+ virtual std::string getStdout() = 0;
+ virtual std::string getStderr() = 0;
+ virtual void clearBuffers() = 0;
+ bool isActive() const { return m_redirectActive; }
+ void activate() {
+ assert( !m_redirectActive && "redirect is already active" );
+ activateImpl();
+ m_redirectActive = true;
+ }
+ void deactivate() {
+ assert( m_redirectActive && "redirect is not active" );
+ deactivateImpl();
+ m_redirectActive = false;
+ }
};
-#endif
+ bool isRedirectAvailable( OutputRedirect::Kind kind);
+ Detail::unique_ptr makeOutputRedirect( bool actual );
+
+ class RedirectGuard {
+ OutputRedirect* m_redirect;
+ bool m_activate;
+ bool m_previouslyActive;
+ bool m_moved = false;
+
+ public:
+ RedirectGuard( bool activate, OutputRedirect& redirectImpl );
+ ~RedirectGuard() noexcept( false );
+
+ RedirectGuard( RedirectGuard const& ) = delete;
+ RedirectGuard& operator=( RedirectGuard const& ) = delete;
+
+ // C++14 needs move-able guards to return them from functions
+ RedirectGuard( RedirectGuard&& rhs ) noexcept;
+ RedirectGuard& operator=( RedirectGuard&& rhs ) noexcept;
+ };
+
+ RedirectGuard scopedActivate( OutputRedirect& redirectImpl );
+ RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl );
} // end namespace Catch
@@ -10455,6 +10460,7 @@ namespace Catch {
class IConfig;
class IEventListener;
using IEventListenerPtr = Detail::unique_ptr;
+ class OutputRedirect;
///////////////////////////////////////////////////////////////////////////
@@ -10541,7 +10547,7 @@ namespace Catch {
private:
- void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
+ void runCurrentTest();
void invokeActiveTestCase();
void resetAssertionInfo();
@@ -10574,6 +10580,7 @@ namespace Catch {
std::vector m_unfinishedSections;
std::vector m_activeSections;
TrackerContext m_trackerContext;
+ Detail::unique_ptr m_outputRedirect;
FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true;
diff --git a/meson.build b/meson.build
index 15ff13ed..c7b91bca 100644
--- a/meson.build
+++ b/meson.build
@@ -8,7 +8,7 @@
project(
'catch2',
'cpp',
- version: '3.6.0', # CML version placeholder, don't delete
+ version: '3.7.0', # CML version placeholder, don't delete
license: 'BSL-1.0',
meson_version: '>=0.54.1',
)
diff --git a/src/catch2/catch_test_case_info.hpp b/src/catch2/catch_test_case_info.hpp
index 00b393b2..67448ae9 100644
--- a/src/catch2/catch_test_case_info.hpp
+++ b/src/catch2/catch_test_case_info.hpp
@@ -112,12 +112,12 @@ namespace Catch {
TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
m_info(info), m_invoker(invoker) {}
- void prepareTestCase() const {
+ void prepareTestCase() const {
m_invoker->prepareTestCase();
}
- void tearDownTestCase() const {
- m_invoker->tearDownTestCase();
+ void tearDownTestCase() const {
+ m_invoker->tearDownTestCase();
}
void invoke() const {
diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp
index 63a9ec86..44560fe0 100644
--- a/src/catch2/catch_version.cpp
+++ b/src/catch2/catch_version.cpp
@@ -36,7 +36,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 6, 0, "", 0 );
+ static Version version( 3, 7, 0, "", 0 );
return version;
}
diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp
index b95580bd..10a63ad0 100644
--- a/src/catch2/catch_version_macros.hpp
+++ b/src/catch2/catch_version_macros.hpp
@@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
-#define CATCH_VERSION_MINOR 6
+#define CATCH_VERSION_MINOR 7
#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED