This commit is contained in:
Martin Hořeňovský
2025-09-30 10:54:31 +02:00
parent 6500dc8149
commit b3fb4b9fea
9 changed files with 243 additions and 136 deletions
+172 -96
View File
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
// Catch v3.10.0
// Generated: 2025-08-24 16:18:04.775778
// Catch v3.11.0
// Generated: 2025-09-30 10:49:12.549018
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -825,6 +825,8 @@ namespace Catch {
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
}
// Reading bazel env vars can change some parts of the config data,
// so we have to process the bazel env before acting on the config.
if ( enableBazelEnvSupport() ) {
readBazelEnvVars();
}
@@ -889,6 +891,8 @@ namespace Catch {
bool Config::showHelp() const { return m_data.showHelp; }
std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
// IConfig interface
bool Config::allowThrows() const { return !m_data.noThrow; }
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
@@ -950,6 +954,26 @@ namespace Catch {
m_data.shardCount = bazelShardOptions->shardCount;
}
}
const auto bazelExitGuardFile = Detail::getEnv( "TEST_PREMATURE_EXIT_FILE" );
if (bazelExitGuardFile) {
m_data.prematureExitGuardFilePath = bazelExitGuardFile;
}
const auto bazelRandomSeed = Detail::getEnv( "TEST_RANDOM_SEED" );
if ( bazelRandomSeed ) {
auto parsedSeed = parseUInt( bazelRandomSeed, 0 );
if ( !parsedSeed ) {
// Currently we handle issues with parsing other Bazel Env
// options by warning and ignoring the issue. So we do the
// same for random seed option.
Catch::cerr()
<< "Warning: could not parse 'TEST_RANDOM_SEED' ('"
<< bazelRandomSeed << "') as proper seed.\n";
} else {
m_data.rngSeed = *parsedSeed;
}
}
}
} // end namespace Catch
@@ -975,28 +999,26 @@ namespace Catch {
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
m_messageId( builder.m_info.sequence ) {
MessageInfo info( CATCH_MOVE( builder.m_info ) );
info.message = builder.m_stream.str();
IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
m_info( CATCH_MOVE( old.m_info ) ) {
m_messageId( old.m_messageId ) {
old.m_moved = true;
}
ScopedMessage::~ScopedMessage() {
if ( !m_moved ){
getResultCapture().popScopedMessage(m_info);
}
if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
}
Capturer::Capturer( StringRef macroName,
SourceLineInfo const& lineInfo,
ResultWas::OfType resultType,
StringRef names ):
m_resultCapture( getResultCapture() ) {
StringRef names ) {
auto trimmed = [&] (size_t start, size_t end) {
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;
@@ -1042,8 +1064,8 @@ namespace Catch {
case ',':
if (start != pos && openings.empty()) {
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, pos);
m_messages.back().message += " := "_sr;
start = pos;
}
break;
@@ -1052,20 +1074,20 @@ namespace Catch {
}
assert(openings.empty() && "Mismatched openings");
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, names.size() - 1);
m_messages.back().message += " := "_sr;
}
Capturer::~Capturer() {
assert( m_captured == m_messages.size() );
for ( size_t i = 0; i < m_captured; ++i ) {
m_resultCapture.popScopedMessage( m_messages[i] );
for (auto const& message : m_messages) {
IResultCapture::popScopedMessage( message.sequence );
}
}
void Capturer::captureValue( size_t index, std::string const& value ) {
assert( index < m_messages.size() );
m_messages[index].message += value;
m_resultCapture.pushScopedMessage( m_messages[index] );
IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
m_captured++;
}
@@ -1149,7 +1171,6 @@ namespace Catch {
}
void cleanUp() {
cleanupSingletons();
cleanUpContext();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
@@ -1161,6 +1182,8 @@ namespace Catch {
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <iomanip>
#include <set>
@@ -1275,6 +1298,50 @@ namespace Catch {
}
}
// Creates empty file at path. The path must be writable, we do not
// try to create directories in path because that's hard in C++14.
void setUpGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
#if defined( _MSC_VER )
std::FILE* file = nullptr;
if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) {
char msgBuffer[100];
const auto err = errno;
std::string errMsg;
if ( !strerror_s( msgBuffer, err ) ) {
errMsg = msgBuffer;
} else {
errMsg = "Could not translate errno to a string";
}
#else
std::FILE* file = std::fopen( guardFilePath.c_str(), "w" );
if ( !file ) {
const auto err = errno;
const char* errMsg = std::strerror( err );
#endif
CATCH_RUNTIME_ERROR( "Could not open the exit guard file '"
<< guardFilePath << "' because '"
<< errMsg << "' (" << err << ')' );
}
const int ret = std::fclose( file );
CATCH_ENFORCE(
ret == 0,
"Error when closing the exit guard file: " << ret );
}
}
// Removes file at path. Assuming we created it in setUpGuardFile.
void tearDownGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
const int ret = std::remove( guardFilePath.c_str() );
CATCH_ENFORCE(
ret == 0,
"Error when removing the exit guard file: " << ret );
}
}
} // anon namespace
Session::Session() {
@@ -1393,6 +1460,7 @@ namespace Catch {
static_cast<void>(std::getchar());
}
int exitCode = runInternal();
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
static_cast<void>(std::getchar());
@@ -1433,6 +1501,10 @@ namespace Catch {
CATCH_TRY {
config(); // Force config to be constructed
// We need to retrieve potential Bazel config with the full Config
// constructor, so we have to create the guard file after it is created.
setUpGuardFile( m_config->getExitGuardFilePath() );
seedRng( *m_config );
if (m_configData.filenamesAsTags) {
@@ -1462,9 +1534,12 @@ namespace Catch {
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
auto const totals = tests.execute();
// If we got here, running the tests finished normally-enough.
// They might've failed, but that would've been reported elsewhere.
tearDownGuardFile( m_config->getExitGuardFilePath() );
if ( tests.hadUnmatchedTestSpecs()
&& m_config->warnAboutUnmatchedTestSpecs() ) {
// UnmatchedTestSpecExitCode
return UnmatchedTestSpecExitCode;
}
@@ -1974,35 +2049,35 @@ namespace Detail {
std::string ret;
// This is enough for the "don't escape invisibles" case, and a good
// lower bound on the "escape invisibles" case.
ret.reserve(string.size() + 2);
ret.reserve( string.size() + 2 );
if (!escapeInvisibles) {
if ( !escapeInvisibles ) {
ret += '"';
ret += string;
ret += '"';
return ret;
}
size_t last_start = 0;
auto write_to = [&]( size_t idx ) {
if ( last_start < idx ) {
ret += string.substr( last_start, idx - last_start );
}
last_start = idx + 1;
};
ret += '"';
for (char c : string) {
switch (c) {
case '\r':
ret.append("\\r");
break;
case '\n':
ret.append("\\n");
break;
case '\t':
ret.append("\\t");
break;
case '\f':
ret.append("\\f");
break;
default:
ret.push_back(c);
break;
for ( size_t i = 0; i < string.size(); ++i ) {
const char c = string[i];
if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
write_to( i );
if ( c == '\r' ) { ret.append( "\\r" ); }
if ( c == '\n' ) { ret.append( "\\n" ); }
if ( c == '\t' ) { ret.append( "\\t" ); }
if ( c == '\f' ) { ret.append( "\\f" ); }
}
}
write_to( string.size() );
ret += '"';
return ret;
@@ -2279,7 +2354,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 10, 0, "", 0 );
static Version version( 3, 11, 0, "", 0 );
return version;
}
@@ -2367,8 +2442,14 @@ namespace Catch {
namespace Catch {
namespace Detail {
void missingCaptureInstance() {
CATCH_INTERNAL_ERROR( "No result capture instance" );
}
} // namespace Detail
IResultCapture::~IResultCapture() = default;
}
} // namespace Catch
@@ -3361,6 +3442,9 @@ namespace Catch {
| Opt( config.allowZeroTests )
["--allow-running-no-tests"]
( "Treat 'No tests run' as a success" )
| Opt( config.prematureExitGuardFilePath, "path" )
["--premature-exit-guard-file"]
( "create a file before running tests and delete it during clean exit" )
| Arg( config.testsOrTags, "test name|pattern|tags" )
( "which test or tests to use" );
@@ -3515,7 +3599,11 @@ namespace {
#endif // Windows/ ANSI/ None
#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || defined( __GLIBC__ ) || defined(__FreeBSD__)
#if defined( CATCH_PLATFORM_LINUX ) \
|| defined( CATCH_PLATFORM_MAC ) \
|| defined( __GLIBC__ ) \
|| defined( __FreeBSD__ ) \
|| defined( CATCH_PLATFORM_QNX )
# define CATCH_INTERNAL_HAS_ISATTY
# include <unistd.h>
#endif
@@ -3639,20 +3727,10 @@ namespace Catch {
namespace Catch {
Context* Context::currentContext = nullptr;
void cleanUpContext() {
delete Context::currentContext;
Context::currentContext = nullptr;
}
void Context::createContext() {
currentContext = new Context();
}
Context Context::currentContext;
Context& getCurrentMutableContext() {
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
return Context::currentContext;
}
SimplePcg32& sharedRng() {
@@ -3757,7 +3835,7 @@ namespace Catch {
#endif
} // namespace Catch
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
#include <fstream>
#include <string>
@@ -4091,23 +4169,27 @@ namespace Catch {
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
};
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name);
}
}
// If its not an exception we care about, pass it along.
// If a filter was previously registered, invoke it
if (previousTopLevelExceptionFilter) {
return previousTopLevelExceptionFilter(ExceptionInfo);
}
// Otherwise, pass along all exceptions.
// This stops us from eating debugger breaks etc.
return EXCEPTION_CONTINUE_SEARCH;
}
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
// For MSVC, we reserve part of the stack memory for handling
// memory overflow structured exception.
FatalConditionHandler::FatalConditionHandler() {
@@ -5565,6 +5647,7 @@ ReporterSpec::ReporterSpec(
#include <cstdio>
#include <sstream>
#include <tuple>
#include <vector>
namespace Catch {
@@ -5576,16 +5659,16 @@ namespace Catch {
std::ostringstream m_referenceStream; // Used for copy state/ flags from
Detail::Mutex m_mutex;
auto add() -> std::size_t {
auto add() -> std::pair<std::size_t, std::ostringstream*> {
Detail::LockGuard _( m_mutex );
if( m_unused.empty() ) {
m_streams.push_back( Detail::make_unique<std::ostringstream>() );
return m_streams.size()-1;
return { m_streams.size()-1, m_streams.back().get() };
}
else {
auto index = m_unused.back();
m_unused.pop_back();
return index;
return { index, m_streams[index].get() };
}
}
@@ -5599,10 +5682,10 @@ namespace Catch {
}
};
ReusableStringStream::ReusableStringStream()
: m_index( Singleton<StringStreams>::getMutable().add() ),
m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
{}
ReusableStringStream::ReusableStringStream() {
std::tie( m_index, m_oss ) =
Singleton<StringStreams>::getMutable().add();
}
ReusableStringStream::~ReusableStringStream() {
static_cast<std::ostringstream*>( m_oss )->str("");
@@ -6085,28 +6168,6 @@ namespace Catch {
m_reporter->benchmarkFailed( error );
}
void RunContext::pushScopedMessage( MessageInfo const& message ) {
Detail::g_messages.push_back( message );
}
void RunContext::popScopedMessage( MessageInfo const& message ) {
// Note: On average, it would probably be better to look for the message
// backwards. However, we do not expect to have to deal with more
// messages than low single digits, so the optimization is tiny,
// and we would have to hand-write the loop to avoid terrible
// codegen of reverse iterators in debug mode.
Detail::g_messages.erase(
std::find_if( Detail::g_messages.begin(),
Detail::g_messages.end(),
[id = message.sequence]( MessageInfo const& msg ) {
return msg.sequence == id;
} ) );
}
void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageScopes.emplace_back( CATCH_MOVE(builder) );
}
std::string RunContext::getCurrentTestName() const {
return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name
@@ -6433,11 +6494,26 @@ namespace Catch {
}
}
IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture())
return *capture;
else
CATCH_INTERNAL_ERROR("No result capture instance");
void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
Detail::g_messages.push_back( CATCH_MOVE( message ) );
}
void IResultCapture::popScopedMessage( unsigned int messageId ) {
// Note: On average, it would probably be better to look for the message
// backwards. However, we do not expect to have to deal with more
// messages than low single digits, so the optimization is tiny,
// and we would have to hand-write the loop to avoid terrible
// codegen of reverse iterators in debug mode.
Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(),
Detail::g_messages.end(),
[=]( MessageInfo const& msg ) {
return msg.sequence ==
messageId;
} ) );
}
void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) );
}
void seedRng(IConfig const& config) {
+42 -34
View File
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
// Catch v3.10.0
// Generated: 2025-08-24 16:18:04.055916
// Catch v3.11.0
// Generated: 2025-09-30 10:49:11.225746
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -101,6 +101,9 @@
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX
#elif defined(__QNX__)
# define CATCH_PLATFORM_QNX
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
# define CATCH_PLATFORM_WINDOWS
@@ -308,13 +311,17 @@
# endif
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
# if defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# else
# if !defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
# endif
// Only some Windows platform families support the console
# if defined(WINAPI_FAMILY_PARTITION)
# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# endif
# endif
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
@@ -564,11 +571,9 @@ namespace Catch {
IConfig const* m_config = nullptr;
IResultCapture* m_resultCapture = nullptr;
CATCH_EXPORT static Context* currentContext;
CATCH_EXPORT static Context currentContext;
friend Context& getCurrentMutableContext();
friend Context const& getCurrentContext();
static void createContext();
friend void cleanUpContext();
public:
constexpr IResultCapture* getResultCapture() const {
@@ -579,21 +584,14 @@ namespace Catch {
m_resultCapture = resultCapture;
}
constexpr void setConfig( IConfig const* config ) { m_config = config; }
};
Context& getCurrentMutableContext();
inline Context const& getCurrentContext() {
// We duplicate the logic from `getCurrentMutableContext` here,
// to avoid paying the call overhead in debug mode.
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
return Context::currentContext;
}
void cleanUpContext();
class SimplePcg32;
SimplePcg32& sharedRng();
}
@@ -1069,10 +1067,9 @@ namespace Catch {
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
virtual void benchmarkFailed( StringRef error ) = 0;
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
static void pushScopedMessage( MessageInfo&& message );
static void popScopedMessage( unsigned int messageId );
static void emplaceUnscopedMessage( MessageBuilder&& builder );
virtual void handleFatalErrorCondition( StringRef message ) = 0;
@@ -1108,7 +1105,18 @@ namespace Catch {
virtual void exceptionEarlyReported() = 0;
};
IResultCapture& getResultCapture();
namespace Detail {
[[noreturn]]
void missingCaptureInstance();
}
inline IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture()) {
return *capture;
} else {
Detail::missingCaptureInstance();
}
}
}
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
@@ -3800,6 +3808,8 @@ namespace Catch {
std::vector<std::string> testsOrTags;
std::vector<std::string> sectionsToRun;
std::string prematureExitGuardFilePath;
};
@@ -3827,6 +3837,8 @@ namespace Catch {
bool showHelp() const;
std::string const& getExitGuardFilePath() const;
// IConfig interface
bool allowThrows() const override;
StringRef name() const override;
@@ -3961,6 +3973,7 @@ namespace Catch {
std::string message;
SourceLineInfo lineInfo;
ResultWas::OfType type;
// The "ID" of the message, used to know when to remove it from reporter context.
unsigned int sequence;
DEPRECATED( "Explicitly use the 'sequence' member instead" )
@@ -4020,13 +4033,12 @@ namespace Catch {
ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage();
MessageInfo m_info;
unsigned int m_messageId;
bool m_moved = false;
};
class Capturer {
std::vector<MessageInfo> m_messages;
IResultCapture& m_resultCapture;
size_t m_captured = 0;
public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
@@ -4074,7 +4086,7 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
@@ -7466,7 +7478,7 @@ namespace Catch {
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 10
#define CATCH_VERSION_MINOR 11
#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -7882,8 +7894,9 @@ namespace Generators {
class FilterGenerator final : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
Predicate m_predicate;
static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
public:
template <typename P = Predicate>
template <typename P>
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
m_generator(CATCH_MOVE(generator)),
m_predicate(CATCH_FORWARD(pred))
@@ -7915,7 +7928,7 @@ namespace Generators {
template <typename T, typename Predicate>
GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, typename std::remove_reference<Predicate>::type>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
}
template <typename T>
@@ -9555,7 +9568,7 @@ namespace Catch {
#define CATCH_TRAP() __asm__(".inst 0xde01")
#endif
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
// If we can use inline assembler, do it because this allows us to break
// directly at the location of the failing check instead of breaking inside
// raise() called from it, i.e. one stack frame below.
@@ -10718,11 +10731,6 @@ namespace Catch {
void benchmarkEnded( BenchmarkStats<> const& stats ) override;
void benchmarkFailed( StringRef error ) override;
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
void emplaceUnscopedMessage( MessageBuilder&& builder ) override;
std::string getCurrentTestName() const override;
const AssertionResult* getLastResult() const override;