Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMartin Hořeňovský2024-08-14 12:05:21 +0200
committerMartin Hořeňovský2024-08-14 12:05:21 +0200
commit31588bb4f56b638dd5afc28d3ebff9b9dcefb88d (patch)
treeb5ab431071a20481be60d6faa4c0edf3238b43f9
parentf24569a1b4af70352d454ca46eb3be45a0ba51ef (diff)
downloadcatch3-3.7.0.tar.gz
v3.7.0v3.7.0
-rw-r--r--CMakeLists.txt2
-rw-r--r--docs/release-notes.md21
-rw-r--r--docs/test-fixtures.md2
-rw-r--r--extras/catch_amalgamated.cpp477
-rw-r--r--extras/catch_amalgamated.hpp267
-rw-r--r--meson.build2
-rw-r--r--src/catch2/catch_test_case_info.hpp6
-rw-r--r--src/catch2/catch_version.cpp2
-rw-r--r--src/catch2/catch_version_macros.hpp2
9 files changed, 514 insertions, 267 deletions
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**<br>
+[3.7.0](#370)<br>
[3.6.0](#360)<br>
[3.5.4](#354)<br>
[3.5.3](#353)<br>
@@ -63,6 +64,26 @@
[Even Older versions](#even-older-versions)<br>
+## 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<do_nothing>{ {} } ){}
} // 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 <cstdio>
#include <cstring>
+#include <iosfwd>
#include <sstream>
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
- #if defined(_MSC_VER)
- #include <io.h> //_dup and _dup2
- #define dup _dup
- #define dup2 _dup2
- #define fileno _fileno
- #else
- #include <unistd.h> // dup and dup2
- #endif
+#if defined( CATCH_CONFIG_NEW_CAPTURE )
+# if defined( _MSC_VER )
+# include <io.h> //_dup and _dup2
+# define dup _dup
+# define dup2 _dup2
+# define fileno _fileno
+# else
+# include <unistd.h> // 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(); }
+ void startRedirect() {
+ m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
+ }
+ void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); }
+ };
- RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
- : m_redirectedCout(redirectedCout),
- m_redirectedCerr(redirectedCerr)
- {}
+ /**
+ * 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;
- RedirectedStreams::~RedirectedStreams() {
- m_redirectedCout += m_redirectedStdOut.str();
- m_redirectedCerr += m_redirectedStdErr.str();
- }
+ 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( "" );
+ }
+ };
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
+#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");
+ // 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;
+
+# 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." );
+ }
}
- CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
+# endif
+
+ ~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
+ }
+
+ 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<long>( read_characters );
+ }
+ return sstr.str();
+ }
+
+ 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<OutputRedirect> makeOutputRedirect( bool actual ) {
+ if ( actual ) {
+ // TODO: Clean this up later
+#if defined( CATCH_CONFIG_NEW_CAPTURE )
+ return Detail::make_unique<FileRedirect>();
#else
- TempFile::TempFile() {
- m_file = std::tmpfile();
- if (!m_file) {
- CATCH_RUNTIME_ERROR("Could not create a temp file.");
+ return Detail::make_unique<StreamRedirect>();
+#endif
+ } else {
+ return Detail::make_unique<NoopRedirect>();
}
}
-#endif
+ RedirectGuard scopedActivate( OutputRedirect& redirectImpl ) {
+ return RedirectGuard( true, redirectImpl );
+ }
- 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
+ RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ) {
+ return RedirectGuard( false, redirectImpl );
}
+ OutputRedirect::~OutputRedirect() = default;
- FILE* TempFile::getFile() {
- return m_file;
- }
+ RedirectGuard::RedirectGuard( bool activate, OutputRedirect& redirectImpl ):
+ m_redirect( &redirectImpl ),
+ m_activate( activate ),
+ m_previouslyActive( redirectImpl.isActive() ) {
- 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();
- }
+ // Skip cases where there is no actual state change.
+ if ( m_activate == m_previouslyActive ) { return; }
- 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 ( m_activate ) {
+ m_redirect->activate();
+ } else {
+ m_redirect->deactivate();
+ }
}
- 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);
+ RedirectGuard::~RedirectGuard() noexcept( false ) {
+ if ( m_moved ) { return; }
+ // Skip cases where there is no actual state change.
+ if ( m_activate == m_previouslyActive ) { return; }
- dup2(m_originalStdout, 1);
- dup2(m_originalStderr, 2);
+ if ( m_activate ) {
+ m_redirect->deactivate();
+ } else {
+ m_redirect->activate();
+ }
+ }
- m_stdoutDest += m_stdoutFile.getContents();
- m_stderrDest += m_stderrFile.getContents();
+ 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;
}
-#endif // CATCH_CONFIG_NEW_CAPTURE
+ 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<std::string>(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<std::size_t>(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 <iterator>
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<callable> 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 <typename Fun>
struct model : public callable {
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(fun_) {}
- Catch::Detail::unique_ptr<callable> clone() const override {
- return Catch::Detail::make_unique<model<Fun>>( *this );
- }
-
void call(Chronometer meter) const override {
call(meter, is_callable<Fun(Chronometer)>());
}
@@ -1613,14 +1608,8 @@ namespace Catch {
Fun fun;
};
- struct do_nothing { void operator()() const {} };
-
- template <typename T>
- BenchmarkFunction(model<T>* c) : f(c) {}
-
public:
- BenchmarkFunction()
- : f(new model<do_nothing>{ {} }) {}
+ BenchmarkFunction();
template <typename Fun,
std::enable_if_t<!is_related<Fun, BenchmarkFunction>::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 <typename Clock, typename Fun, typename... Args>
TimingOf<Fun, Args...> 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 <typename Clock>
std::vector<double> resolution(int k) {
- std::vector<TimePoint<Clock>> times;
- times.reserve(static_cast<size_t>(k + 1));
- for ( int i = 0; i < k + 1; ++i ) {
- times.push_back( Clock::now() );
+ const size_t points = static_cast<size_t>( k + 1 );
+ // To avoid overhead from the branch inside vector::push_back,
+ // we allocate them all and then overwrite.
+ std::vector<TimePoint<Clock>> times(points);
+ for ( auto& time : times ) {
+ time = Clock::now();
}
std::vector<double> deltas;
deltas.reserve(static_cast<size_t>(k));
- for ( size_t idx = 1; idx < times.size(); ++idx ) {
+ for ( size_t idx = 1; idx < points; ++idx ) {
deltas.push_back( static_cast<double>(
( times[idx] - times[idx - 1] ).count() ) );
}
@@ -2103,12 +2086,12 @@ namespace Catch {
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
template <typename Clock>
- ExecutionPlan prepare(const IConfig &cfg, Environment env) const {
+ ExecutionPlan prepare(const IConfig &cfg, Environment env) {
auto min_time = env.clock_resolution.mean * Deta