mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-24 14:26:10 +01:00
v3.7.0
This commit is contained in:
parent
f24569a1b4
commit
31588bb4f5
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(); }
|
||||
|
||||
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<long>( 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<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
|
||||
|
||||
#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" );
|
||||
|
@ -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 * Detail::minimum_ticks;
|
||||
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);
|
||||
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>
|
||||
@ -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 <string>
|
||||
|
||||
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<ITestInvoker> makeTestInvoker( void (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 {
|
||||
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 <cstdio>
|
||||
#include <iosfwd>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
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<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
|
||||
|
||||
@ -10455,6 +10460,7 @@ namespace Catch {
|
||||
class IConfig;
|
||||
class IEventListener;
|
||||
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
|
||||
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<SectionEndInfo> m_unfinishedSections;
|
||||
std::vector<ITracker*> m_activeSections;
|
||||
TrackerContext m_trackerContext;
|
||||
Detail::unique_ptr<OutputRedirect> m_outputRedirect;
|
||||
FatalConditionHandler m_fatalConditionhandler;
|
||||
bool m_lastAssertionPassed = false;
|
||||
bool m_shouldReportUnexpected = true;
|
||||
|
@ -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',
|
||||
)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user