This commit is contained in:
Martin Hořeňovský 2024-08-14 12:05:21 +02:00
parent f24569a1b4
commit 31588bb4f5
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
9 changed files with 531 additions and 284 deletions

View File

@ -33,7 +33,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif() endif()
project(Catch2 project(Catch2
VERSION 3.6.0 # CML version placeholder, don't delete VERSION 3.7.0 # CML version placeholder, don't delete
LANGUAGES CXX LANGUAGES CXX
# HOMEPAGE_URL is not supported until CMake version 3.12, which # HOMEPAGE_URL is not supported until CMake version 3.12, which
# we do not target yet. # we do not target yet.

View File

@ -2,6 +2,7 @@
# Release notes # Release notes
**Contents**<br> **Contents**<br>
[3.7.0](#370)<br>
[3.6.0](#360)<br> [3.6.0](#360)<br>
[3.5.4](#354)<br> [3.5.4](#354)<br>
[3.5.3](#353)<br> [3.5.3](#353)<br>
@ -63,6 +64,26 @@
[Even Older versions](#even-older-versions)<br> [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 ## 3.6.0
### Fixes ### Fixes

View File

@ -88,7 +88,7 @@ class.
### 3. `TEST_CASE_PERSISTENT_FIXTURE` ### 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_PERSISTENT_FIXTURE` behaves in the same way as
[TEST_CASE_METHOD](#1-test_case_method) except that there will only be [TEST_CASE_METHOD](#1-test_case_method) except that there will only be

View File

@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
// Catch v3.6.0 // Catch v3.7.0
// Generated: 2024-05-05 20:53:27.562886 // Generated: 2024-08-14 12:04:53.604337
// ---------------------------------------------------------- // ----------------------------------------------------------
// This file is an amalgamation of multiple different files. // This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly. // You probably shouldn't edit it directly.
@ -128,7 +128,13 @@ namespace Catch {
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
namespace Detail { namespace Detail {
struct do_nothing {
void operator()() const {}
};
BenchmarkFunction::callable::~callable() = default; BenchmarkFunction::callable::~callable() = default;
BenchmarkFunction::BenchmarkFunction():
f( new model<do_nothing>{ {} } ){}
} // namespace Detail } // namespace Detail
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch
@ -1040,6 +1046,7 @@ namespace Catch {
m_messages.back().message += " := "; m_messages.back().message += " := ";
start = pos; start = pos;
} }
break;
default:; // noop default:; // noop
} }
} }
@ -2273,7 +2280,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 3, 6, 0, "", 0 ); static Version version( 3, 7, 0, "", 0 );
return version; return version;
} }
@ -4808,6 +4815,7 @@ namespace Catch {
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <iosfwd>
#include <sstream> #include <sstream>
#if defined( CATCH_CONFIG_NEW_CAPTURE ) #if defined( CATCH_CONFIG_NEW_CAPTURE )
@ -4821,66 +4829,115 @@ namespace Catch {
# endif # endif
#endif #endif
namespace Catch { namespace Catch {
RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) namespace {
: m_originalStream( originalStream ), //! 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 {}
};
/**
* 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;
public:
RedirectedStreamNew( std::ostream& originalStream,
std::ostream& redirectionStream ):
m_originalStream( originalStream ),
m_redirectionStream( redirectionStream ), m_redirectionStream( redirectionStream ),
m_prevBuf( m_originalStream.rdbuf() ) m_prevBuf( m_originalStream.rdbuf() ) {}
{
void startRedirect() {
m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
} }
void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); }
};
RedirectedStream::~RedirectedStream() { /**
m_originalStream.rdbuf( m_prevBuf ); * 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;
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 {
RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} m_cout.stopRedirect();
auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } m_cerr.stopRedirect();
m_clog.stopRedirect();
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();
} }
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 )
// 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 ) # if defined( _MSC_VER )
TempFile::TempFile() { TempFile() {
if ( tmpnam_s( m_buffer ) ) { if ( tmpnam_s( m_buffer ) ) {
CATCH_RUNTIME_ERROR( "Could not get a temp filename" ); CATCH_RUNTIME_ERROR( "Could not get a temp filename" );
} }
if (fopen_s(&m_file, m_buffer, "w+")) { if ( fopen_s( &m_file, m_buffer, "wb+" ) ) {
char buffer[100]; char buffer[100];
if ( strerror_s( buffer, errno ) ) { if ( strerror_s( buffer, errno ) ) {
CATCH_RUNTIME_ERROR("Could not translate errno to a string"); CATCH_RUNTIME_ERROR(
"Could not translate errno to a string" );
} }
CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); CATCH_RUNTIME_ERROR( "Could not open the temp file: '"
<< m_buffer
<< "' because: " << buffer );
} }
} }
# else # else
TempFile::TempFile() { TempFile() {
m_file = std::tmpfile(); m_file = std::tmpfile();
if ( !m_file ) { if ( !m_file ) {
CATCH_RUNTIME_ERROR( "Could not create a temp file." ); CATCH_RUNTIME_ERROR( "Could not create a temp file." );
} }
} }
# endif # endif
TempFile::~TempFile() { ~TempFile() {
// TBD: What to do about errors here? // TBD: What to do about errors here?
std::fclose( m_file ); std::fclose( m_file );
// We manually create the file on Windows only, on Linux // We manually create the file on Windows only, on Linux
@ -4890,31 +4947,48 @@ namespace Catch {
# endif # endif
} }
std::FILE* getFile() { return m_file; }
FILE* TempFile::getFile() { std::string getContents() {
return m_file; ReusableStringStream sstr;
} constexpr long buffer_size = 100;
char buffer[buffer_size + 1] = {};
std::string TempFile::getContents() { long current_pos = ftell( m_file );
std::stringstream sstr; CATCH_ENFORCE( current_pos >= 0,
char buffer[100] = {}; "ftell failed, errno: " << errno );
std::rewind( m_file ); std::rewind( m_file );
while (std::fgets(buffer, sizeof(buffer), 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; sstr << buffer;
current_pos -= static_cast<long>( read_characters );
} }
return sstr.str(); return sstr.str();
} }
OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : void clear() { std::rewind( m_file ); }
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);
}
OutputRedirect::~OutputRedirect() { 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; Catch::cout() << std::flush;
fflush( stdout ); fflush( stdout );
// Since we support overriding these streams, we flush cerr // Since we support overriding these streams, we flush cerr
@ -4922,16 +4996,139 @@ namespace Catch {
Catch::cerr() << std::flush; Catch::cerr() << std::flush;
Catch::clog() << std::flush; Catch::clog() << std::flush;
fflush( stderr ); fflush( stderr );
dup2(m_originalStdout, 1);
dup2(m_originalStderr, 2);
m_stdoutDest += m_stdoutFile.getContents();
m_stderrDest += m_stderrFile.getContents();
} }
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 #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
return Detail::make_unique<StreamRedirect>();
#endif
} else {
return Detail::make_unique<NoopRedirect>();
}
}
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 } // namespace Catch
#if defined( CATCH_CONFIG_NEW_CAPTURE ) #if defined( CATCH_CONFIG_NEW_CAPTURE )
@ -5573,6 +5770,7 @@ namespace Catch {
m_config(_config), m_config(_config),
m_reporter(CATCH_MOVE(reporter)), m_reporter(CATCH_MOVE(reporter)),
m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
m_outputRedirect( makeOutputRedirect( m_reporter->getPreferences().shouldRedirectStdOut ) ),
m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
{ {
getCurrentMutableContext().setResultCapture( this ); getCurrentMutableContext().setResultCapture( this );
@ -5588,6 +5786,7 @@ namespace Catch {
auto const& testInfo = testCase.getTestCaseInfo(); auto const& testInfo = testCase.getTestCaseInfo();
m_reporter->testCaseStarting(testInfo); m_reporter->testCaseStarting(testInfo);
testCase.prepareTestCase();
m_activeTestCase = &testCase; m_activeTestCase = &testCase;
@ -5638,15 +5837,17 @@ namespace Catch {
m_reporter->testCasePartialStarting(testInfo, testRuns); m_reporter->testCasePartialStarting(testInfo, testRuns);
const auto beforeRunTotals = m_totals; const auto beforeRunTotals = m_totals;
std::string oneRunCout, oneRunCerr; runCurrentTest();
runCurrentTest(oneRunCout, oneRunCerr); std::string oneRunCout = m_outputRedirect->getStdout();
std::string oneRunCerr = m_outputRedirect->getStderr();
m_outputRedirect->clearBuffers();
redirectedCout += oneRunCout; redirectedCout += oneRunCout;
redirectedCerr += oneRunCerr; redirectedCerr += oneRunCerr;
const auto singleRunTotals = m_totals.delta(beforeRunTotals); const auto singleRunTotals = m_totals.delta(beforeRunTotals);
auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
m_reporter->testCasePartialEnded(statsForOneRun, testRuns); m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
++testRuns; ++testRuns;
} while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
@ -5657,6 +5858,7 @@ namespace Catch {
deltaTotals.testCases.failed++; deltaTotals.testCases.failed++;
} }
m_totals.testCases += deltaTotals.testCases; m_totals.testCases += deltaTotals.testCases;
testCase.tearDownTestCase();
m_reporter->testCaseEnded(TestCaseStats(testInfo, m_reporter->testCaseEnded(TestCaseStats(testInfo,
deltaTotals, deltaTotals,
CATCH_MOVE(redirectedCout), CATCH_MOVE(redirectedCout),
@ -5690,7 +5892,10 @@ namespace Catch {
m_lastAssertionPassed = true; m_lastAssertionPassed = true;
} }
{
auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ); m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) );
}
if ( result.getResultType() != ResultWas::Warning ) { if ( result.getResultType() != ResultWas::Warning ) {
m_messageScopes.clear(); m_messageScopes.clear();
@ -5707,6 +5912,7 @@ namespace Catch {
} }
void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->assertionStarting( info ); m_reporter->assertionStarting( info );
} }
@ -5725,7 +5931,10 @@ namespace Catch {
SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) ); SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) );
m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
{
auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->sectionStarting( sectionInfo ); m_reporter->sectionStarting( sectionInfo );
}
assertions = m_totals.assertions; assertions = m_totals.assertions;
@ -5785,7 +5994,15 @@ namespace Catch {
m_activeSections.pop_back(); 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_messages.clear();
m_messageScopes.clear(); m_messageScopes.clear();
} }
@ -5802,15 +6019,19 @@ namespace Catch {
} }
void RunContext::benchmarkPreparing( StringRef name ) { void RunContext::benchmarkPreparing( StringRef name ) {
auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->benchmarkPreparing( name ); m_reporter->benchmarkPreparing( name );
} }
void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->benchmarkStarting( info ); m_reporter->benchmarkStarting( info );
} }
void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->benchmarkEnded( stats ); m_reporter->benchmarkEnded( stats );
} }
void RunContext::benchmarkFailed( StringRef error ) { void RunContext::benchmarkFailed( StringRef error ) {
auto _ = scopedDeactivate( *m_outputRedirect );
m_reporter->benchmarkFailed( error ); m_reporter->benchmarkFailed( error );
} }
@ -5841,6 +6062,11 @@ namespace Catch {
} }
void RunContext::handleFatalErrorCondition( StringRef message ) { 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 // First notify reporter that bad things happened
m_reporter->fatalErrorEncountered( message ); m_reporter->fatalErrorEncountered( message );
@ -5900,7 +6126,7 @@ namespace Catch {
return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter()); 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(); auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
m_reporter->sectionStarting(testCaseSection); m_reporter->sectionStarting(testCaseSection);
@ -5911,18 +6137,8 @@ namespace Catch {
Timer timer; Timer timer;
CATCH_TRY { CATCH_TRY {
if (m_reporter->getPreferences().shouldRedirectStdOut) { {
#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) auto _ = scopedActivate( *m_outputRedirect );
RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
timer.start();
invokeActiveTestCase();
#else
OutputRedirect r(redirectedCout, redirectedCerr);
timer.start();
invokeActiveTestCase();
#endif
} else {
timer.start(); timer.start();
invokeActiveTestCase(); invokeActiveTestCase();
} }
@ -5970,8 +6186,9 @@ namespace Catch {
for ( auto it = m_unfinishedSections.rbegin(), for ( auto it = m_unfinishedSections.rbegin(),
itEnd = m_unfinishedSections.rend(); itEnd = m_unfinishedSections.rend();
it != itEnd; it != itEnd;
++it) ++it ) {
sectionEnded( CATCH_MOVE( *it ) ); sectionEnded( CATCH_MOVE( *it ) );
}
m_unfinishedSections.clear(); m_unfinishedSections.clear();
} }
@ -6898,6 +7115,8 @@ namespace Catch {
#include <iterator> #include <iterator>
namespace Catch { namespace Catch {
void ITestInvoker::prepareTestCase() {}
void ITestInvoker::tearDownTestCase() {}
ITestInvoker::~ITestInvoker() = default; ITestInvoker::~ITestInvoker() = default;
namespace { namespace {
@ -10333,7 +10552,7 @@ namespace Catch {
xml( m_stream ) xml( m_stream )
{ {
m_preferences.shouldRedirectStdOut = true; m_preferences.shouldRedirectStdOut = true;
m_preferences.shouldReportAllAssertions = true; m_preferences.shouldReportAllAssertions = false;
m_shouldStoreSuccesfulAssertions = false; m_shouldStoreSuccesfulAssertions = false;
} }
@ -10443,7 +10662,7 @@ namespace Catch {
if( !rootName.empty() ) if( !rootName.empty() )
name = rootName + '/' + name; name = rootName + '/' + name;
if( sectionNode.hasAnyAssertions() if ( sectionNode.stats.assertions.total() > 0
|| !sectionNode.stdOut.empty() || !sectionNode.stdOut.empty()
|| !sectionNode.stdErr.empty() ) { || !sectionNode.stdErr.empty() ) {
XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );

View File

@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
// Catch v3.6.0 // Catch v3.7.0
// Generated: 2024-05-05 20:53:27.071502 // Generated: 2024-08-14 12:04:53.220567
// ---------------------------------------------------------- // ----------------------------------------------------------
// This file is an amalgamation of multiple different files. // This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly. // You probably shouldn't edit it directly.
@ -1584,22 +1584,17 @@ namespace Catch {
private: private:
struct callable { struct callable {
virtual void call(Chronometer meter) const = 0; virtual void call(Chronometer meter) const = 0;
virtual Catch::Detail::unique_ptr<callable> clone() const = 0;
virtual ~callable(); // = default; virtual ~callable(); // = default;
callable() = default; callable() = default;
callable(callable const&) = default; callable(callable&&) = default;
callable& operator=(callable const&) = default; callable& operator=(callable&&) = default;
}; };
template <typename Fun> template <typename Fun>
struct model : public callable { struct model : public callable {
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(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 { void call(Chronometer meter) const override {
call(meter, is_callable<Fun(Chronometer)>()); call(meter, is_callable<Fun(Chronometer)>());
} }
@ -1613,14 +1608,8 @@ namespace Catch {
Fun fun; Fun fun;
}; };
struct do_nothing { void operator()() const {} };
template <typename T>
BenchmarkFunction(model<T>* c) : f(c) {}
public: public:
BenchmarkFunction() BenchmarkFunction();
: f(new model<do_nothing>{ {} }) {}
template <typename Fun, template <typename Fun,
std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0> std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0>
@ -1630,20 +1619,12 @@ namespace Catch {
BenchmarkFunction( BenchmarkFunction&& that ) noexcept: BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
f( CATCH_MOVE( that.f ) ) {} f( CATCH_MOVE( that.f ) ) {}
BenchmarkFunction(BenchmarkFunction const& that)
: f(that.f->clone()) {}
BenchmarkFunction& BenchmarkFunction&
operator=( BenchmarkFunction&& that ) noexcept { operator=( BenchmarkFunction&& that ) noexcept {
f = CATCH_MOVE( that.f ); f = CATCH_MOVE( that.f );
return *this; return *this;
} }
BenchmarkFunction& operator=(BenchmarkFunction const& that) {
f = that.f->clone();
return *this;
}
void operator()(Chronometer meter) const { f->call(meter); } void operator()(Chronometer meter) const { f->call(meter); }
private: private:
@ -1780,7 +1761,7 @@ namespace Catch {
template <typename Clock, typename Fun, typename... Args> template <typename Clock, typename Fun, typename... Args>
TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) { TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) {
auto start = Clock::now(); 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 end = Clock::now();
auto delta = end - start; auto delta = end - start;
return { delta, CATCH_FORWARD(r), 1 }; return { delta, CATCH_FORWARD(r), 1 };
@ -1946,15 +1927,17 @@ namespace Catch {
namespace Detail { namespace Detail {
template <typename Clock> template <typename Clock>
std::vector<double> resolution(int k) { std::vector<double> resolution(int k) {
std::vector<TimePoint<Clock>> times; const size_t points = static_cast<size_t>( k + 1 );
times.reserve(static_cast<size_t>(k + 1)); // To avoid overhead from the branch inside vector::push_back,
for ( int i = 0; i < k + 1; ++i ) { // we allocate them all and then overwrite.
times.push_back( Clock::now() ); std::vector<TimePoint<Clock>> times(points);
for ( auto& time : times ) {
time = Clock::now();
} }
std::vector<double> deltas; std::vector<double> deltas;
deltas.reserve(static_cast<size_t>(k)); 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>( deltas.push_back( static_cast<double>(
( times[idx] - times[idx - 1] ).count() ) ); ( times[idx] - times[idx - 1] ).count() ) );
} }
@ -2103,12 +2086,12 @@ namespace Catch {
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
template <typename Clock> 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 * Detail::minimum_ticks; auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime())); auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun); auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed)); int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
} }
template <typename Clock = default_clock> template <typename Clock = default_clock>
@ -3347,6 +3330,18 @@ namespace Catch {
#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED #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 #ifndef CATCH_CONFIG_HPP_INCLUDED
#define CATCH_CONFIG_HPP_INCLUDED #define CATCH_CONFIG_HPP_INCLUDED
@ -3366,18 +3361,6 @@ namespace Catch {
#define CATCH_WILDCARD_PATTERN_HPP_INCLUDED #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 <string> #include <string>
namespace Catch namespace Catch
@ -5953,6 +5936,8 @@ namespace Catch {
class ITestInvoker { class ITestInvoker {
public: public:
virtual void prepareTestCase();
virtual void tearDownTestCase();
virtual void invoke() const = 0; virtual void invoke() const = 0;
virtual ~ITestInvoker(); // = default virtual ~ITestInvoker(); // = default
}; };
@ -6005,6 +5990,33 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod ); return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod );
} }
template <typename C>
class TestInvokerFixture : public ITestInvoker {
void ( C::*m_testAsMethod )() const;
Detail::unique_ptr<C> m_fixture = nullptr;
public:
TestInvokerFixture( void ( C::*testAsMethod )() const) noexcept : m_testAsMethod( testAsMethod ) {}
void prepareTestCase() override {
m_fixture = Detail::make_unique<C>();
}
void tearDownTestCase() override {
m_fixture.reset();
}
void invoke() const override {
auto* f = m_fixture.get();
( f->*m_testAsMethod )();
}
};
template<typename C>
Detail::unique_ptr<ITestInvoker> makeTestInvokerFixture( void ( C::*testAsMethod )() const ) {
return Detail::make_unique<TestInvokerFixture<C>>( testAsMethod );
}
struct NameAndTags { struct NameAndTags {
constexpr NameAndTags( StringRef name_ = StringRef(), constexpr NameAndTags( StringRef name_ = StringRef(),
StringRef tags_ = StringRef() ) noexcept: StringRef tags_ = StringRef() ) noexcept:
@ -6101,6 +6113,26 @@ static int catchInternalSectionHint = 0;
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) 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, ... ) \ #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( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __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_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_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_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( ... ) 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_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_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_REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define CATCH_SECTION( ... ) #define CATCH_SECTION( ... )
#define CATCH_DYNAMIC_SECTION( ... ) #define CATCH_DYNAMIC_SECTION( ... )
@ -6257,6 +6291,7 @@ static int catchInternalSectionHint = 0;
#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __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 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 REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_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( ... ) 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 TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
#define METHOD_AS_TEST_CASE( method, ... ) #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 REGISTER_TEST_CASE( Function, ... ) (void)(0)
#define SECTION( ... ) #define SECTION( ... )
#define DYNAMIC_SECTION( ... ) #define DYNAMIC_SECTION( ... )
@ -7103,6 +7139,14 @@ namespace Catch {
TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) :
m_info(info), m_invoker(invoker) {} m_info(info), m_invoker(invoker) {}
void prepareTestCase() const {
m_invoker->prepareTestCase();
}
void tearDownTestCase() const {
m_invoker->tearDownTestCase();
}
void invoke() const { void invoke() const {
m_invoker->invoke(); m_invoker->invoke();
} }
@ -7271,7 +7315,7 @@ namespace Catch {
#define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 6 #define CATCH_VERSION_MINOR 7
#define CATCH_VERSION_PATCH 0 #define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED #endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@ -10033,106 +10077,67 @@ namespace Catch {
#define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED #define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED
#include <cstdio> #include <cassert>
#include <iosfwd>
#include <string> #include <string>
namespace Catch { 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 { class OutputRedirect {
bool m_redirectActive = false;
virtual void activateImpl() = 0;
virtual void deactivateImpl() = 0;
public: public:
OutputRedirect(OutputRedirect const&) = delete; enum Kind {
OutputRedirect& operator=(OutputRedirect const&) = delete; //! No redirect (noop implementation)
OutputRedirect(OutputRedirect&&) = delete; None,
OutputRedirect& operator=(OutputRedirect&&) = delete; //! Redirect std::cout/std::cerr/std::clog streams internally
Streams,
//! Redirect the stdout/stderr file descriptors into files
OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); FileDescriptors,
~OutputRedirect();
private:
int m_originalStdout = -1;
int m_originalStderr = -1;
TempFile m_stdoutFile;
TempFile m_stderrFile;
std::string& m_stdoutDest;
std::string& m_stderrDest;
}; };
#endif virtual ~OutputRedirect(); // = default;
// 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;
}
};
bool isRedirectAvailable( OutputRedirect::Kind kind);
Detail::unique_ptr<OutputRedirect> 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 } // end namespace Catch
@ -10455,6 +10460,7 @@ namespace Catch {
class IConfig; class IConfig;
class IEventListener; class IEventListener;
using IEventListenerPtr = Detail::unique_ptr<IEventListener>; using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
class OutputRedirect;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -10541,7 +10547,7 @@ namespace Catch {
private: private:
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); void runCurrentTest();
void invokeActiveTestCase(); void invokeActiveTestCase();
void resetAssertionInfo(); void resetAssertionInfo();
@ -10574,6 +10580,7 @@ namespace Catch {
std::vector<SectionEndInfo> m_unfinishedSections; std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections; std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext; TrackerContext m_trackerContext;
Detail::unique_ptr<OutputRedirect> m_outputRedirect;
FatalConditionHandler m_fatalConditionhandler; FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false; bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true; bool m_shouldReportUnexpected = true;

View File

@ -8,7 +8,7 @@
project( project(
'catch2', 'catch2',
'cpp', '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', license: 'BSL-1.0',
meson_version: '>=0.54.1', meson_version: '>=0.54.1',
) )

View File

@ -36,7 +36,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 3, 6, 0, "", 0 ); static Version version( 3, 7, 0, "", 0 );
return version; return version;
} }

View File

@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 6 #define CATCH_VERSION_MINOR 7
#define CATCH_VERSION_PATCH 0 #define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED #endif // CATCH_VERSION_MACROS_HPP_INCLUDED